docs/design/web-shell/web-shell-composer-add-menu.md
The composer can already do a lot of "add something to this message" work, but almost all of it has no visible entry point. Files must be dragged in or pasted. Referencing a workspace file requires knowing to type @. Invoking a skill requires knowing to type /. A user who does not know a capability exists has no way to discover it.
There is a related gap behind this one: the repository already contains a consolidated "more actions" surface in the composer, but it only renders on touch-like devices. On desktop it does not exist at all, so desktop users have no capability list of any kind.
Give the composer a + button at the front of its toolbar that gathers the "add to this message" capabilities which currently lack a visible entry point.
The + is an entry point, not a capability. It surfaces what already exists.
addMenu joins that list as an opt-in identifier and is absent from the default list, so existing embedders see no change.addMenu to the context-sensitive defaults so empty-state Git controls are preserved. Split-view panes opt in through their pane list and omit only the width control because pane sizing owns their width.+ means. Management surfaces (MCP servers, tools, agents, settings) stay out deliberately — they already have sidebar entry points, and repeating them turns a discoverability fix into navigation redundancy.One group, five items, all of which expand into submenus.
| Item | Description | Behavior |
|---|---|---|
| Add file ▸ | Submenu; choose "Attach to message" or "Upload to workspace", then pick local files | |
| Reference file ▸ | Submenu; searches workspace files by name; inserts a reference | |
| Extensions ▸ | Plain submenu list with descriptions; inserts an enabled extension reference | |
| MCP ▸ | Plain submenu list; no filter; inserts an MCP server reference | |
| Skills ▸ | Submenu; lists skills; prepends the skill's invocation to the front of the input |
Why one file entry, not two. Two adjacent top-level entries that both start with "pick a local file" are hard to distinguish. One "Add file" submenu keeps the main menu compact while making the destination explicit before the native picker opens.
All five top-level rows are one line. The submenu contents provide the needed detail. Below the small-screen breakpoint, menus narrow and secondary descriptions hide so adjacent submenus fit without horizontal clipping.
+ visibility is decided by the host toolbar item list alone. When addMenu is in the list, + is present; when not, it is absent. It does not disappear because the five inner capabilities are all unavailable in the current workspace — that would make the entry point flicker across workspace switches and defeat discoverability precisely when it is needed most. If the host has opted in but every inner capability is unavailable, + opens to an empty-state row explaining that no add actions are available here.+ trigger. A synchronous reference is appended to the draft and leaves the caret after the tag so subsequent typing follows it.| Area | File |
|---|---|
| Menu itself | new client/components/composer/AddMenu.tsx |
| Wiring and enablement | client/components/ChatEditor.tsx |
| Reusable reference sources | new client/hooks/useAtMentionSources.ts, consumed by both menus |
| Copy | client/i18n.tsx |
ChatEditor.tsx has grown large enough to be a signal, but this change does not restructure it — it only guarantees the new menu does not land inside it.
These are facts about the current system that shape the design, and each one rules out an otherwise obvious implementation.
+ menu and the @ panel can never disagree about the same file.@ panel. Directory drill-in is enabled only for the + file submenu; @ continues to use workspace glob search whenever that action exists.+ shows the same five items on touch and desktop. Attachment and reference insertion reuse their existing lanes; skill prepend targets CodeMirror on desktop and the native textarea on touch.
Unverified: right-side flyouts depend on hover, and touch has none. The menu primitive is expected to fall back to click/focus for non-mouse pointers, but that needs a real-device check. If it fails, touch keeps the single item that does not need a submenu and this section is rewritten.
/ already covers it, and dropping this row also removes the draft-state problem that came with it.+ items. Five fixed items this round, gated by the existing toolbar item list mechanism.ChatEditor.@ panel and + consume the same source of truth./ first would produce — but the transition must be visible enough that the user notices the shape of their message changed.File separately; do not fold into this change.
@ panel already offers MCP resource drill-down, which the Web Shell backend ignores. Users believe they referenced a resource; they did not.@ references are consumed by an existing resolver. That describes the TUI path; the daemon path does not do it.Behavior-level, not tied to an implementation shape:
addMenu is not in the host's toolbar item list, + never appears. When it is present, + sits at the front of the toolbar and no other control moves.@ panel uses for servers without resources; it intentionally does not enter resource drill-down.+ itself remains visible whenever addMenu is in the host's toolbar item list. If every inner item is unavailable, + opens to an empty-state row and does not silently vanish.