Skip to content

feat(prompt-input): add the PromptInput toolbar parts - #118

Open
pratikbuilds wants to merge 3 commits into
conversation/02-samplesfrom
conversation/09-prompt-input-toolbar
Open

pratikbuilds wants to merge 3 commits into
conversation/02-samplesfrom
conversation/09-prompt-input-toolbar

Conversation

@pratikbuilds

Copy link
Copy Markdown
Collaborator

Add PromptInputToolbar with an add menu, icon buttons, model and mode pickers, a float above the box, a context chip and a note. The pickers are select-only comboboxes and the add menu is a menu button; both keep or return focus to their trigger and need no optional peer. A toolbar placed last in PromptInput takes its own row.

Stacked on #117: review only this commit. Part of #94.

Notes for review

  • Toolbar parts take their own disabled; PromptInput's context stays private, so a host disabling the prompt passes disabled to the toolbar parts too.
  • No reasoning-level choice in the model picker; onValueChange stays a single id.
  • One class added to PromptInput's root so a trailing toolbar wraps onto its own row (e2e checks with and without a toolbar).
  • Escape in pickers/menu stops bubble-phase listeners (as the suggestions list does); untested inside a dialog.

Verification

  • This commit passes bun run typecheck and bun run lint on its own (the stack is checked commit by commit).
  • At the top of the stack: bun run check, bun run build, bun run dep-guard, bun run contrast-test and bun run test:e2e (Playwright specs and the Storybook axe sweep in light and dark) pass.

Closes #103

@corbits-triage

corbits-triage Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Triage: Fail

@pratikbuilds, please address the following:

  • style(prompt-input): tighten the toolbar popups isn't a conventional commit

Stacked on #117: only this pull request's own changes were checked.

Notes:

  • added code matches a code pattern: src/ui/prompt-input-toolbar.tsx:462 Invented fallback (an empty string, || [] or || 0); handle the missing value instead.

Waiting on a maintainer to approve: request changes.

Changed since the last verdict (Pass at 35dd216): 15 new commits.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated multi-agent review: independent review + second-agent validation; only confirmed items listed (re-verified against branch head 35dd216).

  • should-fix a11y src/ui/prompt-input-toolbar.tsx:156, 399 (+ use-dismissable-popover.ts:79-84) — popups the host opens don't get the dismissal net: closeOnEscape: false disables the hook's document-level Escape handler, so Escape is handled only on the trigger and inside the popup body, and Tab only inside the popup. A keyboard-only user with focus in the textarea cannot dismiss an open AddMenu/select at all (the only remaining dismissal is an outside pointerdown). Smallest fix: a capture-phase document Escape handler (with stopPropagation when open) plus root-level Tab handling; add the host-opened regression test.
  • should-fix a11y toolbar.tsx:494, 552 — the trigger's aria-label={label} replaces the whole accessible name, so a screen-reader user never hears the chosen model/mode while the popup is closed (sighted users see "Claude Opus", they hear "Model"). Fix: aria-label={chosen ? \${label}: ${chosen.name}` : label}or a visually-hidden span +aria-labelledby` including the value; regression-test that the accessible name contains the chosen option.
  • minor api-design — toolbar parts ignore PromptInput's disabled (the context is module-private and no part reads it; each takes its own disabled). Stories demonstrate the threading but no docstring states the contract — document it, or let parts consume the context.
  • nits confirmed — assignRef in toolbar.tsx:12-15 is character-identical to prompt-input.tsx:209-212 (consider one shared src/lib/ref.ts helper; the same idiom recurs ~9× across the series); Home/End open a collapsed select but the docstring's opener list omits them (:510-513 vs :361-365); PromptInputToolbar/Float/Note have no unit tests (prompt-input-toolbar.test.tsx never renders them); select-only combobox typeahead is absent (APG-optional, fine to defer — just note it).

@pratikbuilds
pratikbuilds force-pushed the conversation/09-prompt-input-toolbar branch from 35dd216 to 1fd1dcd Compare October 27, 2026 13:25
@pratikbuilds
pratikbuilds force-pushed the conversation/09-prompt-input-toolbar branch from 1fd1dcd to 3329560 Compare October 27, 2026 13:54
@pratikbuilds

pratikbuilds commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. Here's what happened to each point.

Fixed

