Files
deepseek-harness/packages/client/ui-primitives/README.md
T
Yichen Jiang 56d3e8f82a fix(agent-presets): answer health from the walk alone, and keep the reason reachable
`import.meta.resolve`'s `parentURL` argument takes effect only under
`--experimental-import-meta-resolve`, which no launch passes, so the
fallback resolved from this module rather than from the harness — the one
question it existed to answer. The disk walk is the whole answer now, and
the refusal memo it needed goes with it. A `file:` URL joins the file
branch rather than the package one, where a resolver would only normalize
it and report a missing target as present, and a row is skipped on the
Loader's own `Boolean(disabled)` so `disabled: 0` is checked like the
Loader checks it.

A broken card says so through `aria-disabled` rather than `disabled`, and
refuses the pick in its own handler. `disabled` took it out of the tab
order, which with the reason moved onto the badge left it unreachable
without a pointer — reachable before this change, so hiding it was a
regression rather than a path that never existed.

Both notes this decision partly supersedes are updated in place and
cross-linked, one README pair loses an editing residue that repeated a
sentence, and the single-row diagnostic no longer reads "row row 1".
2026-08-26 14:42:39 +08:00

9.0 KiB

description, kind
description kind
Shared React UI atoms for the dsh web client: controls, icons, markdown and math rendering, and the terminal/read/diff/search/web output cards (zero cordis). package-library

@deepseek-ai/dsh-client-ui-primitives

English | 中文

Summary

dsh-client-ui-primitives is the web client's shared React component library: every feature plugin composes its UI from these atoms, and nothing here depends on Cordis or the slot system. It provides the control set (buttons, pills, inputs, menus, modals, toast banners, disclosure rows, hover cards, connection banners), the icon glyphs and brand marks, positioning hooks for anchored overlays, and the content renderers for agent output: markdown with TeX math, terminal output, file reads, diffs, search results, web retrieval, and JSON inspection. The renderers are built for untrusted model output — raw HTML is dropped, links are neutralized or opened safely, and ANSI escape sequences are parsed rather than passed through. User-facing copy is supplied through label props; the feature plugin that composes an atom owns localization.

Table of Contents


Use this package

Compose feature UI from these atoms whenever the web client needs a standard control or an agent-output renderer. They render through React only and take --dsw-* design tokens from the theme, so they fit any plugin without importing the theme or the slot system.

Controls and icons

Button, Pill, Input, Menu, Modal, Tooltip, DisclosureRow, StateDot, HoverCard, Toast, ConnectionBanner, RiskConfirmation, and the OnboardingSurface first-run takeover cover the common interaction shapes. The ic_ds_* icon set and the FishLogo/BrandWordmark marks fill brand and inline-icon slots. useAnchoredPosition and useAnchoredMaxHeight keep floating panels and bottom-anchored overlays clamped to the viewport and following their anchor. HoverCard keeps its portaled preview reachable across the anchor gap and can expose a copy button through the copyText prop. Toast holds for the window its owner names through holdMs, because how long a banner has to stay depends on how much there is to read; the same value drives its unmount timer and the stylesheet's fade delay, so the two cannot disagree.

Rendering agent output

MarkdownText renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. While a reply streams, it freezes completed blocks and highlights a growing fence from saved Shiki grammar state; the final render uses the same span tree (incremental renderer, streaming fence highlighting). TerminalBlock, ReadBlock, DiffBlock, SearchBlock, and WebBlock render the matching tool-result intent with copy controls, overflow handling, and ANSI processing where applicable. JsonTree and JsonBlock inspect JSON values read-only, while MessageText remains the literal-text primitive for user-authored content.

Localizing copy

The atoms cannot read the application locale, so every piece of user-facing copy arrives through required label props. HoverCard, TerminalBlock, JsonTree, CodeBlock, MarkdownText, JsonBlock, ConnectionBanner, Modal, DiffBlock, ReadBlock, SearchBlock, and WebBlock accept complete localized labels. The package owns no language fallback; omission fails typechecking, and each feature maps its typed t seat into the primitive's label interface.


Understand the implementation

Implementation internals — click to expand

The package is one separation: presentational React atoms with zero Cordis and zero slot knowledge, styled only through --dsw-* tokens, while every feature-specific concern (locale, session data, composition) stays in the composing plugin.

Source map

File Role
src/index.ts Public atom exports
src/markdown/ Markdown and math pipeline: micromark parsing, KaTeX typesetting, incremental streaming renderer, CodeBlock/JsonBlock
src/TerminalBlock.tsx ANSI escape parsing (anser) and terminal card rendering
src/ReadBlock.tsx / src/DiffBlock.tsx Read and diff cards
src/SearchBlock.tsx / src/WebBlock.tsx Search and web-retrieval cards
src/icons/ ic_ds_* glyph components and brand marks
src/useAnchoredPosition.ts / src/useAnchoredMaxHeight.ts Floating-panel and overlay geometry hooks

Streaming markdown

While a reply streams, MarkdownText parses incrementally: all but the trailing two blocks freeze as cached React elements and only the source tail re-parses per chunk, so per-chunk work tracks the tail instead of the whole reply. A growing fenced block tokenizes completed text from saved Shiki grammar state plus the unfinished last line; completed lines retain their DOM, and the settled render uses the same span tree. The settled full parse at finalize also resolves references that crossed the freeze boundary (incremental renderer, streaming fence highlighting).

Geometry and overflow

The output cards share one geometry model: white-space: pre with horizontal scrolling so column-aligned content keeps its alignment, and a head-plus-tail slice behind an expand button past maxLines (default 16) so a long body never stretches the card. TerminalBlock parses ANSI into React spans with a per-line column buffer for cursor movement, honoring erase-in-line, tab stops, and character width.


Further Exploration

These pages place the atoms in the client stack and the design system.

  • ui-renderer — the React renderer that mounts the assembled application and binds slot data.
  • ui-tool — the tool-call presentation layer that composes these output cards.
  • ui-conversation — the chat surface that renders markdown replies and tool cards.
  • ui-theme — the --dsw-* token system these atoms style through.
  • Web styling — the authoritative styling rules for web client components.

Model Experience

None, as the package is a browser-side UI plugin layer that registers nothing model-facing.

KV Cache effect

None; this package neither assembles nor sends a provider request.

Known Limitations and Deferred Work

These limits define how the atoms behave at the edges; they are current package constraints, not a component roadmap.

  • Streaming defers cross-boundary reference resolution — a reference-style link or footnote whose definition sits on the other side of the incremental freeze boundary renders as literal text while the reply streams; the settled full parse at finalize resolves it.
  • Glyph-level icons are redrawn approximations — the fish logo and the sparkle mark come from font glyphs whose vector geometry is not exportable from the local design data; hand-authored recreations stand in until an exact export path exists.
  • Pill and Input have no design source — both atoms are self-defined; the sidebar search field and view-tab strip that resemble them are consumer-owned compositions, not these atoms.
  • No Active StateDot variant — the supported states are done, warning, ongoing, and error.
  • User-facing copy is required at the render site — the atoms are zero-Cordis and cannot reach ctx.locale; each feature must supply complete localized labels through the primitive's typed props (decision).
  • TerminalBlock is not a terminal emulator — it renders settled or still-running command output, not an interactive session: SGR colors, carriage return, backspace, erase-in-line, tab stops, and character width are honored; absolute cursor positioning, screen clearing, and alternate-screen sequences are stripped.

Dev Note

Working context for maintainers — click to expand

None.