DSH Hub

dingyi580/dsh-conversation-rail

dsh-conversation-rail

UIWeb UI0 GitHub stars· updated 2026-08-19

dsh-conversation-rail is a community DeepSeek Harness plugin. Read the repository README before installing.

Install

npx @deepseek-ai/dsh plugin --profile web add github:dingyi580/dsh-conversation-rail

Restart `dsh web` after install. Bundle APIs can change during the developer preview.

README badge

dsh-conversation-rail DSH Hub badge
[![DSH Hub](https://dshhub.dev/badge/dsh-conversation-rail.svg)](https://dshhub.dev/plugins/dsh-conversation-rail)

Paste this into your README. The star count updates with every catalog sync.

From the README

Excerpt from dingyi580/dsh-conversation-rail, cleaned of badges and images.

dsh-conversation-rail · session minimap

English | 中文

A locator for long conversations in DSH Web. A vertical rail sits along the left edge of the conversation, one bar per turn; hover a bar for a preview card, click it to jump to that turn.

  ▏─────                 ┌──────────────────────────────┐
  ▏───────────           │ Can you patch this without…  │  ← question (one line, dark)
  ▏━━━━━━━━━━━━━━  ◀━━━━━┤ CRLF is already ruled out…   │
  ▏──────                │ only the legal re-encodes…   │  ← answer (up to four lines, dimmed)
  ▏────────              └──────────────────────────────┘
  • Bar length = how much text that turn holds (square-root compressed, 10–34px).
  • The bold bar = the turn currently at the top of the scroll area.
  • The preview card carries prose only: your question plus the assistant's plain text. Fenced code blocks, tool calls, shell commands, reasoning traces and injected context never reach the card — you cannot skim JSON.

It draws the whole session, not the loaded window

Conversation history is paginated: the front-end snapshot usually holds only the most recent stretch, so an 8-turn session may have 1 turn in the window. The rail is not bound by that:

  • The skeleton comes from the host's outline endpoint (GET /dsh-conversation-rail/api/outline?sessionId=). The host reads the full log with ctx.sessionQuery.readSession(), folds it into a table of contents of roughly a hundred bytes per turn, and returns that — the history itself never crosses the network.
  • The live snapshot overrides the part that is loaded: it carries jumpable DOM anchors, and for a turn that is still streaming it is the only current source. The two are aligned by the user/message event seq, live winning over outline.
  • Clicking a bar that isn't loaded yet calls session.loadOlder() repeatedly until that turn enters the window, then jumps. The bar pulses while paging, with a ceiling of 40 pages — better to not reach a turn than to page through an entire log because of one click.

The outline uses readSession rather than the lighter filterEvents because the latter does not return source. The user/message event type carries both real questions and context injected by agent.inject() (file-change notices, AGENTS.md, skill bodies…); the official docs state all three project content verbatim and are told apart by source. Without that field, injected context would be drawn as turns.

How it attaches

It takes one seat: conversation.session.header.utilities. Not to draw anything in the header — that slot is session-scoped, so the component receives the framework's useSession snapshot hook and sessionId, and its lifetime follows the session. The component renders null in the header; the actual rail is a fixed overlay on body, aligned to the measured rectangle of [data-conversation-scroll]. It stays out of the host layout, so switching skins, collapsing the sidebar or changing the grid cannot squeeze it.

Turn data comes from ConversationSnapshot.chat: walk order, open a turn on a user node, attribute the following assistant-step text blocks to it. Jumping uses the host's own anchors ([data-chat-anchor-key]), the same path its internal scroll positioning takes.

Known boundaries

Related plugins