dsh-session-eva-status
DSH plugin: colors sidebar session rows by their official status in an Evangelion-flavored palette - orange = pending interaction, red = selected session, purple = finished but unviewed, rainbow title = pinned.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 7, 2026
- Updated
- Oct 7, 2026
Introduction
dsh-session-eva-status
English | 中文
Colors DSH sidebar Session rows by their official UI status, in an Evangelion-flavored palette. The plugin owns no state: it reads facts the Client already has, writes one data- attribute per row, and styles that attribute from a single injected stylesheet.
| Row state | Official fact it reads | Decoration |
|---|---|---|
| Pending interaction (approval / question / plan review) | uiSession.sessionStatus.pendingInteraction | orange #FF5C1A full-height left rail + 1.6 s breathing |
| Selected — the session open in the main view | sessions.list snapshot: retainedBy.mainView > 0 | red #FF3B30 full-height left rail, static |
| Finished but not viewed | …sessionStatus.completionUnread | purple #7A4FD6 full-height left rail |
| Pinned | workspaces.list snapshot: pinnedSessionIds | the row title becomes a static six-color rainbow (background-clip: text); the row itself gets no rail |
| Running / subagents running / idle / archived / blank | — | nothing drawn; the official spinner and archived dimming already say it |
The official priority is pending > running > subagents > completed. When a session has finished but its subagents are still running, the official row shows the ongoing spinner, so this plugin draws nothing and lets that spinner through instead of calling the row "finished".
The plugin's own priority is pending > selected > subagents (yields) > unread. A row that needs you outranks the row you happen to be looking at, and a selected row keeps its red rail while its subagents run — the official spinner is not hidden on selected rows, so both readings stay visible at once.
The design rule behind the palette: shape carries meaning, color and light carry style. The pin icon and the pinned-first ordering stay entirely official — only the pinned row's title text is re-colored, so the pin itself is never restyled or decorated.
Install
The plugin is a Profile dependency, like the other local plugins in this directory. Add it to ~/.dsh/profiles/<profile>/package.json (and to that profile's dsh.bundles list) and restart DSH, or use the plugin CLI with the package path:
dsh plugin --profile <profile> add <path-to>/dsh-session-eva-status
Config
The shipped cordis.patch.yml inserts the entry with these defaults; edit them there.
| Field | Default | Meaning |
|---|---|---|
hideOfficialDot | true | Hide the official status dot on pending and unread rows (their left rail replaces it) |
breatheMs | 1600 | Breathing period for the pending rail (ms, ≥ 200) |
colors | EVA palette | Override pending / selected / unread |
A malformed config throws at load; nothing is silently skipped.
How it works
- Row anchor:
[data-row-key="session:<id>"]— rendered by@deepseek-ai/dsh-client-ui-workspace. - Title anchor: the pinned rainbow is applied to the row's second child. The leading 16 px cell is always rendered, so the title's position in the row is stable and no hashed CSS-module class name has to be matched.
- Status source:
ctx.uiSession.sessionStatus, the Client's ownHostObservable<ReadonlyMap<SessionId, SessionStatus>>(running,pendingInteraction,completionUnread), plusctx.sessions.listfor two more official facts: the subagent catalog that decides the official "subagents running" state, andretainedBy.mainView > 0— the same fact the official row's ownselectedflag is computed from. The pinned set comes fromctx.workspaces.list(pinnedSessionIds). No polling, no event sniffing, no host half. - Row paint: the state is carried by a single
::beforepseudo-element rail, and the row's own background is left completely alone — so official hover and selection feedback is untouched. The rail is deliberately not aninset box-shadow: an inset shadow is a band hugging the border's inner edge, so it follows the row's ownborder-radiusall the way around and curls into a right-facing hook at each end — it reads as a bracket, not a rail. An absolutely positioned pseudo-element is not clipped by the row's radius, so the rail is one straight, square-ended bar spanning the full row height. - React-safe: the marker is a
data-eva-stateattribute React does not manage, so re-renders cannot wipe it; aMutationObserverfiltered todata-row-keyre-reconciles when rows mount, unmount or change identity. - Uninstall is clean: subscriptions, observer, row attributes, stylesheet and the debug handle all go away with the plugin fiber.
Credits and prior art
Nothing here was copied wholesale. This list states exactly what each source contributed.
Code techniques borrowed
- enterhalf/dsh-session-colorful-unread-pin-jobs — the browser-half shape for a DSH Web plugin: the
window.__ModuleLoader__.load({ id, factory })envelope with namedapply/inject, decorating Session rows through[data-row-key^="session:"], re-applying after React re-renders with aMutationObservercoalesced into one microtask, and pre-tagging the injected stylesheet (dataset.plugin/dataset.pluginCss) so a sibling plugin's hot reload cannot claim and delete it. Its 2-second polling, its own unread store and its title-gradient painting were deliberately not reused. dsh-ledger-cn(the local plugin package inC:\DSH\plugins\dsh-ledger-cn, unpublished) — the package layout this plugin follows:private: true,main: lib/index.js, the././clientexport pair, acordis.patch.ymlthat inserts its own Loader row, and the test style of loading the browser bundle behind a fakewindow.__ModuleLoader__with a minimal DOM stub.- Tencent/BrowserSkill —
@wxg-prc-cpg/browser-skill-dsh-plugin— the reference for an external package'sdshmanifest:dsh.bundle.patch,dsh.client.platform,dsh.client.inject, and thefileslist.
Contracts read from the official Harness (deepseek-ai/deepseek-harness)
packages/client/ui-workspace— the Session row DOM (data-row-key, the leading 16 px cell, title, time, pin), the row's own geometry (Rows.module.css: 32 px tall,border-radius: var(--dsw-radius-md)) and theSlotMapdeclaration of the row seats.packages/client/ui-session—ctx.uiSession.sessionStatus, theHostObservable<ReadonlyMap<SessionId, SessionStatus>>this plugin reads.packages/client/ui-primitives—StateDot, whose states define what the official row dot means.packages/client/ui-schedule— the worked example of occupyingsidebar.session.row.leadingandsidebar.session.row.hover(not used yet; kept for a planned hover readout).
No code, stylesheet or copy from the official packages is bundled here: the plugin reads their rendered DOM and their public Client services only.
Known limitations
- Hiding the official dot uses
> :first-childon the row. In a pending or unread row that 16 px cell holds only the status dot (thesidebar.session.row.leadingseat renders only while the row is idle), so nothing else is affected today — a future DSH that puts another occupant there would have it hidden too. - Archived rows are left alone: the official UI already dims their title, and an archived row shows no status dot there anyway.
- The selected rail is an addition on top of the official selection highlight (the row's own background), not a replacement for it.
- The pinned rainbow is text-only (
background-clip: text), so it needs no extra DOM: nothing is inserted into the row, and uninstalling removes the attribute and the rule together. - The palette is deliberately theme-independent (EVA colors, not theme brand colors), and no row background is tinted, so the same rail reads the same on light and dark themes.
- If a future DSH renames
data-row-key, the plugin stops decorating rows and nothing else breaks. - No model-visible surface: the plugin registers no tool, command, prompt or context.
Test
node --test test/client.test.mjs
License
MIT