dsh-timeline
DSH 插件:极简提问时间线——每条提问一个圆点,点击跳转,悬停预览。Minimal question timeline for DeepSeek Harness.
- Stars
- 2
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 16, 2026
Introduction
dsh-timeline
简体中文 | English
A minimal question timeline plugin for DeepSeek Harness: one dot per question you asked, right on the edge of the frame. Click a dot to jump straight to that message; hover to preview what you said and when.

No configuration, no host dependencies, no database — everything comes from the conversation snapshot already in your browser.
Features
- 🎯 One dot per question — every user message gets a dot in a slim rail on the right edge of the frame
- 🔵 Quiet by default — dots are gray; the highlighted dot (hovered, or the segment you are reading) turns blue. State is carried by color alone, so dots never grow and nothing can ever be clipped
- 🌓 Theme-aware — dots and the preview tooltip use the DSH design tokens (
--dsw-alias-*/--dsw-static-*), so they follow light / dark / system switches without a restart - 📍 Segment highlight — scrolling the chat lights the dot of the question whose segment (question + its answer) the viewport bottom is in; at the conversation's bottom the newest dot is lit by default
- 📜 Full-history rail — long conversations auto-paginate (
loadOlder) until every question in the session has a dot, not just the most recent page - 📏 Calm at any length — at most 15 dots are shown at once; older ones scroll inside the rail (hidden scrollbar), dots keep their spacing instead of compressing
- 🖱 Follow-scroll — scroll past the visible dots and the rail scrolls along, keeping the lit dot in view
- ⚡ Click to jump — smooth-scrolls the conversation to the exact message
- 👁 Hover to preview — shows the message number, the time it was asked, the first 80 characters of your question, and a 3-line preview of the AI reply that followed
- 🕐 Absolute time — today's messages show
HH:MM; anything earlier showsMM/DD HH:MM - 🧹 Scrollbar-free — the conversation page's vertical scrollbar is hidden too; the lit dot is the position indicator
- 🖱 Click-through rail — the rail never blocks the conversation; only the dots capture clicks
- 📦 Zero-footprint host — the node half is an empty
apply; nothing runs server-side - 🔤 No i18n bloat — copy is Chinese-only by design (keep it small)
Install
dsh plugin --profile web add github:zhangzheng25/dsh-timeline
Restart DSH, open any conversation with at least one question, and the rail appears on the right.
To develop locally instead:
dsh plugin --profile web add E:\path\to\dsh-timeline # junction-linked, edits apply after rebuild + restart
How it works
shell.overlay (root scope, additive, click-through)
└─ timeline.rail (self-declared child slot, session scope)
└─ useSession snapshot → chat.order + chat.nodes (kind === 'user')
→ dots → data-chat-anchor-key rows → scrollIntoView
- Injection point: the frame-wide
shell.overlayseat declares our own session-scoped child slottimeline.rail; the overlay bridges into the current session via the framework'sSessionProvider. - Data: user messages come from the
useSessionsnapshot (chat.order/chat.nodes), including the message timestamp (data.time) — no session logs, no database. - Current segment:
useCurrentAnchorwatches the conversation scrollport and picks the last user row whose top is at/above the viewport bottom — the segment (question + its answer) the reader is in. Its dot turns blue. - Jump: every chat row carries a
data-chat-anchor-keyattribute whose value is the node key; clicking a dot finds that row andscrollIntoViews it. - UI: the rail is
position: fixedwithpointer-events: none; only the dots opt back in. The dot list caps at 15 dots and scrolls internally; both scrollbars (the list's and the page's) are hidden via injected CSS.
Development
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm run build # tsdown → lib/index.js (host) + lib/client.js (browser)
Build artifacts (lib/) are committed, so end users don't need to build. After changing source: pnpm run build, then restart DSH (or just refresh the page for pure client changes).
Remote-install (copy mode) caveat: when the plugin was added via
dsh plugin addfrom another machine/remote, DSH loads a copy inside the profile —$DSH_HOME/profiles/web/node_modules/dsh-timeline— not this checkout, so local edits have no effect. After building, runpnpm run sync:dshto push the artifacts into the copy (defaults to thewebprofile; setDSH_PROFILEto target another).
Tech notes
- TypeScript + tsdown, following the official
clientBundlepreset shape - Slot declarations come in via type-only imports (
@deepseek-ai/dsh-client-ui-layout/client,@deepseek-ai/dsh-client-ui-conversation/client) dsh.client.injectlists the runtime packages the web loader must provide- The tooltip text is left-aligned explicitly (buttons UA-default to
text-align: center), and sized withwidth: max-contentso absolute positioning can't collapse it to one character per line - Dots are
flex-shrink: 0so flex layouts can never squash the circle into an ellipse; tooltips are portalled todocument.bodybecause anoverflow-y: autoancestor would clip them - Zero-JS theming: dot colors reference DSH design tokens (defined on
body, re-mapped bybody[data-ds-dark-theme]); the tooltip's surface is styled by the injected stylesheet — paper-white in light mode, near-black with white copy in dark mode — with literal fallbacks when the token sheets are absent
Credits
Slimmed-down rework of dsh-milestone by SnowCrescenter-tech — kept the dots, the jump and the hover preview, and added the segment highlight.
License
MIT