From ba85be790c1a8f49a1748b2a01f72637d3f48093 Mon Sep 17 00:00:00 2001 From: Alex Stark Date: Tue, 1 Sep 2026 06:42:06 -0700 Subject: [PATCH] fix(web): repair symbols dropped by the upstream merge fc3751fca kept call sites while dropping the definitions from both sides, so the built app threw "deriveTurnPlans is not defined" on render. tsgo reported 16 errors in apps/web on that commit. - drop the stale deriveTurnPlans call and the extra deriveTimelineEntries argument (upstream removed the function in #8734) - restore the workingStepLabel memo TerminalShellStatus reads - restore NOOP_USE_ARTIFACT_TEMPLATE / NOOP_OPEN_ATTACHMENT and rename ctx.onFileDownload to upstream's ctx.onFileOpen - restore the video play-button rendering from #8919; its props and the userVideos split merged in but nothing rendered them - restore the helpers dropped from ChatMarkdown.workspace-images.test.tsx and delete the orphaned resolveSidebarStageBadgeLabel test that branding.test.ts already covers Claude-Session: https://claude.ai/code/session_01VucnEAuRBJ3icH9fzd7Mqu --- .../ChatMarkdown.workspace-images.test.tsx | 28 +++++++++++++ apps/web/src/components/ChatView.tsx | 25 ++++++++---- apps/web/src/components/Sidebar.logic.test.ts | 39 ------------------ .../src/components/chat/MessagesTimeline.tsx | 40 ++++++++++++++++--- 4 files changed, 80 insertions(+), 52 deletions(-) diff --git a/apps/web/src/components/ChatMarkdown.workspace-images.test.tsx b/apps/web/src/components/ChatMarkdown.workspace-images.test.tsx index 60cc460453f4..6669258df408 100644 --- a/apps/web/src/components/ChatMarkdown.workspace-images.test.tsx +++ b/apps/web/src/components/ChatMarkdown.workspace-images.test.tsx @@ -63,6 +63,34 @@ function renderStreaming(markdown: string): string { return renderToStaticMarkup(); } +function renderFilePreview(cwd: string, relativePath: string): string { + return renderToStaticMarkup( + , + ); +} + +function copiedMarkdownFrom(html: string): string { + const copy = /data-markdown-copy="([^"]*)"/.exec(html)?.[1]?.replaceAll(""", '"'); + expect(copy).toBeDefined(); + return copy ?? ""; +} + +function firstInlineStyle(html: string): Record { + const style = /style="([^"]+)"/.exec(html)?.[1]; + expect(style).toBeDefined(); + return Object.fromEntries( + (style ?? "").split(";").map((declaration) => { + const separator = declaration.indexOf(":"); + return [declaration.slice(0, separator), declaration.slice(separator + 1)]; + }), + ); +} + describe("ChatMarkdown workspace images", () => { beforeEach(() => { testState.resources = []; diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 8b2e7b14899a..5f63bededc0f 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -2394,7 +2394,6 @@ function ChatViewContent(props: ChatViewProps) { () => deriveWorkLogEntries(timelineActivities), [timelineActivities], ); - const turnPlans = useMemo(() => deriveTurnPlans(timelineActivities), [timelineActivities]); // Native subagent fold: memoized by activity-list identity, shared by the // Agents surface, live strip, and workflow cards. v2Projection is null // until orchestration-v2 lands (source precedence lives in the derive). @@ -2464,6 +2463,21 @@ function ChatViewContent(props: ChatViewProps) { () => deriveActivePlanState(threadActivities, activeLatestTurn?.turnId ?? undefined), [activeLatestTurn?.turnId, threadActivities], ); + // Current step for the in-chat working row: only for the running turn's own + // plan (deriveActivePlanState falls back to older turns' plans, which must + // not label fresh work). Falls back to the first pending step so an + // all-pending freshly written plan labels the row, matching the chip and + // the server's planProgress. + const workingStepLabel = useMemo(() => { + if (!activePlan || activePlan.turnId !== (activeLatestTurn?.turnId ?? null)) { + return null; + } + return ( + activePlan.steps.find((step) => step.status === "inProgress")?.step ?? + activePlan.steps.find((step) => step.status === "pending")?.step ?? + null + ); + }, [activeLatestTurn?.turnId, activePlan]); const showPlanFollowUpPrompt = shouldShowPlanFollowUpPrompt({ pendingUserInputCount: pendingUserInputs.length, interactionMode, @@ -2852,13 +2866,8 @@ function ChatViewContent(props: ChatViewProps) { ]); const timelineEntries = useMemo( () => - deriveTimelineEntries( - timelineMessages, - timelineThread?.proposedPlans ?? [], - workLogEntries, - turnPlans, - ), - [timelineMessages, timelineThread?.proposedPlans, turnPlans, workLogEntries], + deriveTimelineEntries(timelineMessages, timelineThread?.proposedPlans ?? [], workLogEntries), + [timelineMessages, timelineThread?.proposedPlans, workLogEntries], ); const [dockedDraftHeroThreadKey, setDockedDraftHeroThreadKey] = useState(null); const draftHeroDockRequested = diff --git a/apps/web/src/components/Sidebar.logic.test.ts b/apps/web/src/components/Sidebar.logic.test.ts index 5a265139e8aa..7bb0078ba406 100644 --- a/apps/web/src/components/Sidebar.logic.test.ts +++ b/apps/web/src/components/Sidebar.logic.test.ts @@ -20,7 +20,6 @@ import { isTrailingDoubleClick, orderItemsByPreferredIds, resolveProjectStatusIndicator, - resolveSidebarStageBadgeLabel, resolveSidebarReasoningLabel, resolveThreadRowClassName, resolveSidebarThreadStatus, @@ -238,44 +237,6 @@ describe("buildMultiSelectThreadContextMenuItems", () => { }); }); -describe("resolveSidebarStageBadgeLabel", () => { - it("returns Nightly for nightly primary server versions", () => { - expect( - resolveSidebarStageBadgeLabel({ - primaryServerVersion: "0.0.28-nightly.20260616.12", - fallbackStageLabel: "Alpha", - }), - ).toBe("Nightly"); - }); - - it("returns the fallback label for stable primary server versions", () => { - expect( - resolveSidebarStageBadgeLabel({ - primaryServerVersion: "0.0.27", - fallbackStageLabel: "Alpha", - }), - ).toBe("Alpha"); - }); - - it("returns the fallback label when the primary server version is missing", () => { - expect( - resolveSidebarStageBadgeLabel({ - primaryServerVersion: null, - fallbackStageLabel: "Dev", - }), - ).toBe("Dev"); - }); - - it("returns the fallback label for malformed nightly prerelease versions", () => { - expect( - resolveSidebarStageBadgeLabel({ - primaryServerVersion: "0.0.28-nightly.20260616", - fallbackStageLabel: "Alpha", - }), - ).toBe("Alpha"); - }); -}); - describe("resolveSidebarReasoningLabel", () => { const model = { slug: "gpt-5.4", diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index f667a440bb27..af9bee5763e6 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -18,7 +18,8 @@ import { const EMPTY_AGENT_PANEL_MODEL = emptyAgentPanelModel(); const NOOP_OPEN_AGENTS = () => {}; const NOOP_RESPONSE_CLICK = () => {}; -const NOOP_DOWNLOAD_ATTACHMENT = (_attachment: ChatFileAttachment) => {}; +const NOOP_USE_ARTIFACT_TEMPLATE = () => {}; +const NOOP_OPEN_ATTACHMENT = (_attachment: ChatFileAttachment) => {}; import { resolveChatListAnchoredEndSpace } from "@t3tools/shared/chatList"; import { createContext, @@ -1071,7 +1072,7 @@ function UserTimelineRow({ row }: { row: Extract
- {regularImages.length > 0 && ( + {(regularImages.length > 0 || userVideos.length > 0) && (
{regularImages.map((image) => (
))} + {userVideos.map((file) => { + const isOpening = ctx.openingVideoAttachmentId === file.id; + return ( +
+ +
+ ); + })}
)} {previewAnnotations.map((annotation, index) => ( @@ -1111,9 +1141,9 @@ function UserTimelineRow({ row }: { row: Extract ))} - {userFiles.length > 0 || unknownAttachments.length > 0 ? ( + {otherUserFiles.length > 0 || unknownAttachments.length > 0 ? (
- {userFiles.map((file) => { + {otherUserFiles.map((file) => { const content = ( <> @@ -1141,7 +1171,7 @@ function UserTimelineRow({ row }: { row: Extract ctx.onFileDownload(file)} + onClick={() => ctx.onFileOpen(file)} className="flex min-w-0 cursor-pointer items-center gap-2 rounded-md py-1 text-left text-sm hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70" > {content}