diff --git a/apps/presentation/dashboard/smoke/workspace-locale-browser-smoke.mjs b/apps/presentation/dashboard/smoke/workspace-locale-browser-smoke.mjs index 4d5064ef18..a996614110 100644 --- a/apps/presentation/dashboard/smoke/workspace-locale-browser-smoke.mjs +++ b/apps/presentation/dashboard/smoke/workspace-locale-browser-smoke.mjs @@ -64,10 +64,22 @@ export async function verifyWorkspaceLocales({ browser, url, installApi, outputD const page = await browser.newPage({ locale: "en-US", viewport: { width: 1440, height: 960 } }); try { - await installApi(page); + const api = await installApi(page); await page.goto(url, { waitUntil: "networkidle" }); await page.getByTestId("personal-goal-home").waitFor(); + assert.match(await page.getByRole("region", { name: "Control plane source" }).innerText(), /This machine/); + const englishText = await page.locator("body").innerText(); + for (const chinese of ["本机", "仅查状态"]) assert.ok(!englishText.includes(chinese), `English Workspace shows ${chinese}`); await page.screenshot({ path: resolve(outputDir, "locale-default-english.png") }); + await page.getByRole("combobox", { name: "Select chat runtime", exact: true }).click(); + const statusOnly = page.getByRole("option", { name: "Status only", exact: true }); + assert.ok(await statusOnly.isVisible(), "English Runtime picker shows the translated status-only route"); + await statusOnly.click(); + await page.getByLabel("Send a message to LoopX").fill("Show the current state."); + await page.getByRole("button", { name: "Send", exact: true }).click(); + await page.locator(".personal-manager-conversation-messages article.is-assistant > strong").getByText("Status only", { exact: true }).waitFor(); + assert.equal(api.turnRequests.length, 0, "Localizing the status-only route must not start an Agent Turn"); + assert.ok(!(await page.locator("body").innerText()).includes("LoopX 状态投影"), "The status-only reply source follows the selected language"); await page.setViewportSize({ width: 390, height: 844 }); await page.locator(".personal-channel-scroll").evaluate((element) => { element.scrollTop = 0; }); const clippedStats = await page.locator(".personal-digest-stats span").evaluateAll((stats) => @@ -86,6 +98,7 @@ export async function verifyWorkspaceLocales({ browser, url, installApi, outputD await page.reload({ waitUntil: "networkidle" }); await page.getByTestId("personal-goal-home").waitFor(); assert.equal(await page.locator("html").getAttribute("lang"), "zh-CN"); + assert.match(await page.getByRole("region", { name: "控制面来源" }).innerText(), /本机/); await page.screenshot({ path: resolve(outputDir, "locale-saved-chinese.png") }); await page.getByRole("button", { name: "设置", exact: true }).click(); await page.getByRole("button", { name: /语言/ }).click(); diff --git a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx index e8069593d8..3c5f12d280 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx @@ -431,6 +431,9 @@ const en = { "header.goalView": "Goal view", "header.live": "Live", "header.manager": "LoopX Manager", + "header.statusOnlyAgent": "Status only", + "header.statusOnlyDescription": "No model calls", + "header.statusProjection": "LoopX state projection", "header.managerModelSettings": "Change steward model and reasoning effort", "header.managerDescription": "Your personal workspace across Goals", "header.managerRuntime": "{profile} · {sandbox}", @@ -875,6 +878,7 @@ const en = { "source.hostPlaceholder": "Enter or search for a Host", "source.invalid": "The SSH source settings are invalid.", "source.loadingHosts": "Loading…", + "source.local": "This machine", "source.localInteractive": "Local interactive", "source.localPort": "Local port", "source.manual": "Manual URL", @@ -1675,6 +1679,9 @@ const zhCN: Record = { "header.goalView": "Goal 视图", "header.live": "实时", "header.manager": "LoopX 管家", + "header.statusOnlyAgent": "仅查状态", + "header.statusOnlyDescription": "不调用模型", + "header.statusProjection": "LoopX 状态投影", "header.managerModelSettings": "调整管家模型与思考深度", "header.managerDescription": "跨 Goal 的个人工作入口", "header.managerRuntime": "{profile} · {sandbox}", @@ -2119,6 +2126,7 @@ const zhCN: Record = { "source.hostPlaceholder": "输入或搜索 Host", "source.invalid": "SSH 来源参数无效。", "source.loadingHosts": "正在读取…", + "source.local": "本机", "source.localInteractive": "本机交互", "source.localPort": "本地端口", "source.manual": "手动 URL", diff --git a/apps/presentation/dashboard/src/features/personal-workspace/status-source-switcher.tsx b/apps/presentation/dashboard/src/features/personal-workspace/status-source-switcher.tsx index 5596a4f1cb..51e306cff8 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/status-source-switcher.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/status-source-switcher.tsx @@ -49,7 +49,7 @@ export function StatusSourceSwitcher({ const [statusUrl, setStatusUrl] = useState(""); const quickAddPrefix = "configured:"; const sourceOptions = [ - ...sources.map((source) => ({ label: source.label, value: source.id })), + ...sources.map((source) => ({ label: source.kind === "local" ? t("source.local") : source.label, value: source.id })), ...configuredHosts .filter((host) => !sources.some((source) => source.label === host.alias)) .map((host) => ({ diff --git a/apps/presentation/dashboard/src/views/dashboard-page.tsx b/apps/presentation/dashboard/src/views/dashboard-page.tsx index 443af5fb54..1743f266c4 100644 --- a/apps/presentation/dashboard/src/views/dashboard-page.tsx +++ b/apps/presentation/dashboard/src/views/dashboard-page.tsx @@ -1342,12 +1342,12 @@ function PersonalGoalHome({ { agentId: "status-only", available: true, - capability: "不调用模型", + capability: t("header.statusOnlyDescription"), adapterKind: "status_projection", interrupt: false, - label: "仅查状态", + label: t("header.statusOnlyAgent"), resume: true, - statusLabel: "只读", + statusLabel: t("common.readOnly"), streaming: false, toolCalls: false, trustScope: "read_only", @@ -2215,9 +2215,9 @@ function PersonalGoalHome({ const answer = personalManagerSnapshot(targetQuestionModel); const usesStatusOnlyRoute = selectedRoute.agentId === "status-only"; const answerMessageId = appendManagerAssistantMessage(targetContextId, { - agentLabel: usesStatusOnlyRoute ? "仅查状态" : "LoopX 管家", + agentLabel: usesStatusOnlyRoute ? t("header.statusOnlyAgent") : t("header.manager"), lines: answer.lines.slice(0, 3), - sourceLabel: usesStatusOnlyRoute ? "LoopX 状态投影 · 仅查状态" : "LoopX 状态投影", + sourceLabel: usesStatusOnlyRoute ? `${t("header.statusProjection")} · ${t("header.statusOnlyAgent")}` : t("header.statusProjection"), text: answer.text, }); void recordProjectionExchange({