diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 257e13c..fbbb45b 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -76,9 +76,9 @@ case '/login': return Login case '/register': - return Register - case '/register-passkey': return RegisterPasskey + case '/register-password': + return Register case '/verify': return Verify case '/reset-password': diff --git a/frontend/src/components/AccountTypeSwitcher.svelte b/frontend/src/components/AccountTypeSwitcher.svelte new file mode 100644 index 0000000..9a099f5 --- /dev/null +++ b/frontend/src/components/AccountTypeSwitcher.svelte @@ -0,0 +1,56 @@ + + +
+ + {$_('register.passkeyAccount')} + + + {$_('register.passwordAccount')} + +
+ + diff --git a/frontend/src/lib/types/routes.ts b/frontend/src/lib/types/routes.ts index e69743a..38cabcf 100644 --- a/frontend/src/lib/types/routes.ts +++ b/frontend/src/lib/types/routes.ts @@ -1,7 +1,7 @@ export const routes = { login: "/login", register: "/register", - registerPasskey: "/register-passkey", + registerPassword: "/register-password", dashboard: "/dashboard", settings: "/settings", security: "/security", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 0812de4..1446702 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -168,8 +168,8 @@ "createButton": "Create Account", "alreadyHaveAccount": "Already have an account?", "signIn": "Sign in", - "wantPasswordless": "Want passwordless security?", - "createPasskeyAccount": "Create a passkey account", + "passkeyAccount": "Passkey", + "passwordAccount": "Password", "validation": { "handleRequired": "Handle is required", "handleNoDots": "Handle cannot contain dots. You can set up a custom domain handle after creating your account.", @@ -765,6 +765,7 @@ "verified": "Verified!", "channelVerified": "Your {channel} has been verified successfully.", "canNowSignIn": "You can now sign in to your account.", + "migrationContinue": "You can close this tab and continue your migration in the original window.", "continue": "Continue", "identifierLabel": "Email or Identifier", "identifierPlaceholder": "you@example.com", @@ -904,6 +905,12 @@ "whyPasskeyBullet1": "Cannot be phished or stolen in data breaches", "whyPasskeyBullet2": "Use hardware-backed cryptographic keys", "whyPasskeyBullet3": "Require your biometric or device PIN to use", + "infoWhyPasskey": "Why use a passkey?", + "infoWhyPasskeyDesc": "Passkeys are cryptographic credentials stored on your device. They cannot be phished, guessed, or stolen in data breaches like passwords can.", + "infoHowItWorks": "How it works", + "infoHowItWorksDesc": "When you sign in, your device will prompt you to verify with Face ID, Touch ID, or your device PIN. No password to remember or type.", + "infoAppAccess": "Using third-party apps", + "infoAppAccessDesc": "After creating your account, you will receive an app password. Use this to sign in to Bluesky apps and other AT Protocol clients.", "passkeyNameLabel": "Passkey Name (optional)", "passkeyNamePlaceholder": "e.g., MacBook Touch ID", "passkeyNameHint": "A friendly name to identify this passkey", diff --git a/frontend/src/locales/fi.json b/frontend/src/locales/fi.json index 4c7085f..269bd69 100644 --- a/frontend/src/locales/fi.json +++ b/frontend/src/locales/fi.json @@ -168,8 +168,8 @@ "createButton": "Luo tili", "alreadyHaveAccount": "Onko sinulla jo tili?", "signIn": "Kirjaudu sisään", - "wantPasswordless": "Haluatko salasanattoman turvallisuuden?", - "createPasskeyAccount": "Luo pääsyavaintili", + "passkeyAccount": "Pääsyavain", + "passwordAccount": "Salasana", "validation": { "handleRequired": "Käyttäjänimi vaaditaan", "handleNoDots": "Käyttäjänimi ei voi sisältää pisteitä. Voit määrittää oman verkkotunnuksen tilin luomisen jälkeen.", @@ -759,6 +759,7 @@ "verified": "Vahvistettu!", "channelVerified": "{channel} on vahvistettu onnistuneesti.", "canNowSignIn": "Voit nyt kirjautua tilillesi.", + "migrationContinue": "Voit sulkea tämän välilehden ja jatkaa siirtoa alkuperäisessä ikkunassa.", "continue": "Jatka", "identifierLabel": "Sähköposti tai tunniste", "identifierPlaceholder": "sinä@esimerkki.fi", @@ -886,6 +887,12 @@ "whyPasskeyBullet1": "Ei voi kalastella tai varastaa tietomurroissa", "whyPasskeyBullet2": "Käyttää laitteistopohjaisia salausavaimia", "whyPasskeyBullet3": "Vaatii biometrisen tunnistuksen tai laitteen PIN-koodin", + "infoWhyPasskey": "Miksi käyttää pääsyavainta?", + "infoWhyPasskeyDesc": "Pääsyavaimet ovat laitteellesi tallennettuja salattuja tunnistetietoja. Niitä ei voi kalastella, arvata tai varastaa tietomurroissa kuten salasanoja.", + "infoHowItWorks": "Miten se toimii", + "infoHowItWorksDesc": "Kirjautuessasi laitteesi pyytää sinua vahvistamaan Face ID:llä, Touch ID:llä tai laitteen PIN-koodilla. Ei salasanaa muistettavaksi tai kirjoitettavaksi.", + "infoAppAccess": "Kolmannen osapuolen sovellusten käyttö", + "infoAppAccessDesc": "Tilin luomisen jälkeen saat sovellussalasanan. Käytä sitä kirjautuaksesi Bluesky-sovelluksiin ja muihin AT Protocol -asiakkaisiin.", "whyPasskeyOnly": "Miksi vain pääsyavain?", "whyPasskeyOnlyDesc": "Pääsyavaintilit ovat turvallisempia kuin salasanapohjaiset tilit, koska ne:", "subtitleInitialDidDoc": "Lataa DID-dokumenttisi jatkaaksesi.", diff --git a/frontend/src/locales/ja.json b/frontend/src/locales/ja.json index 0fc0767..a55d10e 100644 --- a/frontend/src/locales/ja.json +++ b/frontend/src/locales/ja.json @@ -161,8 +161,8 @@ "createButton": "アカウントを作成", "alreadyHaveAccount": "すでにアカウントをお持ちですか?", "signIn": "サインイン", - "wantPasswordless": "パスワードレスをご希望ですか?", - "createPasskeyAccount": "パスキーアカウントを作成", + "passkeyAccount": "パスキー", + "passwordAccount": "パスワード", "validation": { "handleRequired": "ハンドルは必須です", "handleNoDots": "ハンドルにドットは使用できません。アカウント作成後にカスタムドメインを設定できます。", @@ -752,6 +752,7 @@ "verified": "確認完了!", "channelVerified": "{channel} が正常に確認されました。", "canNowSignIn": "アカウントにサインインできるようになりました。", + "migrationContinue": "このタブを閉じて、元のウィンドウで移行を続けてください。", "continue": "続行", "identifierLabel": "メールまたは識別子", "identifierPlaceholder": "you@example.com", @@ -879,6 +880,12 @@ "whyPasskeyBullet1": "フィッシングやデータ侵害で盗まれない", "whyPasskeyBullet2": "ハードウェア支援の暗号鍵を使用", "whyPasskeyBullet3": "生体認証またはデバイスPINが必要", + "infoWhyPasskey": "なぜパスキーを使うのですか?", + "infoWhyPasskeyDesc": "パスキーはデバイスに保存される暗号化資格情報です。パスワードのようにフィッシング、推測、データ侵害による盗難の被害を受けません。", + "infoHowItWorks": "仕組み", + "infoHowItWorksDesc": "サインイン時、デバイスがFace ID、Touch ID、またはデバイスPINでの確認を求めます。覚えたり入力したりするパスワードはありません。", + "infoAppAccess": "サードパーティアプリの使用", + "infoAppAccessDesc": "アカウント作成後、アプリパスワードが発行されます。Blueskyアプリやその他のAT Protocolクライアントへのサインインに使用してください。", "whyPasskeyOnly": "なぜパスキーのみ?", "whyPasskeyOnlyDesc": "パスキーアカウントはパスワードベースのアカウントより安全です:", "subtitleInitialDidDoc": "続行するにはDIDドキュメントをアップロードしてください。", diff --git a/frontend/src/locales/ko.json b/frontend/src/locales/ko.json index 154ca80..20faa0f 100644 --- a/frontend/src/locales/ko.json +++ b/frontend/src/locales/ko.json @@ -161,8 +161,8 @@ "createButton": "계정 만들기", "alreadyHaveAccount": "이미 계정이 있으신가요?", "signIn": "로그인", - "wantPasswordless": "비밀번호 없는 보안을 원하시나요?", - "createPasskeyAccount": "패스키 계정 만들기", + "passkeyAccount": "패스키", + "passwordAccount": "비밀번호", "validation": { "handleRequired": "핸들은 필수입니다", "handleNoDots": "핸들에 점을 포함할 수 없습니다. 계정 생성 후 사용자 정의 도메인을 설정할 수 있습니다.", @@ -752,6 +752,7 @@ "verified": "인증 완료!", "channelVerified": "{channel}이(가) 성공적으로 인증되었습니다.", "canNowSignIn": "이제 계정에 로그인할 수 있습니다.", + "migrationContinue": "이 탭을 닫고 원래 창에서 마이그레이션을 계속할 수 있습니다.", "continue": "계속", "identifierLabel": "이메일 또는 식별자", "identifierPlaceholder": "you@example.com", @@ -879,6 +880,12 @@ "whyPasskeyBullet1": "피싱이나 데이터 유출로 도난당할 수 없음", "whyPasskeyBullet2": "하드웨어 기반 암호화 키 사용", "whyPasskeyBullet3": "생체 인식 또는 기기 PIN 필요", + "infoWhyPasskey": "왜 패스키를 사용하나요?", + "infoWhyPasskeyDesc": "패스키는 기기에 저장된 암호화 자격 증명입니다. 비밀번호처럼 피싱, 추측 또는 데이터 유출로 도난당할 수 없습니다.", + "infoHowItWorks": "작동 방식", + "infoHowItWorksDesc": "로그인할 때 기기에서 Face ID, Touch ID 또는 기기 PIN으로 인증하라는 메시지가 표시됩니다. 기억하거나 입력할 비밀번호가 없습니다.", + "infoAppAccess": "서드파티 앱 사용", + "infoAppAccessDesc": "계정 생성 후 앱 비밀번호를 받게 됩니다. Bluesky 앱 및 기타 AT Protocol 클라이언트에 로그인할 때 사용하세요.", "whyPasskeyOnly": "왜 패스키만 사용하나요?", "whyPasskeyOnlyDesc": "패스키 계정은 비밀번호 기반 계정보다 안전합니다:", "subtitleInitialDidDoc": "계속하려면 DID 문서를 업로드하세요.", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 38c26ba..d58985e 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -161,8 +161,8 @@ "createButton": "Skapa konto", "alreadyHaveAccount": "Har du redan ett konto?", "signIn": "Logga in", - "wantPasswordless": "Vill du ha lösenordsfri säkerhet?", - "createPasskeyAccount": "Skapa ett nyckelbaserat konto", + "passkeyAccount": "Nyckel", + "passwordAccount": "Lösenord", "validation": { "handleRequired": "Användarnamn krävs", "handleNoDots": "Användarnamn kan inte innehålla punkter. Du kan konfigurera ett eget domännamn efter att kontot skapats.", @@ -752,6 +752,7 @@ "verified": "Verifierad!", "channelVerified": "Din {channel} har verifierats.", "canNowSignIn": "Du kan nu logga in på ditt konto.", + "migrationContinue": "Du kan stänga denna flik och fortsätta migreringen i det ursprungliga fönstret.", "continue": "Fortsätt", "identifierLabel": "E-post eller identifierare", "identifierPlaceholder": "du@exempel.se", @@ -879,6 +880,12 @@ "whyPasskeyBullet1": "Kan inte nätfiskas eller stjälas vid dataintrång", "whyPasskeyBullet2": "Använder hårdvarubaserade kryptografiska nycklar", "whyPasskeyBullet3": "Kräver din biometri eller enhets-PIN för att använda", + "infoWhyPasskey": "Varfor anvanda nyckel?", + "infoWhyPasskeyDesc": "Nycklar ar kryptografiska uppgifter som lagras pa din enhet. De kan inte nätfiskas, gissas eller stjälas vid dataintrång som losenord kan.", + "infoHowItWorks": "Hur det fungerar", + "infoHowItWorksDesc": "När du loggar in kommer din enhet att be dig verifiera med Face ID, Touch ID eller din enhets-PIN. Inget lösenord att komma ihåg eller skriva.", + "infoAppAccess": "Använda tredjepartsappar", + "infoAppAccessDesc": "Efter att du skapat ditt konto får du ett applösenord. Använd detta för att logga in på Bluesky-appar och andra AT Protocol-klienter.", "whyPasskeyOnly": "Varför endast nyckel?", "whyPasskeyOnlyDesc": "Nyckelkonton är säkrare än lösenordsbaserade konton eftersom de:", "subtitleInitialDidDoc": "Ladda upp ditt DID-dokument för att fortsätta.", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 40716ac..792c1c8 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -161,8 +161,8 @@ "createButton": "创建账户", "alreadyHaveAccount": "已有账户?", "signIn": "立即登录", - "wantPasswordless": "想要无密码登录?", - "createPasskeyAccount": "创建通行密钥账户", + "passkeyAccount": "通行密钥", + "passwordAccount": "密码", "validation": { "handleRequired": "请输入用户名", "handleNoDots": "用户名不能包含点号。您可以在创建账户后设置自定义域名。", @@ -758,6 +758,7 @@ "verified": "验证成功!", "channelVerified": "您的{channel}已成功验证。", "canNowSignIn": "您现在可以登录账户。", + "migrationContinue": "您可以关闭此标签页,在原窗口中继续迁移。", "continue": "继续", "identifierLabel": "邮箱或标识符", "identifierPlaceholder": "you@example.com", @@ -896,6 +897,12 @@ "whyPasskeyBullet1": "无法被钓鱼或在数据泄露中被盗", "whyPasskeyBullet2": "使用硬件支持的加密密钥", "whyPasskeyBullet3": "需要您的生物识别或设备 PIN 才能使用", + "infoWhyPasskey": "为什么使用通行密钥?", + "infoWhyPasskeyDesc": "通行密钥是存储在您设备上的加密凭证。与密码不同,它们无法被钓鱼、猜测或在数据泄露中被盗。", + "infoHowItWorks": "工作原理", + "infoHowItWorksDesc": "登录时,您的设备会提示您使用 Face ID、Touch ID 或设备 PIN 进行验证。无需记住或输入密码。", + "infoAppAccess": "使用第三方应用", + "infoAppAccessDesc": "创建账户后,您将收到一个应用密码。使用它登录 Bluesky 应用和其他 AT Protocol 客户端。", "passkeyNameLabel": "通行密钥名称(可选)", "passkeyNamePlaceholder": "如 MacBook Touch ID", "passkeyNameHint": "用于识别此通行密钥的友好名称", diff --git a/frontend/src/routes/Comms.svelte b/frontend/src/routes/Comms.svelte index 2d7f578..0647b8b 100644 --- a/frontend/src/routes/Comms.svelte +++ b/frontend/src/routes/Comms.svelte @@ -182,7 +182,7 @@
{:else} -
+ + + diff --git a/frontend/src/routes/RegisterPasskey.svelte b/frontend/src/routes/RegisterPasskey.svelte index 5fe0e47..fa2178c 100644 --- a/frontend/src/routes/RegisterPasskey.svelte +++ b/frontend/src/routes/RegisterPasskey.svelte @@ -1,5 +1,5 @@
- {#if flow?.state.step === 'info'} -
-
-
- {$_('register.migrateTitle')} -

{$_('register.migrateDescription')}

- - {$_('register.migrateLink')} → - -
-
- {/if} - -

{$_('registerPasskey.title')}

-

{getSubtitle()}

+ {#if flow?.state.error}
{flow.state.error}
{/if} {#if loadingServerInfo || !flow} -

{$_('registerPasskey.loading')}

+
{:else if flow.state.step === 'info'} - -
- - - {#if flow.info.handle.includes('.')} -

{$_('registerPasskey.handleDotWarning')}

- {:else if fullHandle()} -

{$_('registerPasskey.handleHint', { values: { handle: fullHandle() } })}

- {/if} +
+
+
+ {$_('register.migrateTitle')} +

{$_('register.migrateDescription')}

+ + {$_('register.migrateLink')} → + +
+
+ + + + -
- {$_('registerPasskey.whyPasskeyOnly')} -

{$_('registerPasskey.whyPasskeyOnlyDesc')}

-
    -
  • {$_('registerPasskey.whyPasskeyBullet1')}
  • -
  • {$_('registerPasskey.whyPasskeyBullet2')}
  • -
  • {$_('registerPasskey.whyPasskeyBullet3')}
  • -
-
- - - - - {:else if flow.state.step === 'key-choice'} @@ -436,11 +447,32 @@ diff --git a/frontend/src/routes/Verify.svelte b/frontend/src/routes/Verify.svelte index 1ad46a6..ae04e62 100644 --- a/frontend/src/routes/Verify.svelte +++ b/frontend/src/routes/Verify.svelte @@ -237,7 +237,10 @@ - {:else if successPurpose === 'migration' || successPurpose === 'signup'} + {:else if successPurpose === 'migration'} +

{$_('verify.channelVerified', { values: { channel: channelLabel(successChannel || '') } })}

+

{$_('verify.migrationContinue')}

+ {:else if successPurpose === 'signup'}

{$_('verify.channelVerified', { values: { channel: channelLabel(successChannel || '') } })}

{$_('verify.canNowSignIn')}

diff --git a/frontend/src/styles/base.css b/frontend/src/styles/base.css index 3e3db56..f1f6435 100644 --- a/frontend/src/styles/base.css +++ b/frontend/src/styles/base.css @@ -431,6 +431,7 @@ hr { @media (min-width: 800px) { .split-layout { grid-template-columns: 1fr 1fr; + align-items: start; } .split-layout.sidebar-right { grid-template-columns: 1.5fr 1fr; @@ -440,6 +441,10 @@ hr { } } +.split-layout > * { + min-width: 0; +} + .form-row { display: grid; grid-template-columns: 1fr;