dsh-plugin-session-outline

August 14, 2026 · View on GitHub

A DeepSeek Harness cordis plugin: a floating session outline navigator on the right side of the conversation, listing every user turn and jumping to it.

  • The entry point is the round button (☰) at the middle-right of the conversation area; click to expand/collapse (Esc or × to close).
  • The outline keeps only user messages (user / steering rows = the start of each turn, numbered), and clicking an entry smoothly scrolls to that turn.
  • The panel is draggable (grab the header); its position is remembered in localStorage.

Interaction details

  • Current position indicator: the topmost visible user message is highlighted in the outline; the header shows a Session outline · N entry count.
  • Previous/next turn: ↑ Previous turn / Next turn ↓ buttons at the panel footer step through turns from the current position (without a current position, Previous = last turn, Next = first turn); while focus is inside the panel, ↑/↓ arrow keys do the same (input fields are never hijacked).
  • Jump feedback: the target entry flashes briefly after a hit.
  • Lazy-load fallback: when reopening DSH, older turns may not be rendered yet (or may have been unloaded by the virtualized list). Clicking such an entry shows ⏳ Loading…, scrolls to the top/bottom of the conversation to trigger loading, and polls until the target appears (up to ~10s).
  • Debounced refresh: MutationObserver bursts are coalesced to 150 ms plus a 2 s safety interval; only structural changes and text changes inside user rows trigger a rescan, so assistant streaming does not cause repeated full scans.
  • No dead button: when the conversation has no user messages yet, even the ☰ entry button is hidden.
  • Bilingual copy: uses the DSH locale system (session.outline namespace, zh/en) and follows the UI language.
  • Entry text is automatically stripped of trailing timestamps (HH:mm, relative times, dates in both zh/en formats).

How it works

  • Slot: shell.overlay (list/root) — the layout's reserved floating layer, independent of the right details column; order: 100 places it after the shipped entries.
  • Data source: the stable DOM anchors data-chat-anchor-key / data-chat-flow-kind (user / steering) on conversation rows — the same anchors the conversation view itself uses.
  • Jumping: manual scrollTop computation + scrollTo({ behavior: 'smooth' }) with a 450 ms position check (not scrollIntoView, which the page's scroll guards / virtualized list may swallow).
  • Performance: collapsed → only a cheap presence probe; expanded → full scan
    • IntersectionObserver for the current position; the plugin's own DOM mutations are ignored; unchanged scans skip re-renders.
  • Styling: DSH design tokens (--dsw-alias-*) only, dark/light aware; the injected <style> carries a data-plugin dedupe guard so HMR reloads never duplicate it.

Compatibility

  • Platform: DSH web (dsh web), dsh.client.platform = "web".
  • peerDependencies:
    • @deepseek-ai/cordis ^4.0.1
    • @deepseek-ai/dsh-client-ui-slots ^0.1.0-rc.6
  • Client declaration: inject: ['slots', 'locale'] (service-level); the package-level dsh.client.inject points at @deepseek-ai/dsh-client-ui-slots (a static module in the platform seed table).
  • The only reliance on DSH web internals is the DOM anchors above and the shell.overlay slot — both are public upstream contracts.

Install

From npm (after publishing)

npm install -g dsh-plugin-session-outline   # or install into the profile's node_modules

Manual

After building, copy the plugin directory into the profile's node_modules:

cp -r dsh-plugin-session-outline ~/.dsh/profiles/node_modules/

Then append to cordis.patch.yml:

- insert:
    - id: session-outline
      name: dsh-plugin-session-outline

Restart DSH and open a conversation — the ☰ button should appear mid-right.

Development

The client half must be bundled into the window.__ModuleLoader__.load({ id, factory }) form to be loaded by the web frontend (dsh-client-modules serves exports["./client"] into the /plugins boot graph).

npm install            # devDependency: esbuild only
npm run bundle         # scripts/bundle.mjs: esbuild → lib/client.js
npm test               # node --test: bundle contract + internals unit tests
npm run preflight      # publish/deploy preflight (stale-bundle guard + contract checks)
npm run check          # node --check lib/client.js
# npm publish runs prepublishOnly → npm run preflight automatically

Tests exercise the pure functions through the internals export (the same convention as dsh-web-app) and black-box the apply/inject contract in a vm sandbox.

Local verification

Deploy to a running DSH profile with the atomic sync script (temporary files + sequential renames, bundle last, so the server never sees a half-updated state):

npm run bundle && npm run sync        # default: ~/.dsh/profiles/node_modules/<name>
# optional: DSH_WEB_URL=http://127.0.0.1:PORT npm run sync  # also prints the new boot rev

Refresh the browser — a changed boot rev means the new bundle is served.

Publishing

Current status: private testing. package.json is marked "private": true, so it must NOT be published to the public npm registry. Install from a local path or a git dependency during testing:

dsh plugin --profile web add file:<this-repo-path>     # local path install
# or as a git dependency (private repos need npm credentials):
npm install git+https://<private-repo-url>.git

Once stable: remove "private": true, then npm publish (prepublishOnly runs preflight + full tests and aborts on failure).

What ships is controlled by the files field: index.js, lib/client.js, README.md, README.zh.md, LICENSE (source and build scripts are not shipped).

Files

index.js                 host half (minimal loader entry)
src/client.jsx           client source (React component + shell.overlay registration)
lib/client.js            client bundle (build artifact — do not hand-edit)
scripts/bundle.mjs       local build script (esbuild)
scripts/sync-profile.mjs atomic profile sync script
test/                    contract + internals tests (node --test)
LICENSE                  MIT

中文文档