/** * First-run onboarding page for agent-native apps. * * Shown when Better Auth is active and the user isn't signed in. * Provides a path to create or sign into an account from day one. * * After first account exists, this page acts as a normal login page. */ import { getLocaleInitScript } from "../localization/server.js"; import { DEFAULT_LOCALE, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, type LocaleCode, } from "../localization/shared.js"; import { signInJourneyInlineScript } from "../shared/sign-in-journey.js"; import { AGENT_NATIVE_SOCIAL_IMAGE_ALT, AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT, AGENT_NATIVE_SOCIAL_IMAGE_PATH, AGENT_NATIVE_SOCIAL_IMAGE_TYPE, AGENT_NATIVE_SOCIAL_IMAGE_WIDTH, withAgentNativeSocialImageCacheBuster, } from "../shared/social-meta.js"; import { normalizeAppBasePath } from "./app-base-path.js"; import { BUILT_IN_AUTH_MARKETING, resolveBuiltInAuthMarketing, type AuthMarketingContent, } from "./auth-marketing.js"; import { resolveGoogleAuthMode, type GoogleAuthMode, } from "./google-auth-mode.js"; import { hasGoogleSignInCredentials } from "./google-oauth-credentials.js"; import { identitySsoLoginButtonHtml } from "./identity-sso-store.js"; import { getPublicOAuthOrigin } from "./oauth-public-origin.js"; import { getWorkspaceGatewayReturnOrigin } from "./oauth-return-url.js"; function hasGoogleOAuth(): boolean { return hasGoogleSignInCredentials(); } function getConnectionLabel(): string { const url = process.env.DATABASE_URL || ""; if (!url) return "SQLite (local file)"; if (url.startsWith("pglite:")) return "PGlite (local Postgres)"; if (url.startsWith("postgres://") || url.startsWith("postgresql://")) { if (url.includes("neon.tech")) return "Neon Postgres"; if (url.includes("supabase")) return "Supabase Postgres"; return "Postgres"; } if (url.startsWith("file:")) return "SQLite (local file)"; if (url.startsWith("libsql://") || url.includes("turso.io")) return "Turso"; return "SQL database"; } function withAppBasePath(path: string): string { const cleanPath = path.startsWith("/") ? path : `/${path}`; const basePath = normalizeAppBasePath( process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH, ); return `${basePath}${cleanPath}`; } const AGENT_NATIVE_TERMS_URL = "https://www.agent-native.com/terms"; const AGENT_NATIVE_PRIVACY_URL = "https://www.agent-native.com/privacy"; const EN_AUTH_COPY = { languageLabel: "Language", systemLanguage: "System", pageTitleSignIn: "Sign in", pageTitleWelcome: "Welcome", welcomeTitle: "Welcome", signInTitle: "Sign in", welcomeBackTitle: "Welcome back", checkEmailTitle: "Check your email", resetPasswordTitle: "Reset password", createAccountSubtitle: "Create an account to get started", googleOnlySubtitle: "Use your workspace Google account to continue", signInSubtitle: "Sign in to your account", finishAccountSubtitle: "Finish creating your account", resetPasswordSubtitle: "Reset your password", upgradeCopy: "Continue signing in to attach this app to your account and migrate local data.", googleButton: "Sign in with Google", dividerOr: "or", createAccount: "Create account", signIn: "Sign in", email: "Email", password: "Password", confirmPassword: "Confirm password", passwordMinPlaceholder: "At least 8 characters", confirmPasswordPlaceholder: "Confirm password", enterPasswordPlaceholder: "Enter password", signupProgress: "Signup progress", progressAccount: "Account", progressVerify: "Verify", progressStart: "Start", verificationSent: "Verification email sent", verifyCopyPrefix: "We sent a secure link to", verifyCopySuffix: ". Click it, return here, and this app will finish signing you in automatically.", verificationNote: "You can keep this tab open. If it has not refreshed after you come back, use Continue.", continue: "Continue", resendEmail: "Resend email", back: "Back", forgotPassword: "Forgot password?", sendResetLink: "Send reset link", backToSignIn: "Back to sign in", localNotePrefix: "Your account is stored in this app's own DB", localNoteSuffix: ", not a third-party service.", runLocally: "Run Locally", runLocallySentence: "Run locally", openSource: "100% free and open source", useOwnGoogleClient: "Use your own Google OAuth client:", copyCommand: "Copy command", copied: "Copied", close: "Close", closeGoogleChoices: "Close Google sign-in choices", legalPrefix: "By signing up, you accept our", legalTerms: "Terms", legalConnector: "and", legalPrivacy: "Privacy Policy", legalSuffix: ".", invalidEmail: "Enter a valid email address, like you@example.com.", signInToContinue: "Sign in to continue.", finishSignInFailed: "Could not finish sign-in automatically.", enterPasswordAfterVerification: "Enter your password after verifying your email.", finishSignInManually: "We could not finish sign-in automatically. Sign in to continue.", stillWaitingVerification: "Still waiting on verification. Click the link in your email, then try Continue again.", checkVerificationFailed: "Could not check verification. Please try again.", checking: "Checking...", checkingVerification: "Checking your verification...", sending: "Sending...", sent: "Sent", sentVerification: "Sent a fresh verification link.", resendVerificationFailed: "Could not resend the verification email.", networkErrorRetry: "Network error. Please try again.", networkErrorDashRetry: "Network error — please try again", passwordsMismatch: "Passwords do not match", creatingAccount: "Creating account…", registrationFailed: "Registration failed", accountCreatedSigningIn: "Account created — signing you in…", emailVerifiedFinishing: "Email verified. Finishing sign-in...", emailVerifiedSignIn: "Email verified. Sign in to continue.", resetEmailSent: "If that email exists, a reset link is on its way.", resetEmailFailed: "Could not send reset email.", signingIn: "Signing in…", invalidLogin: "Invalid email or password", googleNotConfigured: "Google OAuth is not configured.", failedToConnect: "Failed to connect. Please try again.", migrateLocalFallback: "Continue signing in to migrate local data.", googlePopupHelp: "Allow popups for this site and try again", googleNeverFinished: "Google sign-in did not finish. Check the Google OAuth redirect URI and server logs for [agent-native][google-oauth].", }; const AUTH_LOCALE_COPY: Record = { "en-US": EN_AUTH_COPY, "zh-CN": { languageLabel: "语言", systemLanguage: "系统", pageTitleSignIn: "登录", pageTitleWelcome: "欢迎", welcomeTitle: "欢迎", signInTitle: "登录", welcomeBackTitle: "欢迎回来", checkEmailTitle: "检查你的邮箱", resetPasswordTitle: "重置密码", createAccountSubtitle: "创建账户即可开始", googleOnlySubtitle: "使用你的工作区 Google 账户继续", signInSubtitle: "登录你的账户", finishAccountSubtitle: "完成账户创建", resetPasswordSubtitle: "重置你的密码", upgradeCopy: "继续登录,将此应用关联到你的账户并迁移本地数据。", googleButton: "使用 Google 登录", dividerOr: "或", createAccount: "创建账户", signIn: "登录", email: "电子邮箱", password: "密码", confirmPassword: "确认密码", passwordMinPlaceholder: "至少 8 个字符", confirmPasswordPlaceholder: "确认密码", enterPasswordPlaceholder: "输入密码", signupProgress: "注册进度", progressAccount: "账户", progressVerify: "验证", progressStart: "开始", verificationSent: "验证邮件已发送", verifyCopyPrefix: "我们已向", verifyCopySuffix: "发送安全链接。点击链接后回到这里,应用会自动完成登录。", verificationNote: "你可以保持此标签页打开。如果回来后没有自动刷新,请点击继续。", continue: "继续", resendEmail: "重新发送邮件", back: "返回", forgotPassword: "忘记密码?", sendResetLink: "发送重置链接", backToSignIn: "返回登录", localNotePrefix: "你的账户存储在此应用自己的数据库中", localNoteSuffix: ",而不是第三方服务。", runLocally: "本地运行", runLocallySentence: "本地运行", openSource: "100% 免费且开源", useOwnGoogleClient: "使用你自己的 Google OAuth 客户端:", copyCommand: "复制命令", copied: "已复制", close: "关闭", closeGoogleChoices: "关闭 Google 登录选项", legalPrefix: "注册即表示你接受我们的", legalTerms: "条款", legalConnector: "和", legalPrivacy: "隐私政策", legalSuffix: "。", invalidEmail: "请输入有效邮箱地址,例如 you@example.com。", signInToContinue: "登录以继续。", finishSignInFailed: "无法自动完成登录。", enterPasswordAfterVerification: "验证邮箱后请输入密码。", finishSignInManually: "无法自动完成登录。请登录以继续。", stillWaitingVerification: "仍在等待验证。请点击邮件中的链接,然后再次点击继续。", checkVerificationFailed: "无法检查验证状态。请重试。", checking: "正在检查...", checkingVerification: "正在检查验证状态...", sending: "正在发送...", sent: "已发送", sentVerification: "新的验证链接已发送。", resendVerificationFailed: "无法重新发送验证邮件。", networkErrorRetry: "网络错误。请重试。", networkErrorDashRetry: "网络错误 — 请重试", passwordsMismatch: "两次输入的密码不一致", creatingAccount: "正在创建账户…", registrationFailed: "注册失败", accountCreatedSigningIn: "账户已创建 — 正在登录…", emailVerifiedFinishing: "邮箱已验证。正在完成登录...", emailVerifiedSignIn: "邮箱已验证。请登录以继续。", resetEmailSent: "如果该邮箱存在,重置链接已在发送途中。", resetEmailFailed: "无法发送重置邮件。", signingIn: "正在登录…", invalidLogin: "邮箱或密码无效", googleNotConfigured: "Google OAuth 未配置。", failedToConnect: "连接失败。请重试。", migrateLocalFallback: "继续登录以迁移本地数据。", googlePopupHelp: "请允许此网站弹出窗口后重试", googleNeverFinished: "Google 登录未完成。请检查 Google OAuth 重定向 URI 和服务器日志中的 [agent-native][google-oauth]。", }, "zh-TW": { languageLabel: "語言", systemLanguage: "系統", pageTitleSignIn: "登入", pageTitleWelcome: "歡迎", welcomeTitle: "歡迎", signInTitle: "登入", welcomeBackTitle: "歡迎回來", checkEmailTitle: "檢查你的電子郵件", resetPasswordTitle: "重設密碼", createAccountSubtitle: "建立帳號即可開始", googleOnlySubtitle: "使用你的工作區 Google 帳號繼續", signInSubtitle: "登入你的帳號", finishAccountSubtitle: "完成帳號建立", resetPasswordSubtitle: "重設你的密碼", upgradeCopy: "繼續登入,將此應用程式連結到你的帳號並遷移本機資料。", googleButton: "使用 Google 登入", dividerOr: "或", createAccount: "建立帳號", signIn: "登入", email: "電子郵件", password: "密碼", confirmPassword: "確認密碼", passwordMinPlaceholder: "至少 8 個字元", confirmPasswordPlaceholder: "確認密碼", enterPasswordPlaceholder: "輸入密碼", signupProgress: "註冊進度", progressAccount: "帳號", progressVerify: "驗證", progressStart: "開始", verificationSent: "驗證郵件已送出", verifyCopyPrefix: "我們已將安全連結寄到", verifyCopySuffix: "。點擊連結後回到這裡,應用程式會自動完成登入。", verificationNote: "你可以保持此分頁開啟。如果回來後沒有自動重新整理,請點擊繼續。", continue: "繼續", resendEmail: "重新寄送郵件", back: "返回", forgotPassword: "忘記密碼?", sendResetLink: "寄送重設連結", backToSignIn: "返回登入", localNotePrefix: "你的帳號儲存在此應用程式自己的資料庫中", localNoteSuffix: ",而不是第三方服務。", runLocally: "在本機執行", runLocallySentence: "在本機執行", openSource: "100% 免費且開源", useOwnGoogleClient: "使用你自己的 Google OAuth 用戶端:", copyCommand: "複製指令", copied: "已複製", close: "關閉", closeGoogleChoices: "關閉 Google 登入選項", legalPrefix: "註冊即表示你接受我們的", legalTerms: "條款", legalConnector: "和", legalPrivacy: "隱私權政策", legalSuffix: "。", invalidEmail: "請輸入有效的電子郵件地址,例如 you@example.com。", signInToContinue: "登入以繼續。", finishSignInFailed: "無法自動完成登入。", enterPasswordAfterVerification: "驗證電子郵件後請輸入密碼。", finishSignInManually: "無法自動完成登入。請登入以繼續。", stillWaitingVerification: "仍在等待驗證。請點擊郵件中的連結,然後再次點擊繼續。", checkVerificationFailed: "無法檢查驗證狀態。請重試。", checking: "正在檢查...", checkingVerification: "正在檢查驗證狀態...", sending: "正在寄送...", sent: "已送出", sentVerification: "新的驗證連結已送出。", resendVerificationFailed: "無法重新寄送驗證郵件。", networkErrorRetry: "網路錯誤。請重試。", networkErrorDashRetry: "網路錯誤 - 請重試", passwordsMismatch: "兩次輸入的密碼不一致", creatingAccount: "正在建立帳號...", registrationFailed: "註冊失敗", accountCreatedSigningIn: "帳號已建立,正在登入...", emailVerifiedFinishing: "電子郵件已驗證。正在完成登入...", emailVerifiedSignIn: "電子郵件已驗證。請登入以繼續。", resetEmailSent: "如果該電子郵件存在,重設連結已在寄送途中。", resetEmailFailed: "無法寄送重設郵件。", signingIn: "正在登入...", invalidLogin: "電子郵件或密碼無效", googleNotConfigured: "Google OAuth 尚未設定。", failedToConnect: "連線失敗。請重試。", migrateLocalFallback: "繼續登入以遷移本機資料。", googlePopupHelp: "請允許此網站開啟彈出式視窗後重試", googleNeverFinished: "Google 登入未完成。請檢查 Google OAuth 重新導向 URI,以及伺服器記錄中的 [agent-native][google-oauth]。", }, "es-ES": { languageLabel: "Idioma", systemLanguage: "Sistema", pageTitleSignIn: "Iniciar sesión", pageTitleWelcome: "Bienvenido", welcomeTitle: "Bienvenido", signInTitle: "Iniciar sesión", welcomeBackTitle: "Bienvenido de nuevo", checkEmailTitle: "Revisa tu email", resetPasswordTitle: "Restablecer contraseña", createAccountSubtitle: "Crea una cuenta para empezar", googleOnlySubtitle: "Usa tu cuenta de Google del espacio de trabajo", signInSubtitle: "Inicia sesión en tu cuenta", finishAccountSubtitle: "Termina de crear tu cuenta", resetPasswordSubtitle: "Restablece tu contraseña", upgradeCopy: "Sigue iniciando sesión para conectar esta app a tu cuenta y migrar datos locales.", googleButton: "Iniciar sesión con Google", dividerOr: "o", createAccount: "Crear cuenta", signIn: "Iniciar sesión", email: "Email", password: "Contraseña", confirmPassword: "Confirmar contraseña", passwordMinPlaceholder: "Al menos 8 caracteres", confirmPasswordPlaceholder: "Confirmar contraseña", enterPasswordPlaceholder: "Introduce la contraseña", signupProgress: "Progreso de registro", progressAccount: "Cuenta", progressVerify: "Verificar", progressStart: "Empezar", verificationSent: "Email de verificación enviado", verifyCopyPrefix: "Enviamos un enlace seguro a", verifyCopySuffix: ". Haz clic en él, vuelve aquí y esta app terminará de iniciar sesión automáticamente.", verificationNote: "Puedes dejar esta pestaña abierta. Si no se actualiza al volver, usa Continuar.", continue: "Continuar", resendEmail: "Reenviar email", back: "Volver", forgotPassword: "¿Olvidaste tu contraseña?", sendResetLink: "Enviar enlace de restablecimiento", backToSignIn: "Volver a iniciar sesión", localNotePrefix: "Tu cuenta se almacena en la propia base de datos de esta app", localNoteSuffix: ", no en un servicio de terceros.", runLocally: "Ejecutar localmente", runLocallySentence: "Ejecutar localmente", openSource: "100% gratis y de código abierto", useOwnGoogleClient: "Usa tu propio cliente de Google OAuth:", copyCommand: "Copiar comando", copied: "Copiado", close: "Cerrar", closeGoogleChoices: "Cerrar opciones de inicio con Google", legalPrefix: "Al registrarte, aceptas nuestros", legalTerms: "Términos", legalConnector: "y", legalPrivacy: "Política de privacidad", legalSuffix: ".", invalidEmail: "Introduce un email válido, como you@example.com.", signInToContinue: "Inicia sesión para continuar.", finishSignInFailed: "No se pudo completar el inicio automáticamente.", enterPasswordAfterVerification: "Introduce tu contraseña después de verificar tu email.", finishSignInManually: "No se pudo completar el inicio automáticamente. Inicia sesión para continuar.", stillWaitingVerification: "Aún esperamos la verificación. Haz clic en el enlace del email y luego prueba Continuar de nuevo.", checkVerificationFailed: "No se pudo comprobar la verificación. Inténtalo de nuevo.", checking: "Comprobando...", checkingVerification: "Comprobando tu verificación...", sending: "Enviando...", sent: "Enviado", sentVerification: "Se envió un nuevo enlace de verificación.", resendVerificationFailed: "No se pudo reenviar el email de verificación.", networkErrorRetry: "Error de red. Inténtalo de nuevo.", networkErrorDashRetry: "Error de red — inténtalo de nuevo", passwordsMismatch: "Las contraseñas no coinciden", creatingAccount: "Creando cuenta…", registrationFailed: "Error al registrarse", accountCreatedSigningIn: "Cuenta creada — iniciando sesión…", emailVerifiedFinishing: "Email verificado. Terminando inicio de sesión...", emailVerifiedSignIn: "Email verificado. Inicia sesión para continuar.", resetEmailSent: "Si ese email existe, el enlace de restablecimiento está en camino.", resetEmailFailed: "No se pudo enviar el email de restablecimiento.", signingIn: "Iniciando sesión…", invalidLogin: "Email o contraseña no válidos", googleNotConfigured: "Google OAuth no está configurado.", failedToConnect: "No se pudo conectar. Inténtalo de nuevo.", migrateLocalFallback: "Sigue iniciando sesión para migrar datos locales.", googlePopupHelp: "Permite ventanas emergentes para este sitio e inténtalo de nuevo", googleNeverFinished: "El inicio de sesión con Google no terminó. Comprueba el URI de redirección de Google OAuth y los logs del servidor para [agent-native][google-oauth].", }, "fr-FR": { languageLabel: "Langue", systemLanguage: "Système", pageTitleSignIn: "Connexion", pageTitleWelcome: "Bienvenue", welcomeTitle: "Bienvenue", signInTitle: "Connexion", welcomeBackTitle: "Bon retour", checkEmailTitle: "Vérifiez votre e-mail", resetPasswordTitle: "Réinitialiser le mot de passe", createAccountSubtitle: "Créez un compte pour commencer", googleOnlySubtitle: "Utilisez le compte Google de votre espace de travail", signInSubtitle: "Connectez-vous à votre compte", finishAccountSubtitle: "Terminez la création de votre compte", resetPasswordSubtitle: "Réinitialisez votre mot de passe", upgradeCopy: "Continuez la connexion pour associer cette app à votre compte et migrer les données locales.", googleButton: "Se connecter avec Google", dividerOr: "ou", createAccount: "Créer un compte", signIn: "Connexion", email: "E-mail", password: "Mot de passe", confirmPassword: "Confirmer le mot de passe", passwordMinPlaceholder: "Au moins 8 caractères", confirmPasswordPlaceholder: "Confirmer le mot de passe", enterPasswordPlaceholder: "Saisir le mot de passe", signupProgress: "Progression de l'inscription", progressAccount: "Compte", progressVerify: "Vérifier", progressStart: "Démarrer", verificationSent: "E-mail de vérification envoyé", verifyCopyPrefix: "Nous avons envoyé un lien sécurisé à", verifyCopySuffix: ". Cliquez dessus, revenez ici, et cette app terminera automatiquement la connexion.", verificationNote: "Vous pouvez garder cet onglet ouvert. S'il ne s'actualise pas à votre retour, utilisez Continuer.", continue: "Continuer", resendEmail: "Renvoyer l'e-mail", back: "Retour", forgotPassword: "Mot de passe oublié ?", sendResetLink: "Envoyer le lien de réinitialisation", backToSignIn: "Retour à la connexion", localNotePrefix: "Votre compte est stocké dans la base de données propre à cette app", localNoteSuffix: ", pas dans un service tiers.", runLocally: "Exécuter localement", runLocallySentence: "Exécuter localement", openSource: "100 % gratuit et open source", useOwnGoogleClient: "Utilisez votre propre client Google OAuth :", copyCommand: "Copier la commande", copied: "Copié", close: "Fermer", closeGoogleChoices: "Fermer les choix de connexion Google", legalPrefix: "En vous inscrivant, vous acceptez nos", legalTerms: "Conditions", legalConnector: "et", legalPrivacy: "Politique de confidentialité", legalSuffix: ".", invalidEmail: "Saisissez une adresse e-mail valide, comme you@example.com.", signInToContinue: "Connectez-vous pour continuer.", finishSignInFailed: "Impossible de terminer la connexion automatiquement.", enterPasswordAfterVerification: "Saisissez votre mot de passe après avoir vérifié votre e-mail.", finishSignInManually: "Impossible de terminer la connexion automatiquement. Connectez-vous pour continuer.", stillWaitingVerification: "La vérification est toujours en attente. Cliquez sur le lien dans votre e-mail, puis réessayez Continuer.", checkVerificationFailed: "Impossible de vérifier l'état. Veuillez réessayer.", checking: "Vérification...", checkingVerification: "Vérification en cours...", sending: "Envoi...", sent: "Envoyé", sentVerification: "Nouveau lien de vérification envoyé.", resendVerificationFailed: "Impossible de renvoyer l'e-mail de vérification.", networkErrorRetry: "Erreur réseau. Veuillez réessayer.", networkErrorDashRetry: "Erreur réseau — veuillez réessayer", passwordsMismatch: "Les mots de passe ne correspondent pas", creatingAccount: "Création du compte…", registrationFailed: "Échec de l'inscription", accountCreatedSigningIn: "Compte créé — connexion en cours…", emailVerifiedFinishing: "E-mail vérifié. Connexion en cours...", emailVerifiedSignIn: "E-mail vérifié. Connectez-vous pour continuer.", resetEmailSent: "Si cet e-mail existe, un lien de réinitialisation est en route.", resetEmailFailed: "Impossible d'envoyer l'e-mail de réinitialisation.", signingIn: "Connexion…", invalidLogin: "E-mail ou mot de passe invalide", googleNotConfigured: "Google OAuth n'est pas configuré.", failedToConnect: "Connexion impossible. Veuillez réessayer.", migrateLocalFallback: "Continuez la connexion pour migrer les données locales.", googlePopupHelp: "Autorisez les fenêtres pop-up pour ce site et réessayez", googleNeverFinished: "La connexion Google n'a pas abouti. Vérifiez l'URI de redirection Google OAuth et les logs serveur pour [agent-native][google-oauth].", }, "de-DE": { languageLabel: "Sprache", systemLanguage: "System", pageTitleSignIn: "Anmelden", pageTitleWelcome: "Willkommen", welcomeTitle: "Willkommen", signInTitle: "Anmelden", welcomeBackTitle: "Willkommen zurück", checkEmailTitle: "E-Mail prüfen", resetPasswordTitle: "Passwort zurücksetzen", createAccountSubtitle: "Erstelle ein Konto, um zu beginnen", googleOnlySubtitle: "Verwende dein Workspace-Google-Konto", signInSubtitle: "Melde dich bei deinem Konto an", finishAccountSubtitle: "Schließe die Kontoerstellung ab", resetPasswordSubtitle: "Setze dein Passwort zurück", upgradeCopy: "Melde dich weiter an, um diese App mit deinem Konto zu verbinden und lokale Daten zu migrieren.", googleButton: "Mit Google anmelden", dividerOr: "oder", createAccount: "Konto erstellen", signIn: "Anmelden", email: "E-Mail", password: "Passwort", confirmPassword: "Passwort bestätigen", passwordMinPlaceholder: "Mindestens 8 Zeichen", confirmPasswordPlaceholder: "Passwort bestätigen", enterPasswordPlaceholder: "Passwort eingeben", signupProgress: "Registrierungsfortschritt", progressAccount: "Konto", progressVerify: "Prüfen", progressStart: "Start", verificationSent: "Bestätigungs-E-Mail gesendet", verifyCopyPrefix: "Wir haben einen sicheren Link gesendet an", verifyCopySuffix: ". Klicke darauf, kehre hierher zurück, und diese App meldet dich automatisch an.", verificationNote: "Du kannst diesen Tab geöffnet lassen. Wenn er nach deiner Rückkehr nicht aktualisiert wird, nutze Weiter.", continue: "Weiter", resendEmail: "E-Mail erneut senden", back: "Zurück", forgotPassword: "Passwort vergessen?", sendResetLink: "Reset-Link senden", backToSignIn: "Zurück zur Anmeldung", localNotePrefix: "Dein Konto wird in der eigenen Datenbank dieser App gespeichert", localNoteSuffix: ", nicht bei einem Drittanbieter.", runLocally: "Lokal ausführen", runLocallySentence: "Lokal ausführen", openSource: "100 % kostenlos und Open Source", useOwnGoogleClient: "Eigenen Google-OAuth-Client verwenden:", copyCommand: "Befehl kopieren", copied: "Kopiert", close: "Schließen", closeGoogleChoices: "Google-Anmeldeoptionen schließen", legalPrefix: "Mit der Registrierung akzeptierst du unsere", legalTerms: "Bedingungen", legalConnector: "und", legalPrivacy: "Datenschutzrichtlinie", legalSuffix: ".", invalidEmail: "Gib eine gültige E-Mail-Adresse ein, z. B. you@example.com.", signInToContinue: "Melde dich an, um fortzufahren.", finishSignInFailed: "Die Anmeldung konnte nicht automatisch abgeschlossen werden.", enterPasswordAfterVerification: "Gib dein Passwort ein, nachdem du deine E-Mail bestätigt hast.", finishSignInManually: "Die Anmeldung konnte nicht automatisch abgeschlossen werden. Melde dich an, um fortzufahren.", stillWaitingVerification: "Die Bestätigung steht noch aus. Klicke auf den Link in deiner E-Mail und versuche Weiter erneut.", checkVerificationFailed: "Bestätigung konnte nicht geprüft werden. Bitte erneut versuchen.", checking: "Prüfen...", checkingVerification: "Bestätigung wird geprüft...", sending: "Senden...", sent: "Gesendet", sentVerification: "Ein neuer Bestätigungslink wurde gesendet.", resendVerificationFailed: "Bestätigungs-E-Mail konnte nicht erneut gesendet werden.", networkErrorRetry: "Netzwerkfehler. Bitte erneut versuchen.", networkErrorDashRetry: "Netzwerkfehler — bitte erneut versuchen", passwordsMismatch: "Die Passwörter stimmen nicht überein", creatingAccount: "Konto wird erstellt…", registrationFailed: "Registrierung fehlgeschlagen", accountCreatedSigningIn: "Konto erstellt — Anmeldung läuft…", emailVerifiedFinishing: "E-Mail bestätigt. Anmeldung wird abgeschlossen...", emailVerifiedSignIn: "E-Mail bestätigt. Melde dich an, um fortzufahren.", resetEmailSent: "Falls diese E-Mail existiert, ist ein Reset-Link unterwegs.", resetEmailFailed: "Reset-E-Mail konnte nicht gesendet werden.", signingIn: "Anmeldung…", invalidLogin: "E-Mail oder Passwort ungültig", googleNotConfigured: "Google OAuth ist nicht konfiguriert.", failedToConnect: "Verbindung fehlgeschlagen. Bitte erneut versuchen.", migrateLocalFallback: "Melde dich weiter an, um lokale Daten zu migrieren.", googlePopupHelp: "Erlaube Pop-ups für diese Website und versuche es erneut", googleNeverFinished: "Die Google-Anmeldung wurde nicht abgeschlossen. Prüfe die Google-OAuth-Redirect-URI und Serverlogs für [agent-native][google-oauth].", }, "ja-JP": { languageLabel: "言語", systemLanguage: "システム", pageTitleSignIn: "サインイン", pageTitleWelcome: "ようこそ", welcomeTitle: "ようこそ", signInTitle: "サインイン", welcomeBackTitle: "おかえりなさい", checkEmailTitle: "メールを確認してください", resetPasswordTitle: "パスワードをリセット", createAccountSubtitle: "アカウントを作成して始めましょう", googleOnlySubtitle: "ワークスペースの Google アカウントで続行", signInSubtitle: "アカウントにサインイン", finishAccountSubtitle: "アカウント作成を完了", resetPasswordSubtitle: "パスワードをリセットします", upgradeCopy: "サインインを続けて、このアプリをアカウントに接続し、ローカルデータを移行します。", googleButton: "Google でサインイン", dividerOr: "または", createAccount: "アカウントを作成", signIn: "サインイン", email: "メール", password: "パスワード", confirmPassword: "パスワードを確認", passwordMinPlaceholder: "8 文字以上", confirmPasswordPlaceholder: "パスワードを確認", enterPasswordPlaceholder: "パスワードを入力", signupProgress: "登録の進行状況", progressAccount: "アカウント", progressVerify: "確認", progressStart: "開始", verificationSent: "確認メールを送信しました", verifyCopyPrefix: "安全なリンクを送信しました:", verifyCopySuffix: "。リンクをクリックしてここに戻ると、このアプリが自動的にサインインを完了します。", verificationNote: "このタブは開いたままで構いません。戻っても更新されない場合は、続行を押してください。", continue: "続行", resendEmail: "メールを再送信", back: "戻る", forgotPassword: "パスワードをお忘れですか?", sendResetLink: "リセットリンクを送信", backToSignIn: "サインインに戻る", localNotePrefix: "アカウントはこのアプリ自身の DB に保存されます", localNoteSuffix: "。サードパーティサービスには保存されません。", runLocally: "ローカルで実行", runLocallySentence: "ローカルで実行", openSource: "100% 無料でオープンソース", useOwnGoogleClient: "自分の Google OAuth クライアントを使用:", copyCommand: "コマンドをコピー", copied: "コピーしました", close: "閉じる", closeGoogleChoices: "Google サインインの選択肢を閉じる", legalPrefix: "登録すると、以下に同意したものとみなされます:", legalTerms: "利用規約", legalConnector: "および", legalPrivacy: "プライバシーポリシー", legalSuffix: "。", invalidEmail: "you@example.com のような有効なメールアドレスを入力してください。", signInToContinue: "続行するにはサインインしてください。", finishSignInFailed: "サインインを自動で完了できませんでした。", enterPasswordAfterVerification: "メールを確認した後、パスワードを入力してください。", finishSignInManually: "サインインを自動で完了できませんでした。続行するにはサインインしてください。", stillWaitingVerification: "まだ確認待ちです。メール内のリンクをクリックしてから、もう一度続行してください。", checkVerificationFailed: "確認状態をチェックできませんでした。もう一度お試しください。", checking: "確認中...", checkingVerification: "確認状態をチェック中...", sending: "送信中...", sent: "送信済み", sentVerification: "新しい確認リンクを送信しました。", resendVerificationFailed: "確認メールを再送信できませんでした。", networkErrorRetry: "ネットワークエラーです。もう一度お試しください。", networkErrorDashRetry: "ネットワークエラー — もう一度お試しください", passwordsMismatch: "パスワードが一致しません", creatingAccount: "アカウントを作成中…", registrationFailed: "登録に失敗しました", accountCreatedSigningIn: "アカウントを作成しました — サインイン中…", emailVerifiedFinishing: "メールを確認しました。サインインを完了中...", emailVerifiedSignIn: "メールを確認しました。続行するにはサインインしてください。", resetEmailSent: "そのメールが存在する場合、リセットリンクを送信しました。", resetEmailFailed: "リセットメールを送信できませんでした。", signingIn: "サインイン中…", invalidLogin: "メールまたはパスワードが正しくありません", googleNotConfigured: "Google OAuth が設定されていません。", failedToConnect: "接続できませんでした。もう一度お試しください。", migrateLocalFallback: "サインインを続けてローカルデータを移行します。", googlePopupHelp: "このサイトのポップアップを許可してから、もう一度お試しください", googleNeverFinished: "Google サインインが完了しませんでした。Google OAuth リダイレクト URI と [agent-native][google-oauth] のサーバーログを確認してください。", }, "ko-KR": { languageLabel: "언어", systemLanguage: "시스템", pageTitleSignIn: "로그인", pageTitleWelcome: "환영합니다", welcomeTitle: "환영합니다", signInTitle: "로그인", welcomeBackTitle: "다시 오신 것을 환영합니다", checkEmailTitle: "이메일을 확인하세요", resetPasswordTitle: "비밀번호 재설정", createAccountSubtitle: "계정을 만들고 시작하세요", googleOnlySubtitle: "워크스페이스 Google 계정으로 계속하기", signInSubtitle: "계정에 로그인하세요", finishAccountSubtitle: "계정 생성을 완료하세요", resetPasswordSubtitle: "비밀번호를 재설정하세요", upgradeCopy: "계속 로그인하여 이 앱을 계정에 연결하고 로컬 데이터를 마이그레이션하세요.", googleButton: "Google로 로그인", dividerOr: "또는", createAccount: "계정 만들기", signIn: "로그인", email: "이메일", password: "비밀번호", confirmPassword: "비밀번호 확인", passwordMinPlaceholder: "8자 이상", confirmPasswordPlaceholder: "비밀번호 확인", enterPasswordPlaceholder: "비밀번호 입력", signupProgress: "가입 진행 상황", progressAccount: "계정", progressVerify: "확인", progressStart: "시작", verificationSent: "확인 이메일을 보냈습니다", verifyCopyPrefix: "보안 링크를 보냈습니다:", verifyCopySuffix: ". 링크를 클릭하고 여기로 돌아오면 이 앱이 자동으로 로그인을 완료합니다.", verificationNote: "이 탭을 열어 두어도 됩니다. 돌아온 뒤 새로고침되지 않으면 계속을 누르세요.", continue: "계속", resendEmail: "이메일 다시 보내기", back: "뒤로", forgotPassword: "비밀번호를 잊으셨나요?", sendResetLink: "재설정 링크 보내기", backToSignIn: "로그인으로 돌아가기", localNotePrefix: "계정은 이 앱의 자체 DB에 저장됩니다", localNoteSuffix: ", 타사 서비스가 아닙니다.", runLocally: "로컬에서 실행", runLocallySentence: "로컬에서 실행", openSource: "100% 무료 오픈 소스", useOwnGoogleClient: "내 Google OAuth 클라이언트 사용:", copyCommand: "명령 복사", copied: "복사됨", close: "닫기", closeGoogleChoices: "Google 로그인 선택 닫기", legalPrefix: "가입하면 다음에 동의하게 됩니다:", legalTerms: "약관", legalConnector: "및", legalPrivacy: "개인정보 처리방침", legalSuffix: ".", invalidEmail: "you@example.com 같은 올바른 이메일 주소를 입력하세요.", signInToContinue: "계속하려면 로그인하세요.", finishSignInFailed: "자동으로 로그인을 완료할 수 없습니다.", enterPasswordAfterVerification: "이메일을 확인한 후 비밀번호를 입력하세요.", finishSignInManually: "자동으로 로그인을 완료할 수 없습니다. 계속하려면 로그인하세요.", stillWaitingVerification: "아직 확인을 기다리고 있습니다. 이메일의 링크를 클릭한 뒤 계속을 다시 눌러주세요.", checkVerificationFailed: "확인 상태를 확인할 수 없습니다. 다시 시도하세요.", checking: "확인 중...", checkingVerification: "확인 상태 확인 중...", sending: "보내는 중...", sent: "보냄", sentVerification: "새 확인 링크를 보냈습니다.", resendVerificationFailed: "확인 이메일을 다시 보낼 수 없습니다.", networkErrorRetry: "네트워크 오류입니다. 다시 시도하세요.", networkErrorDashRetry: "네트워크 오류 — 다시 시도하세요", passwordsMismatch: "비밀번호가 일치하지 않습니다", creatingAccount: "계정 생성 중…", registrationFailed: "가입 실패", accountCreatedSigningIn: "계정 생성됨 — 로그인 중…", emailVerifiedFinishing: "이메일 확인됨. 로그인 완료 중...", emailVerifiedSignIn: "이메일 확인됨. 계속하려면 로그인하세요.", resetEmailSent: "해당 이메일이 있으면 재설정 링크가 발송됩니다.", resetEmailFailed: "재설정 이메일을 보낼 수 없습니다.", signingIn: "로그인 중…", invalidLogin: "이메일 또는 비밀번호가 올바르지 않습니다", googleNotConfigured: "Google OAuth가 구성되지 않았습니다.", failedToConnect: "연결하지 못했습니다. 다시 시도하세요.", migrateLocalFallback: "계속 로그인하여 로컬 데이터를 마이그레이션하세요.", googlePopupHelp: "이 사이트의 팝업을 허용한 뒤 다시 시도하세요", googleNeverFinished: "Google 로그인이 완료되지 않았습니다. Google OAuth 리디렉션 URI와 [agent-native][google-oauth] 서버 로그를 확인하세요.", }, "pt-BR": { languageLabel: "Idioma", systemLanguage: "Sistema", pageTitleSignIn: "Entrar", pageTitleWelcome: "Boas-vindas", welcomeTitle: "Boas-vindas", signInTitle: "Entrar", welcomeBackTitle: "Bem-vindo de volta", checkEmailTitle: "Confira seu email", resetPasswordTitle: "Redefinir senha", createAccountSubtitle: "Crie uma conta para começar", googleOnlySubtitle: "Use sua conta Google do workspace para continuar", signInSubtitle: "Entre na sua conta", finishAccountSubtitle: "Finalize a criação da sua conta", resetPasswordSubtitle: "Redefina sua senha", upgradeCopy: "Continue entrando para conectar este app à sua conta e migrar dados locais.", googleButton: "Entrar com Google", dividerOr: "ou", createAccount: "Criar conta", signIn: "Entrar", email: "Email", password: "Senha", confirmPassword: "Confirmar senha", passwordMinPlaceholder: "Pelo menos 8 caracteres", confirmPasswordPlaceholder: "Confirmar senha", enterPasswordPlaceholder: "Digite a senha", signupProgress: "Progresso do cadastro", progressAccount: "Conta", progressVerify: "Verificar", progressStart: "Começar", verificationSent: "Email de verificação enviado", verifyCopyPrefix: "Enviamos um link seguro para", verifyCopySuffix: ". Clique nele, volte aqui e este app terminará o login automaticamente.", verificationNote: "Você pode manter esta aba aberta. Se ela não atualizar quando você voltar, use Continuar.", continue: "Continuar", resendEmail: "Reenviar email", back: "Voltar", forgotPassword: "Esqueceu a senha?", sendResetLink: "Enviar link de redefinição", backToSignIn: "Voltar para entrar", localNotePrefix: "Sua conta fica armazenada no banco de dados próprio deste app", localNoteSuffix: ", não em um serviço de terceiros.", runLocally: "Executar localmente", runLocallySentence: "Executar localmente", openSource: "100% grátis e open source", useOwnGoogleClient: "Use seu próprio cliente Google OAuth:", copyCommand: "Copiar comando", copied: "Copiado", close: "Fechar", closeGoogleChoices: "Fechar opções de login com Google", legalPrefix: "Ao se cadastrar, você aceita nossos", legalTerms: "Termos", legalConnector: "e", legalPrivacy: "Política de Privacidade", legalSuffix: ".", invalidEmail: "Digite um email válido, como you@example.com.", signInToContinue: "Entre para continuar.", finishSignInFailed: "Não foi possível concluir o login automaticamente.", enterPasswordAfterVerification: "Digite sua senha depois de verificar seu email.", finishSignInManually: "Não foi possível concluir o login automaticamente. Entre para continuar.", stillWaitingVerification: "Ainda estamos aguardando a verificação. Clique no link do email e tente Continuar novamente.", checkVerificationFailed: "Não foi possível verificar. Tente novamente.", checking: "Verificando...", checkingVerification: "Verificando sua confirmação...", sending: "Enviando...", sent: "Enviado", sentVerification: "Enviamos um novo link de verificação.", resendVerificationFailed: "Não foi possível reenviar o email de verificação.", networkErrorRetry: "Erro de rede. Tente novamente.", networkErrorDashRetry: "Erro de rede — tente novamente", passwordsMismatch: "As senhas não conferem", creatingAccount: "Criando conta…", registrationFailed: "Falha no cadastro", accountCreatedSigningIn: "Conta criada — entrando…", emailVerifiedFinishing: "Email verificado. Concluindo login...", emailVerifiedSignIn: "Email verificado. Entre para continuar.", resetEmailSent: "Se esse email existir, um link de redefinição está a caminho.", resetEmailFailed: "Não foi possível enviar o email de redefinição.", signingIn: "Entrando…", invalidLogin: "Email ou senha inválidos", googleNotConfigured: "Google OAuth não está configurado.", failedToConnect: "Não foi possível conectar. Tente novamente.", migrateLocalFallback: "Continue entrando para migrar dados locais.", googlePopupHelp: "Permita pop-ups para este site e tente novamente", googleNeverFinished: "O login com Google não terminou. Confira o URI de redirecionamento do Google OAuth e os logs do servidor para [agent-native][google-oauth].", }, "hi-IN": { languageLabel: "भाषा", systemLanguage: "सिस्टम", pageTitleSignIn: "साइन इन", pageTitleWelcome: "स्वागत है", welcomeTitle: "स्वागत है", signInTitle: "साइन इन", welcomeBackTitle: "वापस स्वागत है", checkEmailTitle: "अपना ईमेल देखें", resetPasswordTitle: "पासवर्ड रीसेट करें", createAccountSubtitle: "शुरू करने के लिए खाता बनाएं", googleOnlySubtitle: "जारी रखने के लिए अपना workspace Google खाता उपयोग करें", signInSubtitle: "अपने खाते में साइन इन करें", finishAccountSubtitle: "अपना खाता बनाना पूरा करें", resetPasswordSubtitle: "अपना पासवर्ड रीसेट करें", upgradeCopy: "इस ऐप को अपने खाते से जोड़ने और स्थानीय डेटा माइग्रेट करने के लिए साइन इन जारी रखें।", googleButton: "Google से साइन इन करें", dividerOr: "या", createAccount: "खाता बनाएं", signIn: "साइन इन", email: "ईमेल", password: "पासवर्ड", confirmPassword: "पासवर्ड की पुष्टि करें", passwordMinPlaceholder: "कम से कम 8 अक्षर", confirmPasswordPlaceholder: "पासवर्ड की पुष्टि करें", enterPasswordPlaceholder: "पासवर्ड दर्ज करें", signupProgress: "साइनअप प्रगति", progressAccount: "खाता", progressVerify: "सत्यापित करें", progressStart: "शुरू करें", verificationSent: "सत्यापन ईमेल भेजा गया", verifyCopyPrefix: "हमने एक सुरक्षित लिंक भेजा है", verifyCopySuffix: "पर। लिंक खोलें, यहां वापस आएं, और यह ऐप अपने आप आपका साइन इन पूरा करेगा।", verificationNote: "आप यह टैब खुला रख सकते हैं। वापस आने पर अगर यह refresh नहीं होता है, तो Continue दबाएं।", continue: "जारी रखें", resendEmail: "ईमेल फिर भेजें", back: "वापस", forgotPassword: "पासवर्ड भूल गए?", sendResetLink: "रीसेट लिंक भेजें", backToSignIn: "साइन इन पर वापस जाएं", localNotePrefix: "आपका खाता इस ऐप के अपने DB में संग्रहीत है", localNoteSuffix: ", किसी third-party सेवा में नहीं।", runLocally: "लोकल चलाएं", runLocallySentence: "लोकल चलाएं", openSource: "100% मुफ्त और open source", useOwnGoogleClient: "अपना Google OAuth client उपयोग करें:", copyCommand: "कमांड कॉपी करें", copied: "कॉपी हो गया", close: "बंद करें", closeGoogleChoices: "Google साइन-इन विकल्प बंद करें", legalPrefix: "साइन अप करके, आप हमारी", legalTerms: "शर्तें", legalConnector: "और", legalPrivacy: "गोपनीयता नीति", legalSuffix: "स्वीकार करते हैं।", invalidEmail: "एक मान्य ईमेल पता दर्ज करें, जैसे you@example.com.", signInToContinue: "जारी रखने के लिए साइन इन करें।", finishSignInFailed: "साइन इन अपने आप पूरा नहीं हो सका।", enterPasswordAfterVerification: "ईमेल सत्यापित करने के बाद अपना पासवर्ड दर्ज करें।", finishSignInManually: "साइन इन अपने आप पूरा नहीं हो सका। जारी रखने के लिए साइन इन करें।", stillWaitingVerification: "सत्यापन का इंतजार है। ईमेल में लिंक खोलें, फिर Continue दोबारा दबाएं।", checkVerificationFailed: "सत्यापन जांच नहीं हो सकी। कृपया फिर कोशिश करें।", checking: "जांच हो रही है...", checkingVerification: "आपका सत्यापन जांच रहे हैं...", sending: "भेजा जा रहा है...", sent: "भेजा गया", sentVerification: "नया सत्यापन लिंक भेजा गया।", resendVerificationFailed: "सत्यापन ईमेल फिर नहीं भेजा जा सका।", networkErrorRetry: "नेटवर्क त्रुटि। कृपया फिर कोशिश करें।", networkErrorDashRetry: "नेटवर्क त्रुटि — कृपया फिर कोशिश करें", passwordsMismatch: "पासवर्ड मेल नहीं खाते", creatingAccount: "खाता बनाया जा रहा है…", registrationFailed: "रजिस्ट्रेशन असफल", accountCreatedSigningIn: "खाता बन गया — साइन इन हो रहा है…", emailVerifiedFinishing: "ईमेल सत्यापित। साइन इन पूरा हो रहा है...", emailVerifiedSignIn: "ईमेल सत्यापित। जारी रखने के लिए साइन इन करें।", resetEmailSent: "अगर वह ईमेल मौजूद है, तो reset लिंक भेजा जा रहा है।", resetEmailFailed: "रीसेट ईमेल नहीं भेजा जा सका।", signingIn: "साइन इन हो रहा है…", invalidLogin: "ईमेल या पासवर्ड अमान्य है", googleNotConfigured: "Google OAuth configured नहीं है।", failedToConnect: "कनेक्ट नहीं हो सका। कृपया फिर कोशिश करें।", migrateLocalFallback: "स्थानीय डेटा माइग्रेट करने के लिए साइन इन जारी रखें।", googlePopupHelp: "इस साइट के लिए pop-ups allow करें और फिर कोशिश करें", googleNeverFinished: "Google साइन इन पूरा नहीं हुआ। Google OAuth redirect URI और [agent-native][google-oauth] के server logs देखें।", }, "ar-SA": { languageLabel: "اللغة", systemLanguage: "النظام", pageTitleSignIn: "تسجيل الدخول", pageTitleWelcome: "مرحبًا", welcomeTitle: "مرحبًا", signInTitle: "تسجيل الدخول", welcomeBackTitle: "مرحبًا بعودتك", checkEmailTitle: "تحقق من بريدك الإلكتروني", resetPasswordTitle: "إعادة تعيين كلمة المرور", createAccountSubtitle: "أنشئ حسابًا للبدء", googleOnlySubtitle: "استخدم حساب Google الخاص بمساحة العمل للمتابعة", signInSubtitle: "سجّل الدخول إلى حسابك", finishAccountSubtitle: "أكمل إنشاء حسابك", resetPasswordSubtitle: "أعد تعيين كلمة المرور", upgradeCopy: "تابع تسجيل الدخول لربط هذا التطبيق بحسابك وترحيل البيانات المحلية.", googleButton: "تسجيل الدخول باستخدام Google", dividerOr: "أو", createAccount: "إنشاء حساب", signIn: "تسجيل الدخول", email: "البريد الإلكتروني", password: "كلمة المرور", confirmPassword: "تأكيد كلمة المرور", passwordMinPlaceholder: "8 أحرف على الأقل", confirmPasswordPlaceholder: "تأكيد كلمة المرور", enterPasswordPlaceholder: "أدخل كلمة المرور", signupProgress: "تقدم التسجيل", progressAccount: "الحساب", progressVerify: "التحقق", progressStart: "البدء", verificationSent: "تم إرسال رسالة التحقق", verifyCopyPrefix: "أرسلنا رابطًا آمنًا إلى", verifyCopySuffix: ". افتحه، ثم عُد إلى هنا وسيكمل هذا التطبيق تسجيل دخولك تلقائيًا.", verificationNote: "يمكنك إبقاء هذه النافذة مفتوحة. إذا لم يتم التحديث بعد عودتك، استخدم متابعة.", continue: "متابعة", resendEmail: "إعادة إرسال البريد", back: "رجوع", forgotPassword: "هل نسيت كلمة المرور؟", sendResetLink: "إرسال رابط إعادة التعيين", backToSignIn: "العودة إلى تسجيل الدخول", localNotePrefix: "يتم تخزين حسابك في قاعدة بيانات هذا التطبيق", localNoteSuffix: "، وليس في خدمة خارجية.", runLocally: "تشغيل محليًا", runLocallySentence: "تشغيل محليًا", openSource: "مجاني ومفتوح المصدر 100%", useOwnGoogleClient: "استخدم عميل Google OAuth الخاص بك:", copyCommand: "نسخ الأمر", copied: "تم النسخ", close: "إغلاق", closeGoogleChoices: "إغلاق خيارات تسجيل الدخول عبر Google", legalPrefix: "بالتسجيل، فإنك توافق على", legalTerms: "الشروط", legalConnector: "و", legalPrivacy: "سياسة الخصوصية", legalSuffix: ".", invalidEmail: "أدخل بريدًا إلكترونيًا صالحًا، مثل you@example.com.", signInToContinue: "سجّل الدخول للمتابعة.", finishSignInFailed: "تعذر إكمال تسجيل الدخول تلقائيًا.", enterPasswordAfterVerification: "أدخل كلمة المرور بعد التحقق من بريدك الإلكتروني.", finishSignInManually: "تعذر إكمال تسجيل الدخول تلقائيًا. سجّل الدخول للمتابعة.", stillWaitingVerification: "ما زلنا ننتظر التحقق. افتح الرابط في بريدك الإلكتروني ثم جرّب متابعة مرة أخرى.", checkVerificationFailed: "تعذر التحقق من الحالة. حاول مرة أخرى.", checking: "جارٍ التحقق...", checkingVerification: "جارٍ التحقق من حالتك...", sending: "جارٍ الإرسال...", sent: "تم الإرسال", sentVerification: "تم إرسال رابط تحقق جديد.", resendVerificationFailed: "تعذر إعادة إرسال رسالة التحقق.", networkErrorRetry: "خطأ في الشبكة. حاول مرة أخرى.", networkErrorDashRetry: "خطأ في الشبكة — حاول مرة أخرى", passwordsMismatch: "كلمتا المرور غير متطابقتين", creatingAccount: "جارٍ إنشاء الحساب…", registrationFailed: "فشل التسجيل", accountCreatedSigningIn: "تم إنشاء الحساب — جارٍ تسجيل الدخول…", emailVerifiedFinishing: "تم التحقق من البريد. جارٍ إكمال تسجيل الدخول...", emailVerifiedSignIn: "تم التحقق من البريد. سجّل الدخول للمتابعة.", resetEmailSent: "إذا كان هذا البريد موجودًا، فسيصل رابط إعادة التعيين.", resetEmailFailed: "تعذر إرسال بريد إعادة التعيين.", signingIn: "جارٍ تسجيل الدخول…", invalidLogin: "البريد الإلكتروني أو كلمة المرور غير صحيحة", googleNotConfigured: "لم يتم إعداد Google OAuth.", failedToConnect: "تعذر الاتصال. حاول مرة أخرى.", migrateLocalFallback: "تابع تسجيل الدخول لترحيل البيانات المحلية.", googlePopupHelp: "اسمح بالنوافذ المنبثقة لهذا الموقع ثم حاول مرة أخرى", googleNeverFinished: "لم يكتمل تسجيل الدخول عبر Google. تحقق من URI إعادة التوجيه في Google OAuth وسجلات الخادم لـ [agent-native][google-oauth].", }, }; const defaultAuthCopy = AUTH_LOCALE_COPY[DEFAULT_LOCALE]; type AuthMarketingLocalization = Pick< AuthMarketingContent, "tagline" | "description" | "features" >; const AUTH_MARKETING_LOCALE_COPY: Partial< Record>> > = { "zh-CN": { forms: { tagline: "你的 AI 代理与你一起构建、发布和分析表单。", features: [ "用一句话创建完整表单", "即时发布,生成可分享链接和验证码", "按需获取回复摘要、导出和趋势分析", ], }, }, "zh-TW": { forms: { tagline: "你的 AI 代理會和你一起建立、發布與分析表單。", features: [ "用一句話建立完整表單", "立即發布,產生可分享連結與驗證碼", "依需求取得回覆摘要、匯出與趨勢分析", ], }, }, "es-ES": { forms: { tagline: "Tu agente de IA crea, publica y analiza formularios contigo.", features: [ "Crea formularios completos con una sola frase", "Publicación instantánea con enlaces compartibles y captcha", "Resúmenes de respuestas, exportaciones y análisis de tendencias al instante", ], }, }, "fr-FR": { forms: { tagline: "Votre agent IA crée, publie et analyse des formulaires avec vous.", features: [ "Créez des formulaires complets à partir d'une seule phrase", "Publication instantanée avec liens partageables et captcha", "Résumés de réponses, exports et analyse des tendances à la demande", ], }, }, "de-DE": { forms: { tagline: "Dein KI-Agent erstellt, veröffentlicht und analysiert Formulare mit dir.", features: [ "Erstelle vollständige Formulare aus einem einzigen Satz", "Sofortige Veröffentlichung mit teilbaren Links und Captcha", "Antwortzusammenfassungen, Exporte und Trendanalysen auf Abruf", ], }, }, "ja-JP": { forms: { tagline: "AI エージェントがフォームの作成、公開、分析を一緒に進めます。", features: [ "一文から完全なフォームを作成", "共有リンクと CAPTCHA 付きで即時公開", "回答の要約、エクスポート、トレンド分析を必要なときに実行", ], }, }, "ko-KR": { forms: { tagline: "AI 에이전트가 양식 생성, 게시, 분석을 함께 도와줍니다.", features: [ "한 문장으로 완성된 양식 만들기", "공유 링크와 captcha로 즉시 게시", "응답 요약, 내보내기, 추세 분석을 필요할 때 실행", ], }, }, "pt-BR": { forms: { tagline: "Seu agente de IA cria, publica e analisa formulários junto com você.", features: [ "Crie formulários completos a partir de uma única frase", "Publicação instantânea com links compartilháveis e captcha", "Resumos de respostas, exportações e análise de tendências sob demanda", ], }, }, "hi-IN": { forms: { tagline: "आपका AI एजेंट आपके साथ फ़ॉर्म बनाता, प्रकाशित करता और उनका विश्लेषण करता है।", features: [ "एक वाक्य से पूरे फ़ॉर्म बनाएं", "शेयर करने योग्य लिंक और captcha के साथ तुरंत प्रकाशित करें", "ज़रूरत पड़ने पर प्रतिक्रिया सारांश, exports और trend analysis पाएं", ], }, }, "ar-SA": { forms: { tagline: "يساعدك وكيل الذكاء الاصطناعي على إنشاء النماذج ونشرها وتحليلها.", features: [ "أنشئ نماذج كاملة من جملة واحدة", "نشر فوري مع روابط قابلة للمشاركة وcaptcha", "ملخصات للإجابات وتصدير وتحليل اتجاهات عند الطلب", ], }, }, }; function resolveBuiltInMarketingSlug( marketing: AuthMarketingContent | undefined, ): string | undefined { if (!marketing) return undefined; for (const [slug, builtIn] of Object.entries(BUILT_IN_AUTH_MARKETING)) { if ( marketing.appName === builtIn.appName && marketing.tagline === builtIn.tagline ) { return slug; } } return undefined; } export interface SignupLegalNoticeOptions { termsUrl: string; privacyUrl: string; termsLabel?: string; privacyLabel?: string; prefix?: string; connector?: string; suffix?: string; } function normalizeRequestHostname(host: string | undefined): string { const firstHost = host?.split(",")[0]?.trim().toLowerCase() ?? ""; if (!firstHost) return ""; if (firstHost.startsWith("[")) { const close = firstHost.indexOf("]"); return close > 0 ? firstHost.slice(1, close) : firstHost; } return firstHost.replace(/:\d+$/, ""); } function isAgentNativeHostedHost(host: string | undefined): boolean { const hostname = normalizeRequestHostname(host); return ( hostname === "agent-native.com" || hostname.endsWith(".agent-native.com") ); } export interface OnboardingHtmlOptions { /** * Hide email/password forms and show ONLY the Google sign-in button. * Useful for templates (mail, calendar) where Google is required anyway. * If Google OAuth env vars are not configured, an error message is shown. */ googleOnly?: boolean; /** * Product marketing content shown alongside the sign-in form. * When provided, the page uses a split layout: marketing on the left, * sign-in form on the right (stacked on mobile). */ marketing?: { appName: string; tagline: string; description?: string; features?: string[]; runLocalCommand?: string; }; /** * Request context used only to recover branded first-party marketing when a * default auth guard serves before a template-specific auth plugin. */ requestHost?: string; requestPath?: string; requestOrigin?: string; /** * Optional preflight copy shown before redirecting through Google sign-in. * Use this when a hosted app needs to warn about provider-specific consent * screens while leaving self-hosted deployments untouched. */ googleSignInNotice?: { host?: string; title: string; body: string | string[]; continueLabel?: string; cancelLabel?: string; }; /** * Optional email signup legal copy. Builder-hosted `*.agent-native.com` * deployments get the Agent Native links automatically; self-hosted and * custom-domain apps must opt in with their own URLs. */ signupLegalNotice?: SignupLegalNoticeOptions | false; /** * Google sign-in flow: `'popup'`, `'redirect'`, or `'auto'` (default). * Falls back to `GOOGLE_AUTH_MODE` env var, then `'auto'`. Builder web * iframes use popup; Builder desktop preview/editor surfaces use redirect. */ googleAuthMode?: GoogleAuthMode; } export function getOnboardingHtml(opts: OnboardingHtmlOptions = {}): string { const showGoogle = hasGoogleOAuth(); const googleOnly = !!opts.googleOnly; // In a Google-only app, Google is the sole sign-in method, so always render // a working button — never gate it on env vars detected at render time. The // login page is a public, CDN-cacheable shell served to everyone (per-user // and per-config state is resolved client-side after load), so baking a // "not configured" message in here would freeze that error into the cache // for every visitor. A genuinely misconfigured server instead surfaces a // clear error at click time via the auth API. const renderGoogleButton = showGoogle || googleOnly; const appBasePath = normalizeAppBasePath( process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH, ); const publicOAuthOrigin = getPublicOAuthOrigin(); const workspaceGatewayReturnOrigin = getWorkspaceGatewayReturnOrigin(); const googleAuthMode = resolveGoogleAuthMode(opts.googleAuthMode); const localeInitScript = getLocaleInitScript(); const marketing: AuthMarketingContent | undefined = opts.marketing ?? resolveBuiltInAuthMarketing({ requestHost: opts.requestHost, requestPath: opts.requestPath, }); const hasMarketing = !!marketing; const marketingSlug = resolveBuiltInMarketingSlug(marketing); const defaultMarketingCopy: Partial | undefined = marketing ? { tagline: marketing.tagline, description: marketing.description, features: marketing.features, } : undefined; const runLocalCommand = marketing?.runLocalCommand?.trim(); const signupLocalModeNote = isAgentNativeHostedHost(opts.requestHost) && marketing?.signupLocalModeNote?.command.trim() ? { text: marketing.signupLocalModeNote.text.trim(), command: marketing.signupLocalModeNote.command.trim(), } : undefined; const brandMarkSrc = withAppBasePath("/agent-native-icon-dark.svg"); const socialImageUrl = withAgentNativeSocialImageCacheBuster( opts.requestOrigin ? `${opts.requestOrigin}${withAppBasePath(AGENT_NATIVE_SOCIAL_IMAGE_PATH)}` : withAppBasePath(AGENT_NATIVE_SOCIAL_IMAGE_PATH), ); const esc = (s: string) => s .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); const t = (key: keyof typeof EN_AUTH_COPY) => defaultAuthCopy[key]; const i18nAttr = (key: keyof typeof EN_AUTH_COPY | undefined) => key ? ` data-i18n="${key}"` : ""; const i18nAriaAttr = (key: keyof typeof EN_AUTH_COPY | undefined) => key ? ` data-i18n-aria-label="${key}"` : ""; const i18nPlaceholderAttr = (key: keyof typeof EN_AUTH_COPY | undefined) => key ? ` data-i18n-placeholder="${key}"` : ""; const i18nDataAttr = ( attr: string, key: keyof typeof EN_AUTH_COPY | undefined, ) => (key ? ` data-i18n-${attr}="${key}"` : ""); const i18nText = (key: keyof typeof EN_AUTH_COPY) => `${esc(t(key))}`; const localizedValue = ( value: string | undefined, key: keyof typeof EN_AUTH_COPY, ) => (value === undefined ? i18nText(key) : esc(value)); const localizedAnchorLabel = ( value: string | undefined, key: keyof typeof EN_AUTH_COPY, ) => value === undefined ? `${i18nAttr(key)}>${esc(t(key))}` : `>${esc(value)}`; const localeMenuItemsHtml = [ ` `, ...SUPPORTED_LOCALES.map((locale) => { const metadata = LOCALE_METADATA[locale]; const label = metadata.nativeName === metadata.englishName ? `${metadata.nativeName} (${metadata.code})` : `${metadata.nativeName} (${metadata.englishName})`; return ` `; }), ].join("\n"); const localePickerHtml = `
`; const hostedSignupLegalNotice: SignupLegalNoticeOptions | undefined = opts.signupLegalNotice === undefined && isAgentNativeHostedHost(opts.requestHost) ? { termsUrl: AGENT_NATIVE_TERMS_URL, privacyUrl: AGENT_NATIVE_PRIVACY_URL, } : undefined; const signupLegalNotice = opts.signupLegalNotice === false ? undefined : (opts.signupLegalNotice ?? hostedSignupLegalNotice); const signupLegalNoteHtml = signupLegalNotice ? ` ` : ""; const signupLocalModeNoteHtml = signupLocalModeNote ? ` ` : ""; const googleSignInNotice = opts.googleSignInNotice; const googleNoticeBodyParts = googleSignInNotice ? (Array.isArray(googleSignInNotice.body) ? googleSignInNotice.body : [googleSignInNotice.body] ).filter((body) => body.trim().length > 0) : []; const googleNoticeBodyHtml = googleNoticeBodyParts .map( (body, index) => `

${esc(body)}

`, ) .join("\n"); const googleNoticeRunLocalHtml = runLocalCommand ? ` ` : ` `; const googleNoticeRunLocalPanelHtml = runLocalCommand ? ` ` : ""; const googleNoticeHtml = renderGoogleButton && googleSignInNotice ? ` ` : ""; const identitySsoHtml = identitySsoLoginButtonHtml(); const identitySsoScript = identitySsoHtml ? ` function __anIdentitySsoUrl() { var params = new URLSearchParams(); params.set('return', __anResumeHref()); return __anPath('/_agent-native/identity/login') + '?' + params.toString(); } function __anStartIdentitySso(event) { if (event && event.preventDefault) event.preventDefault(); window.location.href = __anIdentitySsoUrl(); return false; } (function __anPrepareIdentitySsoButton() { var identity = document.getElementById('identity-sso-btn'); if (!identity) return; identity.setAttribute('href', __anIdentitySsoUrl()); identity.addEventListener('click', __anStartIdentitySso); })();` : ""; const marketingStyles = hasMarketing ? ` body.has-marketing { padding: 0; position: relative; overflow-x: hidden; } #starfield { position: fixed; inset: 0; width: 100%; height: 100%; opacity: 0.35; pointer-events: none; z-index: 0; } @media (prefers-reduced-motion: reduce) { #starfield { opacity: 0.18; } } .split { position: relative; z-index: 1; display: flex; min-height: 100vh; width: 100%; max-width: 1100px; margin: 0 auto; } .marketing-panel { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: 3rem 3.5rem; } .marketing-content { max-width: 480px; } .app-name { display: flex; align-items: center; gap: 0.625rem; font-size: 2rem; font-weight: 700; color: #fff; margin-bottom: 0.625rem; letter-spacing: -0.02em; } .app-name img.brand-mark { height: 2.21375rem; width: auto; display: block; flex-shrink: 0; } .app-tagline { font-size: 1.25rem; color: #a1a1aa; line-height: 1.6; margin-bottom: 2rem; } .app-desc { font-size: 1rem; color: #71717a; line-height: 1.6; margin-bottom: 2rem; } .feature-list { list-style: none; display: flex; flex-direction: column; gap: 0.875rem; } .feature-list li { display: flex; align-items: flex-start; gap: 0.625rem; font-size: 1rem; color: #a1a1aa; line-height: 1.5; } .feature-list li::before { content: ''; flex-shrink: 0; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: #3f3f46; border: 1px solid #52525b; } .oss-link { display: inline-flex; align-items: center; gap: 0.375rem; font-size: 0.8125rem; font-weight: 600; color: #00B5FF; text-decoration: none; transition: color 0.15s ease; } .oss-link:hover { color: #33C4FF; } .oss-link svg { width: 15px; height: 15px; flex-shrink: 0; } .marketing-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-top: 2rem; } .run-local-button { display: inline-flex; align-items: center; justify-content: center; min-height: 2.25rem; padding: 0.5rem 0.875rem; background: rgba(255,255,255,0.08); color: #fff; border: 1px solid rgba(255,255,255,0.14); border-radius: 8px; font-size: 0.8125rem; font-weight: 500; cursor: pointer; } .run-local-button:hover { background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.24); } .run-local-panel { max-width: 480px; margin-top: 0.75rem; padding: 0.75rem; background: rgba(20,20,20,0.86); border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; box-shadow: 0 14px 36px rgba(0,0,0,0.28); } .run-local-panel[hidden] { display: none; } .run-local-panel code { display: block; overflow-x: auto; padding-bottom: 0.125rem; color: #e5e5e5; font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; font-size: 0.75rem; line-height: 1.5; white-space: nowrap; } .copy-run-local { margin-top: 0.625rem; padding: 0.375rem 0.625rem; background: transparent; color: #a1a1aa; border: 1px solid rgba(255,255,255,0.12); border-radius: 6px; font-size: 0.75rem; cursor: pointer; } .copy-run-local:hover { color: #fff; border-color: rgba(255,255,255,0.22); } .form-panel { flex: 0 0 440px; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 2rem; } .form-panel .card { max-width: 400px; } .form-panel .local-note { max-width: 400px; } @media (max-width: 900px) { .split { flex-direction: column; min-height: auto; } .marketing-panel { padding: 4.25rem 1.5rem 1.5rem; } .app-name { font-size: 1.375rem; } .app-name img.brand-mark { height: 1.58125rem; } .app-tagline { font-size: 1rem; margin-bottom: 1rem; } .app-desc { margin-bottom: 1rem; } .feature-list { gap: 0.5rem; } .form-panel { flex: none; padding: 1.5rem 1rem; } } ` : ""; const marketingPanelHtml = hasMarketing ? `
` : ""; const starfieldScript = hasMarketing ? ` (function initStarfield() { var canvas = document.getElementById('starfield'); if (!canvas) return; var gl = canvas.getContext('webgl', { alpha: false, antialias: false }); if (!gl) return; var vs = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vs, 'attribute vec2 position;void main(){gl_Position=vec4(position,0.0,1.0);}'); gl.compileShader(vs); var fs = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fs, [ 'precision highp float;', 'uniform float iTime;uniform vec2 iResolution;uniform vec3 uPointer;', '#define S(a,b,t) smoothstep(a,b,t)', '#define NUM_LAYERS 4.', 'float N21(vec2 p){vec3 a=fract(vec3(p.xyx)*vec3(213.897,653.453,253.098));a+=dot(a,a.yzx+79.76);return fract((a.x+a.y)*a.z);}', 'vec2 GetPos(vec2 id,vec2 offs,float t){float n=N21(id+offs);float n1=fract(n*10.);float n2=fract(n*100.);float a=t+n;return offs+vec2(sin(a*n1),cos(a*n2))*.4;}', 'vec2 Attract(vec2 p,vec2 cursor,float strength){vec2 delta=cursor-p;float d=length(delta);float pull=1.-smoothstep(.08,1.9,d);pull=pull*pull*(3.-2.*pull);return p+delta*pull*.095*strength;}', 'float df_line(vec2 a,vec2 b,vec2 p){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/dot(ba,ba),0.,1.);return length(pa-ba*h);}', 'float line(vec2 a,vec2 b,vec2 uv){float r1=.025;float r2=.006;float d=df_line(a,b,uv);float d2=length(a-b);float fade=S(1.5,.5,d2);fade+=S(.05,.02,abs(d2-.75));return S(r1,r2,d)*fade;}', 'float NetLayer(vec2 st,float n,float t,vec2 pointer,float pointerStrength){', ' vec2 cell=floor(st);vec2 id=cell+n;vec2 cursor=pointer-cell;st=fract(st)-.5;', ' vec2 p0=Attract(GetPos(id,vec2(-1,-1),t),cursor,pointerStrength);vec2 p1=Attract(GetPos(id,vec2(0,-1),t),cursor,pointerStrength);vec2 p2=Attract(GetPos(id,vec2(1,-1),t),cursor,pointerStrength);', ' vec2 p3=Attract(GetPos(id,vec2(-1,0),t),cursor,pointerStrength);vec2 p4=Attract(GetPos(id,vec2(0,0),t),cursor,pointerStrength);vec2 p5=Attract(GetPos(id,vec2(1,0),t),cursor,pointerStrength);', ' vec2 p6=Attract(GetPos(id,vec2(-1,1),t),cursor,pointerStrength);vec2 p7=Attract(GetPos(id,vec2(0,1),t),cursor,pointerStrength);vec2 p8=Attract(GetPos(id,vec2(1,1),t),cursor,pointerStrength);', ' float m=0.;float sparkle=0.;float d;float s;float pulse;', ' m+=line(p4,p0,st);d=length(st-p0);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p0.x)+fract(p0.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p1,st);d=length(st-p1);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p1.x)+fract(p1.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p2,st);d=length(st-p2);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p2.x)+fract(p2.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p3,st);d=length(st-p3);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p3.x)+fract(p3.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p4,st);d=length(st-p4);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p4.x)+fract(p4.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p5,st);d=length(st-p5);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p5.x)+fract(p5.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p6,st);d=length(st-p6);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p6.x)+fract(p6.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p7,st);d=length(st-p7);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p7.x)+fract(p7.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p4,p8,st);d=length(st-p8);s=(.005/(d*d));s*=S(1.,.7,d);pulse=sin((fract(p8.x)+fract(p8.y)+t)*5.)*.4+.6;pulse=pow(pulse,20.);sparkle+=s*pulse;', ' m+=line(p1,p3,st);m+=line(p1,p5,st);m+=line(p7,p5,st);m+=line(p7,p3,st);', ' float sPhase=(sin(t+n)+sin(t*.1))*.25+.5;sPhase+=pow(sin(t*.1)*.5+.5,50.)*5.;m+=sparkle*sPhase;', ' return m;', '}', 'void mainImage(out vec4 fragColor,in vec2 fragCoord){', ' vec2 uv=(fragCoord-iResolution.xy*.5)/iResolution.y;', ' float t=iTime*.03;float s=sin(t);float c=cos(t);mat2 rot=mat2(c,-s,s,c);vec2 st=uv*rot;vec2 pointerUv=(uPointer.xy-iResolution.xy*.5)/iResolution.y;', ' float m=0.;', ' for(float i=0.;i<1.;i+=1./NUM_LAYERS){float z=fract(t+i);float size=mix(15.,1.,z);float fade=S(0.,.6,z)*S(1.,.8,z);vec2 pointerSt=pointerUv*rot*size;vec2 layerSt=st*size;float warp=1.-smoothstep(.15,2.7,length(layerSt-pointerSt));warp=warp*warp*(3.-2.*warp)*uPointer.z;layerSt-=(pointerSt-layerSt)*warp*.035;m+=fade*NetLayer(layerSt,i,iTime*0.3,pointerSt,uPointer.z);}', ' float cursorLift=1.-smoothstep(.04,.48,length(uv-pointerUv));cursorLift=cursorLift*cursorLift*(3.-2.*cursorLift)*uPointer.z;m*=1.+cursorLift*1.6;', ' vec3 col=vec3(0.35)*m;col*=1.-dot(uv,uv);', ' float tt=min(iTime,5.0);col*=S(0.,20.,tt);', ' col=clamp(col,0.,1.);fragColor=vec4(col,1.);', '}', 'void main(){mainImage(gl_FragColor,gl_FragCoord.xy);}' ].join('\\n')); gl.compileShader(fs); var prog = gl.createProgram(); gl.attachShader(prog, vs); gl.attachShader(prog, fs); gl.linkProgram(prog); gl.useProgram(prog); var buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), gl.STATIC_DRAW); var pos = gl.getAttribLocation(prog, 'position'); gl.enableVertexAttribArray(pos); gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0); var uTime = gl.getUniformLocation(prog, 'iTime'); var uRes = gl.getUniformLocation(prog, 'iResolution'); var uPointer = gl.getUniformLocation(prog, 'uPointer'); var reducedMotionQuery = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)') : null; var reducedMotion = reducedMotionQuery ? reducedMotionQuery.matches : false; var pointerDpr = 1, hasPointer = false; var pointerX = 0, pointerY = 0, pointerStrength = 0; var targetX = 0, targetY = 0, targetStrength = 0; function resize() { var w = window.innerWidth, h = window.innerHeight; pointerDpr = Math.min(window.devicePixelRatio, 1.5); canvas.width = w * pointerDpr; canvas.height = h * pointerDpr; gl.viewport(0, 0, canvas.width, canvas.height); if (!hasPointer) { pointerX = targetX = canvas.width * 0.5; pointerY = targetY = canvas.height * 0.5; } } function onPointerMove(event) { var rect = canvas.getBoundingClientRect(); var x = event.clientX - rect.left; var y = event.clientY - rect.top; hasPointer = true; targetX = x * pointerDpr; targetY = (rect.height - y) * pointerDpr; targetStrength = x >= 0 && x <= rect.width && y >= 0 && y <= rect.height ? 1 : 0; } function fadePointer() { targetStrength = 0; } function easePointer(allowPointer) { if (!allowPointer) { pointerStrength = 0; return; } pointerX += (targetX - pointerX) * 0.22; pointerY += (targetY - pointerY) * 0.22; pointerStrength += (targetStrength - pointerStrength) * 0.14; if (pointerStrength < 0.001 && targetStrength === 0) pointerStrength = 0; } resize(); window.addEventListener('resize', resize); window.addEventListener('pointermove', onPointerMove, { passive: true }); window.addEventListener('mousemove', onPointerMove, { passive: true }); document.addEventListener('pointerleave', fadePointer, { passive: true }); window.addEventListener('blur', fadePointer); var start = performance.now(), last = 0, raf = 0, reducedMotionStaticTime = 20; function draw(timeSeconds, allowPointer) { easePointer(allowPointer !== false && !reducedMotion); gl.uniform1f(uTime, timeSeconds); gl.uniform2f(uRes, canvas.width, canvas.height); gl.uniform3f(uPointer, pointerX, pointerY, pointerStrength); gl.drawArrays(gl.TRIANGLES, 0, 6); } function render(now) { if (reducedMotion) { raf = 0; return; } raf = requestAnimationFrame(render); if (now - last < 33) return; last = now; draw((now - start) * 0.001); } function startAnimation() { if (!raf) raf = requestAnimationFrame(render); } function stopAnimation() { if (raf) { cancelAnimationFrame(raf); raf = 0; } } function onReducedMotionChange() { reducedMotion = reducedMotionQuery ? reducedMotionQuery.matches : false; if (reducedMotion) { stopAnimation(); last = 0; draw(reducedMotionStaticTime, false); } else { startAnimation(); } } draw(reducedMotion ? reducedMotionStaticTime : 0, !reducedMotion); if (reducedMotionQuery) { if (reducedMotionQuery.addEventListener) { reducedMotionQuery.addEventListener('change', onReducedMotionChange); } else { reducedMotionQuery.addListener(onReducedMotionChange); } } if (!reducedMotion) startAnimation(); })();` : ""; return ` ${hasMarketing ? esc(marketing!.appName) + " — " + esc(t("pageTitleSignIn")) : esc(t("pageTitleWelcome"))} ${ hasMarketing ? ` ` : "" } ${localePickerHtml} ${marketingPanelHtml}

