@qilin-agent/client-ui-primitives 3.1.7 → 3.1.8

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.
Files changed (55) hide show
  1. package/README.i18n.yaml +8 -8
  2. package/README.md +11 -4
  3. package/README.zh.md +12 -4
  4. package/lib/AnimatedRows.module.css +7 -0
  5. package/lib/Button.module.css +6 -3
  6. package/lib/DetailDocument.module.css +67 -0
  7. package/lib/HoverCard.module.css +3 -3
  8. package/lib/ImageLightbox.module.css +1 -1
  9. package/lib/InlineEditor.module.css +1 -1
  10. package/lib/Input.module.css +2 -2
  11. package/lib/Menu.module.css +127 -78
  12. package/lib/MenuSurface.module.css +23 -7
  13. package/lib/QuotePreview.module.css +89 -0
  14. package/lib/RowDragPreview.module.css +12 -0
  15. package/lib/SegmentedTabs.module.css +4 -1
  16. package/lib/Tag.module.css +1 -1
  17. package/lib/TerminalBlock.module.css +1 -1
  18. package/lib/Toast.module.css +28 -0
  19. package/lib/UserMessageBubble.module.css +11 -0
  20. package/lib/index.js +2094 -1115
  21. package/lib/markdown/CodeBlock.module.css +23 -0
  22. package/lib/markdown/JsonBlock.module.css +1 -1
  23. package/lib/markdown/MarkdownText.module.css +11 -7
  24. package/lib/settings-form/fields.module.css +2 -2
  25. package/lib/types/AnimatedRows.d.ts +46 -0
  26. package/lib/types/Button.d.ts +13 -4
  27. package/lib/types/DetailDocument.d.ts +79 -0
  28. package/lib/types/ImageLightbox.d.ts +3 -1
  29. package/lib/types/Menu.d.ts +25 -9
  30. package/lib/types/MenuSurface.d.ts +9 -0
  31. package/lib/types/QuotePreview.d.ts +26 -0
  32. package/lib/types/SegmentedTabs.d.ts +3 -1
  33. package/lib/types/Toast.d.ts +19 -1
  34. package/lib/types/Tooltip.d.ts +13 -8
  35. package/lib/types/UserMessageBubble.d.ts +11 -0
  36. package/lib/types/code-highlighting.d.ts +12 -0
  37. package/lib/types/focus.d.ts +8 -0
  38. package/lib/types/icons/index.d.ts +8 -0
  39. package/lib/types/icons/shared-artwork.d.ts +1 -0
  40. package/lib/types/index.d.ts +20 -4
  41. package/lib/types/markdown/MarkdownDelegate.d.ts +19 -2
  42. package/lib/types/markdown/MarkdownText.d.ts +3 -1
  43. package/lib/types/markdown/file-link.d.ts +13 -3
  44. package/lib/types/markdown/mathCompatibility.d.ts +12 -5
  45. package/lib/types/markdown/render.d.ts +2 -1
  46. package/lib/types/markdown/windows-path-destinations.d.ts +12 -0
  47. package/lib/types/motion.d.ts +13 -0
  48. package/lib/types/overlay-top-margin.d.ts +10 -2
  49. package/lib/types/quote-text.d.ts +69 -0
  50. package/lib/types/use-clipboard-copy.d.ts +14 -0
  51. package/lib/types/useAnchoredPosition.d.ts +31 -18
  52. package/lib/types/useRowDragPreview.d.ts +20 -0
  53. package/lib/types/user-text.d.ts +24 -2
  54. package/package.json +8 -6
  55. package/lib/types/use-copy-feedback.d.ts +0 -14
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: e84dcb0e810a21cd
22
- zh: 61c55bd0f405cca5
21
+ en: f651058e890cd942
22
+ zh: 26870959612a099a
23
23
  /qilin-agent-client-ui-primitives/use-this-package/controls-and-icons:
24
- en: d563c3a7f4eba0bc
25
- zh: 49b6834de626eb6c
24
+ en: 1fd46aa4a955a6ae
25
+ zh: 66a7c15a1d08b263
26
26
  /qilin-agent-client-ui-primitives/use-this-package/rendering-agent-output:
27
- en: dc68afccfd4e471f
28
- zh: 062ea9ff0c36135d
27
+ en: b34b54d727512350
28
+ zh: 944eb033382011cd
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: d88fc6047e110c21
37
- zh: 2dee18b40a594ff3
36
+ en: ba35fea05d59baba
37
+ zh: f331003181aa9b59
38
38
  /qilin-agent-client-ui-primitives/understand-the-implementation/input-modality:
39
39
  en: 44dfc06332ca3b5b
40
40
  zh: 94df10cde37c18e6
package/README.md CHANGED
@@ -48,6 +48,8 @@ Check this table before writing a control in a feature package. A plugin cannot
48
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
+ | `AnimatedRows` | Keyed list movement with 200 ms glides and 100 ms entry/exit fades. Starts after local pointer or keyboard input, honors reduced motion, and settles loading or view replacement. Mark rows with `data-row-key`, supply matching `rowKeys`, and position the parent for the inert exit overlay; `role` and `label` belong to the caller. |
52
+ | `useRowDragPreview` | Native drag preview using an inert offscreen row copy. Supply a class for the surface background and, for a child drag handle, pass the row to `start`; `clear` and unmount remove the copy. |
51
53
  | `CommandText` | One command line, gaining a Tab stop, a group role, and its accessible name only while it overflows horizontally. |
52
54
  | `Modal` | Centered dialog over a page mask. |
