dsh-turn-dots ⚫
> Codex-style conversation turn tracker: a vertical column of dots on the LEFT edge of the conversation column, one dot per user input; hovering a dot morphs it into a rounded vertical preview pill; clicking jumps to that turn. Zero core changes.
English | 简体中文
DeepSeek Harness (DSH) Web GUI plugin that draws a vertical turn-dots rail at the left-center of the conversation column: one fixed-pitch dot per user input, regardless of conversation length. Hovering a dot morphs it into a rounded vertical rectangle (Codex style) showing the first 48 characters of that input in vertical writing mode; clicking smooth-scrolls the conversation to that turn. The dot at the current viewport position carries a themed ring so you can track where you are.
> Adapted and slimmed down from dsh-milestone (MIT): it reuses the session-overlay bridge, the fixed-pitch dot geometry, and the row-anchor jump logic, but moves the rail to the left-center, replaces the separate tooltip with the dot-morph interaction, and drops search/bookmarks/fork/badges and every other extra feature.
✨ Features
- One dot = one user input: driven by the session snapshot (
chat.order+nodes.get(key),kind === 'user'), fixed pitch, auto-adapts to message count. - Hover morph: the dot expands into a rounded vertical rectangle (30×84px) carrying the input preview in vertical writing mode (
writing-mode: vertical-rl, works for CJK and Latin);title/aria-labelcarry the full text. - Click to jump: smooth-scrolls to that message (
scrollIntoView) via the[data-chat-anchor-key]row anchors. - Current-position ring: the dot at the viewport top gets a themed ring (pure scrollport observation, no polling).
- Load older: when the session has earlier pages (
hasMore), a···button at the rail top loads them one page at a time, expanding the dots over the whole conversation. - Cold-start resilient: after a DSH restart, if the rail mounts before the conversation scrollport DOM exists, positioning retries for up to ~3s instead of staying hidden until the next message.
- Zero core changes: only official seams —
shell.overlayroot overlay +SessionProviderbridge + a session-scoped child seat. - Theme-aware: all colors come from the
--dsw-alias-*token layer, so the rail follows light/dark and third-party palettes (e.g. the dsh-paper-theme warm cream). - HMR friendly: every registration lives in
ctx.effect/ctx.slots.inject, cleaned up on hot reload.
📦 Install
Prereqs: DeepSeek Harness installed (dsh web runs), Node ≥ 22.19, pnpm.
# Option 1: local directory install (development)
cd dsh-turn-dots && npm install --legacy-peer-deps && npm run build
dsh plugin --profile web add ./dsh-turn-dots
# Option 2: local link install
dsh plugin --profile web add link:./dsh-turn-dots
# Option 3: zero-install quick check (no profile changes)
dsh web --patch ./cordis.patch.ymlRestart dsh web after adding the plugin line (later source changes go through HMR).
🎛️ Usage
1. Open any session with at least two user turns (the rail hides below 2 to avoid a stray dot). 2. A vertical dot column appears at the left-center of the conversation column — one dot per user input. 3. Hover a dot → it morphs into a vertical preview pill; click → smooth-jump to that turn; the dot at the current position carries a ring. 4. Long conversations: DSH only loads the most recent window by default, so the dots reflect the loaded part; when older pages remain (hasMore), a ··· button appears at the rail top — click it to load older messages page by page and expand the dots.
> Tuning knobs live in src/client/TurnDotsRail.tsx: MIN_MARKS (minimum turns before showing, default 2), PREVIEW_LENGTH (preview length, default 48), RAIL_INSET (inset from the scrollport's left edge), SCROLLPORT_MAX_RETRIES (cold-start positioning retry cap, default 20 × 150ms).
🛠️ Local development
npm install --legacy-peer-deps # install build deps
$env:DSH_NODE_MODULES = "$env:USERPROFILE\.dsh\profiles\node_modules"
npm run setup:dsh-workspace # symlink the runtime @deepseek-ai/* packages
npm run verify # ★ one-shot gate (clean + typecheck + build)
npm run typecheck # type check
npm run build # tsc + tsdown → lib/
dsh web --patch ./cordis.patch.yml # zero-install quick checkDirectory layout
dsh-turn-dots/
├── package.json # dsh.bundle.patch + dsh.client dual contract
├── cordis.patch.yml # bundle patch layer (inserts this package)
├── tsdown.config.ts # client bundle (__ModuleLoader__.load + purity gate)
├── scripts/ # build / clean / setup-dsh-workspace / verify
└── src/
├── index.ts # host half: no-op (client-only plugin)
└── client/ # browser half
├── index.ts # apply: shell.overlay bridge + rail registration + styles
├── TurnDotsOverlay.tsx # root overlay → SessionProvider → session rail
├── TurnDotsRail.tsx # left-edge rail: marks / hover morph / jump
├── useCurrentAnchor.ts # current-position ring (scrollport observation)
├── rail-logic.ts # pure helpers: extractText / findRow / currentIndexOf
├── rail-style.ts # dots + morph + ring CSS (token-driven)
└── locales.ts # zh/en copy🧩 Ecosystem position
- Compared with existing plugins:
dsh-milestone(right edge, separate tooltip, feature-rich),dsh-message-preview/dsh-navbar/dsh-turn-scrubber(all right side),dsh-chat-outline(left outline list). This is the only left-center, dot-morph-on-hover implementation, matching Codex's long-conversation navigation. - Approach: dual-contract client plugin (
dsh.client+ bundle patch); the overlay bridge follows dsh-milestone'sshell.overlay+SessionProviderpattern. - Zero core changes: all registrations live in
ctx.effect/ctx.slots.inject, cleaned up on HMR unload.
⚖️ License
MIT © 2026 Blaczz. Portions adapted from dsh-milestone (MIT © 2026 SnowCrescenter-tech); see [LICENSE](./LICENSE). Not affiliated with DeepSeek Harness — a community plugin.