1. Escape in the textarea couldn't close a popup the host had opened (should-fix)

  • The fix: while a popup is open, Escape pressed anywhere outside it closes it, and focus stays where it was. I used the bubble phase rather than capture, and Escapes something else has already handled are skipped. That way, if the @-suggestion list is also open, the first Escape closes the list and the second closes the popup.
  • Tests: three unit tests that failed before the fix, plus a Playwright case.

2. The toolbar ignored PromptInput's disabled (minor)

  • The fix: a new usePromptInputDisabled() makes the add menu, both selects and PromptInputButton follow the prompt's disabled. A part's own disabled still wins.

3. Nits

  • The select's docs now say Home and End open it.
  • PromptInputToolbar, PromptInputFloat and PromptInputNote now have a unit test.

Not changed, and why

  • Putting the chosen value in the trigger's aria-label: in Chromium, the trigger already reads as the name "Model" with the value "Claude Opus", which is how the APG select-only combobox works. Adding the value to the label would make it read twice.
  • A shared assignRef helper: it's copied about 11 times across the stack, so it's better as one clean-up once the stack lands.
  • Typeahead in the select: optional in the APG pattern, so deferred.

Also in this PR

A style(prompt-input) commit tightens the select and add-menu popups:

  • each option is one row, with the description inline
  • the active row has a plain fill instead of a coloured stripe
  • a check marks the chosen option
  • the padding is smaller

Commits: fix(prompt-input): close host-opened popups on escape; follow disabled, style(prompt-input): tighten the toolbar popups

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review · Request changes

Adds the PromptInput toolbar: add menu, model and mode selects, and the icon button.

Findings

  • src/ui/prompt-input-toolbar.tsx:278 — Escape inside the add menu or a select closes a dialog behind it. Both handlers stopPropagation in the bubble phase (prompt-input-toolbar.tsx:569 for the select). Radix DismissableLayer listens for Escape on document in the capture phase, so the dialog dismisses before this handler runs. Stop the key in capture, or preventDefault before the layer sees it, so one Escape closes only the popup.

  • src/ui/prompt-input-toolbar.tsx:56 — A host-opened popup closed from outside (the textarea) does not mark Escape handled. useCloseOnEscapeOutside calls setOpen(false) and leaves defaultPrevented false, and it also listens in the bubble phase, so a dialog's capture listener has already run. The suggestion list stops Escape on the textarea. This path should too.

  • src/ui/prompt-input-toolbar.tsx:313 — A disabled prompt still accepts a choice while the popup is open. disabled is only on the trigger (:252, :544). The menu stays mounted when open is true, and the item onClick checks per-item disabled only. choose (:478) does not check the select's disabled either (:505). defaultOpen, a controlled open, or disabling the prompt while the menu is up still adds a file or changes the model. Ignore the click when the part is disabled, and close the popup when it becomes disabled.

  • src/ui/prompt-input-toolbar.tsx:218 — Dismissing the add menu with a pointer press on a non-focusable node drops focus to document.body. Outside dismiss calls setOpen(false) (use-dismissable-popover.ts:77) and does not focus the trigger; close() would. The note at :172 is right when the press lands on the textarea. When it lands on something that does not take focus, return focus to the trigger.

  • Commit style(prompt-input): tighten the toolbar popups uses style, which CONTRIBUTING.md does not list. refactor matches that change.

Add PromptInputToolbar with an add menu, icon buttons, model and mode
pickers, a float above the box, a context chip and a note. The pickers
are select-only comboboxes and the add menu is a menu button; both keep
or return focus to their trigger and need no optional peer. A toolbar
placed last in PromptInput takes its own row.
A toolbar popup the host opened stayed open when Escape was pressed in
the textarea, because the popups opt out of the shared hook's Escape
handling to run their own. Listen for Escape on the document in the
bubble phase while open and close when focus is outside the popup,
leaving focus where it is. Handled Escapes are skipped, so the
suggestion list still closes first.

The toolbar parts each took their own disabled flag and ignored the
one on PromptInput, so a disabled prompt still offered a live add menu,
selects and buttons. Expose usePromptInputDisabled and default each
part's disabled to it; a part's own disabled still wins.

Also say that Home and End open a select on the first or last option,
and cover the toolbar, float and note parts with a test.
The select and add menu rows stacked a bold name over its description
and marked the active row with an accent stripe as well as a fill.
Draw each option as one row, the description inline and muted, mark
the active row with the neutral fill alone, as Menu does, and keep the
check for the chosen option. Rows sit flush with less padding.
@pratikbuilds
pratikbuilds force-pushed the conversation/09-prompt-input-toolbar branch from 3329560 to e9d2552 Compare October 28, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants