Repository navigation
feat(studio): port the prototype's chat composer, pills, and queued strip - #2253
Merged
Merged
Conversation
peppescg
added this pull request to stack #2227
October 10, 2026 11:44
This was referenced Oct 10, 2026
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 11, 2026 14:57
7ef2c69 to
6e6ebe2
Compare
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 11, 2026 15:28
6e6ebe2 to
ee6bbb9
Compare
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 11, 2026 15:48
ee6bbb9 to
3c9ed80
Compare
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 11, 2026 16:13
3c9ed80 to
dc1856e
Compare
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 13, 2026 13:29
dc1856e to
dd5ebf6
Compare
peppescg
added a commit
that referenced
this pull request
Oct 13, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
peppescg
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 08:45
4c07463 to
dd5e31a
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 08:59
dd5e31a to
e06f4f9
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 08:59
e06f4f9 to
21fb80c
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 09:00
21fb80c to
e2e2bb9
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 09:41
e2e2bb9 to
8d4b799
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 09:41
8d4b799 to
0a5d104
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 09:47
0a5d104 to
d86f0c4
Compare
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 09:48
d86f0c4 to
4cf9ce7
Compare
…l choices Pin the composer behaviour the prototype alignment must keep, before the composer changes: the Enter, queue, and steer matrix through the textarea, the submit labels and placeholders, the send outcome, the escape draft clear, the image size and total limits, the draft's model, effort, mode, and execution from the desktop pills, and the live fork when only the effort changes. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… helpers Port the prototype's composer helpers onto Studio's foundations: - attachment-inline: classify a picked file as an image, inlined text, or refused (audio, binary, over 256 KiB, or over 512 KiB together), and inline text files as delimited blocks in the prompt - file-meta: a file chip's icon and label, on Studio's localFileKind - draft-store: one unsent draft per chat in session storage, under the account-scoped studio. prefix, so an account change or sign-out clears it before the next person's chat renders - permission-mode and reasoning-effort: the Mode and Effort option tables the pill row and the phone sheet share Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Re-skin the chat composer to the prototype (stack-08) on Studio's chat logic: - The frame: the input box with attachment pills, a toolbar (attach, dictation, clear draft, send), and the pill row under it. A draft's mode tints the frame, typed text during a run turns it amber, and a dragged file highlights it as a drop target. - The pills: Mode, Execution (a draft only), and Model with its Effort submenu, on both a draft and a live chat. A live chat's mode changes in place, and a model or effort pick forks it, as before. The live chat's context usage and Compact move from the controls card to the end of the pill row, with the session's execution capabilities. - On a phone, the pill row folds into the Composer options sheet, with a drill-in view per setting instead of native selects. - The prototype has no template catalog, so a draft's Execution picker keeps its own form: in the pill row, and inline in the phone sheet. - Attach files: text files are inlined into the prompt; images keep Studio's count, size, and total limits and the upload path. - The draft persists per chat in the account-scoped draft store. A draft's first send carries its text into the chat it opens. - The queued strip replaces the queued list. Each row's actions menu edits in place, deletes, or steers the message into the live run; it leaves the queue only once the steer is accepted. - The header shows the permission mode when it is not Manual. The Enter, queue, and steer semantics, the seed confirmation, voice input, and the Escape draft clear are unchanged. The escape hints now sit above the composer. Tests that drove the replaced controls now drive the new ones, with their assertions intact: the phone sheet's rows instead of native selects, the queued row's actions menu instead of inline buttons, and "Choose files to attach" instead of "Choose images to attach". The capability test checks that an image is refused, now that text files keep the Attach button visible. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… queue Update the Studio operating guide for the composer: the Mode, Model, and Tools pills and the phone's Composer options, text-file attachments and their limits, per-chat unsent drafts that an account change clears, and steering a queued message from its actions menu. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The composer cleared itself on mount whenever the workspace had cleared a draft with a double Escape earlier, because the clear signal was already non-zero. With drafts now restored from the draft store, coming back to a chat through a remount would wipe its stored draft. Only a signal that changes while the composer is mounted clears it now. The phone sheet's Effort choices also use a fieldset again, as Biome's semantic-element rule asks, with the legend floated off the border. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…he total first A text file's content could close its own block early: a line such as "--- end of notes.md ---" inside the file read to the model as the end of the attachment, and anything after it as text outside the file. - Every block's fence now has one more dash than the longest run of dashes in any attached text, and at least three, so no file content can hold a line that closes its block or opens a new one. Files without such a run keep the same three-dash lines. - The composer checks the 512 KiB total before it reads each file, one at a time, instead of reading the whole selection first. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… disabled The phone options sheet's Mode view ignored its control's disabled state once open, so a run, a compaction, or a mode change that started while the view was open could still take a pick. The view now disables its choices with the setting's pill, as the Model view already did, and the test checks that the inline Execution picker follows the same state. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A queued message stays in the queue until its steer is accepted. If the run settled while that steer was in flight, the queue drain took the same message and started a new run with it, so it was delivered twice. The drain now passes over the message a steer is delivering. It still leaves the queue only once the steer is accepted, and a stale steer keeps it queued as before. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n unconfirmed seed Switching chats while a send was in flight kept the sent text in the composer and stored it under the chat the user opened, losing that chat's own draft. If the send was refused, Enter then sent the first chat's message into the second. - The composer now swaps in the next chat's own draft on every switch, even during a send. Only a draft's first send carries its text, into the chat that send created: the workspace names that chat (`createdChatKey`), and the composer carries only for a chat named after the send began. - A send that settles after a switch leaves its text stored under the chat it was typed in: an accepted send spends that copy, and a refused one keeps it there. - A carry empties the draft view's copy straight away, so going back to it mid-send no longer shows the text a second time. A seed awaiting confirmation was also stored as the draft, overwriting the user's own draft, and a reload or remount restored it as plain text that Enter sent without the confirmation. The composer no longer stores the draft while a seed awaits confirmation, brings back the user's draft once a confirmed seed is sent, and stores the seed only after Edit prompt. A chat switch closes the pending confirmation. The test "carries a draft's text into the chat its first send opens" now names the created chat with `createdChatKey`, as the workspace does; its assertions are unchanged. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The queue drain skips the message a steer is delivering, so a run that settles while that steer is in flight leaves it queued. When the steer then answers stale_run_control, nothing drained the queue again until another run ended: the message sat in the queue and went out late, after the user's next prompt. A steer that fails with no run active now drains the queue, so the message is sent once, as it would have been without the steer. A steer refused while the run is still live keeps the message queued, as before. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A send that settled refused after the composer had swapped to another chat wrote its text back under the chat it was typed in whenever that chat's stored draft was empty. The draft is empty in two cases, and the write was wrong in both: the user cleared it after the switch, or an account change cleared storage, in which case the previous account's text landed in the next account's drafts. The text never leaves its home key during a send, so nothing needs writing back: a refused send's copy is still stored, unless the user or an account change removed it. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Edit prompt, and Escape or an outside click, stayed live during a confirmed seed's send. Closing the confirmation then stored the seed over the user's own draft, and the accepted send restored that seed to the composer, where one Enter sent it again. While the send is in flight, Edit prompt is disabled and the dialog ignores close requests. It closes when the send is accepted, as before. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The phone sheet is modal, but it registered no shortcut scope, so ? opened the shortcuts page and mod+shift+o a new chat from behind it. It now suppresses app shortcuts while open, like Studio's other modal chat surfaces. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Restacking onto main brought #2219's Execution picker and a live chat's execution status into the prototype's single-line pill row. The picker pushed the Model pill out of view at 500 to 640px, and the status pushed Compact out at 1280px. Both now take a line under the pills, inside the same tray, so the pill row keeps Mode, Model, usage, and Compact as the port laid them out. A browser journey checks a draft and a live chat at 500, 640, and 1280px; it fails with the execution UI back in the pill row. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The send=1 confirmation shows an Execution row, so the page no longer says it shows tool access for a new chat. For a new chat the row is the selected execution; for an existing chat it is whether files and the built-in Shell are available. Refs #2207 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
kantord
force-pushed
the
feat/studio-chat-composer-2207b
branch
from
October 14, 2026 10:02
4cf9ce7 to
3297c80
Compare
41 tasks done
Member
|
Post-merge review follow-ups for this PR. No blockers or regressions found. Items worth tracking were filed here:
Other candidate findings were checked and dropped as not worth tracking. Fully or partially written by an AI agent. |
This branch was successfully deployed
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
This is the Composer step of the chat alignment (#2207, part of #2210): step 2 of 4, after the approvals (#2251) and before the transcript and delegation, and the session dialogs. It's stacked on #2252 (Settings), so review that first.
The chat composer now comes from the prototype (
stack-08): its frame and toolbar, the Mode, Model, and Effort pills under the box, the phone options sheet, the queued-message strip, text-file attachments, a per-chat unsent draft, and the permission-mode badge in the header.The prototype's UI runs on Studio's chat logic. These are unchanged:
EnterSendBehaviorpreference andresolveComposerActionchat-queue.ts, the steer flow, its stale-run fallback, and the steer traceuse-voice-input.ts)lib/model-preferences.ts); a live chat changes its mode in place and forks to change its model or effortchat-seed.tssanitisationchat-escape.ts: Escape's order, and the double Escape that clears a draftWhat changed
Frame and toolbar (
chat-composer.tsx,composer-frame.ts,clear-draft-button.tsx)Pills (
composer-option-menu.tsx,model-effort-menu.tsx,effort-picker.tsx,chat-session-controls.tsx)PUT …/mode), and a model or effort pick forks. The card is gone; its context meter, Compact, and its fork and mode notices move to the end of the pill row.Phone options sheet (
composer-options-sheet.tsx)Queued messages (
queued-message-strip.tsx)Attachments (
lib/attachment-inline.ts,lib/file-meta.ts)--- attached file: <name> ---and--- end of <name> ---lines; the name is sanitised so it can't close its block. Binary and audio files are refused with a reason.Drafts (
lib/draft-store.ts)studio.chat.draft.<chat>(newfor a new chat), so it survives a reload and a switch to another chat and back. Attachments are not kept.Header (
permission-mode-badge.tsx)Docs
user-docs/operating/studio.mddescribes the pills, the phone sheet, text attachments, drafts, and steering a queued message.Differences from the prototype
createSession, and a live chat's line shows whether it has files and the built-in Shell. The prototype has neither.<select>, kept verbatim from feat: add execution templates with platform-managed identity #2219. The prototype has no template catalog, and turning the picker into a pill is a pending design decision with the designer.useChatEscape. The hints ("Esc twice to clear draft", "Press Escape again…") keep their text and conditions but now sit above the composer, as in the prototype.Visible change
No regression
Pinning tests
0c5f65ef7was committed first, alone, and passes on the base (7f5301a89) and on this branch, unchanged:chat-composer.pinning.test.tsx(16 tests):chat-workspace.integration.test.tsx:createSessionNew tests
chat-composer.alignment.test.tsx(26 tests): drafts per chat, across remounts and chat switches; attachments dropped on a switch; the draft cleared after an accepted send and kept after a refused one; a new chat's text carried into the chat its send opens; Clear draft and the escape signal; a draft that survives a remount after an earlier escape clear; one account's draft never reaching the next account; text inlining, a file-only send, the binary and audio refusals, files held during a run, drag and drop; the live pills (mode in place, model fork, no Default or Reset, disabled while busy or without model selection); no Model pill without an inventory; the reasoning note; the sheet's Default and Reset; the frame tint; the badge; and the queued strip's Steer, Edit, and Delete.lib/draft-store.test.ts,lib/attachment-inline.test.ts, andlib/file-meta.test.ts(33 tests).Four mutations were checked by hand:
7ef2c69affixesModified tests
No assertion was removed or weakened. These tests drove controls that the prototype replaced, so their steps changed:
chat-composer.test.tsx, "keeps composer controls reachable at narrow widths": Attach files and Composer options instead of Attach images and the composer's "Chat options"; the sheet's Mode and Model rows and its Effort group instead of the labelled Mode, Model, and Effort selects, while the Execution picker is still found by its label; focus still returns to the button.chat-composer.test.tsx, "limits image sending to the selected model capability": the Attach button now stays for text files, so instead of asserting it is absent, the test attaches an image while the model can't take one and expects the refusal. The other assertions are unchanged.chat-composer.configuration.test.tsxand the integration test "creates a draft with its selected controls and excludes a disabled model": they pick the model, effort, and mode through the sheet's views instead ofselectOptions, and keepselectOptionsfor the Execution picker inline in the sheet. The final configuration, the four changes, thecreateSessionbody, and the hidden model's absence are asserted as before.These pass with their assertions unchanged:
chat-escape.test.tsx,approval-shortcuts.test.tsx(including "never answers an ask from keys typed in the composer"),approval-interactions.test.tsx,chat-queue.test.ts,steer-trace.test.tsx,use-voice-input.test.tsx,local-file-preview.test.ts,delegation-workspace.test.tsx, and the e2e chat journeys, includingauth-recovery.spec.ts's exact account-storage key checks.Gate counts
Both columns ran through the shared gate lock on the same machine. Base is
7f5301a89(#2252's head), and this branch is7ef2c69af. An earlier head run (3f66ce813) stopped at Biome's semantic-element rule on the sheet's Effort group;7ef2c69affixes it.7f5301a89)7ef2c69af)task studio:browser)The 81 new web tests are the 18 pins, the 26 composer tests, the 33 helper tests, and the 4 workspace tests. The browser run includes the chat approval, header Stop, and auth-recovery journeys on desktop and mobile Chromium.
task docspassed:matlatl --strictreported 185 documents and 0 broken links. It also rewraps one line ofuser-docs/reference/server-cli.md, because the default workspace path depends on the checkout's path length; that change isn't part of this PR.Screenshots
Captured with a throwaway Playwright journey (Chromium, not committed), with offline BFF routes, a model inventory of three models, and a loopback activity stream that stays open for the running chat. The clock is fixed at 2026-09-25 12:30 UTC, the time zone is
Europe/Rome, and the locale is en-US. Every surface was shot at desktop 1280×800 and mobile 390×844, in light and dark.Before is the base (
7f5301a89) and after is this branch's head (7ef2c69af). Each tree was captured twice. The after runs matched pixel for pixel apart from 6 px in one shot; the before runs differed in 7 shots: two by the transcript's scroll position on a phone (19,604 and 23,648 px), and five by 6–142 px of anti-aliasing. Every pair was pixel-compared, and red boxes mark the regions that differ. Desktop composites are downscaled to 1600px wide.The Prototype column comes from the prototype harness (
stack-08): the gallery where it has the surface, and new captures for the image attachment, the Effort menu, a running chat, and a queued message. Its chats differ, so it's a visual reference, not the same content.Composer
Idle composer, live chat
Intended change: the prototype's frame and toolbar (+, microphone, send), with the pill row under the box. Mode and Model move out of the chat controls card into the pill row; the context meter and Compact sit at its end. The Safety pill and the Enter hint are gone. On a phone the composer runs edge to edge and the pills fold into Composer options. Matches the prototype, except that Studio keeps its context meter and Compact in the row, and has no MCP button yet.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Idle composer, new chat
Intended change: the same frame on a draft, with Mode and Model pills and Execution on its own line under them. Matches the prototype, except the Execution line, which Studio keeps because a draft's execution choice is a real setting; the picker is main's, unchanged. The greeting and starters are unchanged; they move only because the composer is shorter.
These composites were re-captured after the rebase onto #2219, with the same journey. Before is main (
7852ac855) and after is this branch's head (4c07463fa), so they also show changes from the PRs below this one in the stack, such as the top nav from #2228. Each tree was captured twice: the before runs matched pixel for pixel, and the after runs differed by 21 px in one shot. On a phone, the greeting runs past the screen edge in the Before and After columns; that overflow is on main, and #2256 fixes it.Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Image attachment
Intended change: the image becomes a round pill with a thumbnail above the text, and Clear draft (×) appears beside Send. The escape hint now sits above the composer. Matches the prototype, except the mode tint: the prototype's chat is in Accept edits.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Text attachment
New: a Markdown file attaches as a pill and is inlined into the prompt on send. The header shows the Accept edits badge. Matches the prototype. Before has no text attachments; the before column shows the same chat with only the typed text.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Menus
Model menu
Intended change: the prototype's ghost pill and menu, with the model list in a 26rem submenu (filter field, provider groups, check at the right). On a phone, the sheet's Model view with the Effort chips. Matches the prototype, except that Studio has no star for a default model (it has no default-model preference) and its filter is a menu list, not a command palette.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Effort menu
Intended change: the Effort submenu, unchanged in content; on a phone, the Effort chips under the model list. Matches the prototype.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Mode menu
Intended change: the Mode menu gains the posture dots (grey, green, blue); on a phone it is the sheet's Mode view. Matches the prototype.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Phone options sheet
Intended change: Composer options opens a bottom sheet with Add a file, Mode, and Model rows, then the context meter and Compact for a live chat. Before had no composer sheet on a live chat; its controls card stood above the composer. Matches the prototype, except for the usage footer (Studio's) and the missing Insert from MCP row (#1851).
Dark
Mobile, dark:
During a run
Queued message
Intended change: the prototype's strip: an hourglass header with the count, one row per message, and an actions menu (Steer while a run is live, Edit, Delete) in place of the inline pencil and bin. Matches the prototype, except the header reads "1 queued message" with no "↑ edit" or Clear all: Studio's queue drains on its own and has no paused state.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
Composer during a run (steer)
Intended change: the same frame while a run is live, with the Steer preference; the pills are disabled. Matches the prototype, except that Stop stays in the header (#2233) rather than replacing Send.
Dark and mobile
Desktop, dark:
Mobile, light:
Mobile, dark:
The prototype reference code is on
stack-08, inapps/web/src/features/chat/:chat-composer.tsx,composer-frame.ts,clear-draft-button.tsx,composer-option-menu.tsx,model-picker.tsx,effort-picker.tsx,permission-mode-badge.tsx, andqueued-message-strip.tsx, and inapps/web/src/lib/:attachment-inline.ts,file-meta.ts,draft-store.ts,permission-mode.ts, andreasoning-effort.ts.Review fixes
Fixes from the final regression and security review of the stack (stack #2227). Each one comes with a test that fails without the fix.
Fixes for the final review's #2253 findings. Each fix has a test that fails on the pre-fix head (
7ef2c69af) and passes now.createdChatKey. A send that settles after a switch leaves its text stored under the chat it was typed in. Accepted, that copy is spent; refused, it stays. A carry also empties the draft view's copy straight away.chat-composer.alignment.test.tsx: "swaps in the next chat's own draft when another chat opens during a send", "clears only the sent chat's draft when a send is accepted after a switch", "carries a draft's text only into the chat its send created".chat-workspace.integration.test.tsx: "keeps the open chat's draft when the user switches chats during a send", "carries a new chat's first prompt into the chat its send creates"disabledwhile openattachment-inline.test.ts: "fences every block longer than any dash run in the content, so content cannot close one", "outgrows a long run of dashes in any file". Alignment: "refuses a file that would pass the text total before reading it"The Enter, queue, and steer matrix and the step-2 pinning tests are unchanged:
chat-composer.pinning.test.tsxand the pinned integration tests have no modified lines. One existing test changed: "carries a draft's text into the chat its first send opens" now passescreatedChatKey, as the workspace does, and its assertions are unchanged.user-docs/operating/studio.mddocuments the seed, steer, and fence behaviour.Gates on
9e43ec012:studio:checkpasses with 1134 web tests (1120 before, so 14 new) and 195 server tests.studio:browserpasses 66 of 66, the same as before.The only visible change is the phone sheet's Mode view while a compaction is pending: the choices now dim and can't be selected, which matches the prototype's sheet (
disabled={!canEditMode}).Screenshots
Phone Mode view while a compaction is pending: the choices are dimmed and disabled. The prototype disables the same rows (
disabled={!canEditMode},disabled:opacity-50). Its gallery shot shows the enabled view, because the prototype harness has no busy capture.Dark, and the unchanged enabled Mode view
The idle composer is pixel-identical before and after: desktop and mobile, light and dark, 0 changed pixels.
Re-verification (2026-10-11)
stale_run_control, nothing drained the queue until another run ended. A steer that fails with no run active now drains the queue. (3f4e4b0c8) Test: integration "sends a queued message once its steer finds the run already ended".c7df1c7e1) Tests: "never writes a refused send back into the next account's drafts", "keeps a draft the user cleared when its refused send settles elsewhere".f81d27d0b) Test: "keeps the confirmation open and the user's draft while a confirmed seed is sending".?and mod+shift+o acted on the chat behind the modal sheet; it now suppresses app shortcuts while open. (62e2b442a) Test: "blocks app shortcuts while the phone sheet is open".Each test fails without its fix. No visible change, except that Edit prompt is disabled for the moment a confirmed seed is sending.
Rebased onto #2219 (execution templates)
Main's #2219 replaced the composer's Tools choice (
toolAccess) with an Execution template picker, a native<select>, and added a live chat's execution status. This branch keeps both as main has them, on their own line under the prototype's pill row:createSessionreceivesexecutioninstead oftoolAccess.598a40a15moves both to that line. When they sat in the single pill row, the picker pushed Model out of view at 500 to 640px, and the live status pushed Compact out at 1280px. The new browser testapps/web/e2e/composer-execution-line.spec.tschecks a draft and a live chat at 500, 640, and 1280px, on desktop and mobile Chromium. It fails 4 of 4 without the fix and passes 4 of 4 with it.dd5e31ac8updatesuser-docs/operating/studio.md: thesend=1confirmation shows an Execution row, which replaces the stale mention of tool access.executionin thecreateSessionbody, where it picked Tools and assertedtoolAccessbefore the rebase.The new-chat composites under Screenshots were re-captured for this rebase; the other screenshots predate it.
Gates on the restacked head
dd5e31ac8:studio:checkpasses (web 1,252, server 210 tests). At the stack top (#2256):studio:browser118 passed, 30 skipped; themecated --mockintegration suite 8 passed. The browser suite takes 5.3 minutes on a CI runner.Commit hashes cited earlier in this body predate the restacks; the current commits keep the same subjects.
Refs #2207
🤖 Generated with Claude Code