@deepseek-ai/dsh-client-ui-primitives 0.1.0-rc.7 → 0.1.0-rc.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.
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: f96e931472a0946bd023b97b034643f079a67e44
6
- README.zh.md: bf47fa2bca6f685fc52ca6ff786439a8d51230fc
5
+ README.md: a675b3cd0aa9e09e243b69065110e1d2b67ff1d9
6
+ README.zh.md: aa67993ec879635fc0677501665d2e23de996dcf
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- Pure React atoms (zero cordis): StateDot, DisclosureRow, ic_ds_* icons, Button/Pill/Menu/Modal/Input, the Toast transient banner, the OnboardingSurface first-run takeover (body-portaled mask + opaque stage that holds `#root` inert for exactly its own lifetime), the markdown family (MessageText/MarkdownText/JsonBlock), the read-only JsonTree inspector, the `useAnchoredMaxHeight` hook that clamps a bottom-anchored overlay to the viewport space above its anchor (re-measured on resize, scroll, and a caller-supplied dependency), TerminalBlock, DiffBlock, ReadBlock, SearchBlock, and WebBlock.
5
+ Pure React atoms (zero cordis): StateDot, DisclosureRow, ic_ds_* icons, Button/Pill/Menu/Modal/Input, the Toast transient banner, the OnboardingSurface first-run takeover (body-portaled mask + opaque stage that holds `#root` inert for exactly its own lifetime), the markdown family (MessageText/MarkdownText/JsonBlock), the read-only JsonTree inspector, the `useAnchoredMaxHeight` hook that clamps a bottom-anchored overlay to the viewport space above its anchor (re-measured on resize, scroll, and a caller-supplied dependency), the `useAnchoredPosition` hook that holds a fixed-position floating panel under its anchor (measure, offset, clamp inside the viewport margin, re-placed on capture-phase scroll, window resize, and the panel's own size changes), TerminalBlock, DiffBlock, ReadBlock, SearchBlock, and WebBlock.
6
6
 
7
7
  ## Hover cards
8
8
 
@@ -18,7 +18,7 @@ Pure React atoms (zero cordis): StateDot, DisclosureRow, ic_ds_* icons, Button/P
18
18
 
19
19
  ## Terminal output
20
20
 
21
- `TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with the `anser` runtime dependency into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md).
21
+ `TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with `anser` (bundled into this package's browser artifact) into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md).
22
22
 
23
23
  ## Read rendering
24
24
 
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 纯 React 原子组件(零 cordis):StateDot、DisclosureRow、ic_ds_* 图标、Button/Pill/Menu/Modal/Input、Toast 短时横幅、OnboardingSurface 首次使用接管层(portal 到 body 的遮罩加不透明展示层,在且仅在自身生命周期内保持 `#root` 为 `inert`)、markdown 家族(MessageText/MarkdownText/JsonBlock)、只读 JsonTree 检查器、`useAnchoredMaxHeight` 钩子(把底部锚定的浮层高度收敛到锚点上方的视口空间,并在 resize、scroll 与调用方提供的依赖变化时重新测量)、TerminalBlock、DiffBlock、ReadBlock、SearchBlock,以及 WebBlock。
5
+ 纯 React 原子组件(零 cordis):StateDot、DisclosureRow、ic_ds_* 图标、Button/Pill/Menu/Modal/Input、Toast 短时横幅、OnboardingSurface 首次使用接管层(portal 到 body 的遮罩加不透明展示层,在且仅在自身生命周期内保持 `#root` 为 `inert`)、markdown 家族(MessageText/MarkdownText/JsonBlock)、只读 JsonTree 检查器、`useAnchoredMaxHeight` 钩子(把底部锚定的浮层高度收敛到锚点上方的视口空间,并在 resize、scroll 与调用方提供的依赖变化时重新测量)、`useAnchoredPosition` 钩子(让固定定位的浮动面板跟住锚点:测量、偏移、按视口边距钳制,并在捕获阶段滚动、窗口缩放与面板自身尺寸变化时重新定位)、TerminalBlock、DiffBlock、ReadBlock、SearchBlock,以及 WebBlock。
6
6
 
7
7
  ## 悬浮卡片
8
8
 
@@ -18,7 +18,7 @@
18
18
 
19
19
  ## 终端输出
20
20
 
21
- `TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过运行时依赖 `anser` 解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。
21
+ `TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过 `anser`(打进本包浏览器产物)解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。
22
22
 
23
23
  ## Read 渲染
24
24
 
@@ -0,0 +1,79 @@
1
+ /* Capsule geometry from the figma Button component (1:155 instances:
2
+ * h36, pad 14/7, gap 4, r18; the wide New Session form is r24 at h38 —
3
+ * owners with the wide form set their own radius/width). */
4
+ .button {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ gap: 4px;
9
+ border: none;
10
+ border-radius: 18px;
11
+ cursor: pointer;
12
+ font-size: 14px;
13
+ line-height: 22px;
14
+ color: var(--dsw-alias-label-primary);
15
+ background: transparent;
16
+ padding: 0 14px;
17
+ }
18
+
19
+ .button:disabled {
20
+ cursor: not-allowed;
21
+ opacity: 0.4;
22
+ }
23
+
24
+ .md {
25
+ height: 36px;
26
+ }
27
+
28
+ /* Compact height for dense rows; no dedicated figma node (Icon_container
29
+ * 28x28 is the icon-only form) — geometry is ours. */
30
+ .sm {
31
+ height: 28px;
32
+ font-size: 12px;
33
+ line-height: 18px;
34
+ padding: 0 10px;
35
+ border-radius: 14px;
36
+ }
37
+
38
+ .primary {
39
+ background: var(--dsw-alias-button-primary-fill);
40
+ color: var(--dsw-alias-label-primary-foreground);
41
+ }
42
+
43
+ .primary:hover:not(:disabled) {
44
+ background: var(--dsw-alias-button-primary-hover);
45
+ }
46
+
47
+ .ghost:hover:not(:disabled) {
48
+ background: var(--dsw-alias-interactive-bg-hover);
49
+ }
50
+
51
+ .ghost:active:not(:disabled) {
52
+ background: var(--dsw-alias-interactive-bg-active);
53
+ }
54
+
55
+ /* Dialog Cancel (figma 451:18655): bordered capsule on transparent fill. */
56
+ .outline {
57
+ border: 1px solid var(--dsw-alias-border-l2);
58
+ background: transparent;
59
+ }
60
+
61
+ .outline:hover:not(:disabled) {
62
+ background: var(--dsw-alias-interactive-bg-hover);
63
+ }
64
+
65
+ .toolbar {
66
+ background: var(--dsw-alias-button-tool-bar-fill);
67
+ }
68
+
69
+ .toolbar:hover:not(:disabled) {
70
+ background: var(--dsw-alias-button-tool-bar-hover);
71
+ }
72
+
73
+ .icon {
74
+ display: inline-flex;
75
+ width: 16px;
76
+ height: 16px;
77
+ align-items: center;
78
+ justify-content: center;
79
+ }
@@ -0,0 +1,13 @@
1
+ .banner {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ right: 0;
6
+ z-index: 100;
7
+ padding: 4px 12px;
8
+ text-align: center;
9
+ font-size: 12px;
10
+ line-height: 18px;
11
+ background: var(--dsw-alias-state-error-primary);
12
+ color: var(--dsw-alias-label-primary-foreground);
13
+ }
@@ -0,0 +1,107 @@
1
+ /* Geometry mirrors CodeBlock/TerminalBlock (12px radius, code-block surface +
2
+ banner row, markdown code-block font) so a diff card reads as one family with
3
+ a fenced block and a terminal card. The deliberate divergence, shared with
4
+ TerminalBlock: the body keeps `white-space: pre` and scrolls horizontally,
5
+ because folding a source line destroys the indentation a diff is read by. */
6
+
7
+ .block {
8
+ --dsl-diff-radius: 12px;
9
+ --dsl-diff-line-height: 22px;
10
+
11
+ position: relative;
12
+ margin: 16px 0;
13
+ color: var(--dsw-alias-label-primary);
14
+ background: var(--dsw-alias-markdown-code-block);
15
+ border-radius: var(--dsl-diff-radius);
16
+ }
17
+
18
+ /* The copy control floats in the top-right corner over the body, so the card
19
+ has no empty banner row above its first diff line (the TUI diff card has no
20
+ banner either — only the footer). The block is position: relative, so this
21
+ anchors to the card. */
22
+ .copyButton {
23
+ position: absolute;
24
+ top: 8px;
25
+ right: 12px;
26
+ z-index: 1;
27
+ background-color: transparent;
28
+ border: none;
29
+ padding: 0;
30
+ margin: 0;
31
+ color: var(--dsw-alias-label-secondary);
32
+ cursor: pointer;
33
+ font: var(--dsw-font-xs-13);
34
+ }
35
+
36
+ .body {
37
+ padding: 12px 14px;
38
+ font: var(--dsw-font-markdown-code-block);
39
+ overflow-x: auto;
40
+ overflow-y: hidden;
41
+ }
42
+
43
+ /* No wrapping, no word-break: a diff is read by its indentation. */
44
+ .line {
45
+ min-height: var(--dsl-diff-line-height);
46
+ white-space: pre;
47
+ }
48
+
49
+ /* A file header: the path in the primary tone, set apart by weight. The copy
50
+ button floats over this first row's top-right corner, so reserve space at the
51
+ line's end for it — a long path scrolls under the button otherwise, and the
52
+ button's hit area would eat clicks on the path's tail. */
53
+ .path {
54
+ color: var(--dsw-alias-label-primary);
55
+ font-weight: 600;
56
+ padding-right: 56px;
57
+ }
58
+
59
+ /* A same-file second hunk's separator (a scattered edit), in the dim tone. */
60
+ .gap {
61
+ color: var(--dsw-alias-label-tertiary);
62
+ }
63
+
64
+ /* The diff's own meaning-carrying colors: removed on the error token, added on
65
+ the success token. A `- `/`+ ` prefix is drawn here so a copied line and the
66
+ shown line agree, and so the sign reads without relying on color alone. */
67
+ .del::before {
68
+ content: '- ';
69
+ color: var(--dsw-alias-state-error-primary);
70
+ }
71
+
72
+ .del {
73
+ color: var(--dsw-alias-state-error-primary);
74
+ }
75
+
76
+ .add::before {
77
+ content: '+ ';
78
+ color: var(--dsw-alias-state-success-primary);
79
+ }
80
+
81
+ .add {
82
+ color: var(--dsw-alias-state-success-primary);
83
+ }
84
+
85
+ .expand {
86
+ display: block;
87
+ width: 100%;
88
+ padding: 0;
89
+ border: none;
90
+ background-color: transparent;
91
+ color: var(--dsw-alias-label-tertiary);
92
+ cursor: pointer;
93
+ font: inherit;
94
+ text-align: left;
95
+ }
96
+
97
+ .expand:hover {
98
+ color: var(--dsw-alias-label-secondary);
99
+ }
100
+
101
+ /* The change summary, dim under the body: `└ +A -R · N file(s)`, the same
102
+ footer the TUI transcript's diff card draws. */
103
+ .footer {
104
+ padding: 0 14px 12px;
105
+ font: var(--dsw-font-markdown-code-block);
106
+ color: var(--dsw-alias-label-tertiary);
107
+ }
@@ -0,0 +1,69 @@
1
+ /* Shared disclosure header: [16px leading] gap 6 [title 14/24]. */
2
+
3
+ .root {
4
+ display: flex;
5
+ flex-direction: column;
6
+ width: 100%;
7
+ min-width: 0;
8
+ }
9
+
10
+ .row {
11
+ position: relative;
12
+ overflow: hidden;
13
+ display: flex;
14
+ align-items: center;
15
+ height: 24px;
16
+ min-width: 0;
17
+ }
18
+
19
+ .row[data-expandable] {
20
+ cursor: pointer;
21
+ }
22
+
23
+ .leading {
24
+ position: relative;
25
+ flex: none;
26
+ width: 16px;
27
+ height: 16px;
28
+ display: inline-flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ margin-right: 6px;
32
+ padding: 0;
33
+ border: none;
34
+ background: none;
35
+ color: var(--dsw-alias-label-tertiary);
36
+ }
37
+
38
+ button.leading {
39
+ cursor: pointer;
40
+ }
41
+
42
+ .iconIdle {
43
+ display: inline-flex;
44
+ opacity: 1;
45
+ transition: opacity 100ms ease;
46
+ }
47
+
48
+ .chevronHover {
49
+ position: absolute;
50
+ inset: 0;
51
+ margin: auto;
52
+ opacity: 0;
53
+ transition: opacity 100ms ease;
54
+ }
55
+
56
+ .row:hover .iconIdle {
57
+ opacity: 0;
58
+ }
59
+
60
+ .row:hover .chevronHover {
61
+ opacity: 1;
62
+ }
63
+
64
+ .title {
65
+ flex: none;
66
+ font-size: 14px;
67
+ line-height: 24px;
68
+ color: var(--dsw-alias-label-secondary);
69
+ }
@@ -0,0 +1,54 @@
1
+ /* Block, not inline-flex: consumers wrap full-width list rows and an
2
+ * inline wrapper would shrink them; the card still measures this rect. */
3
+ .root {
4
+ position: relative;
5
+ display: block;
6
+ }
7
+
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.
11
+ * Hit-testable on purpose: resting the pointer on the card holds it open
12
+ * (HoverCard's grace close), which a `pointer-events: none` card cannot do. */
13
+ .card {
14
+ --dsw-hovercard-bg: #2C2C2E;
15
+ position: fixed;
16
+ z-index: 100;
17
+ box-sizing: border-box;
18
+ width: 244px;
19
+ padding: 12px 16px;
20
+ border-radius: 12px;
21
+ background: var(--dsw-hovercard-bg);
22
+ box-shadow: var(--dsw-shadow-lv3);
23
+ }
24
+
25
+ .copyable {
26
+ cursor: pointer;
27
+ }
28
+
29
+ .copyable:focus-visible {
30
+ outline: 2px solid var(--dsw-alias-state-business-primary);
31
+ outline-offset: 2px;
32
+ }
33
+
34
+ .feedback {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ }
39
+
40
+ .copied {
41
+ color: #FFFFFF;
42
+ font-size: 14px;
43
+ line-height: 20px;
44
+ text-align: center;
45
+ }
46
+
47
+ .status {
48
+ position: absolute;
49
+ width: 1px;
50
+ height: 1px;
51
+ overflow: hidden;
52
+ clip: rect(0 0 0 0);
53
+ white-space: nowrap;
54
+ }
@@ -0,0 +1,38 @@
1
+ .wrap {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 6px;
5
+ height: 32px;
6
+ padding: 0 8px;
7
+ border: 1px solid var(--dsw-alias-border-l2);
8
+ border-radius: 8px;
9
+ background: var(--dsw-alias-bg-layer-1);
10
+ }
11
+
12
+ .wrap:focus-within {
13
+ border-color: var(--dsw-alias-brand-primary);
14
+ }
15
+
16
+ .icon {
17
+ display: inline-flex;
18
+ width: 16px;
19
+ height: 16px;
20
+ align-items: center;
21
+ justify-content: center;
22
+ color: var(--dsw-alias-label-tertiary);
23
+ }
24
+
25
+ .input {
26
+ flex: 1;
27
+ min-width: 0;
28
+ border: none;
29
+ outline: none;
30
+ background: transparent;
31
+ font-size: 14px;
32
+ line-height: 22px;
33
+ color: var(--dsw-alias-label-primary);
34
+ }
35
+
36
+ .input::placeholder {
37
+ color: var(--dsw-alias-label-dimmed);
38
+ }
@@ -0,0 +1,222 @@
1
+ .root {
2
+ --json-tree-property: #881391;
3
+ --json-tree-string: #c41a16;
4
+ --json-tree-number: #1c00cf;
5
+ --json-tree-keyword: #1c00cf;
6
+ --json-tree-punctuation: #202124;
7
+ --json-tree-icon: #5f6368;
8
+ --json-tree-hover: rgb(60 64 67 / 4%);
9
+
10
+ min-width: 0;
11
+ overflow: auto;
12
+ position: relative;
13
+ color: var(--dsw-alias-label-primary);
14
+ background: var(--dsw-alias-bg-layer-1);
15
+ font: 12px/16px var(--ds-font-family-code);
16
+ overscroll-behavior-x: contain;
17
+ overscroll-behavior-y: auto;
18
+ }
19
+
20
+ :global(body[data-ds-dark-theme]) .root {
21
+ --json-tree-property: #5db0d7;
22
+ --json-tree-string: #f28b82;
23
+ --json-tree-number: #99c8ff;
24
+ --json-tree-keyword: #99c8ff;
25
+ --json-tree-punctuation: #e8eaed;
26
+ --json-tree-icon: #9aa0a6;
27
+ --json-tree-hover: rgb(232 234 237 / 5%);
28
+ }
29
+
30
+ .container {
31
+ box-sizing: border-box;
32
+ width: max-content;
33
+ min-width: 100%;
34
+ margin: 0;
35
+ padding: 6px 8px 8px;
36
+ white-space: pre;
37
+ }
38
+
39
+ .expandedTopLevel {
40
+ box-sizing: border-box;
41
+ width: max-content;
42
+ min-width: 100%;
43
+ padding: 6px 8px 8px 14px;
44
+ }
45
+
46
+ .expandedTopLevel:has(> .topLevelBracket[data-json-root-row]:hover),
47
+ .expandedTopLevel:has(> .topLevelBracket[data-json-root-row][data-json-copy-active]) {
48
+ background: var(--json-tree-hover);
49
+ }
50
+
51
+ .expandedTopLevelContainer {
52
+ padding: 0;
53
+ }
54
+
55
+ .row.topLevelBracket {
56
+ margin-left: 0;
57
+ padding-left: 0;
58
+ }
59
+
60
+ .children {
61
+ margin: 0;
62
+ padding: 0;
63
+ list-style: none;
64
+ }
65
+
66
+ .row {
67
+ position: relative;
68
+ box-sizing: border-box;
69
+ min-width: 100%;
70
+ min-height: 16px;
71
+ margin: 0;
72
+ padding: 0 0 0 10px;
73
+ list-style: none;
74
+ }
75
+
76
+ .row:not(.topLevelBracket):hover:not(:has(.row:hover))::after,
77
+ .row:not(.topLevelBracket)[data-json-copy-active]::after,
78
+ .row:has(> .expander:focus-visible)::after {
79
+ position: absolute;
80
+ z-index: 0;
81
+ top: 0;
82
+ right: 0;
83
+ left: 0;
84
+ height: 16px;
85
+ background: var(--json-tree-hover);
86
+ content: '';
87
+ pointer-events: none;
88
+ }
89
+
90
+ .row > span:not(.expander) {
91
+ position: relative;
92
+ z-index: 1;
93
+ }
94
+
95
+ .label {
96
+ margin-right: 3px;
97
+ color: var(--json-tree-property);
98
+ font-weight: 400;
99
+ }
100
+
101
+ .clickableLabel {
102
+ cursor: pointer;
103
+ }
104
+
105
+ .stringValue {
106
+ color: var(--json-tree-string);
107
+ }
108
+
109
+ .numberValue {
110
+ color: var(--json-tree-number);
111
+ }
112
+
113
+ .keywordValue {
114
+ color: var(--json-tree-keyword);
115
+ }
116
+
117
+ .otherValue {
118
+ color: var(--dsw-alias-label-secondary);
119
+ }
120
+
121
+ .punctuation {
122
+ color: var(--json-tree-punctuation);
123
+ }
124
+
125
+ .preview {
126
+ color: var(--json-tree-punctuation);
127
+ }
128
+
129
+ .previewProperty {
130
+ color: var(--json-tree-punctuation);
131
+ }
132
+
133
+ .previewEllipsis {
134
+ color: var(--dsw-alias-label-tertiary);
135
+ }
136
+
137
+ .copyAnchor {
138
+ position: fixed;
139
+ z-index: 3;
140
+ display: inline-flex;
141
+ }
142
+
143
+ .copyButton {
144
+ box-sizing: border-box;
145
+ display: inline-flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: 20px;
149
+ height: 16px;
150
+ margin: 0;
151
+ padding: 0;
152
+ border: 0;
153
+ border-radius: 3px;
154
+ color: var(--dsw-alias-label-secondary);
155
+ background: var(--dsw-alias-bg-layer-1);
156
+ box-shadow: -5px 0 5px var(--dsw-alias-bg-layer-1);
157
+ cursor: pointer;
158
+ }
159
+
160
+ .copyButton:hover {
161
+ color: var(--dsw-alias-label-primary);
162
+ background: var(--dsw-alias-interactive-bg-hover);
163
+ }
164
+
165
+ .copyButton:focus-visible {
166
+ outline: 1px solid var(--dsw-alias-state-business-primary);
167
+ outline-offset: -1px;
168
+ }
169
+
170
+ .copyButton[data-state='failed'] {
171
+ color: var(--dsw-alias-state-error-primary);
172
+ }
173
+
174
+ .expander {
175
+ position: absolute;
176
+ z-index: 2;
177
+ top: 0;
178
+ left: 0;
179
+ display: inline-flex;
180
+ align-items: center;
181
+ justify-content: flex-start;
182
+ box-sizing: border-box;
183
+ width: 8px;
184
+ height: 16px;
185
+ margin: 0;
186
+ color: var(--json-tree-icon);
187
+ cursor: pointer;
188
+ user-select: none;
189
+ }
190
+
191
+ .expander::before {
192
+ width: 0;
193
+ height: 0;
194
+ border-top: 4px solid transparent;
195
+ border-bottom: 4px solid transparent;
196
+ border-left: 6px solid currentColor;
197
+ content: '';
198
+ transform: scale(0.75);
199
+ transform-origin: 33.333% center;
200
+ }
201
+
202
+ .collapseIcon::before {
203
+ transform: rotate(90deg) scale(0.75);
204
+ }
205
+
206
+ .expander:hover {
207
+ color: var(--dsw-alias-label-primary);
208
+ }
209
+
210
+ .expander:focus-visible {
211
+ outline: none;
212
+ }
213
+
214
+ .collapsedContent {
215
+ margin: 0 1px;
216
+ color: var(--json-tree-punctuation);
217
+ cursor: pointer;
218
+ }
219
+
220
+ .collapsedContent::after {
221
+ content: '…';
222
+ }