53
55
  | `RiskConfirmation` | Sensitive action gated behind an explicit checkbox. |
@@ -57,8 +59,11 @@ Check this table before writing a control in a feature package. A plugin cannot
57
59
  | `ImageLightbox` | Shared image modal with focus restoration and Escape dismissal. |
58
60
  | `Toast` | Transient top-center banner held for the owner's `holdMs`. |
59
61
  | `JsonTree`, `JsonBlock` | Read-only JSON inspection. |
60
- | `MarkdownText`, `MarkdownDelegateProvider`, `CodeBlock` | Untrusted GFM with TeX math, Mermaid diagrams, owner-delegated HTTP(S) navigation, and highlighted code. `CodeBlock` accepts opt-in `lineNumbers`; copied source excludes the gutter, and `contentRef` exposes its stable source wrapper to an owner that uses it as a scrollport. Set `showHeader={false}` when the owner supplies its own language and copy toolbar. |
62
+ | `MarkdownText`, `MarkdownDelegateProvider`, `CodeBlock` | Untrusted GFM with TeX math, Mermaid diagrams, owner-delegated HTTP(S) navigation, and highlighted code. `CodeBlock` accepts opt-in `lineNumbers`; copied source excludes the gutter, and `contentRef` exposes its stable source wrapper to an owner that uses it as a scrollport. Every arm emits one `span.line` per source line, and the line under a hover-capable pointer takes the interactive wash — a background only, so selection, copying, and the gutter's counters are untouched, and `prefers-reduced-motion` drops the fade. The block keeps one keyboard stop, its `pre`, rather than one per line: a line is not a control, and a few-hundred-line fence would otherwise spend the reader's whole Tab run in one block. Set `showHeader={false}` when the owner supplies its own language and copy toolbar. |
61
63
  | `TerminalBlock`, `ReadBlock`, `DiffBlock`, `SearchBlock`, `WebBlock` | The agent-output card matching each tool-result intent. |
