@deepseek-ai/dsh-client-ui-primitives 0.0.1-rc.1 → 0.0.1-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +5 -1
- package/README.zh.md +5 -1
- package/lib/index.js +149 -21
- package/lib/invariant.js +1 -1
- package/lib/types/Toast.d.ts +24 -0
- package/lib/types/Tooltip.d.ts +7 -3
- package/lib/types/icons/index.d.ts +2 -0
- package/lib/types/index.d.ts +1 -0
- package/package.json +3 -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: f96e931472a0946bd023b97b034643f079a67e44
|
|
6
|
+
README.zh.md: 0db2ac1bccbdc4793c488e1ce621ee5c674d9895
|
package/README.md
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
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 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), TerminalBlock, DiffBlock, ReadBlock, SearchBlock, and WebBlock.
|
|
6
6
|
|
|
7
7
|
## Hover cards
|
|
8
8
|
|
|
9
9
|
`HoverCard` keeps its portaled preview reachable across the anchor gap with a pointer-leave grace. A consumer may also pass `copyText`: the card then exposes button semantics for pointer and keyboard activation, includes that value after the `copyLabel` prefix in its accessible name, writes the exact value through the package clipboard helper, and temporarily replaces its content with `copiedLabel` only after the host accepts the write. A non-collapsed text selection intersecting the card suppresses pointer-click activation, while success feedback retains the original card height and clears when the card closes or after one second. `copyLabel` and `copiedLabel` are label props because this zero-cordis atom cannot read the application locale; omitting `copyText` preserves the read/select-only card. Historical rationale: [the archived hover-card copy note](../../../.agents/notes/archived/feature/2026-07-31-hover-card-click-copy.md).
|
|
10
10
|
|
|
11
|
+
## Toast
|
|
12
|
+
|
|
13
|
+
`Toast` is the transient top banner: it slides in, holds at full opacity for three seconds, fades over one second, then calls `onDone` so the owner can unmount it. It renders `role="alert"` with an optional leading icon slot and takes its copy as a required prop (zero-cordis: the owner localizes). It body-portals with `pointer-events: none`, sits 120px from the viewport top, and centers horizontally over the optional `anchor` element (re-measured on window resizes) — the composer passes its card so the banner centers over the chat column rather than the whole window — falling back to the viewport center without one. Re-showing the same message requires a remount — owners key the element by a per-show sequence so an identical repeated message restarts the hold-and-fade cycle instead of silently reusing the faded banner. Under `prefers-reduced-motion: reduce` the slide-in is dropped and only the delayed fade remains. It layers above the ui-attachment image lightbox so a failure reported during a preview stays readable.
|
|
14
|
+
|
|
11
15
|
## Markdown rendering
|
|
12
16
|
|
|
13
17
|
`MarkdownText` renders GFM and `$…$`, `$$…$$`, `\(…\)`, and `\[…\]` TeX math from untrusted assistant output through React elements, with math typeset by KaTeX and trusted commands disabled; block-level same-line `$$…$$` is display math, including `\tag{}`. A narrow micromark extension lets asterisk strong emphasis ending in punctuation close before adjacent CJK text, where prose normally omits the whitespace CommonMark requires; single-asterisk emphasis, non-CJK adjacency, escapes, code, and math retain upstream parsing. It omits raw HTML, neutralizes relative and non-HTTP(S)/mailto links, opens HTTP(S) links with safe external-link attributes, and renders absolute HTTP(S) images without a referrer; relative paths, absolute local paths, `file:` URLs, and unsupported schemes retain their alt text. Inline code whose complete value is an absolute HTTP(S) URL keeps its code styling and gains the same safe external anchor; commands, partial URLs, other schemes, and fenced code remain inert. An optional `fileMentions` resolver lets the owning view link inline code that names a real file: the token keeps code styling and gains a button wired to the resolved opener, with the resolver's accessible label and full-path `title`. The renderer never guesses at what looks like a path — an unresolved token stays inert, mentions apply to settled renders only (the streaming cache must not bake in handlers that could go stale), and a token inside an anchor stays inert because a button cannot nest there. While a reply streams, `MarkdownText` parses incrementally: all but the trailing two blocks freeze as cached React elements and only the source tail behind them re-parses per chunk, so per-chunk work tracks the tail instead of the whole reply ([mechanism and DOM-parity contract](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.md)). `MessageText` remains the literal-text primitive for user-authored content. `extractMarkdownPlainText` removes Markdown presentation markup for compact labels while preserving raw HTML as literal text. Element spacing, responsive images, tables, links, and inline code use the same `--dsw-alias-markdown-*` / `--dsw-font-markdown-*` tokens as deepsuite `@deepseek/md`. Fenced blocks render through `CodeBlock` (language banner, copy control, shiki for the registered grammars).
|
package/README.zh.md
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
纯 React 原子组件(零 cordis):StateDot、DisclosureRow、ic_ds_* 图标、Button/Pill/Menu/Modal/Input、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 与调用方提供的依赖变化时重新测量)、TerminalBlock、DiffBlock、ReadBlock、SearchBlock,以及 WebBlock。
|
|
6
6
|
|
|
7
7
|
## 悬浮卡片
|
|
8
8
|
|
|
9
9
|
`HoverCard` 通过指针离开宽限期,使采用 portal 渲染的预览在跨过与锚点之间的间隙时仍可触及。消费方还可传入 `copyText`:此时卡片为指针与键盘激活提供按钮语义,其无障碍名称会在 `copyLabel` 前缀后包含该值,通过包内剪贴板辅助函数原样写入该值,并且只有宿主接受写入后,才会临时将内容替换为 `copiedLabel`。与卡片相交的非折叠文本选区会阻止指针点击激活;成功反馈保持卡片原有高度,并随卡片关闭或在一秒后清除。`copyLabel` 和 `copiedLabel` 采用 label prop,是因为这个 zero-cordis 原子组件无法读取应用 locale;省略 `copyText` 时,卡片维持只读且可选择文本的行为。历史依据见[已归档的悬浮卡片复制 Agent Note](../../../.agents/notes/archived/feature/2026-07-31-hover-card-click-copy.md)。
|
|
10
10
|
|
|
11
|
+
## Toast
|
|
12
|
+
|
|
13
|
+
`Toast` 是顶部的短时横幅:滑入后满不透明度停留三秒,再用一秒淡出,随后调用 `onDone` 由持有方卸载。它渲染 `role="alert"`,带可选的前置图标插槽,文案是必填 prop(零 cordis,由持有方本地化)。它经 body portal 渲染且 `pointer-events: none`,距视口顶部 120px,水平中心跟随可选的 `anchor` 元素(窗口尺寸变化时重测)——composer 传入自己的卡片,横幅因此在聊天列而非整个窗口上居中——不传则回退到视口居中。重复展示同一条消息需要重新挂载,持有方用每次展示递增的序号作为 key,让相同文案重新走完停留与淡出,而不是静默复用已淡出的横幅。`prefers-reduced-motion: reduce` 下去掉滑入,只保留延迟淡出。它的层级高于 ui-attachment 的图片灯箱,预览打开时报出的失败仍然可读。
|
|
14
|
+
|
|
11
15
|
## Markdown 渲染
|
|
12
16
|
|
|
13
17
|
`MarkdownText` 通过 React 元素渲染来自不受信任 assistant 输出的 GFM 与 `$…$`、`$$…$$`、`\(…\)` 和 `\[…\]` TeX 公式,公式由 KaTeX 排版并禁用受信任命令;块级同一行 `$$…$$` 是显示公式并支持 `\tag{}`。一个小范围的 micromark 扩展允许由星号标记、以标点结尾的粗体在紧邻的 CJK 文本前闭合,以适应 CJK 文本通常省略 CommonMark 所要求空格的写法;单星号强调、紧邻非 CJK 文本的情况、转义、代码与数学公式仍沿用上游解析行为。它会省略原始 HTML,使相对链接及非 HTTP(S)/mailto 链接失效,以安全的外部链接属性打开 HTTP(S) 链接,并在不发送 referrer 的情况下渲染采用绝对 HTTP(S) URL 的图片;相对路径、绝对本地路径、`file:` URL 与不受支持的 scheme 会保留其 alt 文本。完整内容为绝对 HTTP(S) URL 的行内代码会保留代码样式,并获得同样安全的外部链接;命令、非完整 URL、其他 scheme 与围栏代码仍不会成为链接。可选的 `fileMentions` 解析器让持有该组件的视图为命名真实文件的行内代码添加可点击入口:token 保留代码样式,并获得一个连接到解析所得 opener 的按钮,按钮带有解析器提供的无障碍标签和以完整路径为值的 `title`。渲染器绝不猜测哪些内容像路径:未解析的 token 保持不可交互;文件提及仅应用于已定稿的渲染(流式缓存不得固化可能过期的 handler);锚点内的 token 也保持不可交互,因为按钮不能嵌套其中。回复流式输出期间,`MarkdownText` 增量解析:除末尾两个块外全部冻结为缓存的 React 元素,每个分片只重新解析其后的源文本尾部,因此每分片的工作量跟随尾部而非整个回复([机制与 DOM 一致性约定](../../../.agents/notes/implemented/architecture/2026-08-06-web-markdown-incremental-ast-renderer.md))。`MessageText` 仍是用户创作内容使用的字面文本原语。`extractMarkdownPlainText` 会移除 Markdown 呈现标记以用于紧凑标签,同时将原始 HTML 保留为字面文本。元素间距、响应式图片、表格、链接与行内代码使用与 deepsuite `@deepseek/md` 相同的 `--dsw-alias-markdown-*` / `--dsw-font-markdown-*` token。围栏代码块通过 `CodeBlock` 渲染(语言横幅、复制控件,以及对已注册语法使用 shiki)。
|
package/lib/index.js
CHANGED
|
@@ -499,6 +499,67 @@ const IconThinkOutline16 = ({ size = 16, className }) => jsxs("svg", {
|
|
|
499
499
|
fill: "currentColor"
|
|
500
500
|
})]
|
|
501
501
|
});
|
|
502
|
+
/** ic_ds_agent_preset_outline_16 (figma extract): node interiors knock out to transparency via mask, so the glyph sits on any fill. */
|
|
503
|
+
const IconAgentPresetOutline16 = ({ size = 16, className }) => jsxs("svg", {
|
|
504
|
+
width: size,
|
|
505
|
+
height: size,
|
|
506
|
+
className,
|
|
507
|
+
viewBox: "0 0 16 16",
|
|
508
|
+
fill: "none",
|
|
509
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
510
|
+
children: [
|
|
511
|
+
jsxs("mask", {
|
|
512
|
+
id: "mask0_agent_preset_16",
|
|
513
|
+
maskUnits: "userSpaceOnUse",
|
|
514
|
+
x: "0",
|
|
515
|
+
y: "0",
|
|
516
|
+
width: "16",
|
|
517
|
+
height: "16",
|
|
518
|
+
children: [
|
|
519
|
+
jsx("rect", {
|
|
520
|
+
width: "16",
|
|
521
|
+
height: "16",
|
|
522
|
+
fill: "white"
|
|
523
|
+
}),
|
|
524
|
+
jsx("circle", {
|
|
525
|
+
cx: "7.9995",
|
|
526
|
+
cy: "3.28319",
|
|
527
|
+
r: "1.712",
|
|
528
|
+
fill: "black"
|
|
529
|
+
}),
|
|
530
|
+
jsx("circle", {
|
|
531
|
+
cx: "3.51122",
|
|
532
|
+
cy: "11.3855",
|
|
533
|
+
r: "1.712",
|
|
534
|
+
fill: "black"
|
|
535
|
+
}),
|
|
536
|
+
jsx("circle", {
|
|
537
|
+
cx: "12.4878",
|
|
538
|
+
cy: "11.3855",
|
|
539
|
+
r: "1.712",
|
|
540
|
+
fill: "black"
|
|
541
|
+
})
|
|
542
|
+
]
|
|
543
|
+
}),
|
|
544
|
+
jsx("path", {
|
|
545
|
+
mask: "url(#mask0_agent_preset_16)",
|
|
546
|
+
d: "M12.2881 11.0425C12.6002 11.3723 13.0413 11.5786 13.5312 11.5786L13.5342 11.5776C13.1476 12.3233 12.6119 12.9785 11.9639 13.5005C10.9327 14.3309 9.6199 14.8286 8.19336 14.8286C7.29864 14.8285 6.45056 14.6313 5.6875 14.2808C6.08309 14.0281 6.36707 13.6189 6.45215 13.1392C6.99022 13.3561 7.57767 13.476 8.19336 13.4761C9.30019 13.4761 10.3157 13.0915 11.1152 12.4478C11.5935 12.0626 11.9924 11.5848 12.2881 11.0425ZM4.14746 4.36475C4.25569 4.83228 4.55488 5.2247 4.95898 5.4585C4.07956 6.30639 3.53144 7.49605 3.53125 8.81396C3.53125 9.69534 3.77613 10.5202 4.20117 11.2231C3.74959 11.3817 3.38395 11.7232 3.19531 12.1597C2.5541 11.2032 2.17969 10.052 2.17969 8.81396C2.17989 7.05087 2.93868 5.4646 4.14746 4.36475ZM8.19336 2.80029C8.85717 2.80029 9.49784 2.90834 10.0967 3.10791C12.3237 3.85044 13.9725 5.86061 14.1846 8.28369C13.9832 8.20048 13.7627 8.15382 13.5312 8.15381C13.2802 8.15381 13.042 8.20907 12.8271 8.30615C12.6281 6.47264 11.3666 4.95616 9.66895 4.39014C9.2063 4.236 8.70989 4.15186 8.19336 4.15186C7.96112 4.15189 7.7329 4.16981 7.50977 4.20264C7.51947 4.12886 7.52637 4.05348 7.52637 3.97705C7.52628 3.56604 7.3811 3.18914 7.13965 2.89404C7.48183 2.83352 7.83381 2.80033 8.19336 2.80029Z",
|
|
547
|
+
fill: "currentColor"
|
|
548
|
+
}),
|
|
549
|
+
jsx("path", {
|
|
550
|
+
d: "M9.1123 3.28271C9.11205 2.66858 8.61322 2.17041 7.99902 2.17041C7.38504 2.17067 6.88697 2.66874 6.88672 3.28271C6.88672 3.89691 7.38489 4.39574 7.99902 4.396C8.61338 4.396 9.1123 3.89707 9.1123 3.28271ZM10.3115 3.28271C10.3115 4.55981 9.27612 5.59521 7.99902 5.59521C6.72214 5.59496 5.6875 4.55965 5.6875 3.28271C5.68776 2.00599 6.7223 0.971447 7.99902 0.971191C9.27596 0.971191 10.3113 2.00584 10.3115 3.28271Z",
|
|
551
|
+
fill: "currentColor"
|
|
552
|
+
}),
|
|
553
|
+
jsx("path", {
|
|
554
|
+
d: "M4.62402 11.385C4.62377 10.7709 4.12494 10.2727 3.51074 10.2727C2.89676 10.273 2.39869 10.771 2.39844 11.385C2.39844 11.9992 2.89661 12.498 3.51074 12.4983C4.1251 12.4983 4.62402 11.9994 4.62402 11.385ZM5.82324 11.385C5.82324 12.6621 4.78784 13.6975 3.51074 13.6975C2.23386 13.6973 1.19922 12.6619 1.19922 11.385C1.19947 10.1083 2.23402 9.07374 3.51074 9.07349C4.78768 9.07349 5.82299 10.1081 5.82324 11.385Z",
|
|
555
|
+
fill: "currentColor"
|
|
556
|
+
}),
|
|
557
|
+
jsx("path", {
|
|
558
|
+
d: "M13.6006 11.385C13.6003 10.7709 13.1015 10.2727 12.4873 10.2727C11.8733 10.273 11.3753 10.771 11.375 11.385C11.375 11.9992 11.8732 12.498 12.4873 12.4983C13.1017 12.4983 13.6006 11.9994 13.6006 11.385ZM14.7998 11.385C14.7998 12.6621 13.7644 13.6975 12.4873 13.6975C11.2104 13.6973 10.1758 12.6619 10.1758 11.385C10.176 10.1083 11.2106 9.07374 12.4873 9.07349C13.7642 9.07349 14.7995 10.1081 14.7998 11.385Z",
|
|
559
|
+
fill: "currentColor"
|
|
560
|
+
})
|
|
561
|
+
]
|
|
562
|
+
});
|
|
502
563
|
/** ic_ds_browse_outline_16 */
|
|
503
564
|
const IconBrowseOutline16 = ({ size = 16, className }) => jsxs("svg", {
|
|
504
565
|
width: size,
|
|
@@ -2180,15 +2241,17 @@ var Tooltip_module_css_default = {};
|
|
|
2180
2241
|
//#region lib/types/Tooltip.js
|
|
2181
2242
|
/**
|
|
2182
2243
|
* Attach a hover/focus tooltip to an anchor element.
|
|
2183
|
-
* @param props.label - bubble text.
|
|
2244
|
+
* @param props.label - bubble text, or a resolver evaluated only while the bubble is visible.
|
|
2184
2245
|
* @param props.side - placement relative to the anchor (default 'right').
|
|
2185
2246
|
* @param props.delayMs - hover delay in milliseconds; keyboard focus remains immediate.
|
|
2186
2247
|
* @param props.disabled - suppress the bubble while true; the anchor renders identically so
|
|
2187
2248
|
* toggling never remounts it (which would cut its CSS transitions).
|
|
2249
|
+
* @param props.maxWidth - bubble width cap in pixels, for labels long enough that the default
|
|
2250
|
+
* half-viewport cap would render a slab wider than the surface the anchor sits on.
|
|
2188
2251
|
* @param props.children - a single anchor element; its own ref (callback or object) is forwarded alongside the tooltip's.
|
|
2189
2252
|
* @returns the cloned anchor plus a fixed-position bubble while hovered/focused.
|
|
2190
2253
|
*/
|
|
2191
|
-
function Tooltip({ label, side = "right", delayMs = 0, disabled = false, children }) {
|
|
2254
|
+
function Tooltip({ label, side = "right", delayMs = 0, disabled = false, maxWidth, children }) {
|
|
2192
2255
|
const anchor = useRef(null);
|
|
2193
2256
|
const childRef = children.ref;
|
|
2194
2257
|
const mergedRef = useCallback((el) => {
|
|
@@ -2197,27 +2260,40 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, childre
|
|
|
2197
2260
|
else if (childRef != null) childRef.current = el;
|
|
2198
2261
|
}, [childRef]);
|
|
2199
2262
|
const [pos, setPos] = useState(null);
|
|
2263
|
+
const [placement, setPlacement] = useState(side);
|
|
2200
2264
|
const bubble = useRef(null);
|
|
2265
|
+
const resolvedLabel = pos === null ? null : typeof label === "function" ? label() : label;
|
|
2266
|
+
const y = pos === null ? 0 : placement === "right" ? pos.top + (pos.bottom - pos.top) / 2 : placement === "top" ? pos.top - 8 : pos.bottom + 8;
|
|
2267
|
+
const EDGE_MARGIN = 12;
|
|
2201
2268
|
useLayoutEffect(() => {
|
|
2202
2269
|
if (pos === null) return;
|
|
2203
|
-
const
|
|
2270
|
+
const fit = () => {
|
|
2204
2271
|
const el = bubble.current;
|
|
2205
2272
|
/* v8 ignore next -- pos is set only while the bubble is mounted. */
|
|
2206
2273
|
if (el === null) return;
|
|
2207
|
-
const EDGE_MARGIN = 12;
|
|
2208
2274
|
el.style.left = `${pos.x}px`;
|
|
2209
2275
|
const r = el.getBoundingClientRect();
|
|
2210
2276
|
let dx = 0;
|
|
2211
2277
|
if (r.right > window.innerWidth - EDGE_MARGIN) dx = window.innerWidth - EDGE_MARGIN - r.right;
|
|
2212
2278
|
if (r.left + dx < EDGE_MARGIN) dx = EDGE_MARGIN - r.left;
|
|
2213
2279
|
el.style.left = `${pos.x + dx}px`;
|
|
2280
|
+
if (side === "right") return;
|
|
2281
|
+
const fitsBelow = pos.bottom + 8 + r.height <= window.innerHeight - EDGE_MARGIN;
|
|
2282
|
+
const fitsAbove = pos.top - 8 - r.height >= EDGE_MARGIN;
|
|
2283
|
+
if (placement === "bottom" && !fitsBelow && fitsAbove) setPlacement("top");
|
|
2284
|
+
if (placement === "top" && !fitsAbove && fitsBelow) setPlacement("bottom");
|
|
2214
2285
|
};
|
|
2215
|
-
|
|
2216
|
-
window.addEventListener("resize",
|
|
2286
|
+
fit();
|
|
2287
|
+
window.addEventListener("resize", fit);
|
|
2217
2288
|
return () => {
|
|
2218
|
-
window.removeEventListener("resize",
|
|
2289
|
+
window.removeEventListener("resize", fit);
|
|
2219
2290
|
};
|
|
2220
|
-
}, [
|
|
2291
|
+
}, [
|
|
2292
|
+
placement,
|
|
2293
|
+
pos,
|
|
2294
|
+
resolvedLabel,
|
|
2295
|
+
side
|
|
2296
|
+
]);
|
|
2221
2297
|
const showTimer = useRef(null);
|
|
2222
2298
|
const triggers = useRef({
|
|
2223
2299
|
hover: false,
|
|
@@ -2245,15 +2321,11 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, childre
|
|
|
2245
2321
|
/* v8 ignore next -- the ref is attached by event time: events fire on the cloned anchor. */
|
|
2246
2322
|
if (el === null) return;
|
|
2247
2323
|
const r = el.getBoundingClientRect();
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
y: r.top - 8
|
|
2254
|
-
} : {
|
|
2255
|
-
x: r.left + r.width / 2,
|
|
2256
|
-
y: r.bottom + 8
|
|
2324
|
+
setPlacement(side);
|
|
2325
|
+
setPos({
|
|
2326
|
+
x: side === "right" ? r.right + 10 : r.left + r.width / 2,
|
|
2327
|
+
top: r.top,
|
|
2328
|
+
bottom: r.bottom
|
|
2257
2329
|
});
|
|
2258
2330
|
};
|
|
2259
2331
|
const showAfterHoverDelay = () => {
|
|
@@ -2298,16 +2370,72 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, childre
|
|
|
2298
2370
|
}), pos !== null && jsx("span", {
|
|
2299
2371
|
ref: bubble,
|
|
2300
2372
|
className: Tooltip_module_css_default.bubble,
|
|
2301
|
-
"data-side":
|
|
2373
|
+
"data-side": placement,
|
|
2302
2374
|
style: {
|
|
2303
2375
|
left: pos.x,
|
|
2304
|
-
top:
|
|
2376
|
+
top: y,
|
|
2377
|
+
...maxWidth === void 0 ? {} : { maxWidth }
|
|
2305
2378
|
},
|
|
2306
2379
|
role: "tooltip",
|
|
2307
|
-
children:
|
|
2380
|
+
children: resolvedLabel
|
|
2308
2381
|
})] });
|
|
2309
2382
|
}
|
|
2310
2383
|
//#endregion
|
|
2384
|
+
//#region \0dsh-css-stub:./Toast.module.css.mjs
|
|
2385
|
+
var Toast_module_css_default = {};
|
|
2386
|
+
//#endregion
|
|
2387
|
+
//#region lib/types/Toast.js
|
|
2388
|
+
/**
|
|
2389
|
+
* Transient top-center banner: slides in, holds at full opacity, fades out,
|
|
2390
|
+
* then reports done so the owner can unmount it. Re-showing the same text
|
|
2391
|
+
* restarts the cycle when the owner remounts the component (key it by a
|
|
2392
|
+
* per-show sequence). Rendered through a body portal so an owner inside a
|
|
2393
|
+
* transformed or filtered ancestor cannot trap the fixed banner in that
|
|
2394
|
+
* ancestor's box.
|
|
2395
|
+
*
|
|
2396
|
+
* @param props.text - resolved banner copy; the owner passes localized text.
|
|
2397
|
+
* @param props.icon - optional leading glyph (e.g. a warning icon).
|
|
2398
|
+
* @param props.anchor - optional element whose horizontal center the banner
|
|
2399
|
+
* follows (e.g. the composer card, so the banner centers over the chat column
|
|
2400
|
+
* rather than the whole window); omitted, it centers on the viewport.
|
|
2401
|
+
* @param props.onDone - called once the fade completes; unmount the toast here.
|
|
2402
|
+
* @returns the floating banner.
|
|
2403
|
+
*/
|
|
2404
|
+
function Toast({ text, icon, anchor, onDone }) {
|
|
2405
|
+
useEffect(() => {
|
|
2406
|
+
const timer = setTimeout(onDone, 4e3);
|
|
2407
|
+
return () => {
|
|
2408
|
+
clearTimeout(timer);
|
|
2409
|
+
};
|
|
2410
|
+
}, [onDone]);
|
|
2411
|
+
const [left, setLeft] = useState(null);
|
|
2412
|
+
useLayoutEffect(() => {
|
|
2413
|
+
if (anchor == null) return;
|
|
2414
|
+
const measure = () => {
|
|
2415
|
+
const rect = anchor.getBoundingClientRect();
|
|
2416
|
+
setLeft(rect.left + rect.width / 2);
|
|
2417
|
+
};
|
|
2418
|
+
measure();
|
|
2419
|
+
window.addEventListener("resize", measure);
|
|
2420
|
+
return () => {
|
|
2421
|
+
window.removeEventListener("resize", measure);
|
|
2422
|
+
};
|
|
2423
|
+
}, [anchor]);
|
|
2424
|
+
return createPortal(jsxs("div", {
|
|
2425
|
+
className: Toast_module_css_default.toast,
|
|
2426
|
+
role: "alert",
|
|
2427
|
+
style: left === null ? void 0 : { left },
|
|
2428
|
+
children: [icon !== void 0 && jsx("span", {
|
|
2429
|
+
className: Toast_module_css_default.icon,
|
|
2430
|
+
"aria-hidden": true,
|
|
2431
|
+
children: icon
|
|
2432
|
+
}), jsx("span", {
|
|
2433
|
+
className: Toast_module_css_default.text,
|
|
2434
|
+
children: text
|
|
2435
|
+
})]
|
|
2436
|
+
}), document.body);
|
|
2437
|
+
}
|
|
2438
|
+
//#endregion
|
|
2311
2439
|
//#region \0dsh-css-stub:./JsonTree.module.css.mjs
|
|
2312
2440
|
var JsonTree_module_css_default = {};
|
|
2313
2441
|
//#endregion
|
|
@@ -5677,4 +5805,4 @@ function extractMarkdownPlainText(markdown, options = {}) {
|
|
|
5677
5805
|
}
|
|
5678
5806
|
}
|
|
5679
5807
|
//#endregion
|
|
5680
|
-
export { BrandWordmark, Button, CodeBlock, ConnectionBanner, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, FishLogo, HoverCard, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconCopyOutline16, IconDarkOutline16, IconDataOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGoalOutline16, IconInspectOutline12, IconLightOutline16, IconLikeFill16, IconLikeOutline16, IconLinkOutline14, IconLinkOutline16, IconListPenOutline16, IconLoadingOutline16, IconNewChatOutline16, IconPanelLeftOutline16, IconPaperclipOutline16, IconPauseOutline16, IconPersonalizationOutline16, IconPlayOutline16, IconPlusOutline16, IconProjectAddOutline16, IconQuestionOutline14, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconRightUpOutline16, IconSearchOutline16, IconSendOutline14, IconSendOutline16, IconSettingsOutline14, IconSettingsOutline16, IconShareOutline16, IconSkillOutline16, IconSparkle16, IconStopFill16, IconThinkOutline14, IconThinkOutline16, IconTrashOutline16, IconTreeCorner8x10, IconTriangleRightFill14, IconUserOutline16, IconWarningOutline16, Input, JsonBlock, JsonTree, MarkdownText, Menu, MessageText, Modal, OnboardingSurface, Pill, ReadBlock, RiskConfirmation, SearchBlock, StateDot, TerminalBlock, Tooltip, WebBlock, extractMarkdownPlainText, useAnchoredMaxHeight, writeClipboard };
|
|
5808
|
+
export { BrandWordmark, Button, CodeBlock, ConnectionBanner, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, FishLogo, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconCopyOutline16, IconDarkOutline16, IconDataOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGoalOutline16, IconInspectOutline12, IconLightOutline16, IconLikeFill16, IconLikeOutline16, IconLinkOutline14, IconLinkOutline16, IconListPenOutline16, IconLoadingOutline16, IconNewChatOutline16, IconPanelLeftOutline16, IconPaperclipOutline16, IconPauseOutline16, IconPersonalizationOutline16, IconPlayOutline16, IconPlusOutline16, IconProjectAddOutline16, IconQuestionOutline14, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconRightUpOutline16, IconSearchOutline16, IconSendOutline14, IconSendOutline16, IconSettingsOutline14, IconSettingsOutline16, IconShareOutline16, IconSkillOutline16, IconSparkle16, IconStopFill16, IconThinkOutline14, IconThinkOutline16, IconTrashOutline16, IconTreeCorner8x10, IconTriangleRightFill14, IconUserOutline16, IconWarningOutline16, Input, JsonBlock, JsonTree, MarkdownText, Menu, MessageText, Modal, OnboardingSurface, Pill, ReadBlock, RiskConfirmation, SearchBlock, StateDot, TerminalBlock, Toast, Tooltip, WebBlock, extractMarkdownPlainText, useAnchoredMaxHeight, writeClipboard };
|
package/lib/invariant.js
CHANGED
|
@@ -9,7 +9,7 @@ const name = "client-ui-primitives-invariant";
|
|
|
9
9
|
/** Service required before the companion can reserve package ownership. */
|
|
10
10
|
const inject = ["invariants"];
|
|
11
11
|
/**
|
|
12
|
-
* No runtime invariant: pure props-in React atoms with
|
|
12
|
+
* No runtime invariant: pure props-in React atoms with no Cordis API —
|
|
13
13
|
* no events, no services, no mutable cross-plugin state; rendering contracts
|
|
14
14
|
* are asserted directly by this package's component specs.
|
|
15
15
|
*/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Transient top-center banner: slides in, holds at full opacity, fades out,
|
|
4
|
+
* then reports done so the owner can unmount it. Re-showing the same text
|
|
5
|
+
* restarts the cycle when the owner remounts the component (key it by a
|
|
6
|
+
* per-show sequence). Rendered through a body portal so an owner inside a
|
|
7
|
+
* transformed or filtered ancestor cannot trap the fixed banner in that
|
|
8
|
+
* ancestor's box.
|
|
9
|
+
*
|
|
10
|
+
* @param props.text - resolved banner copy; the owner passes localized text.
|
|
11
|
+
* @param props.icon - optional leading glyph (e.g. a warning icon).
|
|
12
|
+
* @param props.anchor - optional element whose horizontal center the banner
|
|
13
|
+
* follows (e.g. the composer card, so the banner centers over the chat column
|
|
14
|
+
* rather than the whole window); omitted, it centers on the viewport.
|
|
15
|
+
* @param props.onDone - called once the fade completes; unmount the toast here.
|
|
16
|
+
* @returns the floating banner.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Toast({ text, icon, anchor, onDone }: {
|
|
19
|
+
text: string;
|
|
20
|
+
icon?: ReactNode;
|
|
21
|
+
anchor?: HTMLElement | null;
|
|
22
|
+
onDone: () => void;
|
|
23
|
+
}): import("react").ReactPortal;
|
|
24
|
+
//# sourceMappingURL=Toast.d.ts.map
|
package/lib/types/Tooltip.d.ts
CHANGED
|
@@ -9,21 +9,25 @@ interface AnchorProps {
|
|
|
9
9
|
onFocus?: FocusEventHandler | undefined;
|
|
10
10
|
onBlur?: FocusEventHandler | undefined;
|
|
11
11
|
}
|
|
12
|
+
type TooltipLabel = string | (() => string);
|
|
12
13
|
/**
|
|
13
14
|
* Attach a hover/focus tooltip to an anchor element.
|
|
14
|
-
* @param props.label - bubble text.
|
|
15
|
+
* @param props.label - bubble text, or a resolver evaluated only while the bubble is visible.
|
|
15
16
|
* @param props.side - placement relative to the anchor (default 'right').
|
|
16
17
|
* @param props.delayMs - hover delay in milliseconds; keyboard focus remains immediate.
|
|
17
18
|
* @param props.disabled - suppress the bubble while true; the anchor renders identically so
|
|
18
19
|
* toggling never remounts it (which would cut its CSS transitions).
|
|
20
|
+
* @param props.maxWidth - bubble width cap in pixels, for labels long enough that the default
|
|
21
|
+
* half-viewport cap would render a slab wider than the surface the anchor sits on.
|
|
19
22
|
* @param props.children - a single anchor element; its own ref (callback or object) is forwarded alongside the tooltip's.
|
|
20
23
|
* @returns the cloned anchor plus a fixed-position bubble while hovered/focused.
|
|
21
24
|
*/
|
|
22
|
-
export declare function Tooltip({ label, side, delayMs, disabled, children }: {
|
|
23
|
-
label:
|
|
25
|
+
export declare function Tooltip({ label, side, delayMs, disabled, maxWidth, children }: {
|
|
26
|
+
label: TooltipLabel;
|
|
24
27
|
side?: TooltipSide;
|
|
25
28
|
delayMs?: number;
|
|
26
29
|
disabled?: boolean;
|
|
30
|
+
maxWidth?: number;
|
|
27
31
|
children: ReactElement<AnchorProps>;
|
|
28
32
|
}): import("react").JSX.Element;
|
|
29
33
|
export {};
|
|
@@ -61,6 +61,8 @@ export declare const IconEditOutline16: ({ size, className }: IconProps) => impo
|
|
|
61
61
|
export declare const IconThinkOutline14: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
62
62
|
/** ic_ds_think_outline_16 */
|
|
63
63
|
export declare const IconThinkOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
64
|
+
/** ic_ds_agent_preset_outline_16 (figma extract): node interiors knock out to transparency via mask, so the glyph sits on any fill. */
|
|
65
|
+
export declare const IconAgentPresetOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
64
66
|
/** ic_ds_browse_outline_16 */
|
|
65
67
|
export declare const IconBrowseOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
66
68
|
/** ic_ds_link_outline_14 */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { FishLogo } from './FishLogo.tsx';
|
|
|
22
22
|
export { BrandWordmark } from './BrandWordmark.tsx';
|
|
23
23
|
export { Tooltip } from './Tooltip.tsx';
|
|
24
24
|
export type { TooltipSide } from './Tooltip.tsx';
|
|
25
|
+
export { Toast } from './Toast.tsx';
|
|
25
26
|
export { writeClipboard } from './clipboard.ts';
|
|
26
27
|
export { JsonTree } from './JsonTree.tsx';
|
|
27
28
|
export type { JsonTreeProps, JsonTreeLabels } from './JsonTree.tsx';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-primitives",
|
|
3
3
|
"description": "Pure React atoms for the dsh web UI: controls, icons, markdown, and JSON inspectors (zero cordis)",
|
|
4
|
-
"version": "0.0.1-rc.
|
|
4
|
+
"version": "0.0.1-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "restricted"
|
|
7
7
|
},
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@types/react": "~18.3.1",
|
|
53
53
|
"@types/react-dom": "~18.3.0",
|
|
54
54
|
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
55
|
-
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.
|
|
55
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2"
|
|
56
56
|
},
|
|
57
57
|
"files": [
|
|
58
58
|
"lib/index.js",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"lib/types/**/*.d.ts"
|
|
61
61
|
],
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.
|
|
63
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
|
|
64
64
|
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
65
65
|
}
|
|
66
66
|
}
|