@qilin-agent/client-ui-primitives 3.1.5 → 3.1.6
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 +10 -10
- package/README.md +8 -5
- package/README.zh.md +8 -5
- package/lib/ImageLightbox.module.css +14 -5
- package/lib/InlineEditor.module.css +23 -0
- package/lib/MenuSurface.module.css +2 -2
- package/lib/SecretText.module.css +52 -0
- package/lib/SegmentedTabs.module.css +55 -0
- package/lib/index.js +1675 -399
- package/lib/types/InlineEditor.d.ts +23 -0
- package/lib/types/Menu.d.ts +18 -8
- package/lib/types/SecretText.d.ts +25 -0
- package/lib/types/SegmentedTabs.d.ts +26 -0
- package/lib/types/TerminalBlock.d.ts +7 -1
- package/lib/types/Tooltip.d.ts +11 -2
- package/lib/types/credential-formats.d.ts +63 -0
- package/lib/types/credential-masks.d.ts +60 -0
- package/lib/types/credential-scanner.d.ts +146 -0
- package/lib/types/format-tables.d.ts +29 -0
- package/lib/types/icons/index.d.ts +7 -1
- package/lib/types/index.d.ts +6 -1
- package/lib/types/plugin-artwork.d.ts +17 -0
- package/lib/types/useAnchoredPosition.d.ts +3 -1
- package/lib/types/user-text.d.ts +13 -2
- package/package.json +5 -5
package/README.i18n.yaml
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
en: ca3db95551971b07
|
|
19
19
|
zh: 083ba0e187ce544a
|
|
20
20
|
/qilin-agent-client-ui-primitives/use-this-package/component-catalog:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: e84dcb0e810a21cd
|
|
22
|
+
zh: 61c55bd0f405cca5
|
|
23
23
|
/qilin-agent-client-ui-primitives/use-this-package/controls-and-icons:
|
|
24
24
|
en: d563c3a7f4eba0bc
|
|
25
25
|
zh: 49b6834de626eb6c
|
|
26
26
|
/qilin-agent-client-ui-primitives/use-this-package/rendering-agent-output:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: dc68afccfd4e471f
|
|
28
|
+
zh: 062ea9ff0c36135d
|
|
29
29
|
/qilin-agent-client-ui-primitives/use-this-package/localizing-copy:
|
|
30
30
|
en: 4aef65dea11de083
|
|
31
31
|
zh: abc22cd7c65cdf38
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
en: ff71d06c5d21ac81
|
|
34
34
|
zh: f5739d0e28e73552
|
|
35
35
|
/qilin-agent-client-ui-primitives/understand-the-implementation/source-map:
|
|
36
|
-
en:
|
|
37
|
-
zh:
|
|
36
|
+
en: d88fc6047e110c21
|
|
37
|
+
zh: 2dee18b40a594ff3
|
|
38
38
|
/qilin-agent-client-ui-primitives/understand-the-implementation/input-modality:
|
|
39
39
|
en: 44dfc06332ca3b5b
|
|
40
40
|
zh: 94df10cde37c18e6
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
en: 8bbfa14c27f3bfd7
|
|
43
43
|
zh: 88f7548ac3a114e1
|
|
44
44
|
/qilin-agent-client-ui-primitives/understand-the-implementation/geometry-and-overflow:
|
|
45
|
-
en:
|
|
46
|
-
zh:
|
|
45
|
+
en: ba5e806334b21fcb
|
|
46
|
+
zh: 6eca18fe3938e488
|
|
47
47
|
/qilin-agent-client-ui-primitives/further-exploration:
|
|
48
48
|
en: 1ef5e5fdbe3d2436
|
|
49
49
|
zh: af5cb7ace2ab6e64
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
en: ca75c51c89c2c9b0
|
|
55
55
|
zh: de54f3467ef3b9be
|
|
56
56
|
/qilin-agent-client-ui-primitives/known-limitations-and-deferred-work:
|
|
57
|
-
en:
|
|
58
|
-
zh:
|
|
57
|
+
en: c664bea9f96feb48
|
|
58
|
+
zh: aed8652787ec7f22
|
|
59
59
|
/qilin-agent-client-ui-primitives/known-limitations-and-deferred-work/dev-note:
|
|
60
60
|
en: e67f633dc6fe7773
|
|
61
61
|
zh: 3bb0c20c93f16f98
|
package/README.md
CHANGED
|
@@ -45,14 +45,14 @@ Check this table before writing a control in a feature package. A plugin cannot
|
|
|
45
45
|
| `Pill` | Selectable capsule button for view switchers and filters; takes `active` and `onClick`. |
|
|
46
46
|
| `Tag` | Read-only capsule badge; `tone` selects one of eight palettes. |
|
|
47
47
|
| `PathLabel` | Single-line file path with subdued directories, a primary filename, and the full path on hover. Fitting paths align left; clipped paths preserve their suffix with a left-edge fade that updates on path and size changes. |
|
|
48
|
-
| `StateDot` | Status mark: `done`, `warning`, `ongoing`, `error`, or `idle`. `aria-hidden`, so the render site owns the name. |
|
|
48
|
+
| `StateDot` | Status mark: `done`, `warning`, `ongoing`, `error`, or `idle`. The `ongoing` loader's animations pin to document time zero on mount and on every animation restart, such as a list reorder, so every visible loader animates in phase. `aria-hidden`, so the render site owns the name. |
|
|
49
49
|
| `ConnectionIndicator` | Inline connection-recovery control across outage, retry, and recovered states. |
|
|
50
50
|
| `DisclosureRow` | 24px compact disclosure that lays title and content side by side. |
|
|
51
51
|
| `CommandText` | One command line, gaining a Tab stop, a group role, and its accessible name only while it overflows horizontally. |
|
|
52
52
|
| `Modal` | Centered dialog over a page mask. |
|
|
53
53
|
| `RiskConfirmation` | Sensitive action gated behind an explicit checkbox. |
|
|
54
54
|
| `OnboardingSurface` | First-run stage that holds the application root inert. |
|
|
55
|
-
| `Tooltip` | Hover text on a cloned anchor, placed right, bottom, or top. |
|
|
55
|
+
| `Tooltip` | Hover text on a cloned anchor, placed right, bottom, or top; `openOnClick` pins it for click readers, and `focusDelayMs` delays the keyboard raise. |
|
|
56
56
|
| `HoverCard` | Hover preview the pointer can rest on and select from; optional copy button. |
|
|
57
57
|
| `ImageLightbox` | Shared image modal with focus restoration and Escape dismissal. |
|
|
58
58
|
| `Toast` | Transient top-center banner held for the owner's `holdMs`. |
|
|
@@ -62,6 +62,7 @@ Check this table before writing a control in a feature package. A plugin cannot
|
|
|
62
62
|
| `SettingsForm`, `SettingsValueField`, `SettingsSecretField` | Staged configuration form over `SettingsFormModel`: fields report what the reader typed, and its save is the single point where a draft becomes a document mutation. |
|
|
63
63
|
| `icons/*`, `QilinSeal`, `ReferenceIcon`, `LinkIcon` | Glyphs and brand marks. Use `LinkIcon` for 14px clickable-link categories and known-site marks. |
|
|
64
64
|
| `FileTypeIcon`, `classifyFileType`, `fileExtension` | A category-colored 28px file or folder glyph and the shared case-insensitive filename mapping behind it. Code and configuration files use detailed full-color technology glyphs; use `LinkIcon` for link-leading glyphs and image previews for image content. |
|
|
65
|
+
| `InlineEditor` | Autofocused multi-line inline editor for short drafts such as queued messages and goals. It grows with its text and its width up to six lines, then scrolls; Enter saves, Shift+Enter breaks the line, Enter during IME composition does nothing, and Escape cancels. The caller supplies the localized `label` and rebinds the scrollbar on an elevated surface. |
|
|
65
66
|
|
|
66
67
|
Four pairs are easy to confuse:
|
|
67
68
|
|
|
@@ -80,7 +81,7 @@ The catalog above lists what each export is for; this section covers the behavio
|
|
|
80
81
|
|
|
81
82
|
The nearest `MarkdownDelegateProvider` supplies optional `openExternalLink` and `openFile` navigation callbacks. Nested providers replace the enclosing capabilities, and callback changes reach already-rendered links without rebuilding Markdown. Its `openFile` makes local Markdown links clickable after settlement. Absolute and workspace-relative paths support percent escapes and `#L24` / `#L24-L30` fragments; ranges open at their first line. Literal `?` and `#` in filenames must be percent-encoded. The tooltip uses the decoded path and supplies the accessible name when the label is empty. The callback receives the decoded path and optional line, while the renderer preserves the label and displays a file icon. Without a callback, local links remain text. URL schemes, queries, unsupported fragments, and malformed destinations never reach the file opener.
|
|
82
83
|
|
|
83
|
-
`MarkdownText` renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. A surrounding `MarkdownDelegateProvider` receives sanitized HTTP(S) URLs from ordinary clicks; modified clicks and links outside a provider retain native external-anchor behavior. When the owner passes a `pathImages` vocabulary, image destinations that are local media paths rewrite to displayable URLs on settled renders only (the same streaming gate as file mentions); without a vocabulary, local destinations remain inert alt text. A load or decode failure replaces the image with its authored alt text, or the original destination when alt is empty; `fileImages` additionally supplies a localized failure prefix. Changing the image source permits a fresh load. While a reply streams, it freezes completed blocks, advances a top-level open fence by completed lines, and highlights that fence from saved Shiki grammar state. Completed token lines enter fixed-size React groups, so later chunks reconcile only the growing group; an unchanged fence retains that DOM when the final full parse resolves cross-document syntax. `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 `projectUserText` projects sent user text into inline plain runs and reference chips for the message bubble and queue rows. When supplied with `UserTextReferences`, file and skill references become keyboard-accessible preview buttons using the same hover and focus styling as prose file links; the first pointer click can open a preview, while subsequent clicks and existing text selections retain native selection handling. Keyboard activation opens previews even when text is selected. A ```` ```mermaid ```` fence renders through the lazily imported library in strict security mode; the sanitized SVG keeps geometry, text, gradients, and classes while scripts, event handlers, `foreignObject`, and `javascript:` URLs are stripped, and a click or Enter/Space opens the enlarged diagram in a modal whose overlay click dismisses it. A rejected diagram shows the first error lines above the fenced source with its copy control.
|
|
84
|
+
`MarkdownText` renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. A surrounding `MarkdownDelegateProvider` receives sanitized HTTP(S) URLs from ordinary clicks; modified clicks and links outside a provider retain native external-anchor behavior. When the owner passes a `pathImages` vocabulary, image destinations that are local media paths rewrite to displayable URLs on settled renders only (the same streaming gate as file mentions); without a vocabulary, local destinations remain inert alt text. A load or decode failure replaces the image with its authored alt text, or the original destination when alt is empty; `fileImages` additionally supplies a localized failure prefix. Changing the image source permits a fresh load. While a reply streams, it freezes completed blocks, advances a top-level open fence by completed lines, and highlights that fence from saved Shiki grammar state. Completed token lines enter fixed-size React groups, so later chunks reconcile only the growing group; an unchanged fence retains that DOM when the final full parse resolves cross-document syntax. `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 `projectUserText` projects sent user text into inline plain runs and reference chips for the message bubble and queue rows, and draws a recognized credential masked ahead of every reference decoration it shares text with: a provider key or token prefix, a JSON Web Token, a private key block, or the password of an address that carries credentials, per the formats in `src/credential-formats.ts`. Only the display changes — the logged text, the copied characters, and the model input keep the original spelling, and a credential that an `@name` token would otherwise render raw wins instead. `UserTextSecretLabels` adds a control that reveals this one value; without it, as in the queue preview and the goal command echo, the value stays masked. When supplied with `UserTextReferences`, file and skill references become keyboard-accessible preview buttons using the same hover and focus styling as prose file links; the first pointer click can open a preview, while subsequent clicks and existing text selections retain native selection handling. Keyboard activation opens previews even when text is selected. A ```` ```mermaid ```` fence renders through the lazily imported library in strict security mode; the sanitized SVG keeps geometry, text, gradients, and classes while scripts, event handlers, `foreignObject`, and `javascript:` URLs are stripped, and a click or Enter/Space opens the enlarged diagram in a modal whose overlay click dismisses it. A rejected diagram shows the first error lines above the fenced source with its copy control.
|
|
84
85
|
|
|
85
86
|
`MarkdownText` defaults to `variant="body"`. Use `variant="compact"` for secondary content: its 13px text and 20px line height follow the content-size setting, all heading levels use the same size with weight 600, and paragraphs and lists use tighter spacing. Text, links, and code keep the tertiary color; dotted underlines distinguish links. Code headers scroll with their blocks. Tables and math stay enabled at the surrounding text size and scroll horizontally within the available width. Both variants share the parser and streaming cache.
|
|
86
87
|
|
|
@@ -88,7 +89,7 @@ The nearest `MarkdownDelegateProvider` supplies optional `openExternalLink` and
|
|
|
88
89
|
|
|
89
90
|
`JsonTree` clamps collapsed strings to `collapsedStringLines` (three by default). Expanded strings show raw text, retain sibling commas, and fit within the window and outer scrolling containers. Resize and ancestor-scroll events update that limit. Row copy feedback updates independently of JSON value rendering; pending clipboard writes cannot update a different row or an unmounted tree.
|
|
90
91
|
|
|
91
|
-
`ImageLightbox` is the shared original-image modal with focus restoration and Escape dismissal. The internal thumbnail renderer receives loading/failure labels from its owner. `HoverCard.inline` keeps a file link in the text flow and uses the shared menu material, keyboard-visible focus, and placement above or below the anchor without covering it. Escape closes an open thumbnail even when focus is elsewhere; subsequent Escape presses reach the owner. `MarkdownDelegateProvider.fileImages` supplies a decoded-path resolver and complete image labels: settled image links gain hover previews, and standalone images gain lightbox activation. Image-only anchors retain their single navigation target. The parser recovers only complete, unescaped, standalone local image references with bare spaces and an unambiguous supported image suffix; code and ambiguous destinations remain literal.
|
|
92
|
+
`ImageLightbox` is the shared original-image modal with focus restoration and Escape dismissal. Like `Modal`, its mask leaves the Windows caption unpainted, and its close control sits 20px below the caption. The internal thumbnail renderer receives loading/failure labels from its owner. `HoverCard.inline` keeps a file link in the text flow and uses the shared menu material, keyboard-visible focus, and placement above or below the anchor without covering it. Escape closes an open thumbnail even when focus is elsewhere; subsequent Escape presses reach the owner. `MarkdownDelegateProvider.fileImages` supplies a decoded-path resolver and complete image labels: settled image links gain hover previews, and standalone images gain lightbox activation. Image-only anchors retain their single navigation target. The parser recovers only complete, unescaped, standalone local image references with bare spaces and an unambiguous supported image suffix; code and ambiguous destinations remain literal.
|
|
92
93
|
|
|
93
94
|
### Localizing copy
|
|
94
95
|
|
|
@@ -116,6 +117,7 @@ The package enforces one separation: presentational React atoms with zero Kylin
|
|
|
116
117
|
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | Read and diff cards |
|
|
117
118
|
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | Search and web-retrieval cards |
|
|
118
119
|
| [`src/icons/`](src/icons/) | `ic_ds_*` glyph components and brand marks |
|
|
120
|
+
| [`src/credential-formats.ts`](src/credential-formats.ts) / [`src/format-tables.ts`](src/format-tables.ts) / [`src/credential-scanner.ts`](src/credential-scanner.ts) / [`src/credential-masks.ts`](src/credential-masks.ts) / [`src/SecretText.tsx`](src/SecretText.tsx) | Credential formats, their derived lookup tables, scanning, mask forms, and the masked run with its reveal control behind `projectUserText` |
|
|
119
121
|
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | Floating-panel and overlay geometry hooks |
|
|
120
122
|
| [`src/input-modality.ts`](src/input-modality.ts) | Document-wide input modality published on `<html>` |
|
|
121
123
|
|
|
@@ -130,7 +132,7 @@ While a reply streams, `MarkdownText` parses incrementally: all but the trailing
|
|
|
130
132
|
|
|
131
133
|
### Geometry and overflow
|
|
132
134
|
|
|
133
|
-
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` uses `CommandText` to keep each authored command line selectable and horizontally scrollable; the prompt and status stay fixed. Only horizontally overflowing text enters the Tab order as a named group, using the owner's localized `commandLine(line)` label. `TerminalBlock` parses ANSI into React spans with a per-line column buffer for cursor movement, honoring erase-in-line, tab stops, and character width.
|
|
135
|
+
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` uses `CommandText` to keep each authored command line selectable and horizontally scrollable; the prompt and status stay fixed. `background` defaults to false and carries the call's background flag. For a background call, `running` indicates launch-call progress: Start shows that indicator; the acknowledgement sets `running` to false and leaves the command static without a state dot or assistive label. Background calls omit output, empty-output placeholders, exit pills, and the output Copy control. Only horizontally overflowing text enters the Tab order as a named group, using the owner's localized `commandLine(line)` label. `TerminalBlock` parses ANSI into React spans with a per-line column buffer for cursor movement, honoring erase-in-line, tab stops, and character width.
|
|
134
136
|
|
|
135
137
|
</details>
|
|
136
138
|
|
|
@@ -174,6 +176,7 @@ These limits define how the atoms behave at the edges; they are current package
|
|
|
174
176
|
- **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.
|
|
175
177
|
- **`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.
|
|
176
178
|
- **No `Active` `StateDot` variant** — the supported states are done, warning, ongoing, error, and idle.
|
|
179
|
+
- **Credential masking recognizes published formats only** — a free-form password, a `name=value` assignment, and an unrecognized prefix stay plain, so the projection never guesses at a value's sensitivity; extending recognition means one row in `src/credential-formats.ts`. Scanning is prefix-pruned and linear: one literal-only alternation finds each place a format can begin, typed class tables measure runs as bounded comparisons, and the run end, the next digit, and the next key-block `END` line are memoized per text. Mask forms reuse one shared mark string, and a private key block's body and footer searches stay inside the range, so masking costs what the range costs.
|
|
177
180
|
- **User-facing copy is required at the render site** — the atoms are zero-Kylin 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)).
|
|
178
181
|
- **`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.
|
|
179
182
|
|
package/README.zh.md
CHANGED
|
@@ -45,14 +45,14 @@ kind: "package-library"
|
|
|
45
45
|
| `Pill` | 可选中的胶囊按钮,用于视图切换与筛选器;接受 `active` 与 `onClick`。 |
|
|
46
46
|
| `Tag` | 只读胶囊徽章;`tone` 选择八种配色之一。 |
|
|
47
47
|
| `PathLabel` | 单行文件路径:目录弱化、文件名为主要信息、悬停时显示完整路径。放得下时左对齐;被裁剪时保留尾部并在左缘渐隐,路径或尺寸变化都会重新判定。 |
|
|
48
|
-
| `StateDot` | 状态标记:`done`、`warning`、`ongoing`、`error` 或 `idle
|
|
48
|
+
| `StateDot` | 状态标记:`done`、`warning`、`ongoing`、`error` 或 `idle`。`ongoing` loading 的动画在挂载时以及每次动画重启(如列表重排)时固定到文档时间零点,所以所有可见 loading 同相动画。它是 `aria-hidden` 的,名称由渲染点提供。 |
|
|
49
49
|
| `ConnectionIndicator` | 行内连接恢复控件,覆盖断线、重试与已恢复三种状态。 |
|
|
50
50
|
| `DisclosureRow` | 24px 紧凑折叠行,标题与内容左右排列。 |
|
|
51
51
|
| `CommandText` | 一行命令文本:仅在横向溢出时才获得 Tab 停靠、group 角色与可访问名。 |
|
|
52
52
|
| `Modal` | 页面遮罩之上的居中对话框。 |
|
|
53
53
|
| `RiskConfirmation` | 以显式复选框把关的敏感操作确认。 |
|
|
54
54
|
| `OnboardingSurface` | 首次运行的引导舞台,期间保持应用根节点 inert。 |
|
|
55
|
-
| `Tooltip` |
|
|
55
|
+
| `Tooltip` | 克隆锚点上的悬停文本,可置于右、下、上三个方向;`openOnClick` 为点击阅读者钉住气泡,`focusDelayMs` 延迟键盘弹出。 |
|
|
56
56
|
| `HoverCard` | 指针可停留、可选中的悬停预览;可选带复制按钮。 |
|
|
57
57
|
| `ImageLightbox` | 共享图片浮层,支持焦点恢复与 Esc 关闭。 |
|
|
58
58
|
| `Toast` | 顶部居中的瞬时横幅,保持时长由所有者的 `holdMs` 决定。 |
|
|
@@ -62,6 +62,7 @@ kind: "package-library"
|
|
|
62
62
|
| `SettingsForm`、`SettingsValueField`、`SettingsSecretField` | 基于 `SettingsFormModel` 的分阶段配置表单:字段如实报告读者输入,保存是草稿变成文档变更的唯一入口。 |
|
|
63
63
|
| `icons/*`、`QilinSeal`、`ReferenceIcon`、`LinkIcon` | 字形与品牌标识。`LinkIcon` 用于 14px 的可点击链接分类及已知站点标记。 |
|
|
64
64
|
| `FileTypeIcon`、`classifyFileType`、`fileExtension` | 按类别着色的 28px 文件或文件夹图形,以及它背后共享的不区分大小写文件名映射。代码与配置文件使用细分的全彩技术图形;链接前置图形使用 `LinkIcon`,图片内容使用图片预览。 |
|
|
65
|
+
| `InlineEditor` | 面向排队消息与目标等短草稿的自动聚焦多行行内编辑器。高度随文字增长、宽度自适应,到六行后滚动;Enter 保存,Shift+Enter 换行,IME 组合期间的 Enter 不生效,Escape 取消。调用方提供本地化的 `label`,并在抬升表面上重绑滚动条。 |
|
|
65
66
|
|
|
66
67
|
有四组容易混淆:
|
|
67
68
|
|
|
@@ -80,7 +81,7 @@ kind: "package-library"
|
|
|
80
81
|
|
|
81
82
|
最近的 `MarkdownDelegateProvider` 提供可选的 `openExternalLink` 和 `openFile` 导航回调。嵌套 Provider 替换外层能力,回调变化无需重新构建 Markdown 即可到达已渲染链接。其 `openFile` 使本地 Markdown 链接在落定后可点击。绝对路径和工作区相对路径支持百分号转义以及 `#L24` / `#L24-L30` 片段;范围定位到起始行。文件名中的字面 `?` 和 `#` 必须百分号编码。悬停提示使用解码后的路径,并在标签为空时提供可访问名称。回调接收解码后的路径和可选行号,渲染器保留标签并显示文件图标。不传回调时,本地链接仍为文本。URL 协议、查询串、不支持的片段及格式错误的目标不会传给文件打开器。
|
|
82
83
|
|
|
83
|
-
`MarkdownText` 渲染不可信的 GFM 与 TeX 公式、阻止不安全的链接与图片,并可把已解析的文件提及转换为显式控件。外层 `MarkdownDelegateProvider` 会接收普通点击产生的已净化 HTTP(S) URL;带修饰键的点击和 Provider 外的链接保留原生外部 anchor 行为。当 owner 传入 `pathImages` 词表时,本地媒体路径的图片目标只在落定渲染阶段重写为可展示 URL(与 file mentions 相同的流式门);不传词表时本地目标保持惰性 alt 文本。加载或解码失败后,图片替换为作者的 alt 文本;alt 为空时显示原始目标路径;`fileImages` 还提供本地化的失败提示前缀。图片源变化后可重新加载。回复流式输出时,它冻结已完成的块、按已完成行推进顶层未闭合 fence,并从保存的 Shiki grammar state 为该 fence 增量高亮。已完成的 token 行进入固定大小的 React 分组,后续分片只 reconcile 正在增长的分组;最终全量解析解决跨文档语法时,未变化的 fence 会保留该 DOM。`TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock` 与 `WebBlock` 把对应的工具结果意图渲染为带复制控件、溢出处理及适用时 ANSI 处理的卡片。`JsonTree` 与 `JsonBlock` 以只读方式检查 JSON 值;`projectUserText` 把已发送的用户文本投影为行内普通文本段与引用 chip
|
|
84
|
+
`MarkdownText` 渲染不可信的 GFM 与 TeX 公式、阻止不安全的链接与图片,并可把已解析的文件提及转换为显式控件。外层 `MarkdownDelegateProvider` 会接收普通点击产生的已净化 HTTP(S) URL;带修饰键的点击和 Provider 外的链接保留原生外部 anchor 行为。当 owner 传入 `pathImages` 词表时,本地媒体路径的图片目标只在落定渲染阶段重写为可展示 URL(与 file mentions 相同的流式门);不传词表时本地目标保持惰性 alt 文本。加载或解码失败后,图片替换为作者的 alt 文本;alt 为空时显示原始目标路径;`fileImages` 还提供本地化的失败提示前缀。图片源变化后可重新加载。回复流式输出时,它冻结已完成的块、按已完成行推进顶层未闭合 fence,并从保存的 Shiki grammar state 为该 fence 增量高亮。已完成的 token 行进入固定大小的 React 分组,后续分片只 reconcile 正在增长的分组;最终全量解析解决跨文档语法时,未变化的 fence 会保留该 DOM。`TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock` 与 `WebBlock` 把对应的工具结果意图渲染为带复制控件、溢出处理及适用时 ANSI 处理的卡片。`JsonTree` 与 `JsonBlock` 以只读方式检查 JSON 值;`projectUserText` 把已发送的用户文本投影为行内普通文本段与引用 chip,供消息气泡和排队行使用,并把识别到的凭据画成掩码,优先于与它共享文本的每一类引用装饰。凭据指提供方密钥或令牌前缀、JSON Web Token、私钥块、带凭据地址的口令,其格式见 `src/credential-formats.ts`。只有显示改变——日志文本、复制内容与模型输入保持原拼写,名字本身是密钥的 `@name` token 也不会把密钥原样渲染。`UserTextSecretLabels` 提供逐值显示控件;不传时(如队列预览与 goal 命令回显)保持遮住。 传入 `UserTextReferences` 时,文件和 skill 引用成为支持键盘操作的预览按钮,复用正文文件链接的悬停和聚焦样式;第一次指针点击可以打开预览,后续点击和已有选区保留原生选择行为。键盘激活在存在选区时仍可打开预览。```` ```mermaid ```` 围栏经懒加载的库以严格安全模式渲染;净化后的 SVG 保留几何、文本、渐变与类名,脚本、事件处理器、`foreignObject` 和 `javascript:` URL 一律剥离,点击或 Enter/Space 在模态中放大显示图示,点击遮罩关闭。渲染被拒时,围栏源码及其复制控件上方显示前几行错误。
|
|
84
85
|
|
|
85
86
|
`MarkdownText` 默认为 `variant="body"`。次级内容使用 `variant="compact"`:其 13px 字号与 20px 行高跟随内容字号设置,各级标题保持同一字号并使用 600 字重,段落与列表采用更紧凑的间距。正文、链接和代码均保持 tertiary 颜色,以点状下划线区分链接。代码标题栏随代码块滚动。表格和公式仍然启用,使用周围文字的字号,并在可用宽度内横向滚动。两个变体共享解析器与流式缓存。
|
|
86
87
|
|
|
@@ -88,7 +89,7 @@ kind: "package-library"
|
|
|
88
89
|
|
|
89
90
|
`JsonTree` 把折叠字符串限制为 `collapsedStringLines` 行(默认三行)。展开后显示原始文本、保留同级逗号,并限制在窗口与外层滚动容器内;尺寸变化和祖先滚动事件会更新此限制。行复制反馈独立于 JSON 值渲染更新;尚未完成的剪贴板写入不会更新另一行或已卸载的树。
|
|
90
91
|
|
|
91
|
-
`ImageLightbox` 是共享原图浮层,支持焦点恢复与 Esc
|
|
92
|
+
`ImageLightbox` 是共享原图浮层,支持焦点恢复与 Esc 关闭。与 `Modal` 相同,其遮罩不覆盖 Windows 顶栏,关闭按钮位于顶栏下方 20px。包内缩略图渲染器由调用方提供加载及失败文案。`HoverCard.inline` 使文件链接保持在文本流内,并使用共享菜单材质、键盘可见焦点,并在锚点上方或下方定位而不遮挡锚点。即使焦点位于其他位置,Esc 也会关闭已打开的缩略图;后续 Esc 按键继续传给 owner。`MarkdownDelegateProvider.fileImages` 提供已解码路径解析器与完整图片文案:消息落定后的图片链接支持悬停预览,独立图片支持点击放大。仅包含图片的链接保留单一导航目标。解析器仅恢复完整、未转义、独立成段且带明确图片扩展名的含裸空格本地图片引用;代码与有歧义的目标保持原文。
|
|
92
93
|
|
|
93
94
|
### 本地化文案
|
|
94
95
|
|
|
@@ -116,6 +117,7 @@ kind: "package-library"
|
|
|
116
117
|
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | 读取与差异卡片 |
|
|
117
118
|
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | 搜索与网页检索卡片 |
|
|
118
119
|
| [`src/icons/`](src/icons/) | `ic_ds_*` 字形组件与品牌标记 |
|
|
120
|
+
| [`src/credential-formats.ts`](src/credential-formats.ts) / [`src/format-tables.ts`](src/format-tables.ts) / [`src/credential-scanner.ts`](src/credential-scanner.ts) / [`src/credential-masks.ts`](src/credential-masks.ts) / [`src/SecretText.tsx`](src/SecretText.tsx) | `projectUserText` 背后的凭据格式、由其派生的查表、扫描、掩码形态,以及带显示控件的掩码文本段 |
|
|
119
121
|
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | 浮动面板与浮层几何钩子 |
|
|
120
122
|
| [`src/input-modality.ts`](src/input-modality.ts) | 发布在 `<html>` 上的全文档输入模态 |
|
|
121
123
|
|
|
@@ -130,7 +132,7 @@ kind: "package-library"
|
|
|
130
132
|
|
|
131
133
|
### 几何与溢出
|
|
132
134
|
|
|
133
|
-
输出卡片共享同一套几何模型:`white-space: pre` 并横向滚动,让按列对齐的内容保持对齐;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制,长正文不会撑高卡片。`TerminalBlock` 用 `CommandText`
|
|
135
|
+
输出卡片共享同一套几何模型:`white-space: pre` 并横向滚动,让按列对齐的内容保持对齐;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制,长正文不会撑高卡片。`TerminalBlock` 用 `CommandText` 让每条作者写的命令行都可选中、可横向滚动;提示符与状态保持固定。`background` 默认为 false,透传调用的后台标志。后台调用的 `running` 表示启动调用进度:Start 显示该指示,回执使 `running` 变为 false,命令保持静态且不显示状态点或辅助标签。后台调用不显示输出、空输出提示、退出状态 pill 或输出 Copy 控件。只有横向溢出的文本才以具名 group 进入 Tab 顺序,名称取自 owner 本地化的 `commandLine(line)`。`TerminalBlock` 把 ANSI 解析为 React span,并带逐行列缓冲处理光标移动,遵循行内擦除、制表位与字符宽度。
|
|
134
136
|
|
|
135
137
|
</details>
|
|
136
138
|
|
|
@@ -174,6 +176,7 @@ kind: "package-library"
|
|
|
174
176
|
- **字形级图标是重新绘制的近似版本**:鱼形标志与闪光标记来自字体字形,而本地设计数据无法导出其矢量几何;在获得精确导出路径前,使用手工重建版本代替。
|
|
175
177
|
- **`Pill` 与 `Input` 没有设计来源**:两个原子组件均自行定义;与其相似的侧边栏搜索字段和视图标签条由消费方组合,不是这些原子组件。
|
|
176
178
|
- **`StateDot` 没有 `Active` 变体**:支持的状态为 done、warning、ongoing、error 和 idle。
|
|
179
|
+
- **凭据脱敏只识别已公布格式**:自由形式口令、`name=value` 赋值与未登记前缀都保持原样,故投影从不猜测某个值是否敏感;扩展识别就是在 `src/credential-formats.ts` 里加一行。扫描按前缀剪枝且线性:一条只含字面量的 alternation 找出每个格式可能的起点,typed 字符类表把 run 测量变成有界比较,run 终点、下一个数字与下一个密钥块 `END` 行按文本各记忆一次。掩码复用同一段共享标记字符串,私钥块正文与页脚的搜索都限于区间内,故掩码代价随区间长度。
|
|
177
180
|
- **面向用户的文案必须由渲染点提供**:这些原子组件是 zero-Kylin 的,拿不到 `ctx.locale`;各功能必须通过原子组件的带类型 prop 提供完整本地化 label(见[决策](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.zh.md))。
|
|
178
181
|
- **`TerminalBlock` 不是终端模拟器**:它渲染已结束或仍在运行的命令输出,而不是交互式会话:SGR 颜色、回车、退格、行内擦除、制表位与字符宽度会被遵循;绝对光标定位、清屏与备用屏幕序列会被剥离。
|
|
179
182
|
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
/* Full-viewport layer that blocks background clicks, including on the Windows caption. */
|
|
1
2
|
.backdrop {
|
|
2
3
|
position: fixed;
|
|
3
4
|
inset: 0;
|
|
4
5
|
z-index: 1000;
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Painted area below the Windows caption: the caption strip stays unpainted, as
|
|
9
|
+
in the Modal primitive, so a maximized window keeps its drag and controls. */
|
|
10
|
+
.stage {
|
|
11
|
+
position: absolute;
|
|
12
|
+
inset: var(--dsh-frame-chrome-top, 0px) 0 0;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
7
16
|
padding: 40px;
|
|
8
17
|
}
|
|
9
18
|
|
|
10
19
|
/* Same mask recipe as the Modal primitive and the settings dialog. A separate
|
|
11
|
-
layer, not a background on .
|
|
20
|
+
layer, not a background on .stage: backdrop-filter there would blur the
|
|
12
21
|
previewed image and the close control along with the page. */
|
|
13
22
|
.mask {
|
|
14
23
|
position: absolute;
|
|
@@ -20,7 +29,7 @@
|
|
|
20
29
|
.image {
|
|
21
30
|
position: relative;
|
|
22
31
|
max-width: min(100%, 1600px);
|
|
23
|
-
max-height:
|
|
32
|
+
max-height: 100%;
|
|
24
33
|
object-fit: contain;
|
|
25
34
|
border-radius: 12px;
|
|
26
35
|
background: var(--qilin-specific-input-major);
|
|
@@ -29,7 +38,7 @@
|
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
.close {
|
|
32
|
-
position:
|
|
41
|
+
position: absolute;
|
|
33
42
|
top: 20px;
|
|
34
43
|
right: 20px;
|
|
35
44
|
z-index: 1;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.editor {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
flex: 1 1 auto;
|
|
4
|
+
min-width: 0;
|
|
5
|
+
min-height: 28px;
|
|
6
|
+
/* Six 20px text lines plus padding and border, then the editor scrolls. */
|
|
7
|
+
max-height: 127px;
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 3px 8px;
|
|
10
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
11
|
+
border-radius: var(--dsw-radius-sm);
|
|
12
|
+
outline: none;
|
|
13
|
+
background: var(--dsw-alias-bg-base);
|
|
14
|
+
color: var(--dsw-alias-label-primary);
|
|
15
|
+
font: var(--dsw-font-xs-13);
|
|
16
|
+
font-family: Inter, var(--dsw-font-family);
|
|
17
|
+
overflow-y: auto;
|
|
18
|
+
resize: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.editor:focus {
|
|
22
|
+
border-color: var(--dsw-alias-state-business-primary);
|
|
23
|
+
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
.surface {
|
|
15
|
-
anchor-name: var(--
|
|
15
|
+
anchor-name: var(--qilin-menu-anchor);
|
|
16
16
|
isolation: isolate;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
:global(html[data-platform='darwin']) .backing {
|
|
39
39
|
display: block;
|
|
40
40
|
position: fixed;
|
|
41
|
-
position-anchor: var(--
|
|
41
|
+
position-anchor: var(--qilin-menu-anchor);
|
|
42
42
|
top: anchor(top);
|
|
43
43
|
left: anchor(left);
|
|
44
44
|
width: anchor-size(width, 0px);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* Masked credential in sent user text: an inline box that flows on the
|
|
2
|
+
consumer's own line like the plain runs and chips around it. It stays inline
|
|
3
|
+
rather than inline-flex, for the baseline reason recorded in
|
|
4
|
+
user-text.module.css. */
|
|
5
|
+
|
|
6
|
+
.secret {
|
|
7
|
+
display: inline;
|
|
8
|
+
margin: 0 2px;
|
|
9
|
+
font-family: var(--qilin-font-markdown-code-font-family);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* A mask character stays in the DOM for copying and assistive technology but is
|
|
13
|
+
not painted; an asterisk tile per character advance replaces it, because a
|
|
14
|
+
font's own asterisk sits near the cap height. */
|
|
15
|
+
.mask {
|
|
16
|
+
position: relative;
|
|
17
|
+
top: -0.125em;
|
|
18
|
+
-webkit-text-fill-color: transparent;
|
|
19
|
+
background-color: currentColor;
|
|
20
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 20'%3E%3Cpath d='M6 5.6v8.8M2.19 7.8l7.62 4.4M2.19 12.2l7.62-4.4' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") left center / 1ch 100% repeat-x;
|
|
21
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 20'%3E%3Cpath d='M6 5.6v8.8M2.19 7.8l7.62 4.4M2.19 12.2l7.62-4.4' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") left center / 1ch 100% repeat-x;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* The reveal control rides the text baseline like every other inline glyph in
|
|
25
|
+
this file, instead of making the box a flex container. */
|
|
26
|
+
.toggle {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
margin-left: 2px;
|
|
30
|
+
padding: 0;
|
|
31
|
+
border: none;
|
|
32
|
+
background: none;
|
|
33
|
+
color: var(--qilin-alias-label-secondary);
|
|
34
|
+
vertical-align: -0.125em;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.toggle:hover {
|
|
39
|
+
color: var(--qilin-alias-label-primary);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.toggle:focus-visible {
|
|
43
|
+
outline: var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color, var(--qilin-alias-state-business-primary));
|
|
44
|
+
outline-offset: 1px;
|
|
45
|
+
border-radius: var(--qilin-radius-xs);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.icon {
|
|
49
|
+
display: block;
|
|
50
|
+
width: 1em;
|
|
51
|
+
height: 1em;
|
|
52
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
.tabs {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: grid;
|
|
4
|
+
padding: 4px;
|
|
5
|
+
border-radius: var(--qilin-radius-lg);
|
|
6
|
+
background: var(--qilin-alias-bg-module-platform);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.indicator {
|
|
10
|
+
position: absolute;
|
|
11
|
+
inset: 4px auto 4px 4px;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
border: 0.5px solid var(--qilin-alias-border-l3);
|
|
14
|
+
border-radius: var(--qilin-radius-md);
|
|
15
|
+
background: var(--qilin-alias-bg-layer-3);
|
|
16
|
+
transition: transform 180ms ease;
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.tab {
|
|
21
|
+
position: relative;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
height: 34px;
|
|
24
|
+
padding: 0 12px;
|
|
25
|
+
border-radius: var(--qilin-radius-md);
|
|
26
|
+
font-size: 14px;
|
|
27
|
+
line-height: 20px;
|
|
28
|
+
background: transparent;
|
|
29
|
+
color: var(--qilin-alias-label-secondary);
|
|
30
|
+
transition: color 180ms ease;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.tab:hover,
|
|
34
|
+
.tab[aria-selected='true'] {
|
|
35
|
+
background: transparent;
|
|
36
|
+
color: var(--qilin-alias-label-primary);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.tab[aria-selected='true'] {
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.tab:focus-visible {
|
|
44
|
+
outline: none;
|
|
45
|
+
text-decoration: underline;
|
|
46
|
+
text-decoration-thickness: 1px;
|
|
47
|
+
text-underline-offset: 4px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@media (prefers-reduced-motion: reduce) {
|
|
51
|
+
.indicator,
|
|
52
|
+
.tab {
|
|
53
|
+
transition: none;
|
|
54
|
+
}
|
|
55
|
+
}
|