Skip to content

refactor(composer): toolbar reads ComposerCapabilities instead of the agent session - #1892

Open
reachrazamair wants to merge 6 commits into
refactor/composer-keyboard-hookfrom
refactor/composer-capabilities
Open

reachrazamair wants to merge 6 commits into
refactor/composer-keyboard-hookfrom
refactor/composer-capabilities

Conversation

@reachrazamair

@reachrazamair reachrazamair commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1891 (and #1890 below it). Step 4 of the Group Chat composer parity plan (Plans/group-chat-composer-parity.md). No visual or behaviour change in AI chat.

What changed

  • src/renderer/components/ChatComposer/capabilities.ts holds ComposerCapabilities: imageAttach, upload, voice, saveToHistory, permission, thinking, modelEffort.
    • deriveAiComposerCapabilities() builds the AI's flags. Terminal mode offers nothing, permission comes from supportsReadOnlyMode, and thinking from the existing supportsThinking.
    • aiCanAttachImages() is the old attach rule moved into one function; InputArea uses it for its other attach checks too.
    • GROUP_CHAT_COMPOSER_CAPABILITIES is a frozen constant (attach, upload and voice on, the four per-tab agent settings off). Nothing renders it yet; the next step does.
  • ToolbarControls no longer takes a Session. It takes permissionMode, terminalCwd and agentId as values. Tab changes go out through onPermissionModeChange / onThinkingModeChange, which the new useAiTabToolbarBinding supplies for AI, with callbacks that depend only on the agent and tab ids. InputArea memoizes the capabilities object, so typing does not hand the toolbar new props.
  • toolbarExtras is a slot in the toggle group, ready for the room's Read-Only toggle.
  • The Prompt Composer button shows when its callback is passed, and the phone sheet mounts when one of its section flags is on, so neither needed a new flag.

Deviation from the plan's flag list

The plan lists 13 flags. Only the seven above have a reader today, so the other six are deferred to the step that first reads each: commandMode, slashCommands and shellTabCompletion (step 8), fileMentions (step 6 or 8), mentionRouting (step 8), and sendMenu (step 8 at the earliest, and possibly never, since the send column already takes its menu as a prop). The plan document records this.

Tests

ToolbarControls.test.tsx is the only existing test changed: it built session props and checked tab writes through a mocked updateSessionWith, and now passes capabilities and values and checks the callbacks. The tab-write checks moved to useAiTabToolbarBinding.test.ts. ComposerOptionsSheet and the InputArea tests pass unchanged. New tests cover each AI flag from its source, the room constant, controls hidden by their flag, and the toolbarExtras placement.

Found, not fixed here

On a phone, AI terminal mode draws a "..." with no sheet behind it. That predates this change; a test now pins the current behaviour.

… agent session

Step 4 of the Group Chat composer parity plan. No visual or behavioural
change in AI chat.

New src/renderer/components/ChatComposer/capabilities.ts:
- ComposerCapabilities: imageAttach, upload, voice, saveToHistory,
  permission, thinking, modelEffort.
- deriveAiComposerCapabilities(): terminal mode offers nothing; permission is
  supportsReadOnlyMode, thinking is the provider streaming its reasoning,
  imageAttach is aiCanAttachImages() (the existing attach rule, now one
  function InputArea also uses for its other attach gates).
- GROUP_CHAT_COMPOSER_CAPABILITIES: attach, upload and voice on, every per-tab
  agent setting off. imageAttach stays on, as the room offers Attach today,
  until step 9 derives it from the moderator's provider. Nothing renders it
  yet.

ToolbarControls no longer takes a Session. Each optional control reads its
flag; the values it shows come as props (permissionMode, terminalCwd, agentId
for the permission wording); tab writes leave through onPermissionModeChange
and onThinkingModeChange, bound for AI by the new useAiTabToolbarBinding
hook, whose callbacks depend only on the agent and tab ids. InputArea
memoizes the capabilities on their sources, so a keystroke does not hand the
memoized toolbar new props. The Prompt Composer button shows when its
callback is passed (InputArea passes it in AI mode only, as before), and the
phone options sheet mounts when one of its section flags is on, which equals
the old AI-mode gate. A toolbarExtras slot renders in the toggle group before
the Enter toggle, for step 5's Read-Only toggle. ComposerOptionsSheet and
ModelEffortPills already took no session and are unchanged.

Deviation from the plan's capability list: only flags with a reader exist.
Deferred, each to be added by the change that first reads it:
- commandMode, slashCommands, shellTabCompletion: step 8, where the shared
  shell mounts the command ladder, slash and shell completion hooks per
  capability.
- fileMentions: step 6 or 8, when the shared textarea's @ picker needs to know
  whether it may offer file and folder categories.
- mentionRouting: step 8, if the shell mounts MentionRoutingBar.
- sendMenu: step 8 at the earliest; the send column already takes its menu as
  an optional prop, so a flag may never be needed.

Tests changed only where they built a session prop: ToolbarControls.test.tsx
rendered the toolbar with session fixtures and asserted tab writes through a
mocked updateSessionWith. It now passes capabilities and values, and asserts
the onPermissionModeChange / onThinkingModeChange calls; the tab-write
assertions moved to useAiTabToolbarBinding.test.ts. New tests cover each AI
flag from its source, the room constant, capability-gated controls, the
toolbarExtras slot, and the phone "..." with no sheet in terminal mode.
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review: 2
  • main
  • rc

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable regression was found.

Summary

Moves optional toolbar controls behind ComposerCapabilities and moves AI tab writes into useAiTabToolbarBinding.

  • The AI toolbar gets its controls and tab settings through a shared contract.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[InputArea] --> B[deriveAiComposerCapabilities]
  A --> C[useAiTabToolbarBinding]
  B --> D[ToolbarControls]
  C --> D
  D --> E[Desktop controls]
  D --> F[ComposerOptionsSheet]
  E --> C
  F --> C
  C --> G[updateSessionWith]
Loading

Reviews (1) · Last reviewed commit: "refactor(composer): toolbar reads Compos..." · Reviewed by Greptile

Raza Rauf added 2 commits October 28, 2026 10:42
Drops "AI tab binding `useAiTabToolbarBinding()`" from the CLAUDE.md
index line: a label and a symbol with no path. Naming the hook with its
full path would take the line to 329 characters, past the docs:verify
cap of 320. The full entry in docs/agent-guides/CANONICAL-UTILITIES.md
already names useAiTabToolbarBinding with its path in its opening
sentence.
…ties (composer index lines trimmed to symbols and paths)
reachrazamair pushed a commit that referenced this pull request Oct 28, 2026
The Progress section and the Step sequence table still said steps 2-5
were on unmerged stacked branches. Step 2 merged into rc as #1890
(9d6f5b2), so its Progress row cites the rc merge commit as step 1's
does, and its Step sequence note says merged. Steps 3, 4 and 5 name
their PRs (#1891, #1892, #1945), and step 3 now sits off rc rather than
on step 2.

The step commits in both tables are unchanged: each branch's tip moved
on 2026-10-28 only by a CLAUDE.md index trim and the merges that carry
it up the stack, which complete no rows. The Progress intro says so.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant