deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
DSH Web Conversation Navigator (pure client plugin, v0.4).
The navigator rail is for locating past messages in a conversation: every user message maps to one navigator node, while assistant messages create no node. It is not a scrollbar, not a task progress bar, not a chain of thought, and not an agent workflow.
Nodes come directly from the current conversation's complete messages data, i.e. messages.filter(kind user/steering).map(message => node):
ctx.sessions.list.getSnapshot().current → ctx.sessions.binding(id).session (SessionFace = ObservableSnapshotchat.order + chat.nodes.get(key) to walk every node.session.subscribe() pushes updates in real time; sessions.list.subscribe() listens for session switches and rebinds the current session's nodes (never mixing in other sessions).key equals the rendered row's data-chat-anchor-key (the official persistent anchor, not a DOM index), used for jumping / active syncing.messageId binds the real message identity: steering nodes use their messageId; user nodes use their persistent event sequence seq (data-layer user nodes have no separate messageId field; seq is their stable ID).type field and the legacy kind field; images/attachments counted by block type);stripMarkdown: code blocks wholesale, backticks, image/link syntax, heading/list/quote markers, bold/italic markers, etc., without reordering);[图片];[附件];overflow-wrap: anywhere);[无文本内容];[图片] / [附件].user message's rendered row by messageId/key (fallback to nearest row if not rendered), smooth-scroll to place the target message at ~30% from the top of the viewport (not flush top, preserving the reply context below), and immediately update the active node; lock active for ~700ms after jumping so IO cannot steal the highlight.IntersectionObserver (root = scroll container, rootMargin mid-band) automatically syncs the active node; does not re-scan the entire conversation on every scroll event.::after), ensuring ease of use.activeNodeId, hoveredNodeId (includes focusedNodeId semantics), previewNodeId, dragging, targetNodeId — all are internal module state of this plugin, affecting only its own DOM.
node = { id, key, messageId, index, el, top, previewText }
id: unique navigator node ID (dct-<key>).key: data-layer node key == rendered row data-chat-anchor-key (official persistent anchor).messageId: real message identity (steering.messageId / user.seq stringified).index: ordinal of that user message in the current conversation (matches message order).el: rendered row element (null when outside virtualization window; node still retained).previewText: locally generated original-text plain preview.div, no independent event listeners; all events delegated on the rail).requestAnimationFrame (diff reuses existing nodes).render('dom')), without rebuilding preview / recomputing text.dotMap cache avoids per-frame DOM queries.Scroll container → [data-conversation-scroll]
Message row → [data-chat-flow-kind] + [data-chat-anchor-key] + [data-chat-flow-key]
Node key → rendered row data-chat-anchor-key (== Conversation node key)
focus-chat fallback → [data-focus-flow] / [data-focus-anchor-key]
flowTop(row) = row.getBoundingClientRect().top - scrollport.getBoundingClientRect().top
dsh.client.platform: "web", window.__ModuleLoader__.load injection, not placed in bundles; inject: ['slots', 'sessions'].@deepseek-ai value imports (bundle purity gate compliant).--dsw-*), automatic light/dark adaptation, no independent design system.Sidebar | Navigator | Main Chat: the navigator is a position: fixed overlay at the document root (layout root / viewport layer), not a Sidebar child, not clipped by Sidebar's overflow / transform / contain. Horizontal positioning reads the sidebar column width directly from AppFrame's grid gridTemplateColumns: the rail is placed at the sidebar's right edge plus 6px inward padding groove into the chat area, not overlapping the sidebar in its default state; when Sidebar width / collapse state changes, ResizeObserver automatically follows. Preview is also an absolute overlay, defaulting to expanding toward the right side of the chat area: it does not change rail dimensions or push the chat / sidebar, so anchors are stable and there is no layout jump.
dsh-conversation-tracker/
├── package.json # dsh.client.platform: "web"; exports ./client
├── index.js # host-side empty apply (loader entry placeholder)
├── client.js # Conversation Navigator full UI logic (pure DOM self-rendering)
└── README.md
dsh plugin --profile web add github:timsok-shit/dsh-conversation-tracker
dsh plugin --profile web remove dsh-conversation-tracker
Pure client DOM plugin with no configurable external items; tunable constants are collected at the top / local constant area of client.js:
| Name | Location | Default | Description |
|---|---|---|---|
SLOT_PX |
render() |
12 |
Equal-spacing slot distance per node (rail adapts = N×SLOT_PX+8, capped at viewport height) |
maxTrackH lower bound |
render() |
40 |
Minimum rail height |
activeLockUntil duration |
jumpTo() |
700 ms |
Lock active during programmatic scrolling to prevent IO from stealing highlight |
| Collapse delay | scheduleCollapse |
220 / touch 900 ms |
Buffer before re-collapsing after leaving the container |
| Preview width cap | showPreview() |
320 px |
Maximum summary card width |
| Layer probe | checkLayerCover() |
900 ms interval | Hide when modal signal overlaps tracker |
Externally exposed (ModuleLoader contract, inject: ['slots', 'sessions']):
apply(ctx): plugin entry. ctx.sessions is the data-layer entry (list.getSnapshot().current → binding(id).session → getSnapshot() reads ConversationSnapshot); ctx.get('slots') for shell.overlay placeholder registration. Side effects: inject #dsh-conversation-tracker-style styles, mount [data-dsh-conv-tracker] container on body, subscribe to session snapshots and list, start MutationObserver / ResizeObserver / periodic layer probe.inject: declares dependency on slots and sessions services.collectDomRows (row association), buildDataNodes (data → nodes), render(mode) (full/dom), showPreview / hidePreview, jumpTo, checkLayerCover, etc., all only read/write this plugin's DOM and read-only session snapshots, with no external side effects.client.js: node --check for syntax → refresh GUI page to re-inject client-modules → verify per "Acceptance".ConversationSnapshot (read-only); never write session data files.elementsFromPoint, avoiding chat-area groove mis-detection causing tracker disappearance).[图片], attachments [附件], empty messages [无文本内容].CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。