authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-30 22:21:12-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-30 22:21:12-07:00
log3f62e6fa65c2a2a91a367be4ee95da1b50007bde
tree7324e9dfd92c915133c35d1d2104c15a0d3dbfd3
parent9842518c9a0af14fb3a3e90993692a6e34032682
signature Signed by PGP key B5690EEEBB952194

fix(web): widen sync banners and simplify the working timer (#8855)


4 files changed, 6 insertions(+), 36 deletions(-)

apps/web/src/components/ChatView.tsx-16
...@@ -2435,21 +2435,6 @@ function ChatViewContent(props: ChatViewProps) {...@@ -2435,21 +2435,6 @@ function ChatViewContent(props: ChatViewProps) {
2435 () => deriveActivePlanState(threadActivities, activeLatestTurn?.turnId ?? undefined),2435 () => deriveActivePlanState(threadActivities, activeLatestTurn?.turnId ?? undefined),
2436 [activeLatestTurn?.turnId, threadActivities],2436 [activeLatestTurn?.turnId, threadActivities],
2437 );2437 );
2438 // Current step for the in-chat working row: only for the running turn's own
2439 // plan (deriveActivePlanState falls back to older turns' plans, which must
2440 // not label fresh work). Falls back to the first pending step so an
2441 // all-pending freshly written plan labels the row, matching the composer and
2442 // the server's planProgress.
2443 const workingStepLabel = useMemo(() => {
2444 if (!activePlan || activePlan.turnId !== (activeLatestTurn?.turnId ?? null)) {
2445 return null;
2446 }
2447 return (
2448 activePlan.steps.find((step) => step.status === "inProgress")?.step ??
2449 activePlan.steps.find((step) => step.status === "pending")?.step ??
2450 null
2451 );
2452 }, [activeLatestTurn?.turnId, activePlan]);
2453 const showPlanFollowUpPrompt = shouldShowPlanFollowUpPrompt({2438 const showPlanFollowUpPrompt = shouldShowPlanFollowUpPrompt({
2454 pendingUserInputCount: pendingUserInputs.length,2439 pendingUserInputCount: pendingUserInputs.length,
2455 interactionMode,2440 interactionMode,
...@@ -7240,7 +7225,6 @@ function ChatViewContent(props: ChatViewProps) {...@@ -7240,7 +7225,6 @@ function ChatViewContent(props: ChatViewProps) {
7240 onOpenAgents={addAgentsSurface}7225 onOpenAgents={addAgentsSurface}
7241 key={activeThread.id}7226 key={activeThread.id}
7242 isWorking={isWorking}7227 isWorking={isWorking}
7243 workingStepLabel={workingStepLabel}
7244 activeTurnStartedAt={activeWorkStartedAt}7228 activeTurnStartedAt={activeWorkStartedAt}
7245 listRef={legendListRef}7229 listRef={legendListRef}
7246 timelineEntries={timelineEntries}7230 timelineEntries={timelineEntries}
apps/web/src/components/chat/ChatComposer.tsx+1-8
...@@ -3439,14 +3439,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -3439,14 +3439,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
3439 />3439 />
3440 {!activityStackItem && (props.threadSyncPhase || inlineTasksBadge) ? (3440 {!activityStackItem && (props.threadSyncPhase || inlineTasksBadge) ? (
3441 <ComposerBanner.Attachment>3441 <ComposerBanner.Attachment>
3442 <ComposerBanner.Root3442 <ComposerBanner.Root data-chat-composer-activity-strip="true">
3443 width={
3444 props.threadSyncPhase && !showComposerTopDrawer && !isComposerCollapsedMobile
3445 ? "content"
3446 : "fill"
3447 }
3448 data-chat-composer-activity-strip="true"
3449 >
3450 {props.threadSyncPhase ? (3443 {props.threadSyncPhase ? (
3451 <ComposerActivityRow phase={props.threadSyncPhase} />3444 <ComposerActivityRow phase={props.threadSyncPhase} />
3452 ) : (3445 ) : (
apps/web/src/components/chat/MessagesTimeline.tsx+1-12
...@@ -171,8 +171,6 @@ interface TimelineRowActivityState {...@@ -171,8 +171,6 @@ interface TimelineRowActivityState {
171 isWorking: boolean;171 isWorking: boolean;
172 isRevertingCheckpoint: boolean;172 isRevertingCheckpoint: boolean;
173 latestTurnId: TurnId | null;173 latestTurnId: TurnId | null;
174 /** Current plan step label for the working row, when the turn has a plan. */
175 workingStepLabel: string | null;
176}174}
177175
178const TimelineRowCtx = createContext<TimelineRowSharedState>(null!);176const TimelineRowCtx = createContext<TimelineRowSharedState>(null!);
...@@ -227,7 +225,6 @@ interface MessagesTimelineProps {...@@ -227,7 +225,6 @@ interface MessagesTimelineProps {
227 agentPanelModel?: AgentPanelModel;225 agentPanelModel?: AgentPanelModel;
228 onOpenAgents?: () => void;226 onOpenAgents?: () => void;
229 isWorking: boolean;227 isWorking: boolean;
230 workingStepLabel?: string | null;
231 activeTurnStartedAt: string | null;228 activeTurnStartedAt: string | null;
232 listRef: React.RefObject<LegendListRef | null>;229 listRef: React.RefObject<LegendListRef | null>;
233 timelineEntries: ReturnType<typeof deriveTimelineEntries>;230 timelineEntries: ReturnType<typeof deriveTimelineEntries>;
...@@ -273,7 +270,6 @@ interface MessagesTimelineProps {...@@ -273,7 +270,6 @@ interface MessagesTimelineProps {
273270
274export const MessagesTimeline = memo(function MessagesTimeline({271export const MessagesTimeline = memo(function MessagesTimeline({
275 isWorking,272 isWorking,
276 workingStepLabel = null,
277 activeTurnStartedAt,273 activeTurnStartedAt,
278 agentPanelModel = EMPTY_AGENT_PANEL_MODEL,274 agentPanelModel = EMPTY_AGENT_PANEL_MODEL,
279 onOpenAgents = NOOP_OPEN_AGENTS,275 onOpenAgents = NOOP_OPEN_AGENTS,
...@@ -583,9 +579,8 @@ export const MessagesTimeline = memo(function MessagesTimeline({...@@ -583,9 +579,8 @@ export const MessagesTimeline = memo(function MessagesTimeline({
583 isWorking,579 isWorking,
584 isRevertingCheckpoint,580 isRevertingCheckpoint,
585 latestTurnId: latestTurn?.turnId ?? null,581 latestTurnId: latestTurn?.turnId ?? null,
586 workingStepLabel,
587 }),582 }),
588 [isRevertingCheckpoint, isWorking, latestTurn?.turnId, workingStepLabel],583 [isRevertingCheckpoint, isWorking, latestTurn?.turnId],
589 );584 );
590585
591 // Stable renderItem — no closure deps. Row components read shared state586 // Stable renderItem — no closure deps. Row components read shared state
...@@ -1323,7 +1318,6 @@ function ProposedPlanTimelineRow({...@@ -1323,7 +1318,6 @@ function ProposedPlanTimelineRow({
1323}1318}
13241319
1325function WorkingTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "working" }> }) {1320function WorkingTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "working" }> }) {
1326 const { workingStepLabel } = use(TimelineRowActivityCtx);
1327 return (1321 return (
1328 <div>1322 <div>
1329 <div className="border-b border-border/60 pb-2 pt-1">1323 <div className="border-b border-border/60 pb-2 pt-1">
...@@ -1337,11 +1331,6 @@ function WorkingTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "workin...@@ -1337,11 +1331,6 @@ function WorkingTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "workin
1337 "Working..."1331 "Working..."
1338 )}1332 )}
1339 </span>1333 </span>
1340 {workingStepLabel ? (
1341 <span className="ml-2 min-w-0 truncate text-muted-foreground/55">
1342 · {workingStepLabel}
1343 </span>
1344 ) : null}
1345 </div>1334 </div>
1346 </div>1335 </div>
1347 {row.showThinking ? (1336 {row.showThinking ? (
docs/user/composer.md+4
...@@ -37,6 +37,10 @@ when starting a thread or changing an existing thread's model....@@ -37,6 +37,10 @@ when starting a thread or changing an existing thread's model.
3737
38## Notices above the composer38## Notices above the composer
3939
40On web and desktop, loading and syncing statuses fill the available banner width beside the
41stash tab. Task progress appears above the composer, while the timeline's working timer shows
42only elapsed time.
43
40On web and desktop, additional notices peek out above the attached banner. Hover over the peek44On web and desktop, additional notices peek out above the attached banner. Hover over the peek
41to reveal them, or focus **Show other notices** with `Tab` and press `Enter` or `Space`. Press45to reveal them, or focus **Show other notices** with `Tab` and press `Enter` or `Space`. Press
42`Escape` to close the stack and return focus to that control. On a touchscreen, tap the peek to46`Escape` to close the stack and return focus to that control. On a touchscreen, tap the peek to