@deepseek-ai/dsh-client-ui-primitives 0.1.1-rc.2 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +90 -24
- package/README.zh.md +91 -25
- package/lib/ConnectionIndicator.module.css +107 -0
- package/lib/DiffBlock.module.css +0 -6
- package/lib/DisclosureRow.module.css +21 -6
- package/lib/ReadBlock.module.css +0 -6
- package/lib/SearchBlock.module.css +0 -8
- package/lib/TerminalBlock.module.css +0 -6
- package/lib/Toast.module.css +7 -10
- package/lib/Tooltip.module.css +0 -7
- package/lib/WebBlock.module.css +0 -6
- package/lib/index.js +878 -334
- package/lib/markdown/CodeBlock.module.css +4 -2
- package/lib/markdown/MarkdownText.module.css +2 -2
- package/lib/types/ConnectionIndicator.d.ts +25 -0
- package/lib/types/DiffBlock.d.ts +28 -9
- package/lib/types/FishLogo.d.ts +7 -0
- package/lib/types/FoldToggle.d.ts +20 -0
- package/lib/types/HoverCard.d.ts +4 -4
- package/lib/types/JsonTree.d.ts +4 -6
- package/lib/types/Modal.d.ts +22 -18
- package/lib/types/OnboardingSurface.d.ts +2 -2
- package/lib/types/ReadBlock.d.ts +13 -1
- package/lib/types/ReferenceIcon.d.ts +16 -0
- package/lib/types/RiskConfirmation.d.ts +2 -1
- package/lib/types/SearchBlock.d.ts +14 -0
- package/lib/types/TerminalBlock.d.ts +4 -10
- package/lib/types/Toast.d.ts +9 -1
- package/lib/types/WebBlock.d.ts +13 -0
- package/lib/types/icons/index.d.ts +8 -0
- package/lib/types/index.d.ts +14 -8
- package/lib/types/markdown/CodeBlock.d.ts +11 -3
- package/lib/types/markdown/JsonBlock.d.ts +1 -1
- package/lib/types/markdown/MarkdownText.d.ts +8 -7
- package/lib/types/markdown/highlight.d.ts +44 -2
- package/lib/types/markdown/render.d.ts +9 -4
- package/lib/types/relative-time.d.ts +23 -0
- package/lib/types/useAnchoredPosition.d.ts +8 -6
- package/lib/types/useDismissOnOutsidePointer.d.ts +3 -1
- package/lib/types/user-text.d.ts +19 -0
- package/lib/user-text.module.css +35 -0
- package/package.json +4 -5
- package/lib/ConnectionBanner.module.css +0 -13
- package/lib/types/ConnectionBanner.d.ts +0 -12
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-primitives/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 42c1110e8735dd2191c8b7a2e4dcc1f2b9b938bc
|
|
6
|
+
README.zh.md: 9f3c06cacebb7a596204dbe1c8e00a549ce1fcae
|
package/README.md
CHANGED
|
@@ -1,44 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "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)."
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-ui-primitives
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`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 indicators), 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.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
### Controls and icons
|
|
31
|
+
|
|
32
|
+
`Button`, `Pill`, `Input`, `Menu`, `Modal`, `Tooltip`, `DisclosureRow`, `StateDot`, `HoverCard`, `Toast`, `ConnectionIndicator`, `RiskConfirmation`, and the `OnboardingSurface` first-run takeover cover the common interaction shapes. The `ic_ds_*` icon set and `FishLogo`/`BrandWordmark` marks fill brand and inline-icon slots. `ConnectionIndicator` renders a warning-colored disconnected action, a connecting label whose one-to-three dots advance every 500ms independently of retry timing, or a success-colored recovered status. Every state reserves the widest supplied label and uses fixed icon and text columns, so copy changes do not move or resize the control. Its owner supplies visibility, the recovery hold, localized labels, and the immediate-reconnect callback; the primitive uses no native title tooltip. `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.
|
|
33
|
+
|
|
34
|
+
### Rendering agent output
|
|
6
35
|
|
|
7
|
-
|
|
36
|
+
`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](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.md), [streaming fence highlighting](../../../.agents/notes/implemented/feature/2026-08-20-web-streaming-fence-highlight.md)). `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.
|
|
8
37
|
|
|
9
|
-
|
|
38
|
+
### Localizing copy
|
|
10
39
|
|
|
11
|
-
|
|
40
|
+
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`, `ConnectionIndicator`, `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.
|
|
12
41
|
|
|
13
|
-
|
|
42
|
+
-----
|
|
14
43
|
|
|
15
|
-
|
|
44
|
+
<a id="understand-the-implementation"></a>
|
|
45
|
+
## Understand the implementation
|
|
16
46
|
|
|
17
|
-
|
|
47
|
+
<details>
|
|
48
|
+
<summary>Implementation internals — click to expand</summary>
|
|
18
49
|
|
|
19
|
-
|
|
50
|
+
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.
|
|
20
51
|
|
|
21
|
-
|
|
52
|
+
### Source map
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
| File | Role |
|
|
55
|
+
|---|---|
|
|
56
|
+
| [`src/index.ts`](src/index.ts) | Public atom exports |
|
|
57
|
+
| [`src/markdown/`](src/markdown/) | Markdown and math pipeline: micromark parsing, KaTeX typesetting, incremental streaming renderer, `CodeBlock`/`JsonBlock` |
|
|
58
|
+
| [`src/TerminalBlock.tsx`](src/TerminalBlock.tsx) | ANSI escape parsing (`anser`) and terminal card rendering |
|
|
59
|
+
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | Read and diff cards |
|
|
60
|
+
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | Search and web-retrieval cards |
|
|
61
|
+
| [`src/icons/`](src/icons/) | `ic_ds_*` glyph components and brand marks |
|
|
62
|
+
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | Floating-panel and overlay geometry hooks |
|
|
24
63
|
|
|
25
|
-
|
|
64
|
+
### Streaming markdown
|
|
26
65
|
|
|
27
|
-
|
|
66
|
+
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](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.md), [streaming fence highlighting](../../../.agents/notes/implemented/feature/2026-08-20-web-streaming-fence-highlight.md)).
|
|
28
67
|
|
|
29
|
-
|
|
68
|
+
### Geometry and overflow
|
|
30
69
|
|
|
31
|
-
|
|
70
|
+
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.
|
|
32
71
|
|
|
33
|
-
|
|
72
|
+
</details>
|
|
34
73
|
|
|
35
|
-
|
|
74
|
+
-----
|
|
36
75
|
|
|
37
|
-
|
|
76
|
+
<a id="further-exploration"></a>
|
|
77
|
+
## Further Exploration
|
|
38
78
|
|
|
79
|
+
These pages place the atoms in the client stack and the design system.
|
|
80
|
+
|
|
81
|
+
- [ui-renderer](../ui-renderer/README.md) — the React renderer that mounts the assembled application and binds slot data.
|
|
82
|
+
- [ui-tool](../ui-tool/README.md) — the tool-call presentation layer that composes these output cards.
|
|
83
|
+
- [ui-conversation](../ui-conversation/README.md) — the chat surface that renders markdown replies and tool cards.
|
|
84
|
+
- [ui-theme](../ui-theme/README.md) — the `--dsw-*` token system these atoms style through.
|
|
85
|
+
- [Web styling](../../../docs/web-styling.md) — the authoritative styling rules for web client components.
|
|
86
|
+
|
|
87
|
+
-----
|
|
88
|
+
|
|
89
|
+
<a id="model-experience"></a>
|
|
39
90
|
## Model Experience
|
|
40
91
|
|
|
41
|
-
None, as the package
|
|
92
|
+
None, as the package is a browser-side UI plugin layer that registers nothing model-facing.
|
|
42
93
|
|
|
43
94
|
#### KV Cache effect
|
|
44
95
|
|
|
@@ -46,9 +97,24 @@ None; this package neither assembles nor sends a provider request.
|
|
|
46
97
|
|
|
47
98
|
## Known Limitations and Deferred Work
|
|
48
99
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
100
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
These limits define how the atoms behave at the edges; they are current package constraints, not a component roadmap.
|
|
104
|
+
|
|
105
|
+
- **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.
|
|
106
|
+
- **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.
|
|
107
|
+
- **`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.
|
|
108
|
+
- **No `Active` `StateDot` variant** — the supported states are done, warning, ongoing, and error.
|
|
109
|
+
- **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](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md)).
|
|
110
|
+
- **`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.
|
|
111
|
+
|
|
112
|
+
<a id="dev-note"></a>
|
|
113
|
+
### Dev Note
|
|
114
|
+
|
|
115
|
+
<details>
|
|
116
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
117
|
+
|
|
118
|
+
None.
|
|
119
|
+
|
|
120
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,54 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "dsh Web 客户端共享的 React UI 原子组件:控件、图标、Markdown 与数学公式渲染,以及终端/读取/差异/搜索/网页输出卡片(零 cordis)。"
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-ui-primitives
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-client-ui-primitives` 是 Web 客户端共享的 React 组件库:每个功能插件都用这些原子组件拼装自己的 UI,而这里没有任何内容依赖 Cordis 或 slot 系统。它提供控件集(按钮、胶囊、输入框、菜单、模态框、Toast 横幅、折叠行、悬浮卡片、连接指示器)、图标字形与品牌标记、锚定浮层用的定位钩子,以及 agent 输出的内容渲染器:带 TeX 公式的 markdown、终端输出、文件读取、差异、搜索结果、网页检索与 JSON 检查。这些渲染器为不受信任的模型输出而设计——原始 HTML 会被丢弃、链接会被失效或安全打开、ANSI 转义序列会被解析而非透传。面向用户的文案通过 label prop 提供;拼装某个原子组件的功能插件负责本地化。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
只要 Web 客户端需要标准控件或 agent 输出渲染器,就用这些原子组件拼装功能 UI。它们只经 React 渲染,并从主题取得 `--dsw-*` 设计 token,因此无需导入主题或 slot 系统即可适配任意插件。
|
|
29
|
+
|
|
30
|
+
### 控件与图标
|
|
31
|
+
|
|
32
|
+
`Button`、`Pill`、`Input`、`Menu`、`Modal`、`Tooltip`、`DisclosureRow`、`StateDot`、`HoverCard`、`Toast`、`ConnectionIndicator`、`RiskConfirmation` 与首次运行接管层 `OnboardingSurface` 覆盖常见的交互形态。`ic_ds_*` 图标集与 `FishLogo`/`BrandWordmark` 标记填充品牌与行内图标 slot。`ConnectionIndicator` 可渲染警告色的断联操作、以独立于 retry 时序的 500ms 节奏推进一至三个点的连接中状态,或成功色的恢复状态。所有状态都为最长的输入 label 预留空间,并使用固定的图标列和文字列,因此文案变化不会移动控件或改变其宽度。它的 owner 提供可见性、恢复驻留时间、本地化 label 与立即重连回调;该原语不使用原生 title tooltip。`useAnchoredPosition` 与 `useAnchoredMaxHeight` 让浮动面板与底部锚定浮层始终钳制在视口内并跟随锚点。`HoverCard` 通过指针离开宽限期让采用 portal 的预览在跨过锚点间隙时仍可触及,并可通过 `copyText` prop 提供复制按钮。 `Toast` 的停留时长由使用方通过 `holdMs` 指定,因为横幅该留多久取决于有多少内容要读;同一个值同时驱动它的卸载定时器与样式表的淡出延迟,两者不可能再错位。
|
|
33
|
+
|
|
34
|
+
### 渲染 agent 输出
|
|
6
35
|
|
|
7
|
-
|
|
36
|
+
`MarkdownText` 渲染不可信的 GFM 与 TeX 公式、阻止不安全的链接与图片,并可把已解析的文件提及转换为显式控件。回复流式输出时,它冻结已完成的块,并从保存的 Shiki grammar state 为不断增长的 fence 增量高亮;最终渲染使用相同的 span 树([增量渲染器](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.zh.md)、[流式 fence 高亮](../../../.agents/notes/implemented/feature/2026-08-20-web-streaming-fence-highlight.zh.md))。`TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock` 与 `WebBlock` 把对应的工具结果意图渲染为带复制控件、溢出处理及适用时 ANSI 处理的卡片。`JsonTree` 与 `JsonBlock` 以只读方式检查 JSON 值;`MessageText` 仍是用户创作内容的字面文本原语。
|
|
8
37
|
|
|
9
|
-
|
|
38
|
+
### 本地化文案
|
|
10
39
|
|
|
11
|
-
|
|
40
|
+
这些原子组件无法读取应用 locale,因此每段面向用户的文案都必须通过 label prop 提供。`HoverCard`、`TerminalBlock`、`JsonTree`、`CodeBlock`、`MarkdownText`、`JsonBlock`、`ConnectionIndicator`、`Modal`、`DiffBlock`、`ReadBlock`、`SearchBlock` 与 `WebBlock` 接收完整的本地化 label。本包不拥有语言回退;遗漏会导致类型检查失败,各功能会把带类型的 `t` 席位映射到 primitive 的 label 接口。
|
|
12
41
|
|
|
13
|
-
|
|
42
|
+
-----
|
|
14
43
|
|
|
15
|
-
|
|
44
|
+
<a id="understand-the-implementation"></a>
|
|
45
|
+
## 理解实现
|
|
16
46
|
|
|
17
|
-
|
|
47
|
+
<details>
|
|
48
|
+
<summary>实现细节——点击展开</summary>
|
|
18
49
|
|
|
19
|
-
|
|
50
|
+
本包只做一件事:提供零 cordis、零 slot 知识、仅经 `--dsw-*` token 设置样式的纯 React 原子组件,而所有功能专属的关注点(locale、会话数据、组合)都留在拼装它们的插件中。
|
|
20
51
|
|
|
21
|
-
|
|
52
|
+
### 源码地图
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
| 文件 | 职责 |
|
|
55
|
+
|---|---|
|
|
56
|
+
| [`src/index.ts`](src/index.ts) | 原子组件公开导出 |
|
|
57
|
+
| [`src/markdown/`](src/markdown/) | Markdown 与数学公式流水线:micromark 解析、KaTeX 排版、增量流式渲染器、`CodeBlock`/`JsonBlock` |
|
|
58
|
+
| [`src/TerminalBlock.tsx`](src/TerminalBlock.tsx) | ANSI 转义解析(`anser`)与终端卡片渲染 |
|
|
59
|
+
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | 读取与差异卡片 |
|
|
60
|
+
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | 搜索与网页检索卡片 |
|
|
61
|
+
| [`src/icons/`](src/icons/) | `ic_ds_*` 字形组件与品牌标记 |
|
|
62
|
+
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | 浮动面板与浮层几何钩子 |
|
|
24
63
|
|
|
25
|
-
|
|
64
|
+
### 流式 markdown
|
|
26
65
|
|
|
27
|
-
|
|
66
|
+
回复流式输出期间,`MarkdownText` 增量解析:除末尾两个块外全部冻结为缓存的 React 元素,每个分片只重新解析其后的源文本尾部,因此每分片的工作量跟随尾部而非整个回复。不断增长的 fenced block 会从已保存的 Shiki grammar state 加上尚未完成的最后一行继续分词;已完成行保留其 DOM,定稿渲染则使用相同的 span 树。定稿时的全量解析还会解析跨过冻结边界的引用([增量渲染器](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.zh.md)、[流式 fence 高亮](../../../.agents/notes/implemented/feature/2026-08-20-web-streaming-fence-highlight.zh.md))。
|
|
28
67
|
|
|
29
|
-
|
|
68
|
+
### 几何与溢出
|
|
30
69
|
|
|
31
|
-
|
|
70
|
+
输出卡片共享同一套几何模型:`white-space: pre` 并横向滚动,让按列对齐的内容保持对齐;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制,长正文不会撑高卡片。`TerminalBlock` 把 ANSI 解析为 React span,并带逐行列缓冲处理光标移动,遵循行内擦除、制表位与字符宽度。
|
|
32
71
|
|
|
33
|
-
|
|
72
|
+
</details>
|
|
34
73
|
|
|
35
|
-
|
|
74
|
+
-----
|
|
36
75
|
|
|
37
|
-
|
|
76
|
+
<a id="further-exploration"></a>
|
|
77
|
+
## 进一步探索
|
|
38
78
|
|
|
79
|
+
以下页面说明这些原子组件在客户端技术栈与设计系统中的位置。
|
|
80
|
+
|
|
81
|
+
- [ui-renderer](../ui-renderer/README.zh.md)——挂载组装后应用并绑定 slot 数据的 React 渲染器。
|
|
82
|
+
- [ui-tool](../ui-tool/README.zh.md)——拼装这些输出卡片的工具调用展示层。
|
|
83
|
+
- [ui-conversation](../ui-conversation/README.zh.md)——渲染 markdown 回复与工具卡片的聊天界面。
|
|
84
|
+
- [ui-theme](../ui-theme/README.zh.md)——这些原子组件样式所依赖的 `--dsw-*` token 体系。
|
|
85
|
+
- [Web 样式](../../../docs/web-styling.zh.md)——Web 客户端组件的权威样式规则。
|
|
86
|
+
|
|
87
|
+
-----
|
|
88
|
+
|
|
89
|
+
<a id="model-experience"></a>
|
|
39
90
|
## 模型体验
|
|
40
91
|
|
|
41
|
-
|
|
92
|
+
无。该包是浏览器端 UI 插件层,不注册任何面向模型的内容。
|
|
42
93
|
|
|
43
94
|
#### KV Cache 影响
|
|
44
95
|
|
|
45
96
|
无;该包既不组装也不发送提供方请求。
|
|
46
97
|
|
|
47
|
-
##
|
|
98
|
+
## 已知限制与延期工作
|
|
99
|
+
|
|
100
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
这些限制说明原子组件在边缘情况下的行为;它们是当前包约束,不是组件路线图。
|
|
104
|
+
|
|
105
|
+
- **流式期间跨边界引用解析被推迟**:定义落在增量冻结边界另一侧的引用式链接或脚注,在回复流式输出期间渲染为字面文本;定稿时的全量解析会将其解析。
|
|
106
|
+
- **字形级图标是重新绘制的近似版本**:鱼形标志与闪光标记来自字体字形,而本地设计数据无法导出其矢量几何;在获得精确导出路径前,使用手工重建版本代替。
|
|
107
|
+
- **`Pill` 与 `Input` 没有设计来源**:两个原子组件均自行定义;与其相似的侧边栏搜索字段和视图标签条由消费方组合,不是这些原子组件。
|
|
108
|
+
- **`StateDot` 没有 `Active` 变体**:支持的状态为 done、warning、ongoing 和 error。
|
|
109
|
+
- **面向用户的文案必须由渲染点提供**:这些原子组件是 zero-Cordis 的,拿不到 `ctx.locale`;各功能必须通过 primitive 的带类型 prop 提供完整本地化 label(见[决策](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.zh.md))。
|
|
110
|
+
- **`TerminalBlock` 不是终端模拟器**:它渲染已结束或仍在运行的命令输出,而不是交互式会话:SGR 颜色、回车、退格、行内擦除、制表位与字符宽度会被遵循;绝对光标定位、清屏与备用屏幕序列会被剥离。
|
|
111
|
+
|
|
112
|
+
<a id="dev-note"></a>
|
|
113
|
+
### 开发备注
|
|
114
|
+
|
|
115
|
+
<details>
|
|
116
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
117
|
+
|
|
118
|
+
无。
|
|
48
119
|
|
|
49
|
-
|
|
50
|
-
- **字形级图标是重新绘制的近似版本**:鱼形标志(以及 ui-conversation 持有的闪光图标)来自字体字形,而本地设计数据无法导出其矢量几何;在获得精确导出路径前,使用手工重建版本代替。
|
|
51
|
-
- **Pill 与 Input 没有设计来源**:两个原子组件均自行定义;与其相似的侧边栏搜索字段和视图标签条由消费方组合,不是这些原子组件。
|
|
52
|
-
- **StateDot 没有 `Active` 变体**:支持的状态为 done、warning、ongoing 和 error。
|
|
53
|
-
- **面向用户的文案经 label props 本地化,默认值为原中文字面量**:这些原子组件是 zero-cordis 的,拿不到 `ctx.locale`,因此 `HoverCard`(`copyLabel`/`copiedLabel`)、`TerminalBlock`(`labels`)、`JsonTree`(`labels`)、`CodeBlock`(`copyLabel`/`copiedLabel`)、`MarkdownText`(`codeLabels`)、`JsonBlock`(`truncatedLabel`)、`ConnectionBanner`(`label`)和 `Modal`(`closeLabel`)都把文案作为可选 props 接收。已本地化的插件用自己的 `t` 席位传入字典驱动的 label;什么都不传的消费方得到的就是这些默认值。`WebBlock` 尚未跟进这一模式:它的来源列表截断提示与 fetch 截断提示、以及空搜索提示仍是内联中文,待同样的 label-prop 处理。
|
|
54
|
-
- **`TerminalBlock` 不是终端模拟器**:它渲染已结束或仍在运行的命令输出,而不是交互式会话:SGR 颜色与属性会被遵循,进度行所用的行内光标移动同样被遵循——回车、退格、行内擦除、制表位与字符宽度。绝对光标定位、清屏与备用屏幕序列会被剥离。基础 16 色中的洋红与青色没有对应 token,保持字面 rgb。
|
|
120
|
+
</details>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
.indicator {
|
|
2
|
+
flex: none;
|
|
3
|
+
display: inline-grid;
|
|
4
|
+
grid-template-columns: 14px max-content;
|
|
5
|
+
align-items: center;
|
|
6
|
+
column-gap: 4px;
|
|
7
|
+
height: 32px;
|
|
8
|
+
padding: 0 10px;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
border: none;
|
|
11
|
+
border-radius: 8px;
|
|
12
|
+
font-family: inherit;
|
|
13
|
+
font-size: 12px;
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
line-height: 18px;
|
|
16
|
+
white-space: nowrap;
|
|
17
|
+
transition: background-color 160ms ease-out, color 160ms ease-out;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.warning {
|
|
21
|
+
background: var(--dsw-alias-state-warn-tertiary);
|
|
22
|
+
color: var(--dsw-alias-state-warn-label);
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.warning:active {
|
|
27
|
+
background: color-mix(
|
|
28
|
+
in srgb,
|
|
29
|
+
var(--dsw-alias-state-warn-tertiary),
|
|
30
|
+
var(--dsw-alias-state-warn-primary) 10%
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.warning:focus-visible {
|
|
35
|
+
outline: 2px solid var(--dsw-alias-state-warn-label);
|
|
36
|
+
outline-offset: 2px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.success {
|
|
40
|
+
background: var(--dsw-alias-state-success-tertiary);
|
|
41
|
+
color: var(--dsw-alias-state-success-primary);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.icon {
|
|
45
|
+
display: grid;
|
|
46
|
+
place-items: center;
|
|
47
|
+
width: 14px;
|
|
48
|
+
height: 14px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.label {
|
|
52
|
+
display: grid;
|
|
53
|
+
text-align: left;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.stateLabel,
|
|
57
|
+
.hoverLabel,
|
|
58
|
+
.sizeLabel {
|
|
59
|
+
grid-area: 1 / 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.sizeLabel {
|
|
63
|
+
visibility: hidden;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.warning:is(:hover, :focus-visible) .stateLabel {
|
|
67
|
+
visibility: hidden;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.hoverLabel {
|
|
71
|
+
visibility: hidden;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.warning:is(:hover, :focus-visible) .hoverLabel {
|
|
75
|
+
visibility: visible;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.dots {
|
|
79
|
+
display: inline-block;
|
|
80
|
+
width: 1.5em;
|
|
81
|
+
text-align: left;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.secondDot {
|
|
85
|
+
animation: reveal-second-dot 1.5s step-end infinite;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.thirdDot {
|
|
89
|
+
animation: reveal-third-dot 1.5s step-end infinite;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@keyframes reveal-second-dot {
|
|
93
|
+
0%, 33.32% { visibility: hidden; }
|
|
94
|
+
33.33%, 100% { visibility: visible; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@keyframes reveal-third-dot {
|
|
98
|
+
0%, 66.65% { visibility: hidden; }
|
|
99
|
+
66.66%, 100% { visibility: visible; }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@media (prefers-reduced-motion: reduce) {
|
|
103
|
+
.secondDot,
|
|
104
|
+
.thirdDot {
|
|
105
|
+
animation: none;
|
|
106
|
+
}
|
|
107
|
+
}
|
package/lib/DiffBlock.module.css
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* Geometry mirrors CodeBlock/TerminalBlock (12px radius, code-block surface +
|
|
2
|
-
banner row, markdown code-block font) so a diff card reads as one family with
|
|
3
|
-
a fenced block and a terminal card. The deliberate divergence, shared with
|
|
4
|
-
TerminalBlock: the body keeps `white-space: pre` and scrolls horizontally,
|
|
5
|
-
because folding a source line destroys the indentation a diff is read by. */
|
|
6
|
-
|
|
7
1
|
.block {
|
|
8
2
|
--dsl-diff-radius: 12px;
|
|
9
3
|
--dsl-diff-line-height: 22px;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
/* Shared disclosure header: [16px leading] gap 6 [title
|
|
1
|
+
/* Shared disclosure header: [16px leading] gap 6 [title 13/24] at the default
|
|
2
|
+
size. The Settings font-size preference moves the row through the
|
|
3
|
+
body-published axis: title size follows the secondary tier
|
|
4
|
+
(--dsh-content-font-size-secondary: one step under the body — setting −1 at
|
|
5
|
+
≤14, setting −2 above), and the row height, leading box, and glyph edge
|
|
6
|
+
shift by the body px delta so the icon keeps its optical share of the
|
|
7
|
+
line. */
|
|
2
8
|
|
|
3
9
|
.root {
|
|
4
10
|
display: flex;
|
|
@@ -12,7 +18,7 @@
|
|
|
12
18
|
overflow: hidden;
|
|
13
19
|
display: flex;
|
|
14
20
|
align-items: center;
|
|
15
|
-
height: 24px;
|
|
21
|
+
height: calc(24px + var(--dsh-content-font-delta, 0px));
|
|
16
22
|
min-width: 0;
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -23,8 +29,8 @@
|
|
|
23
29
|
.leading {
|
|
24
30
|
position: relative;
|
|
25
31
|
flex: none;
|
|
26
|
-
width: 16px;
|
|
27
|
-
height: 16px;
|
|
32
|
+
width: calc(16px + var(--dsh-content-font-delta, 0px));
|
|
33
|
+
height: calc(16px + var(--dsh-content-font-delta, 0px));
|
|
28
34
|
display: inline-flex;
|
|
29
35
|
align-items: center;
|
|
30
36
|
justify-content: center;
|
|
@@ -35,6 +41,15 @@
|
|
|
35
41
|
color: var(--dsw-alias-label-tertiary);
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
/* Flow-row glyphs render at 14px inside the 16px box; the CSS edge overrides
|
|
45
|
+
each svg's own width/height attributes so every registered icon scales
|
|
46
|
+
without a per-callsite size prop. StateDot (its svg carries data-state)
|
|
47
|
+
stays at its fixed figma size — it is a status mark, not text furniture. */
|
|
48
|
+
.leading svg:not([data-state]) {
|
|
49
|
+
width: calc(14px + var(--dsh-content-font-delta, 0px));
|
|
50
|
+
height: calc(14px + var(--dsh-content-font-delta, 0px));
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
button.leading {
|
|
39
54
|
cursor: pointer;
|
|
40
55
|
}
|
|
@@ -63,7 +78,7 @@ button.leading {
|
|
|
63
78
|
|
|
64
79
|
.title {
|
|
65
80
|
flex: none;
|
|
66
|
-
font-size:
|
|
67
|
-
line-height: 24px;
|
|
81
|
+
font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
82
|
+
line-height: calc(24px + var(--dsh-content-font-delta, 0px));
|
|
68
83
|
color: var(--dsw-alias-label-secondary);
|
|
69
84
|
}
|
package/lib/ReadBlock.module.css
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* Geometry mirrors CodeBlock (12px radius, code-block surface + banner row,
|
|
2
|
-
markdown code-block font) so a read card and a fenced code block read as one
|
|
3
|
-
family. Content keeps `white-space: pre` and scrolls horizontally rather than
|
|
4
|
-
folding, because a source line's indentation is part of what a reader is
|
|
5
|
-
reading. */
|
|
6
|
-
|
|
7
1
|
.block {
|
|
8
2
|
--dsl-read-radius: 12px;
|
|
9
3
|
--dsl-read-line-height: 22px;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* Geometry mirrors CodeBlock and TerminalBlock (12px radius, code-block
|
|
2
|
-
surface + banner row, markdown code-block font) so a search card reads as one
|
|
3
|
-
family with them. The deliberate divergence they share: the result rows keep
|
|
4
|
-
`white-space: pre` and scroll horizontally, because folding a long match line
|
|
5
|
-
or path destroys the alignment a reader scans by. */
|
|
6
|
-
|
|
7
1
|
.block {
|
|
8
2
|
--dsl-search-radius: 12px;
|
|
9
3
|
--dsl-search-line-height: 22px;
|
|
@@ -15,8 +9,6 @@
|
|
|
15
9
|
border-radius: var(--dsl-search-radius);
|
|
16
10
|
}
|
|
17
11
|
|
|
18
|
-
/* The banner: result summary on the left, the copy control holding its
|
|
19
|
-
intrinsic width on the right. */
|
|
20
12
|
.header {
|
|
21
13
|
display: flex;
|
|
22
14
|
align-items: center;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* Geometry mirrors CodeBlock (12px radius, code-block surface + banner rows,
|
|
2
|
-
markdown code-block font) so a terminal card and a fenced code block read as
|
|
3
|
-
one family. The one deliberate divergence: output keeps `white-space: pre`
|
|
4
|
-
and scrolls horizontally, because folding a column-aligned command's output
|
|
5
|
-
destroys its alignment. */
|
|
6
|
-
|
|
7
1
|
.block {
|
|
8
2
|
--dsl-terminal-radius: 12px;
|
|
9
3
|
--dsl-terminal-line-height: 22px;
|
package/lib/Toast.module.css
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
banner blocking nothing. */
|
|
1
|
+
/* The fade delay comes from the component as `--dsh-toast-hold`, so one value
|
|
2
|
+
drives both the unmount timer and this animation; the fallback matches the
|
|
3
|
+
component's own default. The fade DURATION still has to agree with FADE_MS
|
|
4
|
+
in Toast.tsx, which no owner varies. */
|
|
6
5
|
|
|
7
6
|
.toast {
|
|
8
7
|
position: fixed;
|
|
@@ -11,9 +10,7 @@
|
|
|
11
10
|
/* Above the 1000 the image lightbox backdrop uses: a failure reported while
|
|
12
11
|
a preview is open must stay readable. */
|
|
13
12
|
z-index: 1100;
|
|
14
|
-
/*
|
|
15
|
-
after the CSS fade finished while a throttled background-tab timer has
|
|
16
|
-
not yet unmounted the still-hit-testable fixed element. */
|
|
13
|
+
/* Announcements never intercept clicks. */
|
|
17
14
|
pointer-events: none;
|
|
18
15
|
display: flex;
|
|
19
16
|
align-items: center;
|
|
@@ -29,7 +26,7 @@
|
|
|
29
26
|
transform: translateX(-50%);
|
|
30
27
|
animation:
|
|
31
28
|
dsh-toast-in 160ms ease-out,
|
|
32
|
-
dsh-toast-fade 1000ms ease 3000ms forwards;
|
|
29
|
+
dsh-toast-fade 1000ms ease var(--dsh-toast-hold, 3000ms) forwards;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
.icon {
|
|
@@ -65,6 +62,6 @@
|
|
|
65
62
|
not movement) still ends the banner before the timed unmount. */
|
|
66
63
|
@media (prefers-reduced-motion: reduce) {
|
|
67
64
|
.toast {
|
|
68
|
-
animation: dsh-toast-fade 1000ms ease 3000ms forwards;
|
|
65
|
+
animation: dsh-toast-fade 1000ms ease var(--dsh-toast-hold, 3000ms) forwards;
|
|
69
66
|
}
|
|
70
67
|
}
|
package/lib/Tooltip.module.css
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* Visual spec mirrors deepsuite @deepseek/ui Tooltip.css (size m, no arrow),
|
|
2
|
-
except padding tightened 6/12 -> 3/7, type 14/22 -> 13/20, radius
|
|
3
|
-
10 -> 8, a tooltip-bg plate,
|
|
4
|
-
one text color across both themes (the plate stays dark in light and dark
|
|
5
|
-
mode). Behavior (fixed positioning off the anchor rect) is local — the
|
|
6
|
-
upstream Floating stack is intentionally not vendored. */
|
|
7
|
-
|
|
8
1
|
.bubble {
|
|
9
2
|
position: fixed;
|
|
10
3
|
z-index: 100;
|