ui-sidebar shell landed on master (#643: geometry-only shell, the
browsing region moved to the sidebar.workspaces slot); this branch's
sidebar.settings foot seat re-applies on top — the hardcoded foot row
becomes the seat, the seat rides the railIn crossfade, and the shell
spec's renderSlot stub key-splits region vs settings.
The shell is now a pure composition face: no dictionaries, no locale
dependency, and three new chrome content seats (settings.trigger /
settings.header / settings.close) whose slot content also carries the
accessible names (trigger text, dialog aria-labelledby, visually hidden
close label). ui-settings-general returns as the owner of copy that
belongs to no single feature: chrome content, the General section with
its item slot, and the settings dictionaries. Slot types split homes —
trigger/header/close/section live in the shell contract; the
settings.general.item entry moves to the locale package (the common
dependency of every item registrant), with ui-theme consuming it
through a re-export seam; the verbatim duplicate merges are gone and
the dependency graph is a clean DAG.
The five settings-surface registrants carried near-identical
spec-check/ledger-judge/subscribe scaffolding (three jscpd clones);
ui-slots now owns deferRegistration() — ledger-judged presence, refresh
for registrant-localized labels, one-call disposal — and every
registrant shrinks to its registration body.
The ui-layout apply bench provides a real LocaleService before the
theme plugin boots (ui-theme now injects slots/locale to register its
Appearance row); drop locale's unused clsx dependency.
Locale and ui-theme apply coverage on a real Context + SlotCore:
dictionary assembly, declaration-aware registration both ways,
inject-time getter re-sync, service write-back through the event flow,
HMR collapse recovery, and teardown reclamation. Four settings-surface
packages sit at full per-file coverage.
Generated docs follow the ui-models rename and the removed
ui-settings-general package; the English note side picks up the
feature-owner self-registration doctrine.
Settings collaboration direction (recorded in the note): the shell only
provides composition faces — feature plugins register themselves. The
General section moves into the ui-settings shell (order 0, skeleton
rows) and declares the settings.general.item list slot; locale registers
the Language row and ui-theme the Appearance row (each with its own
store mirror, dictionaries, and ledger-judged deferral); the
ui-settings-general package is gone. ui-settings-models becomes
ui-models — a feature package that contributes its Settings section
rather than a settings-owned satellite. The item-slot SlotMap entry is
authored in the ui-settings contract and repeated verbatim in
locale/ui-theme (reference-cycle avoidance; declaration merging keeps
the copies identical).
New and touched sources reach the CI per-file 100% thresholds: HoverCard
(timers, placement clamp, disabled guard), Menu label/danger/pointer-leave
branches, WorkspaceBrowser (mode switch, search, rail icons, rename dialog,
drag), rows and tree derivations, the workspace fixture stubs, the rename/
insertSessionBefore wire rows, and the entity move semantics. HoverCard's
position state narrows to {left, top} (equivalent refactor, no behavior
change).
Replace optional chains on always-present globals with the repo's
typeof guards (store.ts precedent), drop the non-null assertion by
failing loud on an impossible registry miss, and fix two arrow-parens
slips; cover the no-localStorage boot path in both service suites.
- rename same-title no-op moves inside the serialized creation chain
- insertSessionBefore maps only the typed WorkspaceMoveInvalidError to
workspace-move-invalid; storage failures stay internal
- workspace upsert rejects snapshots older than the installed projection
- flat-mode empty state shows when the query hides the intent row
- intent row no longer forces group expansion; header twist stays live
- group-by menu rides a portal; menu clicks stop propagating to the row
- intent row uses the same single-slot indent in both list modes
- regenerate cordis api/catalog + doc graphs
Full per-file coverage for the three settings packages: invariant
companions, store mirroring with revision guards, behavior-shaped
section/shell specs (props-fed, real store engine), and apply-level
suites on a real Context + SlotCore covering declaration-aware deferral
and HMR collapse recovery. All three registrants now judge presence on
the slot ledger instead of a local disposer, which went stale when a
parent redeclaration cascade removed the entry (ds-review-bot finding);
the locale re-register path keeps the same idempotence.
Regenerate the module-graph/config-catalog/event-graph docs for the
locale rename and new packages; allowlist the three settings READMEs;
complete the RFC code block and add its English pair. Cover the
ThemeService media-query paths (stubbed matchMedia) and mark the
unreachable registry fallback. General section re-registration now
judges presence on the slot ledger instead of a local disposer, which
went stale when an HMR collapse removed the entry.
Sidebar session list grows the figma 239-10458 feature set and the
workspace/session browsing region moves wholesale into ui-workspace:
- Group-by menu (WorkSpace / In one list): flat mode lists every session
top-level, strictly newest-first; the choice persists across reloads.
- Session rows get a 500ms hover detail card (title / relative time /
status line) and a ... menu (Rename / Fork session / Delete session,
visual-only for now); workspace headers get ... with Rename (wired) and
Delete workspace (visual-only).
- workspace.rename RPC: trims, rejects duplicate titles on the create
chain (workspace-name-conflict), no-op on same title; modal dialog with
client-side duplicate pre-check.
- workspace.insertSessionBefore RPC (DOM-insertBefore semantics, omitted
anchor appends): HTML5 drag reorder of root sessions inside a workspace
group; order truth stays host-side, the view refreshes from the
response/changed frame.
- Activity pinning removed: the session/event touchSession chain is gone;
workspace accounts are manually owned (new sessions prepend, explicit
reordering only). Contracts and tests updated, api catalog regenerated.
- ui-sidebar reduced to the column shell (brand, fold state machine, New
Session, Settings) exposing one sidebar.workspaces hole with a two-fact
owner share {wide, expandSidebar}; ui-workspace owns the whole region
(header, search, grouped/flat lists, dialogs, drag) plus the picker via
a shared WorkspaceCreateFlow. The old sidebar.workspace picker slot and
its deferral indirection are gone.
- ui-primitives: Menu gains label entries, danger rows, and
closeOnPointerLeave; new HoverCard (portaled, open-delay, disabled
guard). Hover card and row menu never coexist.
Add the browser Settings surface as slot-composed plugins over new
preference services:
- Rename dsh-client-i18n to dsh-client-locale (locale is the domain
name); LocaleService adds getLocale()/setLocale(id), immutable
snapshots, a locale/change event, and dsh.locale persistence.
- ThemeService owns the light/dark/system preference (default system),
resolves system via prefers-color-scheme, publishes theme/change
snapshots, persists dsh.theme, and no longer touches the DOM;
ui-layout's ThemePresenter applies resolved snapshots
(body[data-ds-dark-theme] + alias tokens) and cleans up on dispose.
- ui-sidebar drops the phase-1 settings dropdown/modal; the foot renders
the new sidebar.settings slot with the column state.
- New ui-settings shell occupies sidebar.settings: foot trigger row and
the centered 1080x700 panel (figma 501:29947) with 24% mask, close
button / mask click / Escape all closing, and a 188px nav projected
from the settings.section list slot it declares. Nav labels are
registrant-localized; sections re-register on locale change, so the
ledger version is the shell's only subscription.
- ui-settings-general registers the General section: Permission and
Tool Call skeletons, live Language (locale menu) and Appearance
(Light/Dark/System cubes following the persisted preference); its
slot store mirrors both service snapshots via apply-side listeners.
- ui-settings-models registers the Models nav entry with an empty
content column.
- Portaled menus pin z-index above modal overlays (a menu anchored
inside the settings dialog rendered underneath it and was
unclickable).
- theme/data/list-pen icons in ui-primitives; settings copy ships as
zh/en dictionaries; fixture manifests gain the settings rows.
Integrate the Commander adapter with master's `dsh web --workspace-root`
(workspace-aware session flow).
- args.ts: add `--workspace-root <path>` to the web subcommand; WebInvocation
carries workspaceRoot.
- web.ts: keep the adapter-parsed signature, take (host, port, dev,
workspaceRoot) and pass workspaceRoot through to AppCLIEntry (drop master's
re-added parseArgs and CLI host/port validation — the schema owns those).
- bin.ts forwards invocation.workspaceRoot; args.spec + the Agent Note pair note
the flag.
Integrate the Commander adapter with master's headless rework (dsh -p now boots
through AppCLIEntry — the same cordis.yml composition as dsh web, web-observable
while it runs — via toFetchHandler(ctx.apiProxy)).
- headless.ts: keep master's AppCLIEntry-based body but take the adapter's
pre-parsed `task` (drop the re-added parseArgs). Old startHost path and the
now-deleted packages/host/runtime dependency are gone.
- remove the stale packages/host/runtime dir left on disk by master's deletion
(mirrors the earlier packages/ui/acp cleanup).
ds-review-bot: `dsh web -p task`, `dsh web --resume s`, and `dsh --config c.yml
web` reached the web action with those values in program.opts() but the action
ignored them and served — silently dropping mode-specific inputs. The web
action now reads the parent opts and fails loud (exit 1) on a leaked
--config/-p/--resume, matching the root mode's mixing guard. Covered in
args.spec.ts.
Also (ds-review-bot): tui-demo/README documented the removed `dsh
[path-to-cordis.yml]` positional form; corrected to bare `dsh` / `dsh --config
<path>`. Agent Note + Chinese pair note the web-leak guard.