64
+ | `useClipboardCopy` | Copies the current text with one-second success feedback, prevents overlapping or success-window repeats, and cancels its reset timer on unmount; late write completion cannot update an unmounted control. |
65
+ | `UserMessageBubble` | User-message background, radius, content inset, typography, and plain-text wrapping. Accepts native div props; callers own row alignment, width, content, and actions. |
66
+ | `QuotePreview` | One quoted excerpt: quote mark, width-capped text, a copy control, and a jump control when the owner names a source. A clipped excerpt opens its full text on hover. Every string arrives through required labels. |
62
67
  | `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
68
  | `icons/*`, `QilinSeal`, `ReferenceIcon`, `LinkIcon` | Glyphs and brand marks. Use `LinkIcon` for 14px clickable-link categories and known-site marks. |
64
69
  | `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. |
@@ -75,13 +80,13 @@ Writing your own component in your own package is fine when the need is genuinel
75
80
 
76
81
  ### Controls and icons
77
82
 
78
- The catalog above lists what each export is for; this section covers the behavior that props alone do not show. The `ic_ds_*` icon set fills the inline-icon slots, and the `QilinSeal` stamp is the brand mark. Upstream weight-suffixed icon names (`Icon*OutlineRegular`, `Icon*OutlineMedium`) resolve as compat aliases to the nearest same-name size-suffixed glyph, and the two names without a drawn QiLin glyph (Users, WorkspaceTree) port the upstream artwork, so dsh-community plugins importing `@deepseek-ai/dsh-client-ui-primitives` render. `FileTypeIcon` renders the traditional 28px Excel, folder, HTML, image, Markdown, generic, PDF, PPT, video, and Word glyphs, and uses the imported square technology artwork for the established 48 code and configuration categories. The import replaces artwork only: archive-only categories do not extend `CodeFileType`. `classifyFileType` applies exact filename, prefix, suffix, optional project-context, and extension rules in that order; React names win over TypeScript/JavaScript, Angular suffixes win over their base extension, and a Dart file becomes Flutter only when the supplied project files contain a `pubspec.yaml` with `flutter:`. Markdown and SVG remain traditional Markdown and image files. Office mappings include XLSM/Numbers as Excel, KEY as slides, and RTF/ODT/Pages as documents. `fileExtension` exposes the same basename and final-dot parsing for adjacent metadata labels. Traditional glyphs use a solid category-colored sheet with a white mark and translucent white corner; the generic file uses a grey sheet and darker grey corner. Callers may override the sheet color through `--qilin-file-type-icon-color`. The full-color technology artwork is the deliberate exception and retains its embedded palette. All glyphs are decorative and carry no label. `LinkIcon` remains the smaller leading glyph for clickable artifact links — globe, folder, code, image, document, or plain paper, and for a `url` link the mark of a well-known site named by its `href` — the developer sites a transcript usually cites (GitHub, GitLab, npm, PyPI, Stack Overflow, MDN, Wikipedia, Hacker News, YouTube, X, Bilibili, Zhihu, Juejin, CSDN) and mainstream search, video, social, shopping, and reference sites (Google, Baidu, DuckDuckGo, TikTok, Netflix, Spotify, Facebook, Instagram, Reddit, Telegram, WhatsApp, WeChat, QQ, Weibo, Taobao, AliExpress, eBay, Quora, V2EX, Apple) — while `classifyLinkPath` folds the shared file types into that existing six-category vocabulary. `ConnectionIndicator` renders a warning-colored disconnected action whose permanent retry glyph marks the retry action beside the owner-supplied outage label, a connecting spinner beside a label whose one-to-three dots advance every 500ms independently of retry timing, or a success-colored recovered status. Clicking either warning state requests an immediate reconnect; no hover interaction changes the copy. The pill fades in on appearance, fades out for 150ms before unmounting, and sizes to its current label. 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. `rankByName` is the `/` menu's shared candidate ranker for the command and skill sources: the query must be a case-insensitive ordered subsequence of the name; prefix hits rank first, then alignment score, then source order. `Menu.autoFocus` focuses its first enabled item, supports Arrow Up/Down and Home/End navigation, and focuses the first button in the anchor on Escape; it is opt-in for action menus.
83
+ The catalog above lists what each export is for; this section covers the behavior that props alone do not show. The `ic_ds_*` icon set fills the inline-icon slots, and the `QilinSeal` stamp is the brand mark. Upstream weight-suffixed icon names (`Icon*OutlineRegular`, `Icon*OutlineMedium`) resolve as compat aliases to the nearest same-name size-suffixed glyph, and the two names without a drawn QiLin glyph (Users, WorkspaceTree) port the upstream artwork, so dsh-community plugins importing `@deepseek-ai/dsh-client-ui-primitives` render. `FileTypeIcon` renders the traditional 28px Excel, folder, HTML, image, Markdown, generic, PDF, PPT, video, and Word glyphs, and uses the imported square technology artwork for the established 48 code and configuration categories. The import replaces artwork only: archive-only categories do not extend `CodeFileType`. `classifyFileType` applies exact filename, prefix, suffix, optional project-context, and extension rules in that order; React names win over TypeScript/JavaScript, Angular suffixes win over their base extension, and a Dart file becomes Flutter only when the supplied project files contain a `pubspec.yaml` with `flutter:`. Markdown and SVG remain traditional Markdown and image files. Office mappings include XLSM/Numbers as Excel, KEY as slides, and RTF/ODT/Pages as documents. `fileExtension` exposes the same basename and final-dot parsing for adjacent metadata labels. Traditional glyphs use a solid category-colored sheet with a white mark and translucent white corner; the generic file uses a grey sheet and darker grey corner. Callers may override the sheet color through `--qilin-file-type-icon-color`. The full-color technology artwork is the deliberate exception and retains its embedded palette. All glyphs are decorative and carry no label. `LinkIcon` remains the smaller leading glyph for clickable artifact links — globe, folder, code, image, document, or plain paper, and for a `url` link the mark of a well-known site named by its `href` — the developer sites a transcript usually cites (GitHub, GitLab, npm, PyPI, Stack Overflow, MDN, Wikipedia, Hacker News, YouTube, X, Bilibili, Zhihu, Juejin, CSDN) and mainstream search, video, social, shopping, and reference sites (Google, Baidu, DuckDuckGo, TikTok, Netflix, Spotify, Facebook, Instagram, Reddit, Telegram, WhatsApp, WeChat, QQ, Weibo, Taobao, AliExpress, eBay, Quora, V2EX, Apple) — while `classifyLinkPath` folds the shared file types into that existing six-category vocabulary. `ConnectionIndicator` renders a warning-colored disconnected action whose permanent retry glyph marks the retry action beside the owner-supplied outage label, a connecting spinner beside a label whose one-to-three dots advance every 500ms independently of retry timing, or a success-colored recovered status. Clicking either warning state requests an immediate reconnect; no hover interaction changes the copy. The pill fades in on appearance, fades out for 150ms before unmounting, and sizes to its current label. Its owner supplies visibility, the recovery hold, localized labels, and the immediate-reconnect callback; the primitive uses no native title tooltip. `useAnchoredPosition` places a body-portaled fixed panel against the visual viewport — scroll, resize, browser zoom, and the soft keyboard all re-place it before paint — and publishes the panel's size caps as `--qilin-anchored-max-width`/`--qilin-anchored-max-height`, mirroring the earlier `--dsh-` names for sheets that still read them; `useAnchoredMaxHeight` keeps bottom-anchored overlays clamped to the viewport. `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. `rankByName` is the `/` menu's shared candidate ranker for the command and skill sources: the query must be a case-insensitive ordered subsequence of the name; prefix hits rank first, then alignment score, then source order. `Menu.autoFocus` focuses its first enabled item, supports Arrow Up/Down and Home/End navigation, and focuses the first button in the anchor on Escape; it is opt-in for action menus.
79
84
 
80
85
  ### Rendering agent output
81
86
 
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.
87
+ 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. Paths support percent escapes, `#L24` / `#L24-L30` fragments, and `:24` / `:24-30` / `:24:5` suffixes; ranges open at their first line, columns are consumed but not selected, and a fragment disables suffix parsing. Zero, unsafe integers, descending ranges, and unsupported fragments open the file without selecting a line; literal `?`, `#`, and numeric colon suffixes in filenames must be percent-encoded. Explicit `./`, `../`, slash/backslash-rooted paths (including UNC), and Windows drive paths such as `C:/work/file.ts` take precedence over URL parsing. Otherwise, destinations the absolute URL parser accepts remain URLs — `index.ts:24`, `Makefile:12`, and `localhost:3000` stay links — so a bare file reference with a numeric suffix takes `./index.ts:24`. A destination with a backslash and no URL scheme (a drive letter is not one) is a Windows path when it has no `/` or has a backslash before a letter or digit. Such a path keeps every backslash, including one before punctuation, and still decodes character references, so `D:\repo\.github` is not read as an escaped `.`. 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. Queries, malformed escapes, plain URLs, and control characters never reach the file opener.
83
88
 
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.
89
+ `MarkdownText` renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. Strikethrough requires `~~`; a single `~`, as in `10~20`, stays literal. 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. Each mounted image appends a `load` query parameter to a rewritten non-data, non-blob URL, because browsers reuse a same-URL image within one document despite `no-store`; a newly rendered image of an overwritten file therefore loads its current bytes. 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. The same projection renders a blockquoted span as one quote surface when the caller supplies quote labels: the excerpt prints literally, so a reference token inside it never decorates, and a credential keeps its masking. `quoteInsertionOf` serializes a selection back into that exact form with its `qilin-quote:` source token, so the composer and the transcript read one scanner rather than two. 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.
85
90
 
86
91
  `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.
87
92
 
@@ -117,9 +122,11 @@ The package enforces one separation: presentational React atoms with zero Kylin
117
122
  | [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | Read and diff cards |
118
123
  | [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | Search and web-retrieval cards |
119
124
  | [`src/icons/`](src/icons/) | `ic_ds_*` glyph components and brand marks |
125
+ | [`src/quote-text.ts`](src/quote-text.ts) / [`src/QuotePreview.tsx`](src/QuotePreview.tsx) | Sent-user-text quotes in both directions: blockquote scanning and decoding, the `qilin-quote:` source token, the insertion serializer a composer gesture calls, and the excerpt surface with its copy and jump controls |
120
126
  | [`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` |
121
127
  | [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | Floating-panel and overlay geometry hooks |
122
128
  | [`src/input-modality.ts`](src/input-modality.ts) | Document-wide input modality published on `<html>` |
129
+ | [`src/motion.ts`](src/motion.ts) | Shared `prefersReducedMotion()` query that this package's animations and the workspace rows ask before starting movement |
123
130
 
124
131
  <a id="input-modality"></a>
125
132
  ### Input modality
package/README.zh.md CHANGED
@@ -48,6 +48,8 @@ kind: "package-library"
48
48
  | `StateDot` | 状态标记:`done`、`warning`、`ongoing`、`error` 或 `idle`。`ongoing` loading 的动画在挂载时以及每次动画重启(如列表重排)时固定到文档时间零点,所以所有可见 loading 同相动画。它是 `aria-hidden` 的,名称由渲染点提供。 |
49
49
  | `ConnectionIndicator` | 行内连接恢复控件,覆盖断线、重试与已恢复三种状态。 |
50
50
  | `DisclosureRow` | 24px 紧凑折叠行,标题与内容左右排列。 |
51
+ | `AnimatedRows` | 带键列表的位移动画:移动 200 ms,进出淡变 100 ms。列表内发生指针或键盘输入后才启用,遵循减少动态效果设置,加载或替换视图时直接完成。为行标记 `data-row-key` 并传入对应的 `rowKeys`,为父容器设置定位以承载不可交互的退出层;`role` 与 `label` 由调用方提供。 |
52
+ | `useRowDragPreview` | 使用不可交互的离屏行副本生成原生拖拽预览。传入设置表面底色的类名;拖拽来自子级手柄时,将行元素传给 `start`;`clear` 与卸载会移除副本。 |
51
53
  | `CommandText` | 一行命令文本:仅在横向溢出时才获得 Tab 停靠、group 角色与可访问名。 |
52
54
  | `Modal` | 页面遮罩之上的居中对话框。 |
53
55
  | `RiskConfirmation` | 以显式复选框把关的敏感操作确认。 |
@@ -57,8 +59,11 @@ kind: "package-library"
57
59
  | `ImageLightbox` | 共享图片浮层,支持焦点恢复与 Esc 关闭。 |
58
60
  | `Toast` | 顶部居中的瞬时横幅,保持时长由所有者的 `holdMs` 决定。 |
59
61
  | `JsonTree`、`JsonBlock` | 只读 JSON 查看。 |
60
- | `MarkdownText`、`MarkdownDelegateProvider`、`CodeBlock` | 不可信 GFM 与 TeX 数学、owner 委托的 HTTP(S) 导航,以及高亮代码。`CodeBlock` 可通过 `lineNumbers` 开启行号;复制的源码不含行号栏,`contentRef` 则向需要把稳定源码包装节点用作滚动区的 owner 提供该节点。调用方提供自己的语言与复制工具栏时,设置 `showHeader={false}`。 |
62
+ | `MarkdownText`、`MarkdownDelegateProvider`、`CodeBlock` | 不可信 GFM 与 TeX 数学、owner 委托的 HTTP(S) 导航,以及高亮代码。`CodeBlock` 可通过 `lineNumbers` 开启行号;复制的源码不含行号栏,`contentRef` 则向需要把稳定源码包装节点用作滚动区的 owner 提供该节点。每条源码行在三个渲染分支里都是一个 `span.line`,支持悬停的指针落在哪一行,该行就整行加上交互底色——只改背景,因此选择、复制与行号计数器都不受影响;`prefers-reduced-motion` 下取消淡入。键盘停靠点仍只有 `pre` 一个,而不是逐行一个:一行不是控件,几百行的代码块否则会把读者的整段 Tab 走位耗在这一块里。调用方提供自己的语言与复制工具栏时,设置 `showHeader={false}`。 |
61
63
  | `TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock`、`WebBlock` | 与各类工具结果意图对应的 agent 输出卡片。 |
64
+ | `useClipboardCopy` | 复制当前文本并显示一秒成功反馈,阻止写入期间及成功反馈期间的重复操作,卸载时清除重置计时器;迟到的写入结果不会更新已卸载控件。 |
65
+ | `UserMessageBubble` | 用户消息的背景、圆角、内容内边距、字体与纯文本换行规则。接收原生 div props;行对齐、宽度、内容和操作由调用方拥有。 |
66
+ | `QuotePreview` | 单条引文:引号标记、限宽文本、复制控件,以及持有方指明来源时的跳转控件。被裁切的引文在悬停时展开全文。所有文案都通过必填 label 传入。 |
62
67
  | `SettingsForm`、`SettingsValueField`、`SettingsSecretField` | 基于 `SettingsFormModel` 的分阶段配置表单:字段如实报告读者输入,保存是草稿变成文档变更的唯一入口。 |
63
68
  | `icons/*`、`QilinSeal`、`ReferenceIcon`、`LinkIcon` | 字形与品牌标识。`LinkIcon` 用于 14px 的可点击链接分类及已知站点标记。 |
64
69
  | `FileTypeIcon`、`classifyFileType`、`fileExtension` | 按类别着色的 28px 文件或文件夹图形,以及它背后共享的不区分大小写文件名映射。代码与配置文件使用细分的全彩技术图形;链接前置图形使用 `LinkIcon`,图片内容使用图片预览。 |
@@ -75,13 +80,14 @@ kind: "package-library"
75
80
 
76
81
  ### 控件与图标
77
82
 
78
- 上面的目录说明每个导出的用途;本节讲 props 本身看不出来的行为。`ic_ds_*` 图标集填充行内图标 slot,`QilinSeal` 印章是品牌标记。上游字重后缀图标名(`Icon*OutlineRegular`、`Icon*OutlineMedium`)按兼容别名解析到尺寸就近的同名字形,本集没有的两个名字(Users、WorkspaceTree)原样移植上游字形,使引用 `@deepseek-ai/dsh-client-ui-primitives` 的 dsh 社区插件可以正常渲染。`FileTypeIcon` 渲染传统的 28px Excel、folder、HTML、image、Markdown、generic、PDF、PPT、video 与 Word 图形,并为现有 48 个代码和配置类别使用导入的方形技术图形。该导入只替换图形:资源包中额外的类别不会扩展 `CodeFileType`。`classifyFileType` 按完整文件名、前缀、后缀、可选项目上下文、扩展名的顺序匹配;React 文件名优先于 TypeScript/JavaScript,Angular 后缀优先于基础扩展名,只有传入的项目文件包含带 `flutter:` 的 `pubspec.yaml` 时 Dart 文件才使用 Flutter。Markdown 与 SVG 仍分别使用传统 Markdown 与图片图形。办公文件映射包含 XLSM/Numbers 的表格图标、KEY 的幻灯片图标,以及 RTF/ODT/Pages 的文档图标。`fileExtension` 为相邻元数据 label 暴露同一套 basename 与最终点号解析。传统图形使用实色分类底板、白色标记和半透明白色折角;通用文件使用灰色底板与较深灰色折角。调用方可通过 `--qilin-file-type-icon-color` 覆盖底板颜色。全彩技术图形是明确例外,会保留其内嵌调色板。所有图形都是装饰性的,不自带 label。`LinkIcon` 仍是可点击产物链接较小的前置图形——地球、文件夹、代码、图片、文档或纸张,`url` 链接的 `href` 指向已知站点时则改用该站点自己的标记——转写内容常引用的开发者站点(GitHub、GitLab、npm、PyPI、Stack Overflow、MDN、Wikipedia、Hacker News、YouTube、X、Bilibili、知乎、掘金、CSDN),以及主流搜索、视频、社交、购物与参考资料站点(Google、百度、DuckDuckGo、TikTok、Netflix、Spotify、Facebook、Instagram、Reddit、Telegram、WhatsApp、微信、QQ、微博、淘宝、速卖通、eBay、Quora、V2EX、Apple)——`classifyLinkPath` 把共享文件类型折叠进原有六类词汇。`ConnectionIndicator` 可渲染警告色的断联操作(常驻重试图形指明重试动作,断联文案由持有方提供)、spinner 加一至三个点以独立于 retry 时序的 500ms 节奏推进的连接中状态,或成功色的恢复状态。点击任一警告状态都会请求立即重连;没有任何悬停交互会改变文案。药丸出现时淡入、卸载前淡出 150ms,宽度随当前 label 自适应。它的持有方提供可见性、恢复驻留时间、本地化 label 与立即重连回调;该原语不使用原生 title tooltip。`useAnchoredPosition` 与 `useAnchoredMaxHeight` 让浮动面板与底部锚定浮层始终钳制在视口内并跟随锚点。`HoverCard` 通过指针离开宽限期让采用 portal 的预览在跨过锚点间隙时仍可触及,并可通过 `copyText` prop 提供复制按钮。`Toast` 的停留时长由使用方通过 `holdMs` 指定,因为横幅该留多久取决于有多少内容要读;同一个值同时驱动它的卸载定时器与样式表的淡出延迟,两者不可能再错位。`rankByName` 是 `/` 菜单命令源与 skill(技能)源共享的候选排序器:查询必须是名字的不区分大小写的有序子序列;前缀命中排最前,其次按对齐分数,再按来源顺序。 `Menu.autoFocus` 聚焦首个启用项,支持上下方向键与 Home/End 导航,并在 Escape 时聚焦 anchor 内的第一个按钮;操作菜单可显式启用。
83
+ 上面的目录说明每个导出的用途;本节讲 props 本身看不出来的行为。`ic_ds_*` 图标集填充行内图标 slot,`QilinSeal` 印章是品牌标记。上游字重后缀图标名(`Icon*OutlineRegular`、`Icon*OutlineMedium`)按兼容别名解析到尺寸就近的同名字形,本集没有的两个名字(Users、WorkspaceTree)原样移植上游字形,使引用 `@deepseek-ai/dsh-client-ui-primitives` 的 dsh 社区插件可以正常渲染。`FileTypeIcon` 渲染传统的 28px Excel、folder、HTML、image、Markdown、generic、PDF、PPT、video 与 Word 图形,并为现有 48 个代码和配置类别使用导入的方形技术图形。该导入只替换图形:资源包中额外的类别不会扩展 `CodeFileType`。`classifyFileType` 按完整文件名、前缀、后缀、可选项目上下文、扩展名的顺序匹配;React 文件名优先于 TypeScript/JavaScript,Angular 后缀优先于基础扩展名,只有传入的项目文件包含带 `flutter:` 的 `pubspec.yaml` 时 Dart 文件才使用 Flutter。Markdown 与 SVG 仍分别使用传统 Markdown 与图片图形。办公文件映射包含 XLSM/Numbers 的表格图标、KEY 的幻灯片图标,以及 RTF/ODT/Pages 的文档图标。`fileExtension` 为相邻元数据 label 暴露同一套 basename 与最终点号解析。传统图形使用实色分类底板、白色标记和半透明白色折角;通用文件使用灰色底板与较深灰色折角。调用方可通过 `--qilin-file-type-icon-color` 覆盖底板颜色。全彩技术图形是明确例外,会保留其内嵌调色板。所有图形都是装饰性的,不自带 label。`LinkIcon` 仍是可点击产物链接较小的前置图形——地球、文件夹、代码、图片、文档或纸张,`url` 链接的 `href` 指向已知站点时则改用该站点自己的标记——转写内容常引用的开发者站点(GitHub、GitLab、npm、PyPI、Stack Overflow、MDN、Wikipedia、Hacker News、YouTube、X、Bilibili、知乎、掘金、CSDN),以及主流搜索、视频、社交、购物与参考资料站点(Google、百度、DuckDuckGo、TikTok、Netflix、Spotify、Facebook、Instagram、Reddit、Telegram、WhatsApp、微信、QQ、微博、淘宝、速卖通、eBay、Quora、V2EX、Apple)——`classifyLinkPath` 把共享文件类型折叠进原有六类词汇。`ConnectionIndicator` 可渲染警告色的断联操作(常驻重试图形指明重试动作,断联文案由持有方提供)、spinner 加一至三个点以独立于 retry 时序的 500ms 节奏推进的连接中状态,或成功色的恢复状态。点击任一警告状态都会请求立即重连;没有任何悬停交互会改变文案。药丸出现时淡入、卸载前淡出 150ms,宽度随当前 label 自适应。它的持有方提供可见性、恢复驻留时间、本地化 label 与立即重连回调;该原语不使用原生 title tooltip。`useAnchoredPosition` 把 portal 到 body 的定宽面板锚定到可视视口——滚动、尺寸变化、浏览器缩放和软键盘都会在绘制前重新摆放——并以 `--qilin-anchored-max-width`/`--qilin-anchored-max-height` 发布面板尺寸上限,同时镜像旧的 `--dsh-` 名称供仍读取它们的样式表使用;`useAnchoredMaxHeight` 让底部锚定浮层始终钳制在视口内。`HoverCard` 通过指针离开宽限期让采用 portal 的预览在跨过锚点间隙时仍可触及,并可通过 `copyText` prop 提供复制按钮。`Toast` 的停留时长由使用方通过 `holdMs` 指定,因为横幅该留多久取决于有多少内容要读;同一个值同时驱动它的卸载定时器与样式表的淡出延迟,两者不可能再错位。`rankByName` 是 `/` 菜单命令源与 skill(技能)源共享的候选排序器:查询必须是名字的不区分大小写的有序子序列;前缀命中排最前,其次按对齐分数,再按来源顺序。 `Menu.autoFocus` 聚焦首个启用项,支持上下方向键与 Home/End 导航,并在 Escape 时聚焦 anchor 内的第一个按钮;操作菜单可显式启用。
79
84
 
85
+ <a id="rendering-agent-output"></a>
80
86
  ### 渲染 agent 输出
81
87
 
82
- 最近的 `MarkdownDelegateProvider` 提供可选的 `openExternalLink` 和 `openFile` 导航回调。嵌套 Provider 替换外层能力,回调变化无需重新构建 Markdown 即可到达已渲染链接。其 `openFile` 使本地 Markdown 链接在落定后可点击。绝对路径和工作区相对路径支持百分号转义以及 `#L24` / `#L24-L30` 片段;范围定位到起始行。文件名中的字面 `?` 和 `#` 必须百分号编码。悬停提示使用解码后的路径,并在标签为空时提供可访问名称。回调接收解码后的路径和可选行号,渲染器保留标签并显示文件图标。不传回调时,本地链接仍为文本。URL 协议、查询串、不支持的片段及格式错误的目标不会传给文件打开器。
88
+ 最近的 `MarkdownDelegateProvider` 提供可选的 `openExternalLink` 和 `openFile` 导航回调。嵌套 Provider 替换外层能力,回调变化无需重新构建 Markdown 即可到达已渲染链接。其 `openFile` 使本地 Markdown 链接在落定后可点击。路径支持百分号转义、`#L24` / `#L24-L30` 片段以及 `:24` / `:24-30` / `:24:5` 后缀;范围定位到起始行,列号会被解析但不作为选中位置,存在片段时不再解析后缀。行号为 0、超出安全整数、范围倒序以及不支持的片段都只打开文件而不选中行;文件名中的字面 `?`、`#` 和数字冒号后缀必须百分号编码。以 `./`、`../` 开头、以斜杠或反斜杠开头(含 UNC)的路径,以及 `C:/work/file.ts` 这类 Windows 盘符路径,优先按路径处理;其余能被绝对 URL 解析器接受的目标仍视为 URL——`index.ts:24`、`Makefile:12` 与 `localhost:3000` 都是链接——因此带数字后缀的裸文件引用应写成 `./index.ts:24`。带反斜杠且无 URL 协议(盘符不算协议)的目标,在自身不含 `/`、或反斜杠后紧跟字母或数字时视为 Windows 路径;此类路径保留每个反斜杠(包括紧挨标点的那个),同时仍解码字符引用,因此 `D:\repo\.github` 不会被读成转义的 `.`。悬停提示使用解码后的路径,并在标签为空时提供可访问名称。回调接收解码后的路径和可选行号,渲染器保留标签并显示文件图标。不传回调时,本地链接仍为文本。查询串、格式错误的转义、普通 URL 及控制字符不会传给文件打开器。
83
89
 
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 在模态中放大显示图示,点击遮罩关闭。渲染被拒时,围栏源码及其复制控件上方显示前几行错误。
90
+ `MarkdownText` 渲染不可信的 GFM 与 TeX 公式、阻止不安全的链接与图片,并可把已解析的文件提及转换为显式控件。删除线需要 `~~`;单个 `~`(如 `10~20`)保持字面文本。外层 `MarkdownDelegateProvider` 会接收普通点击产生的已净化 HTTP(S) URL;带修饰键的点击和 Provider 外的链接保留原生外部 anchor 行为。当 owner 传入 `pathImages` 词表时,本地媒体路径的图片目标只在落定渲染阶段重写为可展示 URL(与 file mentions 相同的流式门);不传词表时本地目标保持惰性 alt 文本。加载或解码失败后,图片替换为作者的 alt 文本;alt 为空时显示原始目标路径;`fileImages` 还提供本地化的失败提示前缀。图片源变化后可重新加载。每张已挂载的图片还会在重写后的非 data、非 blob URL 上追加 `load` 查询参数,因为浏览器在同一文档内会复用同 URL 的图片(即使响应带 `no-store`);因此被覆盖文件的新渲染图片加载的是当前字节。回复流式输出时,它冻结已完成的块、按已完成行推进顶层未闭合 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 命令回显)保持遮住。同一投影在调用方提供引文文案时把 blockquote 段落渲染为一个引文界面:引文按字面打印,其中的引用 token 不会装饰,凭据仍保持遮蔽。`quoteInsertionOf` 把选区序列化回该形式并带上 `qilin-quote:` 来源标记,输入框与转写因此共用一套扫描器而不是两套。 传入 `UserTextReferences` 时,文件和 skill 引用成为支持键盘操作的预览按钮,复用正文文件链接的悬停和聚焦样式;第一次指针点击可以打开预览,后续点击和已有选区保留原生选择行为。键盘激活在存在选区时仍可打开预览。```` ```mermaid ```` 围栏经懒加载的库以严格安全模式渲染;净化后的 SVG 保留几何、文本、渐变与类名,脚本、事件处理器、`foreignObject` 和 `javascript:` URL 一律剥离,点击或 Enter/Space 在模态中放大显示图示,点击遮罩关闭。渲染被拒时,围栏源码及其复制控件上方显示前几行错误。
85
91
 
86
92
  `MarkdownText` 默认为 `variant="body"`。次级内容使用 `variant="compact"`:其 13px 字号与 20px 行高跟随内容字号设置,各级标题保持同一字号并使用 600 字重,段落与列表采用更紧凑的间距。正文、链接和代码均保持 tertiary 颜色,以点状下划线区分链接。代码标题栏随代码块滚动。表格和公式仍然启用,使用周围文字的字号,并在可用宽度内横向滚动。两个变体共享解析器与流式缓存。
87
93
 
@@ -117,9 +123,11 @@ kind: "package-library"
117
123
  | [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | 读取与差异卡片 |
118
124
  | [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | 搜索与网页检索卡片 |
119
125
  | [`src/icons/`](src/icons/) | `ic_ds_*` 字形组件与品牌标记 |
126
+ | [`src/quote-text.ts`](src/quote-text.ts) / [`src/QuotePreview.tsx`](src/QuotePreview.tsx) | 已发送用户文本引文的双向实现:blockquote 扫描与解码、`qilin-quote:` 来源标记、输入框手势调用的插入序列化,以及带复制与跳转控件的引文界面 |
120
127
  | [`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` 背后的凭据格式、由其派生的查表、扫描、掩码形态,以及带显示控件的掩码文本段 |
121
128
  | [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | 浮动面板与浮层几何钩子 |
122
129
  | [`src/input-modality.ts`](src/input-modality.ts) | 发布在 `<html>` 上的全文档输入模态 |
130
+ | [`src/motion.ts`](src/motion.ts) | 共享的 `prefersReducedMotion()` 查询:本包动画与工作区行在开始运动前都会询问它 |
123
131
 
124
132
  <a id="input-modality"></a>
125
133
  ### 输入模态
@@ -0,0 +1,7 @@
1
+ .exits {
2
+ position: absolute;
3
+ inset: 0;
4
+ contain: strict;
5
+ overflow: clip;
6
+ pointer-events: none;
7
+ }
@@ -7,7 +7,10 @@
7
7
  justify-content: center;
8
8
  gap: 4px;
9
9
  border: none;
10
- border-radius: 18px;
10
+ /* Paper geometry keeps the capsule line and only softens it: the original
11
+ r18 pill relaxes to the panel step (16px), which still reads as a capsule
12
+ at the 36–38px control height without the fully round ends. */
13
+ border-radius: var(--qilin-radius-panel);
11
14
  cursor: pointer;
