Sessions & Messages

dsh-plugin-session-outline

alonelypigeon/dsh-plugin-session-outline · ★ 0

alonelypigeon

DeepSeek Harness cordis plugin: session outline navigator in the right-side overlay

Repository is no longer available (checked 2026-11-15). This entry is retained for tracking and cannot be installed.

License
MIT
Added
2026-08-23
Maintenance
active
Install kind
github

No manifest check is available; listing does not imply compatibility or security review.

README

Source README · body: en · Read Chinese README

Table of contents
  1. Interaction details
  2. How it works
  3. Compatibility
  4. Install
  5. From npm (after publishing)
  6. Manual
  7. Development
  8. Local verification
  9. Publishing
  10. Files

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

中文文档