${esc(t(googleOnly ? "signInTitle" : "welcomeTitle"))}

${esc(t(googleOnly ? "googleOnlySubtitle" : "createAccountSubtitle"))}

${identitySsoHtml} ${ renderGoogleButton ? ` ${googleOnly ? "" : `\n
${esc(t("dividerOr"))}
\n`} ` : "" } ${ googleOnly ? "" : `
${signupLegalNoteHtml} ${signupLocalModeNoteHtml}

1${esc(t("progressAccount"))}
2${esc(t("progressVerify"))}
3${esc(t("progressStart"))}

${esc(t("verificationSent"))}

${esc(t("verifyCopyPrefix"))} ${esc(t("verifyCopySuffix"))}

${esc(t("verificationNote"))}

${esc(t("forgotPassword"))}

${esc(t("backToSignIn"))}

` }

${esc(t("localNotePrefix"))} (${getConnectionLabel()})${esc(t("localNoteSuffix"))}

${marketingCloseHtml} `; } /** @deprecated Use getOnboardingHtml() instead */ export const ONBOARDING_HTML = getOnboardingHtml(); /** * HTML for the password reset page — shown when the user clicks the link in * their reset email. Posts `{ newPassword, token }` to Better Auth's * `/reset-password` endpoint, then redirects to the login page. */ export function getResetPasswordHtml(): string { return ` Reset password

Choose a new password

Set a new password for your account.

Back to sign in
`; }