12
15
  font-size: 14px;
13
16
  line-height: 22px;
@@ -32,7 +35,7 @@
32
35
  font-size: 12px;
33
36
  line-height: 18px;
34
37
  padding: 0 10px;
35
- border-radius: 14px;
38
+ border-radius: var(--qilin-radius-lg);
36
39
  }
37
40
 
38
41
  .primary {
@@ -54,7 +57,7 @@
54
57
 
55
58
  /* Dialog Cancel (figma 451:18655): bordered capsule on transparent fill. */
56
59
  .outline {
57
- border: 0.5px solid var(--qilin-alias-border-l3);
60
+ border: 1px solid var(--qilin-alias-border-l3);
58
61
  background: transparent;
59
62
  }
60
63
 
@@ -0,0 +1,67 @@
1
+ .section {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 12px;
5
+ min-width: 0;
6
+ }
7
+
8
+ .document {
9
+ min-width: 0;
10
+ /* A long README scrolls inside the section instead of pushing the page's later sections out of reach. */
11
+ max-height: min(560px, 60vh);
12
+ overflow-y: auto;
13
+ /* Horizontal rules separate the document from its neighbors; a full frame would clash with the images' own corners. */
14
+ border-block: 1px solid var(--qilin-alias-border-l1);
15
+ /* Keep the scrollbar off the text and the text width independent of overflow. */
16
+ scrollbar-gutter: stable;
17
+ padding: 12px 0;
18
+ overflow-wrap: anywhere;
19
+ }
20
+
21
+ .document:focus-visible {
22
+ outline: var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color, var(--qilin-alias-state-business-primary));
23
+ outline-offset: 3px;
24
+ }
25
+
26
+ .failure {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 10px;
30
+ }
31
+
32
+ .error {
33
+ display: inline-flex;
34
+ align-items: center;
35
+ gap: 6px;
36
+ margin: 0;
37
+ font-size: 13px;
38
+ line-height: 20px;
39
+ color: var(--qilin-alias-state-error-primary);
40
+ }
41
+
42
+ .skeleton {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 10px;
46
+ }
47
+
48
+ .bar {
49
+ display: block;
50
+ height: 12px;
51
+ border-radius: var(--qilin-radius-xs);
52
+ background: var(--qilin-alias-bg-skeleton);
53
+ animation: qilin-plugin-detail-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite;
54
+ }
55
+
56
+ .long { width: min(480px, 90%); }
57
+ .short { width: min(240px, 50%); }
58
+
59
+ @keyframes qilin-plugin-detail-skeleton {
60
+ 0% { opacity: 1; }
61
+ 40% { opacity: 0.6; }
62
+ 80%, 100% { opacity: 1; }
63
+ }
64
+
65
+ @media (prefers-reduced-motion: reduce) {
66
+ .bar { animation: none; }
67
+ }
@@ -6,12 +6,12 @@
6
6
  }
