feat(plugins): add chat-input-actions slot for plugin toolbar icons (#1794)
* feat(plugins): add chat-input-actions slot for plugin toolbar icons
Let plugins render icon buttons into the chat composer toolbar, beside
the model picker, mic, and send button, via the existing named-slot
mechanism (registerComponent). The host mounts the slot in both the
desktop and mobile toolbars and forwards { taskId, taskTitle,
activeSessionId, sessionIds } as slotProps — the active session plus
every kandev session id on the task. Resolving a session id to an
agent/ACP transcript id is left to the plugin (server-side, via the Host
data API); the host only propagates ids.
Docs (PLUGIN-API.md, public plugins/plugins-authoring) document the new
slot and its props.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(plugins): read store optionally in chat-input-actions slot
ChatInputPluginActions read the app store via useAppStore, which throws
outside a StateProvider — breaking existing toolbar tests that render the
composer in isolation. Switch to useOptionalAppStore (fallback to no
sessions) so the slot never hard-requires a provider.
Also: gate the slot behind hideAgentControls to match the other
session-specific toolbar controls, drop a redundant String() coercion,
tidy a dangling docs reference, and cover the taskId=null path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -123,7 +123,13 @@ interface PluginRegistry {
|
||||
|
||||
// Named slot injection. Host renders all components registered for a slot via
|
||||
// <PluginSlot name="..." props={...}/>. Initial slots: "task-sidebar",
|
||||
// "settings-nav", "main-nav-footer".
|
||||
// "settings-nav", "main-nav-footer", "chat-input-actions". The last renders
|
||||
// icon buttons in the chat composer toolbar (beside the model picker, mic,
|
||||
// and send) and forwards `{ taskId, taskTitle, activeSessionId, sessionIds }`
|
||||
// as `slotProps` — the active session plus every kandev session id on the
|
||||
// task. Resolving a session id to an agent/ACP transcript id (e.g. to key
|
||||
// tokscale cost data on a session) is the plugin's job, done server-side in
|
||||
// the plugin backend via the Host data API; the host only propagates ids.
|
||||
registerComponent(slot: string, Component: React.ComponentType<{ slotProps?: unknown }>): void;
|
||||
|
||||
// WS action handler. Bridged into the existing lib/ws dispatch; called with the
|
||||
@@ -161,6 +167,11 @@ defaults, or the bare component when the route opted out (`topbar: false`).
|
||||
message to any `registry.getWsHandlers(action)`.
|
||||
- `components/plugins/plugin-slot.tsx`: `<PluginSlot name props/>` renders all
|
||||
slot components; drop into task detail sidebar + settings nav as initial hosts.
|
||||
The chat composer toolbar
|
||||
(`components/task/chat/chat-input-toolbar-desktop.tsx` and
|
||||
`-mobile.tsx`, via `chat-input-plugin-actions.tsx`) hosts the
|
||||
`chat-input-actions` slot, passing
|
||||
`{ taskId, taskTitle, activeSessionId, sessionIds }`.
|
||||
|
||||
## Security posture (documented, enforced where cheap)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user