/** * RoleSwitchDock — the rolebox contribution to the dsh * `'conversation.input.dock'` slot (browser half). * * The dock is a list/session-scoped full-width row above the composer card * (declared by `@deepseek-ai/dsh-client-ui-conversation` as * `{ kind: 'list', scope: 'session', owner: InputZone }`). This component is * registered into that slot by the client plugin entry (`client.ts`). * * The dock answers a STATUS question — "which role is running?" — so the * redesign makes the VALUE the title. The shipped static label ("Role") is * gone; the header renders the active role's display NAME. A label the user * already knows (the strip is 8px above a composer, glyph-marked, and the only * such strip) is replaced by the one fact the user does not have. * * - a 40px toggle header — lead glyph, the active role NAME, an * `Active`/`Base` chip, a chevron — plus a sibling status seat outside * the button. The dock starts COLLAPSED on mount and on every session * change so it never blocks the composer; * - the active role is carried by FIVE redundant channels and never by a * coloured side border (a banned anti-slop tell): the spelled-out chip in * the header (which survives total loss of hue perception), a reserved * 20px trailing mark seat on every row holding a check glyph, an inherited * font-weight step (400 -> 500) on the active row, the host active-nav * fill, and `aria-current`. The shipped 6px dot is gone; * - the status seat is a SIBLING of the toggle button, not a descendant: * a live region nested inside an interactive control rewrites the * control's accessible name on every status change. It is rendered * UNCONDITIONALLY so the region stays mounted (mounting a live region * together with its text is unreliable across screen readers), and it is * empty and silent at rest — the value seat and the chip carry the steady * state. A successful mutation writes a confirmation into it visually * hidden, so a collapse is still announced; * - the disclosure is ALWAYS MOUNTED and animated via * `grid-template-rows: 0fr -> 1fr` plus `visibility`, so closing can * animate and closed content is neither focusable nor exposed to the * accessibility tree. The button carries `aria-expanded` and * `aria-controls`; * - a filter row (shown while roles exist) narrows the list client-side by * name and description as the user types, with a clear affordance, a live * `n of N` count, and an explicit no-match row. The query survives * collapse/expand and resets on session change; * - on mount (and on every `sessionId` change) the session's persisted * active role is hydrated from `GET /rolebox/roles/active?session=…`. A * tri-state (`loading` | `ready` | `unknown`) prevents the dock from * claiming "Base agent" before the probe has answered; * - a successful switch or clear collapses the dock — the collapse IS the * confirmation (no toast, no checkmark flash, no colour pulse) — and a * FAILED mutation keeps the list open so the Retry row stays reachable; * - a clear-to-base row (visible only while a role is active) issues * `DELETE /rolebox/roles/active?session=…`; * - a failed LOAD is recoverable: the empty state offers a Reload action * that preserves the open list and the typed query. * * The slot contract (dsh-client-ui-slots' `SlotCore.register` + * `PropsRuntime` / `InjectFace` / `PropsLocale`) is consumed STRUCTURALLY: * `@deepseek-ai/dsh-client-ui-slots` is not installed yet, so * `RoleSwitchDockProps` duck-types the composed four-share intersection * against the observed `.d.ts` shapes — see the module docstring of * `client.ts` for the citation map. The only external module this file * imports is `react`, whose type surface is supplied by the temporary * `react.stub.d.ts` in this directory — which declares `useState`, * `useEffect`, `useRef`, `createElement` and `Fragment`. No hook beyond * those five may be used. * * This module is BROWSER code: it must not import node builtins, and it uses * the browser `fetch` global with relative (same-origin) paths. It touches * the DOM in exactly ONE place, by design: a single `useRef` on the header * toggle, used to restore focus after a successful switch/clear. Collapsing * the disclosure hides the row the user just activated, so without that call * focus falls to `` and the keyboard user's next keystroke goes nowhere. * No scroll listeners, no measurement, no other DOM reads. * * @module */ /** * Structural role DTO — the `GET /rolebox/roles` list item. Mirrors the * route's `RoleSwitchRoleDto` (`web-role-switch-route.ts`): all five * keys are always present; `model` / `mode` are `null` when the definition * carries no override. */ export interface RoleSwitchRoleDto { id: string; name: string; description: string; model: string | null; mode: string | null; } /** Structural success body of `POST /rolebox/roles/switch`. */ export interface RoleSwitchOkBody { ok: true; session: string; role: string; } /** Structural success body of `GET /rolebox/roles/active` (`role` is `null` for the base agent). */ export interface RoleSwitchActiveBody { session: string; role: string | null; } /** Structural success body of `DELETE /rolebox/roles/active` (`role` is always `null`). */ export interface RoleSwitchClearOkBody { ok: true; session: string; role: null; } /** Structural error body of `POST /rolebox/roles/switch` (non-2xx). */ export interface RoleSwitchErrorBody { ok: false; error: string; } /** * Composed props of the dock entry — a duck-type of the slot framework's * `PropsRuntime<'conversation.input.dock'> & InjectFace<...> & PropsLocale<'conversation'>` * intersection, restricted to the two seats this component consumes: * * - `sessionId` — the framework-resolved session id, delivered through the * entry's inject factory (`client.ts` passes `inject: (sessionId) => * ({ sessionId })`, per the InjectParams of a `scope: 'session'` slot). * - `t` — the locale seat promised by declaring `locale: 'conversation'`. * Declared (optional) so the component satisfies the four-share * composition, but the dock renders hardcoded English text: the * 'conversation' dictionary keys are not known at this layer, and unknown * keys must not be routed through `t`. */ export interface RoleSwitchDockProps { /** Framework-resolved session id, delivered via the entry's inject factory. */ sessionId: string; /** Locale seat (declared `locale: 'conversation'`); accepted, not used. */ t?: (key: string, params?: Record) => string; } /** `GET /rolebox/roles` — same-origin relative path on the dsh web server. */ export declare const ROLES_ENDPOINT = "/rolebox/roles"; /** `GET /rolebox/roles/active` — the session's persisted active role. */ export declare const ACTIVE_ENDPOINT = "/rolebox/roles/active"; /** `DELETE /rolebox/roles/active` — clear the session's active role (same path as `ACTIVE_ENDPOINT`, method `DELETE`). */ export declare const CLEAR_ENDPOINT = "/rolebox/roles/active"; /** `POST /rolebox/roles/switch` — same-origin relative path. */ export declare const SWITCH_ENDPOINT = "/rolebox/roles/switch"; /** * The id wiring the header's `aria-controls` to the animated disclosure * region. Exported so the tests can assert the pair without duplicating the * literal. */ export declare const DOCK_DISCLOSURE_ID = "rolebox-dock-disclosure"; /** * `ROLE_DATALIST_ID` (`"rolebox-role-list"`) — retained as an exported * contract constant. The pre-restyle dock fed a `` with this id * into the role-id input; the picker restyle dropped the input/datalist * pair from the render tree, but the export stays (value unchanged) for * consumers that wire the old surface. */ export declare const ROLE_DATALIST_ID = "rolebox-role-list"; /** * The dock component: a dsh-styled picker (header + animated disclosure). * * Behavior: * - starts collapsed (and re-collapses on every `sessionId` change, and * after a successful switch/clear). The header's VALUE seat reports the * active role's display name while the list is hidden, and one header * click expands it; * - on mount (and on every `sessionId` change), fetches the switchable * roles from `GET /rolebox/roles` and the session's persisted active * role from `GET /rolebox/roles/active?session=…` — the active probe is * best-effort (a failure just leaves the seat showing the base agent); * - the expanded list is preceded by a filter row: a keystroke filter * narrows the rows by name and description (case-insensitive substring), * the clear button (visible only while a query is typed) restores the * full list, and an explicit no-match row reports an empty result set; * - a row click posts `{ role, session: sessionId }` to * `POST /rolebox/roles/switch`, then collapses on success (the collapse * is the confirmation). On failure the previous active role is preserved, * the list stays open, the server error is shown, and a Retry row re-runs * the failed switch; * - the clear-to-base row (visible only while a role is active) issues * `DELETE /rolebox/roles/active?session=…`, collapsing on success; a * failed clear keeps the previous active role and shows the error with a * Retry row; * - rows and the clear/retry controls are disabled while a mutation is * in flight (the filter stays usable — filtering is not a mutation). * * @param props - composed slot props (see {@link RoleSwitchDockProps}). */ export declare function RoleSwitchDock({ sessionId }: RoleSwitchDockProps): any; //# sourceMappingURL=role-switch-dock.d.ts.map