diff options
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 36 | ||||
| -rw-r--r-- | packages/app/src/i18n/en.ts | 46 | ||||
| -rw-r--r-- | packages/app/src/i18n/zh.ts | 44 | ||||
| -rw-r--r-- | packages/app/src/pages/error.tsx | 104 | ||||
| -rw-r--r-- | specs/06-app-i18n-audit.md | 63 |
5 files changed, 197 insertions, 96 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 8f3bc1f41..66221fd42 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -4,6 +4,7 @@ import { Portal } from "solid-js/web" import { useParams } from "@solidjs/router" import { useLayout } from "@/context/layout" import { useCommand } from "@/context/command" +import { useLanguage } from "@/context/language" // import { useServer } from "@/context/server" // import { useDialog } from "@opencode-ai/ui/context/dialog" import { usePlatform } from "@/context/platform" @@ -29,6 +30,7 @@ export function SessionHeader() { // const dialog = useDialog() const sync = useSync() const platform = usePlatform() + const language = useLanguage() const projectDirectory = createMemo(() => base64Decode(params.dir ?? "")) const project = createMemo(() => { @@ -138,7 +140,7 @@ export function SessionHeader() { <div class="flex min-w-0 flex-1 items-center gap-2 overflow-visible"> <Icon name="magnifying-glass" size="normal" class="icon-base shrink-0" /> <span class="flex-1 min-w-0 text-14-regular text-text-weak truncate h-4.5 flex items-center"> - Search {name()} + {language.t("session.header.search.placeholder", { project: name() })} </span> </div> @@ -181,7 +183,7 @@ export function SessionHeader() { }} aria-hidden={!showReview()} > - <TooltipKeybind title="Toggle review" keybind={command.keybind("review.toggle")}> + <TooltipKeybind title={language.t("command.review.toggle")} keybind={command.keybind("review.toggle")}> <Button variant="ghost" class="group/review-toggle size-6 p-0" @@ -209,7 +211,7 @@ export function SessionHeader() { </div> <TooltipKeybind class="hidden md:block shrink-0" - title="Toggle terminal" + title={language.t("command.terminal.toggle")} keybind={command.keybind("terminal.toggle")} > <Button @@ -245,20 +247,20 @@ export function SessionHeader() { aria-hidden={!showShare()} > <Popover - title="Publish on web" + title={language.t("session.share.popover.title")} description={ shareUrl() - ? "This session is public on the web. It is accessible to anyone with the link." - : "Share session publicly on the web. It will be accessible to anyone with the link." + ? language.t("session.share.popover.description.shared") + : language.t("session.share.popover.description.unshared") } trigger={ - <Tooltip class="shrink-0" value="Share session"> + <Tooltip class="shrink-0" value={language.t("command.session.share")}> <Button variant="secondary" classList={{ "rounded-r-none": shareUrl() !== undefined }} style={{ scale: 1 }} > - Share + {language.t("session.share.action.share")} </Button> </Tooltip> } @@ -275,7 +277,9 @@ export function SessionHeader() { onClick={shareSession} disabled={state.share} > - {state.share ? "Publishing..." : "Publish"} + {state.share + ? language.t("session.share.action.publishing") + : language.t("session.share.action.publish")} </Button> </div> } @@ -290,7 +294,9 @@ export function SessionHeader() { onClick={unshareSession} disabled={state.unshare} > - {state.unshare ? "Unpublishing..." : "Unpublish"} + {state.unshare + ? language.t("session.share.action.unpublishing") + : language.t("session.share.action.unpublish")} </Button> <Button size="large" @@ -299,7 +305,7 @@ export function SessionHeader() { onClick={viewShare} disabled={state.unshare} > - View + {language.t("session.share.action.view")} </Button> </div> </div> @@ -307,7 +313,13 @@ export function SessionHeader() { </div> </Popover> <Show when={shareUrl()} fallback={<div class="size-6" aria-hidden="true" />}> - <Tooltip value={state.copied ? "Copied" : "Copy link"} placement="top" gutter={8}> + <Tooltip + value={ + state.copied ? language.t("session.share.copy.copied") : language.t("session.share.copy.copyLink") + } + placement="top" + gutter={8} + > <IconButton icon={state.copied ? "check" : "copy"} variant="secondary" diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 67a08c829..cab1bd29d 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -289,6 +289,38 @@ export const dict = { "toast.update.action.installRestart": "Install and restart", "toast.update.action.notYet": "Not yet", + "error.page.title": "Something went wrong", + "error.page.description": "An error occurred while loading the application.", + "error.page.details.label": "Error Details", + "error.page.action.restart": "Restart", + "error.page.action.checking": "Checking...", + "error.page.action.checkUpdates": "Check for updates", + "error.page.action.updateTo": "Update to {{version}}", + "error.page.report.prefix": "Please report this error to the OpenCode team", + "error.page.report.discord": "on Discord", + "error.page.version": "Version: {{version}}", + + "error.chain.unknown": "Unknown error", + "error.chain.causedBy": "Caused by:", + "error.chain.apiError": "API error", + "error.chain.status": "Status: {{status}}", + "error.chain.retryable": "Retryable: {{retryable}}", + "error.chain.responseBody": "Response body:\n{{body}}", + "error.chain.didYouMean": "Did you mean: {{suggestions}}", + "error.chain.modelNotFound": "Model not found: {{provider}}/{{model}}", + "error.chain.checkConfig": "Check your config (opencode.json) provider/model names", + "error.chain.mcpFailed": + "MCP server \"{{name}}\" failed. Note, OpenCode does not support MCP authentication yet.", + "error.chain.providerAuthFailed": "Provider authentication failed ({{provider}}): {{message}}", + "error.chain.providerInitFailed": "Failed to initialize provider \"{{provider}}\". Check credentials and configuration.", + "error.chain.configJsonInvalid": "Config file at {{path}} is not valid JSON(C)", + "error.chain.configJsonInvalidWithMessage": "Config file at {{path}} is not valid JSON(C): {{message}}", + "error.chain.configDirectoryTypo": + "Directory \"{{dir}}\" in {{path}} is not valid. Rename the directory to \"{{suggestion}}\" or remove it. This is a common typo.", + "error.chain.configFrontmatterError": "Failed to parse frontmatter in {{path}}:\n{{message}}", + "error.chain.configInvalid": "Config file at {{path}} is invalid", + "error.chain.configInvalidWithMessage": "Config file at {{path}} is invalid: {{message}}", + "notification.permission.title": "Permission required", "notification.permission.description": "{{sessionTitle}} in {{projectName}} needs permission", "notification.question.title": "Question", @@ -312,6 +344,20 @@ export const dict = { "session.context.addToContext": "Add {{selection}} to context", + "session.header.search.placeholder": "Search {{project}}", + + "session.share.popover.title": "Publish on web", + "session.share.popover.description.shared": "This session is public on the web. It is accessible to anyone with the link.", + "session.share.popover.description.unshared": "Share session publicly on the web. It will be accessible to anyone with the link.", + "session.share.action.share": "Share", + "session.share.action.publish": "Publish", + "session.share.action.publishing": "Publishing...", + "session.share.action.unpublish": "Unpublish", + "session.share.action.unpublishing": "Unpublishing...", + "session.share.action.view": "View", + "session.share.copy.copied": "Copied", + "session.share.copy.copyLink": "Copy link", + "prompt.loading": "Loading prompt...", "terminal.loading": "Loading terminal...", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index ef2be2f37..2ded1d677 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -288,6 +288,36 @@ export const dict = { "toast.update.action.installRestart": "安装并重启", "toast.update.action.notYet": "稍后", + "error.page.title": "出了点问题", + "error.page.description": "加载应用程序时发生错误。", + "error.page.details.label": "错误详情", + "error.page.action.restart": "重启", + "error.page.action.checking": "检查中...", + "error.page.action.checkUpdates": "检查更新", + "error.page.action.updateTo": "更新到 {{version}}", + "error.page.report.prefix": "请将此错误报告给 OpenCode 团队", + "error.page.report.discord": "在 Discord 上", + "error.page.version": "版本: {{version}}", + + "error.chain.unknown": "未知错误", + "error.chain.causedBy": "原因:", + "error.chain.apiError": "API 错误", + "error.chain.status": "状态: {{status}}", + "error.chain.retryable": "可重试: {{retryable}}", + "error.chain.responseBody": "响应内容:\n{{body}}", + "error.chain.didYouMean": "你是不是想输入: {{suggestions}}", + "error.chain.modelNotFound": "未找到模型: {{provider}}/{{model}}", + "error.chain.checkConfig": "请检查你的配置 (opencode.json) 中的 provider/model 名称", + "error.chain.mcpFailed": "MCP 服务器 \"{{name}}\" 启动失败。注意: OpenCode 暂不支持 MCP 认证。", + "error.chain.providerAuthFailed": "提供商认证失败 ({{provider}}): {{message}}", + "error.chain.providerInitFailed": "无法初始化提供商 \"{{provider}}\"。请检查凭据和配置。", + "error.chain.configJsonInvalid": "配置文件 {{path}} 不是有效的 JSON(C)", + "error.chain.configJsonInvalidWithMessage": "配置文件 {{path}} 不是有效的 JSON(C): {{message}}", + "error.chain.configDirectoryTypo": "{{path}} 中的目录 \"{{dir}}\" 无效。请将目录重命名为 \"{{suggestion}}\" 或移除它。这是一个常见拼写错误。", + "error.chain.configFrontmatterError": "无法解析 {{path}} 中的 frontmatter:\n{{message}}", + "error.chain.configInvalid": "配置文件 {{path}} 无效", + "error.chain.configInvalidWithMessage": "配置文件 {{path}} 无效: {{message}}", + "notification.permission.title": "需要权限", "notification.permission.description": "{{sessionTitle}}({{projectName}})需要权限", "notification.question.title": "问题", @@ -311,6 +341,20 @@ export const dict = { "session.context.addToContext": "将 {{selection}} 添加到上下文", + "session.header.search.placeholder": "搜索 {{project}}", + + "session.share.popover.title": "发布到网页", + "session.share.popover.description.shared": "此会话已在网页上公开。任何拥有链接的人都可以访问。", + "session.share.popover.description.unshared": "在网页上公开分享此会话。任何拥有链接的人都可以访问。", + "session.share.action.share": "分享", + "session.share.action.publish": "发布", + "session.share.action.publishing": "正在发布...", + "session.share.action.unpublish": "取消发布", + "session.share.action.unpublishing": "正在取消发布...", + "session.share.action.view": "查看", + "session.share.copy.copied": "已复制", + "session.share.copy.copyLink": "复制链接", + "prompt.loading": "正在加载提示...", "terminal.loading": "正在加载终端...", diff --git a/packages/app/src/pages/error.tsx b/packages/app/src/pages/error.tsx index 4326206d8..460c87295 100644 --- a/packages/app/src/pages/error.tsx +++ b/packages/app/src/pages/error.tsx @@ -4,6 +4,7 @@ import { Button } from "@opencode-ai/ui/button" import { Component, Show } from "solid-js" import { createStore } from "solid-js/store" import { usePlatform } from "@/context/platform" +import { useLanguage } from "@/context/language" import { Icon } from "@opencode-ai/ui/icon" export type InitError = { @@ -11,6 +12,8 @@ export type InitError = { data: Record<string, unknown> } +type Translator = ReturnType<typeof useLanguage>["t"] + function isInitError(error: unknown): error is InitError { return ( typeof error === "object" && @@ -38,30 +41,32 @@ function safeJson(value: unknown): string { return json ?? String(value) } -function formatInitError(error: InitError): string { +function formatInitError(error: InitError, t: Translator): string { const data = error.data switch (error.name) { - case "MCPFailed": - return `MCP server "${data.name}" failed. Note, opencode does not support MCP authentication yet.` + case "MCPFailed": { + const name = typeof data.name === "string" ? data.name : "" + return t("error.chain.mcpFailed", { name }) + } case "ProviderAuthError": { const providerID = typeof data.providerID === "string" ? data.providerID : "unknown" const message = typeof data.message === "string" ? data.message : safeJson(data.message) - return `Provider authentication failed (${providerID}): ${message}` + return t("error.chain.providerAuthFailed", { provider: providerID, message }) } case "APIError": { - const message = typeof data.message === "string" ? data.message : "API error" + const message = typeof data.message === "string" ? data.message : t("error.chain.apiError") const lines: string[] = [message] if (typeof data.statusCode === "number") { - lines.push(`Status: ${data.statusCode}`) + lines.push(t("error.chain.status", { status: data.statusCode })) } if (typeof data.isRetryable === "boolean") { - lines.push(`Retryable: ${data.isRetryable}`) + lines.push(t("error.chain.retryable", { retryable: data.isRetryable })) } if (typeof data.responseBody === "string" && data.responseBody) { - lines.push(`Response body:\n${data.responseBody}`) + lines.push(t("error.chain.responseBody", { body: data.responseBody })) } return lines.join("\n") @@ -72,24 +77,38 @@ function formatInitError(error: InitError): string { modelID: string suggestions?: string[] } + + const suggestionsLine = Array.isArray(suggestions) && suggestions.length + ? [t("error.chain.didYouMean", { suggestions: suggestions.join(", ") })] + : [] + return [ - `Model not found: ${providerID}/${modelID}`, - ...(Array.isArray(suggestions) && suggestions.length ? ["Did you mean: " + suggestions.join(", ")] : []), - `Check your config (opencode.json) provider/model names`, + t("error.chain.modelNotFound", { provider: providerID, model: modelID }), + ...suggestionsLine, + t("error.chain.checkConfig"), ].join("\n") } case "ProviderInitError": { const providerID = typeof data.providerID === "string" ? data.providerID : "unknown" - return `Failed to initialize provider "${providerID}". Check credentials and configuration.` + return t("error.chain.providerInitFailed", { provider: providerID }) } case "ConfigJsonError": { + const path = typeof data.path === "string" ? data.path : safeJson(data.path) const message = typeof data.message === "string" ? data.message : "" - return `Config file at ${data.path} is not valid JSON(C)` + (message ? `: ${message}` : "") + if (message) return t("error.chain.configJsonInvalidWithMessage", { path, message }) + return t("error.chain.configJsonInvalid", { path }) + } + case "ConfigDirectoryTypoError": { + const path = typeof data.path === "string" ? data.path : safeJson(data.path) + const dir = typeof data.dir === "string" ? data.dir : safeJson(data.dir) + const suggestion = typeof data.suggestion === "string" ? data.suggestion : safeJson(data.suggestion) + return t("error.chain.configDirectoryTypo", { dir, path, suggestion }) + } + case "ConfigFrontmatterError": { + const path = typeof data.path === "string" ? data.path : safeJson(data.path) + const message = typeof data.message === "string" ? data.message : safeJson(data.message) + return t("error.chain.configFrontmatterError", { path, message }) } - case "ConfigDirectoryTypoError": - return `Directory "${data.dir}" in ${data.path} is not valid. Rename the directory to "${data.suggestion}" or remove it. This is a common typo.` - case "ConfigFrontmatterError": - return `Failed to parse frontmatter in ${data.path}:\n${data.message}` case "ConfigInvalidError": { const issues = Array.isArray(data.issues) ? data.issues.map( @@ -97,7 +116,13 @@ function formatInitError(error: InitError): string { ) : [] const message = typeof data.message === "string" ? data.message : "" - return [`Config file at ${data.path} is invalid` + (message ? `: ${message}` : ""), ...issues].join("\n") + const path = typeof data.path === "string" ? data.path : safeJson(data.path) + + const line = message + ? t("error.chain.configInvalidWithMessage", { path, message }) + : t("error.chain.configInvalid", { path }) + + return [line, ...issues].join("\n") } case "UnknownError": return typeof data.message === "string" ? data.message : safeJson(data) @@ -107,20 +132,20 @@ function formatInitError(error: InitError): string { } } -function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): string { - if (!error) return "Unknown error" +function formatErrorChain(error: unknown, t: Translator, depth = 0, parentMessage?: string): string { + if (!error) return t("error.chain.unknown") if (isInitError(error)) { - const message = formatInitError(error) + const message = formatInitError(error, t) if (depth > 0 && parentMessage === message) return "" - const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\n${t("error.chain.causedBy")}\n` : "" return indent + `${error.name}\n${message}` } if (error instanceof Error) { const isDuplicate = depth > 0 && parentMessage === error.message const parts: string[] = [] - const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\n${t("error.chain.causedBy")}\n` : "" const header = `${error.name}${error.message ? `: ${error.message}` : ""}` const stack = error.stack?.trim() @@ -153,7 +178,7 @@ function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): st } if (error.cause) { - const causeResult = formatErrorChain(error.cause, depth + 1, error.message) + const causeResult = formatErrorChain(error.cause, t, depth + 1, error.message) if (causeResult) { parts.push(causeResult) } @@ -164,16 +189,16 @@ function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): st if (typeof error === "string") { if (depth > 0 && parentMessage === error) return "" - const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\n${t("error.chain.causedBy")}\n` : "" return indent + error } - const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\n${t("error.chain.causedBy")}\n` : "" return indent + safeJson(error) } -function formatError(error: unknown): string { - return formatErrorChain(error, 0) +function formatError(error: unknown, t: Translator): string { + return formatErrorChain(error, t, 0) } interface ErrorPageProps { @@ -182,6 +207,7 @@ interface ErrorPageProps { export const ErrorPage: Component<ErrorPageProps> = (props) => { const platform = usePlatform() + const language = useLanguage() const [store, setStore] = createStore({ checking: false, version: undefined as string | undefined, @@ -206,51 +232,53 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => { <div class="w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8"> <Logo class="w-58.5 opacity-12 shrink-0" /> <div class="flex flex-col items-center gap-2 text-center"> - <h1 class="text-lg font-medium text-text-strong">Something went wrong</h1> - <p class="text-sm text-text-weak">An error occurred while loading the application.</p> + <h1 class="text-lg font-medium text-text-strong">{language.t("error.page.title")}</h1> + <p class="text-sm text-text-weak">{language.t("error.page.description")}</p> </div> <TextField - value={formatError(props.error)} + value={formatError(props.error, language.t)} readOnly copyable multiline class="max-h-96 w-full font-mono text-xs no-scrollbar" - label="Error Details" + label={language.t("error.page.details.label")} hideLabel /> <div class="flex items-center gap-3"> <Button size="large" onClick={platform.restart}> - Restart + {language.t("error.page.action.restart")} </Button> <Show when={platform.checkUpdate}> <Show when={store.version} fallback={ <Button size="large" variant="ghost" onClick={checkForUpdates} disabled={store.checking}> - {store.checking ? "Checking..." : "Check for updates"} + {store.checking ? language.t("error.page.action.checking") : language.t("error.page.action.checkUpdates")} </Button> } > <Button size="large" onClick={installUpdate}> - Update to {store.version} + {language.t("error.page.action.updateTo", { version: store.version ?? "" })} </Button> </Show> </Show> </div> <div class="flex flex-col items-center gap-2"> <div class="flex items-center justify-center gap-1"> - Please report this error to the OpenCode team + {language.t("error.page.report.prefix")} <button type="button" class="flex items-center text-text-interactive-base gap-1" onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")} > - <div>on Discord</div> + <div>{language.t("error.page.report.discord")}</div> <Icon name="discord" class="text-text-interactive-base" /> </button> </div> <Show when={platform.version}> - <p class="text-xs text-text-weak">Version: {platform.version}</p> + {(version) => ( + <p class="text-xs text-text-weak">{language.t("error.page.version", { version: version() })}</p> + )} </Show> </div> </div> diff --git a/specs/06-app-i18n-audit.md b/specs/06-app-i18n-audit.md index e47dac30d..50110f7c0 100644 --- a/specs/06-app-i18n-audit.md +++ b/specs/06-app-i18n-audit.md @@ -9,8 +9,8 @@ This report documents the remaining user-facing strings in `packages/app/src` th ## Current State - The app uses `useLanguage().t("...")` with dictionaries in `packages/app/src/i18n/en.ts` and `packages/app/src/i18n/zh.ts`. -- Recent progress (already translated): `packages/app/src/pages/home.tsx`, `packages/app/src/pages/layout.tsx`, `packages/app/src/pages/session.tsx`, `packages/app/src/components/prompt-input.tsx`, `packages/app/src/components/dialog-connect-provider.tsx` (plus new keys added in both dictionaries). -- Dictionary parity check: `en.ts` and `zh.ts` currently contain the same key set (314 keys each; no missing or extra keys). +- Recent progress (already translated): `packages/app/src/pages/home.tsx`, `packages/app/src/pages/layout.tsx`, `packages/app/src/pages/session.tsx`, `packages/app/src/components/prompt-input.tsx`, `packages/app/src/components/dialog-connect-provider.tsx`, `packages/app/src/components/session/session-header.tsx`, `packages/app/src/pages/error.tsx` (plus new keys added in both dictionaries). +- Dictionary parity check: `en.ts` and `zh.ts` currently contain the same key set (354 keys each; no missing or extra keys). ## Methodology @@ -30,28 +30,11 @@ This report documents the remaining user-facing strings in `packages/app/src` th File: `packages/app/src/pages/error.tsx` -This is the largest remaining untranslated surface and is user-visible during app failures. - -**Page UI copy** (headings/buttons/labels): -- "Something went wrong" -- "An error occurred while loading the application." -- Text field label: "Error Details" -- Buttons: "Restart", "Checking...", "Check for updates", "Update to {version}" -- Reporting help: "Please report this error..." and the link caption "on Discord" -- Version display prefix: "Version: {platform.version}" - -**Error chain / formatting strings** (shown inside the details field): -- "Unknown error" -- "Caused by:" -- "Status:", "Retryable:", "Response body:" -- Generic API fallback: "API error" -- Suggestion prefix: "Did you mean: ..." - -**Recommendation:** -- Translate all framing/UI labels and action buttons. -- Decide whether to translate highly technical diagnostics. A good compromise is: - - Translate the labels ("Caused by", "Unknown error", "Status") - - Keep raw messages from servers/providers as-is +Completed (2026-01-20): + +- Localized page UI copy via `error.page.*` keys (title, description, buttons, report text, version label). +- Localized error chain framing and common init error templates via `error.chain.*` keys. +- Kept raw server/provider error messages as-is when provided (only localizing labels and structure). ## Highest Priority: Components @@ -81,20 +64,11 @@ Completed (2026-01-20): File: `packages/app/src/components/session/session-header.tsx` -**Representative untranslated strings** -- Search placeholder: "Search {projectName}" -- Tooltips: "Toggle review", "Toggle terminal", "Share session" -- Share/publish popover: - - "Publish on web" - - "This session is public on the web..." - - "Share session publicly on the web..." - - Button states: "Publishing..." / "Publish", "Unpublishing..." / "Unpublish" - - Buttons: "Share", "View" -- Copy tooltip: "Copied" / "Copy link" - -**Recommendation:** -- Most of these should become `session.share.*` keys. -- Reuse command keys where appropriate (e.g. `command.review.toggle`, `command.terminal.toggle`) instead of introducing new duplicates. +Completed (2026-01-20): + +- Localized search placeholder via `session.header.search.placeholder`. +- Localized share/publish UI via `session.share.*` keys (popover title/description, button states, copy tooltip). +- Reused existing command keys for toggle/share tooltips (`command.review.toggle`, `command.terminal.toggle`, `command.session.share`). ## Medium Priority: Components @@ -237,11 +211,9 @@ This is only thrown in DEV and is more of a developer diagnostic. Optional to tr ## Prioritized Implementation Plan -1. `packages/app/src/components/session/session-header.tsx` -2. `packages/app/src/pages/error.tsx` -3. `packages/app/src/components/session/session-new-view.tsx` -4. `packages/app/src/components/session-context-usage.tsx` + locale formatting improvements (also `packages/app/src/components/session/session-context-tab.tsx`) -5. Small stragglers: +1. `packages/app/src/components/session/session-new-view.tsx` +2. `packages/app/src/components/session-context-usage.tsx` + locale formatting improvements (also `packages/app/src/components/session/session-context-tab.tsx`) +3. Small stragglers: - `packages/app/src/components/session-lsp-indicator.tsx` - `packages/app/src/components/session/session-sortable-tab.tsx` - `packages/app/src/components/titlebar.tsx` @@ -250,7 +222,7 @@ This is only thrown in DEV and is more of a developer diagnostic. Optional to tr - `packages/app/src/context/global-sync.tsx` - `packages/app/src/context/file.tsx` + `packages/app/src/context/local.tsx` - `packages/app/src/utils/prompt.ts` -6. Decide on the terminal naming approach (`packages/app/src/context/terminal.tsx`). +4. Decide on the terminal naming approach (`packages/app/src/context/terminal.tsx`). ## Suggested Key Naming Conventions @@ -270,10 +242,9 @@ Also reuse existing command keys for tooltip titles whenever possible (e.g. `com ## Appendix: Remaining Files At-a-Glance Pages: -- `packages/app/src/pages/error.tsx` +- (none) Components: -- `packages/app/src/components/session/session-header.tsx` - `packages/app/src/components/session/session-new-view.tsx` - `packages/app/src/components/session-context-usage.tsx` - `packages/app/src/components/session/session-context-tab.tsx` (formatting locale) |
