Dock run state into the composer card and add prompt-card previews / 运行态内嵌输入卡与审批·提问卡预览行#6164
Merged
Merged
Conversation
Replace the floating run-status pill with a run strip inside the composer card, so starting or finishing a turn no longer shifts the surrounding layout, and give stop a fixed home next to the send button while a turn runs (send switches to a queue-guidance icon). The strip is a small state machine: retry > waiting-approval > waiting-ask > streaming. While a tool approval or ask question is blocked on the user, the strip says so (with the pending tool label) instead of ticking whimsical "working" seconds, and the card hands the running accent off to the prompt card above it. Accessibility: the per-second ticker was inside an aria-live region and re-announced every second; it is now aria-hidden, with a separate sr-only status region that announces only stable state transitions. The approval card also gains a hint when the approval mode is relaxed (ask -> auto/yolo) while a request is pending, since switching modes does not auto-resolve the pending prompt. Verified: tsc --noEmit, tsc -p tsconfig.test.json, check:css, new composer-run-strip test (16 assertions), and the full frontend test chain all pass.
The in-flow strip added ~30px to the card's content height, which a user-resized card (fixed --composer-height, minimum 104px) could not absorb: the meta row overflowed the card bottom and was clipped behind the app status bar. Position the strip absolutely on the card's top edge instead — it no longer participates in height math (resized cards keep their exact height) and turn start/finish causes zero layout shift. The strip is non-interactive (pointer-events: none) so the resize handle underneath keeps working.
The composer is disabled while a tool approval is pending, which also disabled the ask/auto/yolo bar — making the controller's designed mode-switch semantics unreachable from the UI: SetToolApprovalMode drains pending approvals the relaxed posture allows (fresh-human tools are never drained, approval.go drainLocked). Scope the disable so the approval bar stays active exactly when the pending approval itself is the reason: relaxing the mode now resolves drainable requests in place, and for surviving fresh-human requests the approval card's mode-switch hint explains that an explicit decision is still needed. Verified live against the dev instance: a pending bash approval auto-resolves when switching ask -> auto, the run strip returns from waiting to streaming, and the composer stays disabled for non-approval reasons (covered by composer-run-strip assertions).
The absolute-positioned tab overlapped the guidance shelf, context cards, and pasted blocks when those sat above the card. Switch to an in-flow row: auto-height cards grow naturally, and user-resized cards get COMPOSER_RUN_STRIP_RESERVED (30px) added to --composer-height via inline style while the strip is visible — so the meta row stays fully visible and reverts when the turn ends.
The ask card used full-width single-column option cards (42px each) with full-width back/submit rows, consuming ~400px+ of viewport. This was the compact shelf's grid being overridden by the ask-specific 1fr rule at higher specificity. Switch to a responsive multi-column grid (auto-fill, minmax 200px) with slightly shorter items (36px), raise the scroll cap to min(220px, 30vh), and move back/submit into a right-aligned pill row with a subtle border-top separator. Four options now render in two rows at ~93px total; the overall card height drops from ~400px to ~230px.
The multi-column grid truncates option descriptions to one line, and narrower columns made the loss worse — a cut-off description is the one piece of information the user needs before answering. Add a quick-pick-style detail row pinned under the options: it previews the full description of the hovered/focused option, falling back to the latest selected option and then the first described option so it never blanks. Each option also carries a native title tooltip as a redundant fallback. PromptShelf gains a `note` slot (between actions and quick actions) and PromptAction gains `title`/`onHoverChange`; both are opt-in and inert for existing callers (approval card, clear-context card).
The allow-once / allow-session / always-allow / deny pills carry real
semantic differences (scope, persistence) that the labels alone do not
explain. Reuse the ask card's quick-pick pattern: a consequence row
under the pills previews what the hovered or arrow-selected action will
do, with a native title fallback on each pill. The subject block stays
default-visible and untouched — what you approve must remain fully
inspectable.
The tool actions are now a descriptor list driving both the buttons and
the preview, replacing the hand-rolled branch JSX. Also map
exit_plan_mode to a friendly label ("Plan approval") — the composer run
strip previously showed the raw tool name while a plan was waiting.
d052eff to
fb1840f
Compare
Review found the strip compensation baked into the React render path only: composerCardStyle wrote height+30px while the live drag updater and drag-start measurement wrote raw logical heights to the same CSS variable. Dragging a resized composer during a running turn briefly shrank the card by the strip height (re-clipping the meta row) until release re-added the compensation — a visible jump. Make the variable single-meaning: --composer-height always carries the user's logical height from every writer (render, live drag, keyboard), and the strip reservation moves into a separate --composer-run-strip-reserved variable consumed by a CSS calc on .composer-card--resized. DOM-based fallback measurements subtract the strip so auto-height cards convert to the same space. Regression covered in composer-run-strip: drag start/release during a running turn stays in logical-height space, the reservation survives the drag, and the idle card releases it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Redesign of the composer run state, plus interaction fixes and prompt-card polish that fell out of testing it. Frontend-only (desktop React UI, locales, CSS, frontend tests).
Run state docks into the composer card
Interaction fixes found while testing
Prompt card polish
exit_plan_modenow maps to a friendly label — the run strip previously showed the raw tool name while a plan waited for confirmation.PromptShelf gains an opt-in
noteslot and PromptAction gainstitle/onHoverChange; both are inert for existing callers.Tests
composer-run-strip.test.tsx(24 assertions): strip placement/a11y, stop button, waiting states, approval-bar scoping, guidance restore on cancel, and a real-time pause-accounting case.approval-modal-file-reference.test.tsx+4: consequence row follows selection/arrows/hover, title fallback; the "full subject visible by default" contract is asserted unchanged.ask-card-layout.test.ts+3: detail row default/hover behavior, title fallback.composer-goal-toggle.test.tsxupdated for the new stop button and placeholder.tsc --noEmit(src + test configs),check:css, and the full frontend test chain, re-run after rebasing onto latestmain-v2.Cache-impact: none - desktop frontend only (React UI, locales, CSS, frontend tests); no prompts, tool schemas, or provider-visible bytes change.
Cache-guard: not applicable - no prompt or tool surface in the changed set.
System-prompt-review: no files under internal/ change in this PR.