diff --git a/packages/opencode/src/cli/cmd/tui/i18n/en.ts b/packages/opencode/src/cli/cmd/tui/i18n/en.ts index 039767425..5154882f4 100644 --- a/packages/opencode/src/cli/cmd/tui/i18n/en.ts +++ b/packages/opencode/src/cli/cmd/tui/i18n/en.ts @@ -560,6 +560,10 @@ export const dict: Record = { // Session badges "tui.session.badge.auto": "Auto", + // Context rebuild boundary marker (inserted by /rebuild) + "tui.session.rebuild_boundary.label": "context rebuilt", + "tui.session.rebuild_boundary.detail": "earlier messages summarized", + // Workspace trust "trust.title": "Accessing workspace:", "trust.safety_check": "Quick safety check: Is this a project you created or one you trust? (Like your own code, a well-known open source project, or work from your team). If not, take a moment to review what's in this folder first.", diff --git a/packages/opencode/src/cli/cmd/tui/i18n/zh.ts b/packages/opencode/src/cli/cmd/tui/i18n/zh.ts index 7347d5fe4..c067ca8f6 100644 --- a/packages/opencode/src/cli/cmd/tui/i18n/zh.ts +++ b/packages/opencode/src/cli/cmd/tui/i18n/zh.ts @@ -581,6 +581,10 @@ export const dict = { // Session badges "tui.session.badge.auto": "自动", + // Context rebuild boundary marker (inserted by /rebuild) + "tui.session.rebuild_boundary.label": "上下文已重建", + "tui.session.rebuild_boundary.detail": "较早消息已摘要", + // Workspace trust "trust.title": "访问工作区:", "trust.safety_check": "安全确认:这是你自己创建或信任的项目吗?(如你自己的代码、知名开源项目或团队内部项目)。如果不是,请先检查此目录下的内容。", diff --git a/packages/opencode/src/cli/cmd/tui/i18n/zht.ts b/packages/opencode/src/cli/cmd/tui/i18n/zht.ts index 23cc47497..e74894971 100644 --- a/packages/opencode/src/cli/cmd/tui/i18n/zht.ts +++ b/packages/opencode/src/cli/cmd/tui/i18n/zht.ts @@ -550,6 +550,10 @@ export const dict = { // Session badges "tui.session.badge.auto": "自動", + // Context rebuild boundary marker (inserted by /rebuild) + "tui.session.rebuild_boundary.label": "上下文已重建", + "tui.session.rebuild_boundary.detail": "較早訊息已摘要", + // Workspace trust "trust.title": "存取工作區:", "trust.safety_check": "安全確認:這是你自己建立或信任的專案嗎?(如你自己的程式碼、知名開源專案或團隊內部專案)。如果不是,請先檢查此目錄下的內容。", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index bc38ccb43..201167b53 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1536,7 +1536,16 @@ function UserMessage(props: { return parsed ? [parsed] : [] })[0] }) + // A context rebuild (`/rebuild`) inserts a single user message carrying a + // `checkpoint` part plus `synthetic: true` text parts (the rendered context + // and index). Neither renders — `checkpoint` has no PART_MAPPING entry and + // synthetic text is excluded from `text()` above — so the boundary used to be + // completely invisible in the transcript, unlike compaction which at least + // leaves a visible summary message behind. Surface it as a one-line marker + // row so the user can see that a rebuild happened and where. + const rebuildBoundary = createMemo(() => props.parts.some((x) => x.type === "checkpoint")) const { theme } = useTheme() + const t = useLanguage().t const [hover, setHover] = createSignal(false) const queued = createMemo(() => props.pending && props.message.id > props.pending) const color = createMemo(() => local.agent.color(props.message.agent)) @@ -1603,6 +1612,17 @@ function UserMessage(props: { ) }} + + + + + {" "} + ⟲ {t("tui.session.rebuild_boundary.label")}{" "} + + {t("tui.session.rebuild_boundary.detail")} + + + { + test("english copy names the rebuild and what happened to earlier messages", () => { + expect(en["tui.session.rebuild_boundary.label"]).toBe("context rebuilt") + expect(en["tui.session.rebuild_boundary.detail"]).toBe("earlier messages summarized") + }) + + test("localized dictionaries define the marker copy", () => { + for (const key of KEYS) { + for (const [name, dict] of Object.entries({ en, zh, zht })) { + expect(dict[key], `${name} is missing ${key}`).toBeTruthy() + } + // Untranslated keys silently fall back to English via the `base` merge in + // context/language.tsx, so an English string here means a missed + // translation rather than a crash — assert it is actually translated. + expect(zh[key]).not.toBe(en[key]) + expect(zht[key]).not.toBe(en[key]) + } + }) +})