The definitive slot model for the web client, replacing the first-generation
define/register two-step, ScopedSlots whitelist faces, and binding handles:
- 'root' is the only a-priori slot (SlotsService built-in); the shell renders
exactly ctx.slots.renderSlot('root', {}).
- register is the single API: children = slot declaration + render
authorization + runtime spec in one options object; misconfiguration fails
loud at load (duplicate declaration, undeclared contribution, one store
handle under two scopes).
- Component props arrive in four auto-derived shares: PropsRuntime<K>
(owner params + session/global standard kits via declare-merge),
PropsRenderSlots<S>, PropsStore<H>, and the inject business face.
sessionId is framework-supplied; hooks are framework-made only.
- Framework store seat: defineStore factories declare schema/actions/persist;
read = useStore, write = baked actions only; store scope derives from the
mounting entry; per-session persist keys and clearPersisted lifecycle.
- inject factories read the apply closure's own ctx (binding handles retired;
root-ctx back door closed); SessionProvider is self-wired render-prop.
- Rendering sits behind the SlotRenderer install seam; runtime stays
React-free; ownership ledger keyed to the single entry axis closes the
stale-authority window (StaleAuthorizationError probes).
Docs: the slot type-chain note is refreshed in place as the slot system
standard RFC (bilingual pair re-recorded); the web client architecture RFC
defers its slot sections there; packages/client/AGENTS.md gains the slot and
props discipline; gui-testing/web-styling notes drop missions/ references.
Tests: suites rewritten to the standard (props fed directly, real store
engines via createXXXStore().create(), no render machinery); load-time
negative samples for declaration/authorization/store conflicts; verified by
real-host playwright run (three columns, empty state, collapse, keyed session
remount, cross-slot selection sharing).
docs(ui-sidebar): point contract reference at the committed slot standard RFC
missions/ is workspace-local and never committed; the README must not cite it.
@deepseek-ai/dsh-client-ui-sidebar
Sidebar plugin: session multi-level tree (cwd grouping + parentId nesting), search, by-workspace grouping, state dots, three creation entries. Contract: the slot system standard.
src/client/contract/slots.ts is the single-domain contract file: SidebarRootInjected (the registrant's own injected share — plain service callbacks: onOpen/onCreate/onToggleSidebar) and SidebarRootComponentProps = PropsRuntime<'sidebar'> & SidebarRootInjected (owner {collapsed,width} plus the standard useSessions hook, resolved off ui-layout's SlotMap declaration, never re-stated). apply registers SidebarRoot cast-free against that composition; the inject factory closes over the plugin's own ctx.
There is no plugin store: rows derive in the component (useMemo over the useSessions snapshot + local expansion/search state) through the pure deriveRows in tree.ts.
The /client export surface is the plugin body (apply/inject) plus the contract types only — SidebarRoot, the row components, and the tree derivation are internal (the slot registration closes over them; tests import src paths directly).
Model Experience
None, as the sidebar renders the browser session list; nothing here reaches a model request.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
- State dots have two live data states (running/none) — the done/error/amber sources arrive with P-II approvals and notifications; the four-color primitive is already wired.
- Group-by menu ships by-workspace only — Update/Status grouping strategies are drawn without specs and deferred.
- "New task completed" unread marking is local viewing state — completion-time > last-seen never reaches the host.