Migration (multi-agent sweep over 216 files, 60 batches):
- Every user-visible dashboard + TUI string moved to t() with the exact
English inline default (en rendering byte-identical)
- Catalogs merged from per-batch fragments: en/zh-CN/zh-TW/fr/es now
carry ~5,930 keys each across common/app/errors/cli namespaces;
CLI bundles regenerated (6 locales incl. ko)
Integration fixes:
- 18 type errors: reserved {{count}} interpolations renamed, malformed
plural call, hand-rolled t-param types replaced with TFunction<"app">
- 23 lint errors: superseded label constants/helpers removed
- ExecutorStatusBar hook-order violation (keyboard-open early return
moved below hooks)
- TUI tests wrapped in I18nextProvider (uninitialized fallback renders
literal {{placeholders}}); dashboard vitest.setup boots a minimal en
i18next instance for the same reason
Known WIP (next commits): ~457 residual strings across 50 batches,
Korean drafts for swept keys, and a dashboard test-suite pass that is
still being stabilized (~283 failures under investigation — fake-timer
waitFor interaction, likely stale node_modules vs merged lockfile).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import "./AuthTokenRecoveryDialog.css";
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { clearAuthToken, setAuthToken } from "../auth";
|
|
|
|
export interface AuthTokenRecoveryDialogProps {
|
|
open: boolean;
|
|
}
|
|
|
|
export function AuthTokenRecoveryDialog({ open }: AuthTokenRecoveryDialogProps) {
|
|
const { t } = useTranslation("app");
|
|
const [tokenInput, setTokenInput] = useState("");
|
|
const tokenInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
tokenInputRef.current?.focus();
|
|
}, [open]);
|
|
|
|
const handleSetToken = useCallback(() => {
|
|
const token = tokenInput.trim();
|
|
if (!token) return;
|
|
|
|
setAuthToken(token);
|
|
window.location.reload();
|
|
}, [tokenInput]);
|
|
|
|
const handleClearAndRetry = useCallback(() => {
|
|
clearAuthToken();
|
|
window.location.reload();
|
|
}, []);
|
|
|
|
if (!open) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="modal-overlay open auth-token-recovery-overlay"
|
|
role="presentation"
|
|
onKeyDownCapture={(event) => {
|
|
if (event.key === "Escape") {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
}}
|
|
>
|
|
<div
|
|
className="modal modal-md"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="auth-token-recovery-title"
|
|
aria-describedby="auth-token-recovery-description"
|
|
>
|
|
<div className="modal-header auth-token-recovery-header">
|
|
<h3 id="auth-token-recovery-title">{t("auth.tokenRequired", "Authentication token required")}</h3>
|
|
</div>
|
|
|
|
<div className="auth-token-recovery-content">
|
|
<p id="auth-token-recovery-description">
|
|
{t("auth.tokenRecoveryDescription", "This dashboard session can't authenticate with the daemon. Set a replacement token or clear the current token and retry.")}
|
|
</p>
|
|
|
|
<div className="auth-token-recovery-field">
|
|
<label htmlFor="auth-token-recovery-input">{t("auth.replacementToken", "Replacement token")}</label>
|
|
<input
|
|
ref={tokenInputRef}
|
|
id="auth-token-recovery-input"
|
|
className="input"
|
|
type="password"
|
|
value={tokenInput}
|
|
onChange={(event) => setTokenInput(event.target.value)}
|
|
placeholder={t("auth.pasteToken", "Paste token")}
|
|
autoComplete="off"
|
|
spellCheck={false}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="modal-actions auth-token-recovery-actions">
|
|
<button
|
|
type="button"
|
|
className="btn"
|
|
onClick={handleClearAndRetry}
|
|
>
|
|
{t("auth.clearAndRetry", "Clear token and retry")}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary"
|
|
onClick={handleSetToken}
|
|
disabled={tokenInput.trim().length === 0}
|
|
>
|
|
{t("auth.setAndReload", "Set token and reload")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|