7
7
 
8
8
  /* Preview card (figma session hover card): 244 wide, r12, pad 12/16, the
9
- * menu card's elevation. Surface is #2C2C2E in both themes (figma value,
10
- * light/dark identical), so a component-level variable, not a theme token.
9
+ * menu card's elevation. The surface token is scheme-independent (#2c2c2e in
10
+ * both themes, figma value) and lives in ui-theme's design platform, where the
11
+ * dsh-era compat alias reads it too.
11
12
  * Hit-testable on purpose: resting the pointer on the card holds it open
12
13
  * (HoverCard's grace close), which a `pointer-events: none` card cannot do. */
13
14
  .card {
14
- --qilin-hovercard-bg: #2C2C2E;
15
15
  position: fixed;
16
16
  z-index: 100;
17
17
  box-sizing: border-box;
@@ -46,7 +46,7 @@
46
46
  place-items: center;
47
47
  width: 36px;
48
48
  height: 36px;
49
- border: 0.5px solid var(--qilin-alias-border-l2-darkmode-thin);
49
+ border: 1px solid var(--qilin-alias-border-l2-darkmode-thin);
50
50
  border-radius: 999px;
51
51
  corner-shape: round;
52
52
  background: var(--qilin-specific-input-major);
@@ -7,7 +7,7 @@
7
7
  max-height: 127px;
8
8
  margin: 0;
9
9
  padding: 3px 8px;
10
- border: 0.5px solid var(--dsw-alias-border-l4);
10
+ border: 1px solid var(--dsw-alias-border-l4);
11
11
  border-radius: var(--dsw-radius-sm);
12
12
  outline: none;
13
13
  background: var(--dsw-alias-bg-base);
@@ -4,8 +4,8 @@
4
4
  gap: 6px;
5
5
  height: 32px;
6
6
  padding: 0 8px;
7
- border: 0.5px solid var(--qilin-alias-border-l4);
8
- border-radius: 8px;
7
+ border: 1px solid var(--qilin-alias-border-l4);
8
+ border-radius: var(--qilin-radius-sm);
9
9
  background: var(--qilin-alias-bg-layer-1);
10
10
  }
11
11