@deepseek-ai/dsh-client-ui-primitives 0.1.6-alpha.2 → 0.1.7-alpha.1
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 +25 -11
- package/README.zh.md +25 -11
- package/lib/ConnectionIndicator.module.css +1 -10
- package/lib/HoverCard.module.css +27 -0
- package/lib/Menu.module.css +68 -48
- package/lib/Modal.module.css +6 -2
- package/lib/OnboardingSurface.module.css +1 -2
- package/lib/PathLabel.module.css +28 -0
- package/lib/RiskConfirmation.module.css +1 -1
- package/lib/SegmentedControl.module.css +76 -0
- package/lib/SegmentedTabs.module.css +55 -0
- package/lib/StateDot.module.css +64 -37
- package/lib/TerminalBlock.module.css +13 -6
- package/lib/TextShimmer.module.css +27 -0
- package/lib/Toast.module.css +31 -2
- package/lib/Tooltip.module.css +12 -0
- package/lib/index.js +5418 -2739
- package/lib/markdown/MarkdownText.module.css +1 -1
- package/lib/settings-form/SettingsForm.module.css +53 -0
- package/lib/settings-form/fields.module.css +145 -0
- package/lib/types/ConfigField.d.ts +33 -0
- package/lib/types/DisclosureRow.d.ts +4 -1
- package/lib/types/HoverCard.d.ts +14 -4
- package/lib/types/LinkIcon.d.ts +15 -30
- package/lib/types/Menu.d.ts +50 -5
- package/lib/types/PathLabel.d.ts +13 -0
- package/lib/types/PermissionIcon.d.ts +39 -0
- package/lib/types/ReferenceIcon.d.ts +12 -7
- package/lib/types/SegmentedControl.d.ts +35 -0
- package/lib/types/SegmentedTabs.d.ts +26 -0
- package/lib/types/SiteGlyph.d.ts +1 -1
- package/lib/types/StateDot.d.ts +6 -4
- package/lib/types/TerminalBlock.d.ts +15 -3
- package/lib/types/TextShimmer.d.ts +13 -0
- package/lib/types/Toast.d.ts +19 -2
- package/lib/types/Tooltip.d.ts +12 -2
- package/lib/types/code-file-types.d.ts +1 -1
- package/lib/types/code-highlighting.d.ts +19 -0
- package/lib/types/icons/index.d.ts +376 -195
- package/lib/types/icons/props.d.ts +1 -1
- package/lib/types/icons/shared-artwork.d.ts +36 -0
- package/lib/types/index.d.ts +21 -4
- package/lib/types/overlay-top-margin.d.ts +14 -0
- package/lib/types/plugin-artwork.d.ts +17 -0
- package/lib/types/settings-form/SettingsForm.d.ts +46 -0
- package/lib/types/settings-form/fields.d.ts +69 -0
- package/lib/types/settings-form/form-model.d.ts +214 -0
- package/lib/types/useAnchoredMaxHeight.d.ts +4 -1
- package/lib/types/useAnchoredPosition.d.ts +3 -1
- package/package.json +5 -3
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: df8c96e9fa4572b6371f8406ecd385b1eb80078e
|
|
6
|
+
README.zh.md: ef3cdeb70bbb7619f9e95bd24b5a1b97e9c159da
|
package/README.md
CHANGED
|
@@ -38,29 +38,38 @@ Check this table before writing a control in a feature package. A plugin cannot
|
|
|
38
38
|
|---|---|
|
|
39
39
|
| `Button` | Clickable action; `variant` selects `primary`, `ghost`, `outline`, or `toolbar`. |
|
|
40
40
|
| `Switch` | Two-state toggle, 36×20. `label` is required, so the control cannot ship unnamed. |
|
|
41
|
+
| `SegmentedControl` | Tablist of two or more equal-width segments with one sliding indicator, for switching a card or panel between a few modes; the owner holds the selection and `label` names the list. `id` seeds each tab's id (`<id>-<value>`) and the panel it controls (`<id>-<value>-panel`), which the owner renders and points back at the tab with `aria-labelledby`; a segment may be `disabled` with a `title`, and `disabled` on the control locks every segment while the shown panel has work in flight. |
|
|
41
42
|
| `Checkbox` | Labeled native checkbox with controlled state, keyboard interaction, and disabled styling; the caller supplies localized `label` text. |
|
|
42
43
|
| `Input` | Single-line text entry for search boxes and inline forms. |
|
|
43
|
-
| `Menu` | Dropdown of items, separators, and group labels, with nested submenus. While open, ↑/↓ (with Home and End) walk the list, Tab settles the focused row, and Escape or Shift+Tab close back to the anchor; selecting a row also returns the keyboard to the anchor unless the owner moved it itself. Only a keyboard on the anchor or inside the list is intercepted, and `autoFocus` decides solely whether opening focuses the first row. |
|
|
44
|
+
| `Menu`, `MenuItemButton` | Dropdown of `items` data rows, separators, and group labels, with nested submenus; `children` adds component rows, each a `MenuItemButton` (`separatorBefore` starts a new group), in the same list. Every row shares the styling, the keyboard walk, and the focus return; closing stays the owner's state change for both kinds. While open, ↑/↓ (with Home and End) walk the list, Tab settles the focused row, and Escape or Shift+Tab close back to the anchor; selecting a row also returns the keyboard to the anchor unless the owner moved it itself. Only a keyboard on the anchor or inside the list is intercepted, and `autoFocus` decides solely whether opening focuses the first row. |
|
|
44
45
|
| `Pill` | Selectable capsule button for view switchers and filters; takes `active` and `onClick`. |
|
|
46
|
+
| `SegmentedTabs` | Controlled equal-width tabs with a sliding indicator and Left/Right, Home, and End navigation. The caller supplies labels, tab/panel ids, and panel content. |
|
|
45
47
|
| `Tag` | Read-only capsule badge; `tone` selects one of eight palettes. |
|
|
46
|
-
| `
|
|
48
|
+
| `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. |
|
|
49
|
+
| `StateDot` | Solid green `done`, amber `warning`, red `error`, and neutral-grey `idle` marks in a 10px slot, plus a tertiary-grey 14px rotating `ongoing` loader whose animations pin to document time zero so every visible loader rotates in phase. `aria-hidden`, so the render site owns the name. `appearance="step"` shows a filled check for completion and a hollow pending circle. |
|
|
47
50
|
| `ConnectionIndicator` | Inline connection-recovery control across outage, retry, and recovered states. |
|
|
48
|
-
| `DisclosureRow` | 24px compact disclosure that lays title and content side by side. |
|
|
51
|
+
| `DisclosureRow` | 24px compact disclosure that lays title and content side by side. Memoized with shallow prop comparison; keep callbacks and React-node props stable when their content is unchanged. |
|
|
49
52
|
| `Modal` | Centered dialog over a page mask. |
|
|
50
53
|
| `RiskConfirmation` | Sensitive action gated behind an explicit checkbox. |
|
|
51
54
|
| `OnboardingSurface` | First-run stage that holds the application root inert. |
|
|
52
|
-
| `Tooltip` | Hover text
|
|
55
|
+
| `Tooltip` | Hover text anchored to a cloned child; optional `portal` rendering escapes clipping containers. |
|
|
53
56
|
| `HoverCard` | Hover preview the pointer can rest on and select from; optional copy button. |
|
|
54
57
|
| `Toast` | Transient top-center banner held for the owner's `holdMs`. |
|
|
58
|
+
| `SettingsForm`, `SettingsValueField`, `SettingsSecretField` | The frame and the controls of a plugin's settings page: the frame takes its copy as `labels`, saves only on its button, and discards on unmount; a value field shows staged text with the overridden badge and reset; a secret field starts blank and reports only whether a value is configured. |
|
|
59
|
+
| `SettingsFormModel`, `settingsNumberField`, `settingsTextField` | The staged-edit model behind such a page over a settings scope: drafts are staged and written on save, a field is overridden by its presence in the user layer, and a save that did not land keeps its drafts. |
|
|
55
60
|
| `JsonTree`, `JsonBlock` | Read-only JSON inspection. |
|
|
56
61
|
| `MarkdownText`, `MarkdownDelegateProvider`, `CodeBlock` | Untrusted GFM with TeX math, 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. |
|
|
57
62
|
| `TerminalBlock`, `ReadBlock`, `DiffBlock`, `SearchBlock`, `WebBlock` | The agent-output card matching each tool-result intent. |
|
|
58
|
-
| `icons/*`, `FishLogo`, `BrandWordmark`, `
|
|
59
|
-
| `
|
|
63
|
+
| `icons/*`, `FishLogo`, `BrandWordmark`, `ReferenceIconRegular`/`ReferenceIconMedium`, `LinkIconRegular`/`LinkIconMedium` | Glyphs and brand marks. Use `LinkIconMedium` for 14px clickable-link categories and known-site marks. |
|
|
64
|
+
| `PermissionIconReadOnlyRegular`/`Medium`, `PermissionIconWorkspaceWriteRegular`/`Medium`, `PermissionIconFullAccessRegular`/`Medium` | Permission-mode glyphs for read-only, workspace-write, and full-access choices. |
|
|
65
|
+
| `PluginArtworkTerminal`/`Loop`/`Subagent`/`Search`/`Default` | Fixed-palette 36×36 plugin artwork for the plugin-management surfaces; `Default` marks plugins without artwork of their own. Def ids are per-instance, so the same artwork repeats safely on one page. |
|
|
66
|
+
| `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 `LinkIconMedium` for link-leading glyphs and image previews for image content. |
|
|
67
|
+
| `languageForPath`, `CODE_HIGHLIGHT_EXTENSIONS`, `useCodeHighlighter` | The filename grammar selection and lazy line-token highlighter shared by code preview and diff review. |
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
Four pairs are easy to confuse:
|
|
62
70
|
|
|
63
71
|
- **`Tag` against `Pill`.** Reach for `Tag` for a read-only badge at the 11px capsule size, and for `Pill` when the capsule is selectable (`active` and `onClick`, as view switchers and filters use) or when it must sit on a 24px text line — `TerminalBlock` renders its exit status as a static `Pill` for exactly that reason. Size decides as much as interactivity here; the two are not interchangeable.
|
|
72
|
+
- **`Pill` against `SegmentedControl`.** A row of `Pill`s is a set of independent chips — each one toggles on its own, and several may be active. `SegmentedControl` is one choice among a few mutually exclusive modes, drawn as a tablist with one indicator, and it comes with the tab keyboard pattern (arrow keys walk the segments, only the selected one is in the tab sequence); the Models settings add card switches its two forms with it.
|
|
64
73
|
- **`DisclosureRow` against a card.** The row lays its title and content side by side at a fixed 24px. A card that stacks a name over a description is a different layout, and belongs in the feature package — `ui-settings-plugins`' `PluginCard` is the precedent and records why.
|
|
65
74
|
- **`FoldToggle` against the exported surface.** It is package-internal and not exported; the output cards use it for their head-tail fold.
|
|
66
75
|
|
|
@@ -68,7 +77,7 @@ Writing your own component in your own package is fine when the need is genuinel
|
|
|
68
77
|
|
|
69
78
|
### Controls and icons
|
|
70
79
|
|
|
71
|
-
The catalog above lists what each export is for; this section covers the behavior that props alone do not show.
|
|
80
|
+
The catalog above lists what each export is for; this section covers the behavior that props alone do not show. Product icon names omit artboard sizes and end in `Regular` for the supplied one-pixel artwork or `Medium` for the same geometry at a 1.3px stroke; the `size` prop controls rendered dimensions ([decision](../../../.agents/notes/implemented/architecture/2026-09-16-size-neutral-product-icon-weights.md)). Each product, reference, link, and permission glyph intentionally keeps both weight exports even when the current product uses only one, so callers can choose emphasis without adding another API later; fill-only pairs render identically. `IconWarningOutlineRegular`/`Medium` use a circle; `IconWarningTriangleOutlineRegular`/`Medium` use a rounded triangle. `FishLogo` and `BrandWordmark` fill brand slots. `FileTypeIcon` renders the traditional 28px spreadsheet, 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. Spreadsheet mappings include CSV, TSV, Excel workbooks and templates, OpenDocument spreadsheets, and Numbers; KEY maps to slides, while RTF/ODT/Pages map to 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 `--dsh-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. `LinkIconMedium` is the 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, the shared ongoing loader 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; `useAnchoredMaxHeight` and portalled `Menu` widen their 12px top viewport margin to the frame's published `--dsh-frame-top-clearance`. `HoverCard` keeps its portaled preview reachable across the anchor gap and can expose a copy button through the `copyText` prop. Its `preview` variant uses the anchor or `widthAnchorRef` element’s width minus 48px, centers the panel with 24px side insets, and places it above or below the row within the viewport. The panel keeps the frame’s top clearance, caps its height at 420px, and follows content and anchor resizing; Escape or pointer and keyboard activation of the anchor dismisses it. The whole preview fades in and out over 100ms; a closing preview stops receiving pointer input and unmounts after the fade. Re-entering the anchor during the fade restores it. Reduced-motion preferences suppress the transition. Copy labels are required only when copying is enabled. `Toast` uses the owner's `holdMs` for both the fade delay and its hold-and-fade lifetime. Rerenders with unchanged `holdMs` do not restart that lifetime; completion uses the latest callback, and fully faded actions cannot receive input. A new component key restarts the banner. `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.
|
|
72
81
|
|
|
73
82
|
### Rendering agent output
|
|
74
83
|
|
|
@@ -91,6 +100,8 @@ The atoms cannot read the application locale, so every piece of user-facing copy
|
|
|
91
100
|
<a id="understand-the-implementation"></a>
|
|
92
101
|
## Understand the implementation
|
|
93
102
|
|
|
103
|
+
Menu.listClassName styles the menu card independently of the anchor wrapper, including in portal mode.
|
|
104
|
+
|
|
94
105
|
<details>
|
|
95
106
|
<summary>Implementation internals — click to expand</summary>
|
|
96
107
|
|
|
@@ -105,8 +116,11 @@ The package enforces one separation: presentational React atoms with zero Cordis
|
|
|
105
116
|
| [`src/TerminalBlock.tsx`](src/TerminalBlock.tsx) | ANSI escape parsing (`anser`) and terminal card rendering |
|
|
106
117
|
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | Read and diff cards |
|
|
107
118
|
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | Search and web-retrieval cards |
|
|
108
|
-
| [`src/icons/`](src/icons/) | `
|
|
119
|
+
| [`src/icons/`](src/icons/) | Size-neutral `Regular` and `Medium` product glyph components |
|
|
120
|
+
| [`src/code-highlighting.ts`](src/code-highlighting.ts) | Shared filename grammar selection and lazy line highlighting |
|
|
121
|
+
| [`src/plugin-artwork.tsx`](src/plugin-artwork.tsx) | Fixed-palette plugin artwork with per-instance SVG def ids |
|
|
109
122
|
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | Floating-panel and overlay geometry hooks |
|
|
123
|
+
| [`src/settings-form/`](src/settings-form/) | The settings page kit: the staged form model over a settings scope, the value and secret fields, and the form frame |
|
|
110
124
|
|
|
111
125
|
### Streaming markdown
|
|
112
126
|
|
|
@@ -114,7 +128,7 @@ While a reply streams, `MarkdownText` parses incrementally: all but the trailing
|
|
|
114
128
|
|
|
115
129
|
### Geometry and overflow
|
|
116
130
|
|
|
117
|
-
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.
|
|
131
|
+
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. Hosts opt out of the shared geometry per surface: rebinding `--dsl-terminal-command-whitespace` / `--dsl-terminal-line-whitespace` to `pre-wrap` wraps commands and output in full with no sideways scroll, `maxLines: Infinity` disables the fold for hosts capping height through `--dsl-terminal-output-max-height` instead, `copyText` overrides the copy payload (and keeps the control rendered before any output), and `runStateDot: false` omits the run-state dot when the surrounding row already carries the state, reclaiming its gutter via `--dsl-terminal-gutter`. The banner divider follows the rendered body, so a running card that streams live output separates its command from the text like a settled card.
|
|
118
132
|
|
|
119
133
|
</details>
|
|
120
134
|
|
|
@@ -153,7 +167,7 @@ These limits define how the atoms behave at the edges; they are current package
|
|
|
153
167
|
- **Known-site marks are a fixed list** — only the named hosts resolve to their own mark, and every other external host keeps the globe; recognizing an arbitrary site would require fetching its icon over the network.
|
|
154
168
|
- **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.
|
|
155
169
|
- **A long highlighted fence retains its complete token DOM** — streaming avoids re-parsing, re-tokenizing, and reconciling the completed prefix, but it does not discard old colors or virtualize token spans. Final DOM cardinality therefore still follows the fence's token count; nested/container fences and a pathological single long line remain on the general tail path.
|
|
156
|
-
- **
|
|
170
|
+
- **The fish logo is a redrawn approximation** — its font glyph has no exportable vector geometry in the local design data, so a hand-authored recreation stands in until an exact export path exists.
|
|
157
171
|
- **`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.
|
|
158
172
|
- **No `Active` `StateDot` variant** — the supported states are done, warning, ongoing, error, and idle.
|
|
159
173
|
- **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)).
|
package/README.zh.md
CHANGED
|
@@ -38,29 +38,38 @@ kind: "package-library"
|
|
|
38
38
|
|---|---|
|
|
39
39
|
| `Button` | 可点击操作;`variant` 选择 `primary`、`ghost`、`outline` 或 `toolbar`。 |
|
|
40
40
|
| `Switch` | 36×20 的双态开关。`label` 必填,控件不可能在没有名称的情况下发布。 |
|
|
41
|
+
| `SegmentedControl` | 两段或更多等宽分段加一个滑动指示块的 tablist,用于在几种模式间切换一张卡片或面板;选中项由调用方持有,`label` 为列表命名。`id` 派生每个 tab 的 id(`<id>-<value>`)及其控制的面板 id(`<id>-<value>-panel`),面板由调用方渲染并用 `aria-labelledby` 指回 tab;分段可 `disabled` 并带 `title`,控件级 `disabled` 在当前面板有进行中的操作时锁住全部分段。 |
|
|
41
42
|
| `Checkbox` | 带标签的原生复选框,支持受控状态、键盘交互和禁用样式;调用方提供本地化的 `label` 文本。 |
|
|
42
43
|
| `Input` | 单行文本输入,用于搜索框与行内表单。 |
|
|
43
|
-
| `Menu` |
|
|
44
|
+
| `Menu`, `MenuItemButton` | 由 `items` 数据行、分隔线与分组标题构成的下拉菜单,支持嵌套子菜单;`children` 在同一列表中加入组件行,每行一个 `MenuItemButton`(`separatorBefore` 开启新分组)。所有行共享样式、键盘走位与焦点归还;两类行的关闭都是 owner 状态的改变。打开期间 `↑`/`↓`(以及 Home、End)在列表中走位,Tab 选定聚焦行,Escape 或 Shift+Tab 关闭并把焦点还给锚点;选定一行同样把键盘还给锚点——除非拥有者自己移动了焦点。只拦截位于锚点或列表内的键盘,`autoFocus` 仅决定打开时是否聚焦首行。 |
|
|
44
45
|
| `Pill` | 可选中的胶囊按钮,用于视图切换与筛选器;接受 `active` 与 `onClick`。 |
|
|
46
|
+
| `SegmentedTabs` | 受控的等宽分段标签,支持滑动指示条及左/右方向键、Home、End 导航。调用方提供文案、标签与面板 id,以及面板内容。 |
|
|
45
47
|
| `Tag` | 只读胶囊徽章;`tone` 选择八种配色之一。 |
|
|
46
|
-
| `
|
|
48
|
+
| `PathLabel` | 单行文件路径:目录使用弱化颜色,文件名使用主色,悬停可查看完整路径。空间足够时靠左显示;溢出时保留尾部并在左侧渐隐,路径或尺寸变化时更新。 |
|
|
49
|
+
| `StateDot` | 10px 槽内的绿色 `done`、琥珀色 `warning`、红色 `error`、中性灰色 `idle` 圆点,以及 tertiary 灰色 14px 旋转 `ongoing` loading,其动画固定到文档时间零点,所以所有可见 loading 同相旋转。它是 `aria-hidden` 的,名称由渲染点提供。 `appearance="step"` 以实心勾表示完成、空心圆表示等待。 |
|
|
47
50
|
| `ConnectionIndicator` | 行内连接恢复控件,覆盖断线、重试与已恢复三种状态。 |
|
|
48
|
-
| `DisclosureRow` | 24px
|
|
51
|
+
| `DisclosureRow` | 24px 紧凑折叠行,标题与内容左右排列。使用浅层 prop 比较进行 memo;内容未变时,保持回调与 React 节点 prop 的引用稳定。 |
|
|
49
52
|
| `Modal` | 页面遮罩之上的居中对话框。 |
|
|
50
53
|
| `RiskConfirmation` | 以显式复选框把关的敏感操作确认。 |
|
|
51
54
|
| `OnboardingSurface` | 首次运行的引导舞台,期间保持应用根节点 inert。 |
|
|
52
|
-
| `Tooltip` |
|
|
55
|
+
| `Tooltip` | 锚定在克隆子元素上的悬停文本;可通过 `portal` 渲染到外层,避免被容器裁剪。 |
|
|
53
56
|
| `HoverCard` | 指针可停留、可选中的悬停预览;可选带复制按钮。 |
|
|
54
57
|
| `Toast` | 顶部居中的瞬时横幅,保持时长由所有者的 `holdMs` 决定。 |
|
|
58
|
+
| `SettingsForm`、`SettingsValueField`、`SettingsSecretField` | 插件设置页的框架与控件:框架以 `labels` 接收文案,只在按钮点击时保存,卸载即丢弃;值字段显示暂存文本以及已覆盖标签和重置;密文字段每次为空,只报告是否已配置。 |
|
|
59
|
+
| `SettingsFormModel`、`settingsNumberField`、`settingsTextField` | 这类页面背后基于设置 scope 的暂存编辑模型:草稿先暂存、保存时写入,字段是否被覆盖看用户层是否含有它,未落地的保存保留草稿。 |
|
|
55
60
|
| `JsonTree`、`JsonBlock` | 只读 JSON 查看。 |
|
|
56
61
|
| `MarkdownText`、`MarkdownDelegateProvider`、`CodeBlock` | 不可信 GFM 与 TeX 数学、owner 委托的 HTTP(S) 导航,以及高亮代码。`CodeBlock` 可通过 `lineNumbers` 开启行号;复制的源码不含行号栏,`contentRef` 则向需要把稳定源码包装节点用作滚动区的 owner 提供该节点。调用方提供自己的语言与复制工具栏时,设置 `showHeader={false}`。 |
|
|
57
62
|
| `TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock`、`WebBlock` | 与各类工具结果意图对应的 agent 输出卡片。 |
|
|
58
|
-
| `icons/*`、`FishLogo`、`BrandWordmark`、`
|
|
59
|
-
| `
|
|
63
|
+
| `icons/*`、`FishLogo`、`BrandWordmark`、`ReferenceIconRegular`/`ReferenceIconMedium`、`LinkIconRegular`/`LinkIconMedium` | 字形与品牌标识。`LinkIconMedium` 用于 14px 的可点击链接分类及已知站点标记。 |
|
|
64
|
+
| `PermissionIconReadOnlyRegular`/`Medium`、`PermissionIconWorkspaceWriteRegular`/`Medium`、`PermissionIconFullAccessRegular`/`Medium` | 只读、工作区写入与完全访问选项使用的权限模式图形。 |
|
|
65
|
+
| `PluginArtworkTerminal`/`Loop`/`Subagent`/`Search`/`Default` | 插件管理界面使用的固定配色 36×36 插件插画;`Default` 用于没有自有插画的插件。def id 按实例生成,同一插画可在一页中安全重复。 |
|
|
66
|
+
| `FileTypeIcon`、`classifyFileType`、`fileExtension` | 按类别着色的 28px 文件或文件夹图形,以及它背后共享的不区分大小写文件名映射。代码与配置文件使用细分的全彩技术图形;链接前置图形使用 `LinkIconMedium`,图片内容使用图片预览。 |
|
|
67
|
+
| `languageForPath`、`CODE_HIGHLIGHT_EXTENSIONS`、`useCodeHighlighter` | 代码预览与 diff review 共用的文件名 grammar 选择和惰性逐行 token 高亮。 |
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
有四组容易混淆:
|
|
62
70
|
|
|
63
71
|
- **`Tag` 与 `Pill`。** 11px 胶囊尺寸的只读徽章用 `Tag`;胶囊可选中(`active` 与 `onClick`,视图切换与筛选器就是这样用的),或者必须落在 24px 文本行上时用 `Pill`——`TerminalBlock` 把退出状态渲染成静态 `Pill` 正是后一种情况。这里尺寸和是否可交互同样是判据,两者不可互换。
|
|
72
|
+
- **`Pill` 与 `SegmentedControl`。** 一排 `Pill` 是一组彼此独立的 chip——每个各自开关,可以同时激活多个。`SegmentedControl` 是在几种互斥模式中选一,画成带一个指示块的 tablist,并自带 tab 键盘模式(方向键在分段间移动,只有选中项在 Tab 序列里);模型设置页的新增卡片就用它切换两张表单。
|
|
64
73
|
- **`DisclosureRow` 与卡片。** 该行以固定 24px 把标题与内容左右排列。把名称叠在描述之上的卡片是另一种布局,属于功能包——`ui-settings-plugins` 的 `PluginCard` 是先例,并记录了原因。
|
|
65
74
|
- **`FoldToggle` 与对外导出面。** 它是包内组件,未导出;输出卡片用它做头尾折叠。
|
|
66
75
|
|
|
@@ -68,7 +77,7 @@ kind: "package-library"
|
|
|
68
77
|
|
|
69
78
|
### 控件与图标
|
|
70
79
|
|
|
71
|
-
上面的目录说明每个导出的用途;本节讲 props
|
|
80
|
+
上面的目录说明每个导出的用途;本节讲 props 本身看不出来的行为。产品图标名称不含画板尺寸,以 `Regular` 表示原始 1px 图形,以 `Medium` 表示同一几何的 1.3px 描边;`size` prop 控制渲染尺寸([决定](../../../.agents/notes/implemented/architecture/2026-09-16-size-neutral-product-icon-weights.zh.md))。每个产品、引用、链接与权限图形都会有意保留两种线重导出,即使当前产品只使用其中一种,也让调用方无需再次扩展 API 就能选择强调程度;仅填充的成对图形外观相同。`IconWarningOutlineRegular`/`Medium` 使用圆形;`IconWarningTriangleOutlineRegular`/`Medium` 使用圆角三角形。`FishLogo` 与 `BrandWordmark` 填充品牌 slot。`FileTypeIcon` 渲染传统的 28px spreadsheet、folder、HTML、image、Markdown、generic、PDF、PPT、video 与 Word 图形,并为现有 48 个代码和配置类别使用导入的方形技术图形。该导入只替换图形:资源包中额外的类别不会扩展 `CodeFileType`。`classifyFileType` 按完整文件名、前缀、后缀、可选项目上下文、扩展名的顺序匹配;React 文件名优先于 TypeScript/JavaScript,Angular 后缀优先于基础扩展名,只有传入的项目文件包含带 `flutter:` 的 `pubspec.yaml` 时 Dart 文件才使用 Flutter。Markdown 与 SVG 仍分别使用传统 Markdown 与图片图形。表格映射包括 CSV、TSV、Excel 工作簿与模板、OpenDocument 表格和 Numbers;KEY 映射为幻灯片,RTF/ODT/Pages 映射为文档。`fileExtension` 为相邻元数据 label 暴露同一套 basename 与最终点号解析。传统图形使用实色分类底板、白色标记和半透明白色折角;通用文件使用灰色底板与较深灰色折角。调用方可通过 `--dsh-file-type-icon-color` 覆盖底板颜色。全彩技术图形是明确例外,会保留其内嵌调色板。所有图形都是装饰性的,不自带 label。`LinkIconMedium` 是可点击产物链接的前置图形——地球、文件夹、代码、图片、文档或纸张,`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` 可渲染警告色的断联操作(常驻重试图形指明重试动作,断联文案由持有方提供)、共享 ongoing loading 加一至三个点以独立于 retry 时序的 500ms 节奏推进的连接中状态,或成功色的恢复状态。点击任一警告状态都会请求立即重连;没有任何悬停交互会改变文案。药丸出现时淡入、卸载前淡出 150ms,宽度随当前 label 自适应。它的持有方提供可见性、恢复驻留时间、本地化 label 与立即重连回调;该原语不使用原生 title tooltip。`useAnchoredPosition` 与 `useAnchoredMaxHeight` 让浮动面板与底部锚定浮层始终钳制在视口内并跟随锚点;`useAnchoredMaxHeight` 与 portal 模式的 `Menu` 会把 12px 的视口顶部边距加宽到框架发布的 `--dsh-frame-top-clearance`。`HoverCard` 通过指针离开宽限期让采用 portal 的预览在跨过锚点间隙时仍可触及,并可通过 `copyText` prop 提供复制按钮。其 `preview` 变体使用 anchor 或 `widthAnchorRef` 元素的宽度减去 48px,左右各内缩 24px,并在视口内放置于行的上方或下方。浮层避开框架顶部保留区,高度最多 420px,会跟随内容及 anchor 尺寸变化;Escape 或通过鼠标和键盘激活锚点可将其关闭。整个浮层的淡入和淡出各持续 100ms;关闭中的浮层停止接收指针输入,淡出后卸载。在淡出期间移回锚点可恢复显示。减少动态效果偏好会禁用过渡动画。只有启用复制时才必须提供复制标签。`Toast` 使用调用方的 `holdMs` 同时控制淡出延迟与停留加淡出的总时长。`holdMs` 未变时,父组件重渲染不会重启该生命周期;完成时调用最新回调,完全淡出的操作不能接收输入。新的组件 key 会重新开始横幅周期。`rankByName` 是 `/` 菜单命令源与 skill(技能)源共享的候选排序器:查询必须是名字的不区分大小写的有序子序列;前缀命中排最前,其次按对齐分数,再按来源顺序。 `Menu.autoFocus` 聚焦首个启用项,支持上下方向键与 Home/End 导航,并在 Escape 时聚焦 anchor 内的第一个按钮;操作菜单可显式启用。
|
|
72
81
|
|
|
73
82
|
### 渲染 agent 输出
|
|
74
83
|
|
|
@@ -91,6 +100,8 @@ kind: "package-library"
|
|
|
91
100
|
<a id="understand-the-implementation"></a>
|
|
92
101
|
## 理解实现
|
|
93
102
|
|
|
103
|
+
Menu.listClassName 独立控制菜单卡片样式,不影响入口容器,也适用于 portal 模式。
|
|
104
|
+
|
|
94
105
|
<details>
|
|
95
106
|
<summary>实现细节——点击展开</summary>
|
|
96
107
|
|
|
@@ -105,8 +116,11 @@ kind: "package-library"
|
|
|
105
116
|
| [`src/TerminalBlock.tsx`](src/TerminalBlock.tsx) | ANSI 转义解析(`anser`)与终端卡片渲染 |
|
|
106
117
|
| [`src/ReadBlock.tsx`](src/ReadBlock.tsx) / [`src/DiffBlock.tsx`](src/DiffBlock.tsx) | 读取与差异卡片 |
|
|
107
118
|
| [`src/SearchBlock.tsx`](src/SearchBlock.tsx) / [`src/WebBlock.tsx`](src/WebBlock.tsx) | 搜索与网页检索卡片 |
|
|
108
|
-
| [`src/icons/`](src/icons/) | `
|
|
119
|
+
| [`src/icons/`](src/icons/) | 与尺寸无关的 `Regular` 和 `Medium` 产品图标组件 |
|
|
120
|
+
| [`src/code-highlighting.ts`](src/code-highlighting.ts) | 共享的文件名 grammar 选择与惰性逐行高亮 |
|
|
121
|
+
| [`src/plugin-artwork.tsx`](src/plugin-artwork.tsx) | 固定配色插件插画,SVG def id 按实例生成 |
|
|
109
122
|
| [`src/useAnchoredPosition.ts`](src/useAnchoredPosition.ts) / [`src/useAnchoredMaxHeight.ts`](src/useAnchoredMaxHeight.ts) | 浮动面板与浮层几何钩子 |
|
|
123
|
+
| [`src/settings-form/`](src/settings-form/) | 设置页套件:基于设置 scope 的暂存表单模型、值字段与密文字段、表单框架 |
|
|
110
124
|
|
|
111
125
|
### 流式 Markdown
|
|
112
126
|
|
|
@@ -114,7 +128,7 @@ kind: "package-library"
|
|
|
114
128
|
|
|
115
129
|
### 几何与溢出
|
|
116
130
|
|
|
117
|
-
输出卡片共享同一套几何模型:`white-space: pre` 并横向滚动,让按列对齐的内容保持对齐;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制,长正文不会撑高卡片。`TerminalBlock` 把 ANSI 解析为 React span
|
|
131
|
+
输出卡片共享同一套几何模型:`white-space: pre` 并横向滚动,让按列对齐的内容保持对齐;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制,长正文不会撑高卡片。`TerminalBlock` 把 ANSI 解析为 React span,并带逐行列缓冲处理光标移动,遵循行内擦除、制表位与字符宽度。宿主可按表层选择退出共享几何:把 `--dsl-terminal-command-whitespace` / `--dsl-terminal-line-whitespace` 重绑为 `pre-wrap` 让命令与输出完整换行且不横向滚动;`maxLines: Infinity` 为改用 `--dsl-terminal-output-max-height` 限高滚动的宿主禁用折叠;`copyText` 覆盖复制载荷(并让控件在任何输出出现之前就保持渲染);`runStateDot: false` 在外围行已携带同一状态时省去状态点,并经 `--dsl-terminal-gutter` 收回其落区。横幅分割线跟随渲染出的正文:正在流式输出的 running 卡片像已结束卡片一样把命令与文本分隔开。
|
|
118
132
|
|
|
119
133
|
</details>
|
|
120
134
|
|
|
@@ -153,7 +167,7 @@ kind: "package-library"
|
|
|
153
167
|
- **已知站点标记是固定列表**:只有列名的主机解析为自己的标记,其余外部主机仍使用地球;要识别任意站点需要通过网络抓取它的图标。
|
|
154
168
|
- **流式期间跨边界引用解析被推迟**:定义落在增量冻结边界另一侧的引用式链接或脚注,在回复流式输出期间渲染为字面文本;定稿时的全量解析会将其解析。
|
|
155
169
|
- **长高亮 fence 会保留完整 token DOM**:流式路径避免重新解析、重新 tokenize 和 reconcile 已完成前缀,但不会丢弃旧颜色或虚拟化 token span。因此最终 DOM 数量仍随 fence 的 token 数增长;嵌套/容器内 fence 与病态的单个超长行仍走通用尾部路径。
|
|
156
|
-
-
|
|
170
|
+
- **鱼形标志是重新绘制的近似版本**:它来自字体字形,而本地设计数据无法导出其矢量几何;在获得精确导出路径前,使用手工重建版本代替。
|
|
157
171
|
- **`Pill` 与 `Input` 没有设计来源**:两个原子组件均自行定义;与其相似的侧边栏搜索字段和视图标签条由消费方组合,不是这些原子组件。
|
|
158
172
|
- **`StateDot` 没有 `Active` 变体**:支持的状态为 done、warning、ongoing、error 和 idle。
|
|
159
173
|
- **面向用户的文案必须由渲染点提供**:这些原子组件是 zero-Cordis 的,拿不到 `ctx.locale`;各功能必须通过原子组件的带类型 prop 提供完整本地化 label(见[决策](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.zh.md))。
|
|
@@ -77,14 +77,6 @@
|
|
|
77
77
|
text-align: left;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
.spinner {
|
|
81
|
-
animation: spinner-rotate 0.9s linear infinite;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@keyframes spinner-rotate {
|
|
85
|
-
to { transform: rotate(360deg); }
|
|
86
|
-
}
|
|
87
|
-
|
|
88
80
|
.dots {
|
|
89
81
|
display: inline-block;
|
|
90
82
|
width: 1em;
|
|
@@ -112,8 +104,7 @@
|
|
|
112
104
|
@media (prefers-reduced-motion: reduce) {
|
|
113
105
|
.indicator,
|
|
114
106
|
.secondDot,
|
|
115
|
-
.thirdDot
|
|
116
|
-
.spinner {
|
|
107
|
+
.thirdDot {
|
|
117
108
|
animation: none;
|
|
118
109
|
}
|
|
119
110
|
|
package/lib/HoverCard.module.css
CHANGED
|
@@ -52,3 +52,30 @@
|
|
|
52
52
|
clip: rect(0 0 0 0);
|
|
53
53
|
white-space: nowrap;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
.preview {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
padding: 0;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
border: 0;
|
|
62
|
+
border-radius: 12px;
|
|
63
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
64
|
+
color: var(--dsw-alias-label-primary);
|
|
65
|
+
box-shadow: var(--dsw-elevation-panel);
|
|
66
|
+
opacity: 1;
|
|
67
|
+
transition: opacity var(--dsh-hover-preview-fade) linear;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@starting-style {
|
|
71
|
+
.preview { opacity: 0; }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.preview[data-closing] {
|
|
75
|
+
opacity: 0;
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media (prefers-reduced-motion: reduce) {
|
|
80
|
+
.preview { transition: none; }
|
|
81
|
+
}
|
package/lib/Menu.module.css
CHANGED
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
display: inline-flex;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
/*
|
|
7
|
-
* r12, elevation-prominent (hairline stroke in shadow), 4px inset padding. */
|
|
6
|
+
/* Compact translucent dropdown card shared by feature menus. */
|
|
8
7
|
.list,
|
|
9
8
|
.submenu {
|
|
10
9
|
/* min-widths below are the design's outer card widths — include the pad. */
|
|
11
10
|
box-sizing: border-box;
|
|
12
|
-
padding:
|
|
11
|
+
padding: 3px;
|
|
13
12
|
display: flex;
|
|
14
13
|
flex-direction: column;
|
|
15
14
|
gap: 0;
|
|
16
15
|
border: 0;
|
|
17
|
-
border-radius:
|
|
16
|
+
border-radius: 16px;
|
|
18
17
|
background: var(--dsw-specific-menu);
|
|
18
|
+
backdrop-filter: var(--dsw-menu-backdrop-filter);
|
|
19
19
|
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
20
20
|
box-shadow: var(--dsw-elevation-prominent);
|
|
21
21
|
/* Elevated surface: the scrollbar thumb takes the l2 elevation tokens. The
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/* Primary
|
|
30
|
+
/* Primary cards grow with their content between compact and readable bounds. */
|
|
31
31
|
.list {
|
|
32
32
|
position: absolute;
|
|
33
33
|
top: calc(100% + 4px);
|
|
34
34
|
left: 0;
|
|
35
35
|
z-index: 100;
|
|
36
|
-
min-width:
|
|
36
|
+
min-width: 144px;
|
|
37
37
|
max-width: 360px;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -60,12 +60,13 @@
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/* Viewport fit: the card stops 12px short of the viewport's top/bottom edges
|
|
63
|
-
* (
|
|
63
|
+
* (the portal MARGIN in Menu.tsx; the top side widens to the frame's published
|
|
64
|
+
* top clearance, the macOS window strip) and taller content scrolls inside
|
|
64
65
|
* .viewport, so a pinned .footer stays visible. Menus with submenu rows skip
|
|
65
66
|
* this class — the overflow clip would crop the side card, so they rely on
|
|
66
67
|
* staying short. */
|
|
67
68
|
.scrollable {
|
|
68
|
-
max-height: calc(100vh -
|
|
69
|
+
max-height: calc(100vh - 12px - max(12px, var(--dsh-frame-top-clearance, 12px)));
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
.viewport {
|
|
@@ -84,8 +85,8 @@
|
|
|
84
85
|
flex: none;
|
|
85
86
|
display: flex;
|
|
86
87
|
flex-direction: column;
|
|
87
|
-
margin-top:
|
|
88
|
-
padding-top:
|
|
88
|
+
margin-top: 3px;
|
|
89
|
+
padding-top: 3px;
|
|
89
90
|
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -93,21 +94,20 @@
|
|
|
93
94
|
position: relative;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
/* Menu cell
|
|
97
|
-
* gap 8 between leading icon / label / trailing check. */
|
|
97
|
+
/* Menu cell: compact text, icon, and selection spacing. */
|
|
98
98
|
.item {
|
|
99
99
|
display: flex;
|
|
100
100
|
align-items: center;
|
|
101
|
-
gap:
|
|
101
|
+
gap: 6px;
|
|
102
102
|
width: 100%;
|
|
103
|
-
min-height:
|
|
104
|
-
padding: 8px
|
|
103
|
+
min-height: 34px;
|
|
104
|
+
padding: 6px 8px;
|
|
105
105
|
border: none;
|
|
106
|
-
border-radius:
|
|
106
|
+
border-radius: 8px;
|
|
107
107
|
background: transparent;
|
|
108
108
|
cursor: pointer;
|
|
109
|
-
font-size:
|
|
110
|
-
line-height:
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
line-height: 20px;
|
|
111
111
|
color: var(--dsw-alias-label-primary);
|
|
112
112
|
text-align: left;
|
|
113
113
|
}
|
|
@@ -125,33 +125,33 @@
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
.denseList .item {
|
|
128
|
-
min-height:
|
|
129
|
-
padding-block:
|
|
128
|
+
min-height: 30px;
|
|
129
|
+
padding-block: 4px;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
.denseList .label {
|
|
133
|
-
padding-block:
|
|
133
|
+
padding-block: 3px;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
.list.compactList,
|
|
137
137
|
.submenu.compactList {
|
|
138
|
-
min-width:
|
|
138
|
+
min-width: 156px;
|
|
139
139
|
padding: 2px;
|
|
140
140
|
border-radius: 7px;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
.compactList .item {
|
|
144
|
-
min-height:
|
|
145
|
-
gap:
|
|
146
|
-
padding:
|
|
144
|
+
min-height: 24px;
|
|
145
|
+
gap: 5px;
|
|
146
|
+
padding: 2px 6px;
|
|
147
147
|
border-radius: 5px;
|
|
148
|
-
font-size:
|
|
149
|
-
line-height:
|
|
148
|
+
font-size: 11px;
|
|
149
|
+
line-height: 17px;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
.compactList .itemIcon {
|
|
153
|
-
width:
|
|
154
|
-
height:
|
|
153
|
+
width: 12px;
|
|
154
|
+
height: 12px;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
.compactList .separator {
|
|
@@ -159,9 +159,9 @@
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
.compactList .label {
|
|
162
|
-
padding:
|
|
163
|
-
font-size:
|
|
164
|
-
line-height:
|
|
162
|
+
padding: 3px 6px;
|
|
163
|
+
font-size: 10px;
|
|
164
|
+
line-height: 15px;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
.item:disabled {
|
|
@@ -172,13 +172,25 @@
|
|
|
172
172
|
.itemIcon {
|
|
173
173
|
display: inline-flex;
|
|
174
174
|
flex: none;
|
|
175
|
-
width:
|
|
176
|
-
height:
|
|
175
|
+
width: 14px;
|
|
176
|
+
height: 14px;
|
|
177
177
|
align-items: center;
|
|
178
178
|
justify-content: center;
|
|
179
179
|
color: var(--dsw-alias-label-tertiary);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
.itemIcon svg,
|
|
183
|
+
.check {
|
|
184
|
+
width: 14px;
|
|
185
|
+
height: 14px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.compactList .itemIcon svg,
|
|
189
|
+
.compactList .check {
|
|
190
|
+
width: 12px;
|
|
191
|
+
height: 12px;
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
.itemLabel {
|
|
183
195
|
flex: 1;
|
|
184
196
|
min-width: 0;
|
|
@@ -223,30 +235,38 @@
|
|
|
223
235
|
|
|
224
236
|
/* Heading row: non-interactive small grey text, padding aligned with items. */
|
|
225
237
|
.label {
|
|
226
|
-
padding: 8px
|
|
227
|
-
font-size:
|
|
228
|
-
line-height:
|
|
238
|
+
padding: 6px 8px;
|
|
239
|
+
font-size: 11px;
|
|
240
|
+
line-height: 15px;
|
|
229
241
|
color: var(--dsw-alias-label-tertiary);
|
|
230
242
|
}
|
|
231
243
|
|
|
232
|
-
/* Separator cell (figma 122:9481): py 4 / px 2 around the hairline.
|
|
244
|
+
/* Separator cell (figma 122:9481): py 4 / px 2 around the hairline. l2, not
|
|
245
|
+
* l1, for the same reason as .footer: l1 is near-invisible on this surface. */
|
|
233
246
|
.separator {
|
|
234
247
|
height: 0.5px;
|
|
235
|
-
margin:
|
|
236
|
-
background: var(--dsw-alias-border-
|
|
248
|
+
margin: 3px 2px;
|
|
249
|
+
background: var(--dsw-alias-border-l2);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* A data separator directly followed by a row that starts a group draws one
|
|
253
|
+
* line, not two; a group start on the list's first row draws none. */
|
|
254
|
+
.separator + .separator,
|
|
255
|
+
.separator + .itemWrap > .separator:first-child,
|
|
256
|
+
.itemWrap:first-child > .separator:first-child {
|
|
257
|
+
display: none;
|
|
237
258
|
}
|
|
238
259
|
|
|
239
|
-
/* Nested card to the right of the parent row
|
|
260
|
+
/* Nested card to the right of the parent row.
|
|
240
261
|
* Bottom-aligned with the parent menu card (grows upward): itemWrap sits in
|
|
241
|
-
* .list's
|
|
242
|
-
* Horizontal: list pad (
|
|
243
|
-
* `100% + 6px` collapses to ~2px between outer card edges.
|
|
262
|
+
* .list's 3px pad, so bottom: -3px matches the list's outer bottom edge.
|
|
263
|
+
* Horizontal: list pad (3px) + 6px card gap = 9px past itemWrap.
|
|
244
264
|
* ::before bridges the full gap so the pointer can cross without mouseLeave. */
|
|
245
265
|
.submenu {
|
|
246
266
|
position: absolute;
|
|
247
267
|
top: auto;
|
|
248
|
-
bottom: -
|
|
249
|
-
left: calc(100% +
|
|
268
|
+
bottom: -3px;
|
|
269
|
+
left: calc(100% + 9px);
|
|
250
270
|
z-index: 101;
|
|
251
271
|
min-width: 163px;
|
|
252
272
|
}
|
|
@@ -256,6 +276,6 @@
|
|
|
256
276
|
position: absolute;
|
|
257
277
|
top: 0;
|
|
258
278
|
bottom: 0;
|
|
259
|
-
left: -
|
|
260
|
-
width:
|
|
279
|
+
left: -9px;
|
|
280
|
+
width: 9px;
|
|
261
281
|
}
|
package/lib/Modal.module.css
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Full-viewport layer (figma Mask + Dialog 451:18655): mask + centered card.
|
|
1
|
+
/* Full-viewport layer (figma Mask + Dialog 451:18655): mask + centered card.
|
|
2
|
+
24px of air on every side; the top/bottom widen to the frame's published
|
|
3
|
+
top clearance (the macOS window strip) so a tall card stays clear of it.
|
|
4
|
+
Cards size against this padding box: consumers cap growth with
|
|
5
|
+
`max-height: 100%`, never their own viewport calc. */
|
|
2
6
|
.root {
|
|
3
7
|
position: fixed;
|
|
4
8
|
inset: 0;
|
|
@@ -6,7 +10,7 @@
|
|
|
6
10
|
display: flex;
|
|
7
11
|
align-items: center;
|
|
8
12
|
justify-content: center;
|
|
9
|
-
padding: 24px;
|
|
13
|
+
padding: max(24px, var(--dsh-frame-top-clearance, 24px)) 24px;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
/* User/spec mask: rgba(0,0,0,0.24) + blur(2px) via --dsw-alias-bg-mask-1 /
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* Auto margin keeps fitting text at the left. On overflow it becomes zero,
|
|
2
|
+
so flex-end preserves the filename suffix at the right edge. */
|
|
3
|
+
.path {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex: 1 1 auto;
|
|
6
|
+
justify-content: flex-end;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
font-size: 12px;
|
|
10
|
+
white-space: nowrap;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.path[data-path-clipped] {
|
|
14
|
+
mask-image: linear-gradient(to right, transparent, black 28px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.text {
|
|
18
|
+
flex: none;
|
|
19
|
+
margin-right: auto;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.directory {
|
|
23
|
+
color: var(--dsw-alias-label-tertiary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.name {
|
|
27
|
+
color: var(--dsw-alias-label-primary);
|
|
28
|
+
}
|