@deepseek-ai/dsh-client-ui-primitives 0.1.3-alpha.2 → 0.1.5-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 +3 -3
- package/README.zh.md +3 -3
- package/lib/ConnectionIndicator.module.css +4 -4
- package/lib/Toast.module.css +7 -2
- package/lib/index.js +88 -13
- package/lib/types/Menu.d.ts +1 -1
- package/lib/types/icons/index.d.ts +4 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/markdown/MarkdownText.d.ts +8 -5
- package/lib/types/markdown/render.d.ts +19 -0
- package/package.json +1 -1
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: 18b136c78539d8ba6da3b17239112967faf63602
|
|
6
|
+
README.zh.md: f91435e44a5ca79b245fde3dd025a732d35e54e9
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-client-ui-primitives`
|
|
12
|
+
Use `dsh-client-ui-primitives` to build web-client controls and render agent output with shared React UI. It includes standard controls, icons, anchored overlays, and renderers for Markdown with TeX, terminal output, file reads, diffs, search, web retrieval, and JSON. The renderers handle untrusted model output by dropping raw HTML, restricting links, and parsing ANSI escape sequences. Callers must supply localized labels, and the components rely only on React and `--dsw-*` design tokens.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -64,11 +64,11 @@ Writing your own component in your own package is fine when the need is genuinel
|
|
|
64
64
|
|
|
65
65
|
### Controls and icons
|
|
66
66
|
|
|
67
|
-
The catalog above lists what each export is for; this section covers the behavior that props alone do not show. The `ic_ds_*` icon set and `FishLogo`/`BrandWordmark` marks fill brand and inline-icon slots. `LinkIcon` draws the leading category glyph for clickable artifact links — globe, folder, code, image, document, or plain paper, all riding `currentColor` — and `classifyLinkPath` derives a file path's category from its extension. `ConnectionIndicator` renders a warning-colored disconnected action, a connecting label whose one-to-three dots advance every 500ms independently of retry timing, or a success-colored recovered status. Every state reserves the widest supplied label and uses fixed icon and text columns, so copy changes do not move or resize the control. Its owner supplies visibility, the recovery hold, localized labels, and the immediate-reconnect callback; the primitive uses no native title tooltip. `useAnchoredPosition` and `useAnchoredMaxHeight` keep floating panels and bottom-anchored overlays clamped to the viewport and following their anchor. `HoverCard` keeps its portaled preview reachable across the anchor gap and can expose a copy button through the `copyText` prop. `Toast` holds for the window its owner names through `holdMs`, because how long a banner has to stay depends on how much there is to read; the same value drives its unmount timer and the stylesheet's fade delay, so the two cannot disagree. `rankByName` is the `/` menu's shared candidate ranker for the command and skill sources: the query must be a case-insensitive ordered subsequence of the name; prefix hits rank first, then alignment score, then source order.
|
|
67
|
+
The catalog above lists what each export is for; this section covers the behavior that props alone do not show. The `ic_ds_*` icon set and `FishLogo`/`BrandWordmark` marks fill brand and inline-icon slots. `LinkIcon` draws the leading category glyph for clickable artifact links — globe, folder, code, image, document, or plain paper, all riding `currentColor` — and `classifyLinkPath` derives a file path's category from its extension. `ConnectionIndicator` renders a warning-colored disconnected action, a connecting label whose one-to-three dots advance every 500ms independently of retry timing, or a success-colored recovered status. Hover or keyboard focus shows only the reconnect action label, including while the connecting dots animate. Every state reserves the widest supplied label and uses fixed icon and text columns, so copy changes do not move or resize the control. Its owner supplies visibility, the recovery hold, localized labels, and the immediate-reconnect callback; the primitive uses no native title tooltip. `useAnchoredPosition` and `useAnchoredMaxHeight` keep floating panels and bottom-anchored overlays clamped to the viewport and following their anchor. `HoverCard` keeps its portaled preview reachable across the anchor gap and can expose a copy button through the `copyText` prop. `Toast` holds for the window its owner names through `holdMs`, because how long a banner has to stay depends on how much there is to read; the same value drives its unmount timer and the stylesheet's fade delay, so the two cannot disagree. `rankByName` is the `/` menu's shared candidate ranker for the command and skill sources: the query must be a case-insensitive ordered subsequence of the name; prefix hits rank first, then alignment score, then source order.
|
|
68
68
|
|
|
69
69
|
### Rendering agent output
|
|
70
70
|
|
|
71
|
-
`MarkdownText` renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. While a reply streams, it freezes completed blocks, advances a top-level open fence by completed lines, and highlights that fence from saved Shiki grammar state. Completed token lines enter fixed-size React groups, so later chunks reconcile only the growing group; an unchanged fence retains that DOM when the final full parse resolves cross-document syntax. `TerminalBlock`, `ReadBlock`, `DiffBlock`, `SearchBlock`, and `WebBlock` render the matching tool-result intent with copy controls, overflow handling, and ANSI processing where applicable. `JsonTree` and `JsonBlock` inspect JSON values read-only, while `projectUserText` projects sent user text into inline plain runs and reference chips for the message bubble and queue rows.
|
|
71
|
+
`MarkdownText` renders untrusted GFM and TeX math, blocks unsafe links and images, and can turn resolved file mentions into explicit controls. When the owner passes a `pathImages` vocabulary, image destinations that are local media paths rewrite to displayable URLs on settled renders only (the same streaming gate as file mentions); without a vocabulary, local destinations remain inert alt text. A load or decode failure replaces the image with its authored alt text, or the original destination when alt is empty. Changing the image source permits a fresh load. While a reply streams, it freezes completed blocks, advances a top-level open fence by completed lines, and highlights that fence from saved Shiki grammar state. Completed token lines enter fixed-size React groups, so later chunks reconcile only the growing group; an unchanged fence retains that DOM when the final full parse resolves cross-document syntax. `TerminalBlock`, `ReadBlock`, `DiffBlock`, `SearchBlock`, and `WebBlock` render the matching tool-result intent with copy controls, overflow handling, and ANSI processing where applicable. `JsonTree` and `JsonBlock` inspect JSON values read-only, while `projectUserText` projects sent user text into inline plain runs and reference chips for the message bubble and queue rows.
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
### Localizing copy
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-library"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-client-ui-primitives
|
|
12
|
+
使用 `dsh-client-ui-primitives`,通过共享 React UI 构建 Web 客户端控件并渲染 agent 输出。它提供标准控件、图标、锚定浮层,以及用于带 TeX 公式的 Markdown、终端输出、文件读取、差异、搜索、网页检索和 JSON 的渲染器。这些渲染器会丢弃原始 HTML、限制链接并解析 ANSI 转义序列,以处理不受信任的模型输出。调用方必须提供本地化 label;这些组件仅依赖 React 和 `--dsw-*` 设计 token。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -64,11 +64,11 @@ kind: "package-library"
|
|
|
64
64
|
|
|
65
65
|
### 控件与图标
|
|
66
66
|
|
|
67
|
-
上面的目录说明每个导出的用途;本节讲 props 本身看不出来的行为。`ic_ds_*` 图标集与 `FishLogo`/`BrandWordmark` 标记填充品牌与行内图标 slot。`LinkIcon` 为可点击产物链接绘制前置分类图形——地球、文件夹、代码、图片、文档或纸张,全部随 `currentColor`——`classifyLinkPath` 按扩展名推导文件路径的类别。`ConnectionIndicator` 可渲染警告色的断联操作、以独立于 retry 时序的 500ms
|
|
67
|
+
上面的目录说明每个导出的用途;本节讲 props 本身看不出来的行为。`ic_ds_*` 图标集与 `FishLogo`/`BrandWordmark` 标记填充品牌与行内图标 slot。`LinkIcon` 为可点击产物链接绘制前置分类图形——地球、文件夹、代码、图片、文档或纸张,全部随 `currentColor`——`classifyLinkPath` 按扩展名推导文件路径的类别。`ConnectionIndicator` 可渲染警告色的断联操作、以独立于 retry 时序的 500ms 节奏推进一至三个点的连接中状态,或成功色的恢复状态。悬停或键盘聚焦时只显示重连操作文案,连接中的圆点动画也保持隐藏。所有状态都为最长的输入 label 预留空间,并使用固定的图标列和文字列,因此文案变化不会移动控件或改变其宽度。它的 owner 提供可见性、恢复驻留时间、本地化 label 与立即重连回调;该原语不使用原生 title tooltip。`useAnchoredPosition` 与 `useAnchoredMaxHeight` 让浮动面板与底部锚定浮层始终钳制在视口内并跟随锚点。`HoverCard` 通过指针离开宽限期让采用 portal 的预览在跨过锚点间隙时仍可触及,并可通过 `copyText` prop 提供复制按钮。 `Toast` 的停留时长由使用方通过 `holdMs` 指定,因为横幅该留多久取决于有多少内容要读;同一个值同时驱动它的卸载定时器与样式表的淡出延迟,两者不可能再错位。 `rankByName` 是 `/` 菜单命令源与 skill 源共享的候选排序器:查询必须是名字的不区分大小写的有序子序列;前缀命中排最前,其次按对齐分数,再按来源顺序。
|
|
68
68
|
|
|
69
69
|
### 渲染 agent 输出
|
|
70
70
|
|
|
71
|
-
`MarkdownText` 渲染不可信的 GFM 与 TeX
|
|
71
|
+
`MarkdownText` 渲染不可信的 GFM 与 TeX 公式、阻止不安全的链接与图片,并可把已解析的文件提及转换为显式控件。当 owner 传入 `pathImages` 词表时,本地媒体路径的图片目标只在落定渲染阶段重写为可展示 URL(与 file mentions 相同的流式门);不传词表时本地目标保持惰性 alt 文本。加载或解码失败后,图片替换为作者的 alt 文本;alt 为空时显示原始目标路径。图片源变化后可重新加载。回复流式输出时,它冻结已完成的块、按已完成行推进顶层未闭合 fence,并从保存的 Shiki grammar state 为该 fence 增量高亮。已完成的 token 行进入固定大小的 React 分组,后续分片只 reconcile 正在增长的分组;最终全量解析解决跨文档语法时,未变化的 fence 会保留该 DOM。`TerminalBlock`、`ReadBlock`、`DiffBlock`、`SearchBlock` 与 `WebBlock` 把对应的工具结果意图渲染为带复制控件、溢出处理及适用时 ANSI 处理的卡片。`JsonTree` 与 `JsonBlock` 以只读方式检查 JSON 值;`projectUserText` 把已发送的用户文本投影为行内普通文本段与引用 chip,供消息气泡和排队行使用。
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
### 本地化文案
|
|
@@ -90,13 +90,13 @@
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
@keyframes reveal-second-dot {
|
|
93
|
-
0%, 33.32% {
|
|
94
|
-
33.33%, 100% {
|
|
93
|
+
0%, 33.32% { opacity: 0; }
|
|
94
|
+
33.33%, 100% { opacity: 1; }
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
@keyframes reveal-third-dot {
|
|
98
|
-
0%, 66.65% {
|
|
99
|
-
66.66%, 100% {
|
|
98
|
+
0%, 66.65% { opacity: 0; }
|
|
99
|
+
66.66%, 100% { opacity: 1; }
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
@media (prefers-reduced-motion: reduce) {
|
package/lib/Toast.module.css
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
.toast {
|
|
7
7
|
position: fixed;
|
|
8
|
-
top:
|
|
8
|
+
top: 40px;
|
|
9
9
|
left: 50%;
|
|
10
10
|
/* Above the 1000 the image lightbox backdrop uses: a failure reported while
|
|
11
11
|
a preview is open must stay readable. */
|
|
@@ -15,7 +15,12 @@
|
|
|
15
15
|
display: flex;
|
|
16
16
|
align-items: center;
|
|
17
17
|
gap: 10px;
|
|
18
|
-
|
|
18
|
+
/* Fixed boxes with `left` set shrink-to-fit against the space RIGHT of
|
|
19
|
+
`left` (the -50% translate happens after sizing), so an anchored banner
|
|
20
|
+
near the window edge would wrap early. max-content sizes the box from
|
|
21
|
+
its text alone, capped by the max-width below. */
|
|
22
|
+
width: max-content;
|
|
23
|
+
max-width: min(640px, calc(100vw - 48px));
|
|
19
24
|
padding: 12px 16px;
|
|
20
25
|
border-radius: 14px;
|
|
21
26
|
background: var(--dsw-alias-button-contrast-fill);
|
package/lib/index.js
CHANGED
|
@@ -1205,6 +1205,35 @@ const IconClockOutline16 = ({ size = 16, className }) => jsxs("svg", {
|
|
|
1205
1205
|
strokeWidth: "1.25"
|
|
1206
1206
|
})]
|
|
1207
1207
|
});
|
|
1208
|
+
/** Thin-stroke gauge: dial arc open at the bottom, filled hub, square-cut needle to the upper right.
|
|
1209
|
+
* The dial center sits at y=8.75, not 8: the bottom opening leaves the glyph top-heavy, and the
|
|
1210
|
+
* 0.75 drop optically centers the drawn extent in the 16 box. */
|
|
1211
|
+
const IconGaugeOutline16 = ({ size = 16, className }) => jsxs("svg", {
|
|
1212
|
+
width: size,
|
|
1213
|
+
height: size,
|
|
1214
|
+
className,
|
|
1215
|
+
viewBox: "0 0 16 16",
|
|
1216
|
+
fill: "none",
|
|
1217
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1218
|
+
children: [
|
|
1219
|
+
jsx("path", {
|
|
1220
|
+
d: "M3.49 13.26A6.375 6.375 0 1 1 12.51 13.26",
|
|
1221
|
+
stroke: "currentColor",
|
|
1222
|
+
strokeWidth: "1.25"
|
|
1223
|
+
}),
|
|
1224
|
+
jsx("path", {
|
|
1225
|
+
d: "M8 8.75L11.4 5.35",
|
|
1226
|
+
stroke: "currentColor",
|
|
1227
|
+
strokeWidth: "1.25"
|
|
1228
|
+
}),
|
|
1229
|
+
jsx("circle", {
|
|
1230
|
+
cx: "8",
|
|
1231
|
+
cy: "8.75",
|
|
1232
|
+
r: "1.55",
|
|
1233
|
+
fill: "currentColor"
|
|
1234
|
+
})
|
|
1235
|
+
]
|
|
1236
|
+
});
|
|
1208
1237
|
/** ic_send_outline_14 (figma extract): thin-stroke upward send arrow. */
|
|
1209
1238
|
const IconSendOutline14 = ({ size = 14, className }) => jsx("svg", {
|
|
1210
1239
|
width: size,
|
|
@@ -6490,6 +6519,30 @@ function remoteImageUrl(url) {
|
|
|
6490
6519
|
return;
|
|
6491
6520
|
}
|
|
6492
6521
|
}
|
|
6522
|
+
/** Protocols a vocabulary-rewritten image destination may carry. */
|
|
6523
|
+
function vocabularyImageUrl(url) {
|
|
6524
|
+
try {
|
|
6525
|
+
const protocol = new URL(url).protocol;
|
|
6526
|
+
return protocol === "http:" || protocol === "https:" || protocol === "blob:" || protocol === "data:" ? url : void 0;
|
|
6527
|
+
} catch {
|
|
6528
|
+
return;
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
/**
|
|
6532
|
+
* The displayable source for one image destination: absolute HTTP(S) as
|
|
6533
|
+
* authored, otherwise the context's local-path vocabulary when it vouches for
|
|
6534
|
+
* the destination. Either miss leaves the authored fallback (alt text) to the
|
|
6535
|
+
* caller.
|
|
6536
|
+
* @param url - The authored markdown destination.
|
|
6537
|
+
* @param pathImages - Rewriting vocabulary, when the render pass has one.
|
|
6538
|
+
* @returns The displayable image URL, or undefined.
|
|
6539
|
+
*/
|
|
6540
|
+
function imageSource(url, pathImages) {
|
|
6541
|
+
const remote = remoteImageUrl(sanitizeUrl(normalizeUri(url)));
|
|
6542
|
+
if (remote !== void 0) return remote;
|
|
6543
|
+
const rewritten = pathImages?.resolve(url);
|
|
6544
|
+
return rewritten === void 0 ? void 0 : vocabularyImageUrl(rewritten);
|
|
6545
|
+
}
|
|
6493
6546
|
/**
|
|
6494
6547
|
* Create an empty {@link ReferenceTargets}.
|
|
6495
6548
|
* @returns Fresh empty maps.
|
|
@@ -6604,7 +6657,7 @@ function renderNode(node, key, context) {
|
|
|
6604
6657
|
inLink: true
|
|
6605
6658
|
}), key, !anchorWrapsOnlyImages(node.children));
|
|
6606
6659
|
case "linkReference": return renderLinkReference(node, key, context);
|
|
6607
|
-
case "image": return renderImage(node.url, node.alt ?? "", key);
|
|
6660
|
+
case "image": return renderImage(node.url, node.alt ?? "", key, context);
|
|
6608
6661
|
case "imageReference": return renderImageReference(node, key, context);
|
|
6609
6662
|
case "footnoteReference": return renderFootnoteReference(node, key, context);
|
|
6610
6663
|
case "definition":
|
|
@@ -6738,20 +6791,36 @@ function inlineCodeHttpUrl(value) {
|
|
|
6738
6791
|
return;
|
|
6739
6792
|
}
|
|
6740
6793
|
}
|
|
6741
|
-
function renderImage(url, alt, key) {
|
|
6742
|
-
const imageSrc =
|
|
6794
|
+
function renderImage(url, alt, key, context) {
|
|
6795
|
+
const imageSrc = imageSource(url, context.pathImages);
|
|
6743
6796
|
if (imageSrc === void 0) return jsx("span", {
|
|
6744
6797
|
className: css$22.imageAlt,
|
|
6745
6798
|
children: alt
|
|
6746
6799
|
}, key);
|
|
6800
|
+
return jsx(MarkdownImage, {
|
|
6801
|
+
src: imageSrc,
|
|
6802
|
+
alt,
|
|
6803
|
+
destination: url
|
|
6804
|
+
}, `${key}:${imageSrc}`);
|
|
6805
|
+
}
|
|
6806
|
+
/** Failed loads retain the authored alt or destination; a new source remounts the image. */
|
|
6807
|
+
function MarkdownImage({ src, alt, destination }) {
|
|
6808
|
+
const [failed, setFailed] = useState(false);
|
|
6809
|
+
if (failed) return jsx("span", {
|
|
6810
|
+
className: css$22.imageAlt,
|
|
6811
|
+
children: alt || destination
|
|
6812
|
+
});
|
|
6747
6813
|
return jsx("img", {
|
|
6748
6814
|
className: css$22.image,
|
|
6749
|
-
src
|
|
6815
|
+
src,
|
|
6750
6816
|
alt,
|
|
6817
|
+
onError: () => {
|
|
6818
|
+
setFailed(true);
|
|
6819
|
+
},
|
|
6751
6820
|
loading: "lazy",
|
|
6752
6821
|
decoding: "async",
|
|
6753
6822
|
referrerPolicy: "no-referrer"
|
|
6754
|
-
}
|
|
6823
|
+
});
|
|
6755
6824
|
}
|
|
6756
6825
|
/** The bracketed source text a reference reverts to when its definition is missing. */
|
|
6757
6826
|
function referenceSuffix(node) {
|
|
@@ -6775,7 +6844,7 @@ function renderLinkReference(node, key, context) {
|
|
|
6775
6844
|
function renderImageReference(node, key, context) {
|
|
6776
6845
|
const definition = context.targets.definitions.get(node.identifier.toUpperCase());
|
|
6777
6846
|
if (definition === void 0) return `![${node.alt ?? ""}${referenceSuffix(node)}`;
|
|
6778
|
-
return renderImage(definition.url, node.alt ?? "", key);
|
|
6847
|
+
return renderImage(definition.url, node.alt ?? "", key, context);
|
|
6779
6848
|
}
|
|
6780
6849
|
function renderFootnoteReference(node, key, context) {
|
|
6781
6850
|
const id = node.identifier.toUpperCase();
|
|
@@ -6838,7 +6907,7 @@ function renderFootnoteSection(context) {
|
|
|
6838
6907
|
* full parse self-heals it.
|
|
6839
6908
|
*/
|
|
6840
6909
|
/** One settled full render: parse with math, resolve references, append the footnote section. */
|
|
6841
|
-
function renderSettled(text, labels, fileMentions) {
|
|
6910
|
+
function renderSettled(text, labels, fileMentions, pathImages) {
|
|
6842
6911
|
const root = parseGfmWithMath(text);
|
|
6843
6912
|
const targets = createReferenceTargets();
|
|
6844
6913
|
collectReferenceTargets(root.children, targets);
|
|
@@ -6846,6 +6915,7 @@ function renderSettled(text, labels, fileMentions) {
|
|
|
6846
6915
|
streaming: false,
|
|
6847
6916
|
labels,
|
|
6848
6917
|
fileMentions,
|
|
6918
|
+
pathImages,
|
|
6849
6919
|
targets,
|
|
6850
6920
|
footnoteOrder: [],
|
|
6851
6921
|
footnoteCounts: /* @__PURE__ */ new Map()
|
|
@@ -6912,6 +6982,7 @@ var StreamingRenderer = class {
|
|
|
6912
6982
|
streaming: true,
|
|
6913
6983
|
labels: this.labels,
|
|
6914
6984
|
fileMentions: void 0,
|
|
6985
|
+
pathImages: void 0,
|
|
6915
6986
|
targets: frameTargets,
|
|
6916
6987
|
footnoteOrder: this.frozenFootnoteOrder,
|
|
6917
6988
|
footnoteCounts: this.frozenFootnoteCounts
|
|
@@ -6928,6 +6999,7 @@ var StreamingRenderer = class {
|
|
|
6928
6999
|
streaming: true,
|
|
6929
7000
|
labels: this.labels,
|
|
6930
7001
|
fileMentions: void 0,
|
|
7002
|
+
pathImages: void 0,
|
|
6931
7003
|
targets: frameTargets,
|
|
6932
7004
|
footnoteOrder: [...this.frozenFootnoteOrder],
|
|
6933
7005
|
footnoteCounts: new Map(this.frozenFootnoteCounts)
|
|
@@ -6953,21 +7025,23 @@ var StreamingRenderer = class {
|
|
|
6953
7025
|
* `labels` forwards localized fence and footnote chrome — pass a
|
|
6954
7026
|
* reference-stable object (memoized per locale revision), because a new
|
|
6955
7027
|
* identity discards the streaming render cache mid-message. `fileMentions`
|
|
6956
|
-
* links inline-code tokens its resolver recognizes as real files
|
|
6957
|
-
*
|
|
7028
|
+
* links inline-code tokens its resolver recognizes as real files, and
|
|
7029
|
+
* `pathImages` rewrites image destinations that are local file paths into
|
|
7030
|
+
* displayable URLs its resolver vouches for; both vocabularies are the
|
|
7031
|
+
* single streaming gate — they apply to settled renders only, because a
|
|
6958
7032
|
* streaming message's vocabulary is not final and frozen cached elements
|
|
6959
7033
|
* must not bake in handlers that could go stale.
|
|
6960
7034
|
* @returns A GFM document with TeX math rendered through KaTeX; raw HTML,
|
|
6961
7035
|
* relative links, and unsafe protocols are disabled, while absolute HTTP(S)
|
|
6962
7036
|
* images render directly.
|
|
6963
7037
|
*/
|
|
6964
|
-
const MarkdownText = memo(function MarkdownText({ text, streaming = false, labels, fileMentions }) {
|
|
7038
|
+
const MarkdownText = memo(function MarkdownText({ text, streaming = false, labels, fileMentions, pathImages }) {
|
|
6965
7039
|
const streamRef = useRef(null);
|
|
6966
7040
|
const streamLabelsRef = useRef(labels);
|
|
6967
7041
|
const children = useMemo(() => {
|
|
6968
7042
|
if (!streaming) {
|
|
6969
7043
|
streamRef.current = null;
|
|
6970
|
-
return renderSettled(text, labels, fileMentions);
|
|
7044
|
+
return renderSettled(text, labels, fileMentions, pathImages);
|
|
6971
7045
|
}
|
|
6972
7046
|
if (streamRef.current === null || streamLabelsRef.current !== labels) {
|
|
6973
7047
|
streamRef.current = new StreamingRenderer(labels);
|
|
@@ -6978,7 +7052,8 @@ const MarkdownText = memo(function MarkdownText({ text, streaming = false, label
|
|
|
6978
7052
|
text,
|
|
6979
7053
|
streaming,
|
|
6980
7054
|
labels,
|
|
6981
|
-
fileMentions
|
|
7055
|
+
fileMentions,
|
|
7056
|
+
pathImages
|
|
6982
7057
|
]);
|
|
6983
7058
|
return jsx("div", {
|
|
6984
7059
|
className: css$22.markdown,
|
|
@@ -7261,6 +7336,6 @@ function extractMarkdownPlainText(markdown, options = {}) {
|
|
|
7261
7336
|
}
|
|
7262
7337
|
}
|
|
7263
7338
|
//#endregion
|
|
7264
|
-
export { BrandWordmark, Button, CodeBlock, ConnectionIndicator, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, DocumentFileIcon, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX, FishLogo, HoverCard, IconAgentPresetOutline16, IconAlarmClockOutline16, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconClockOutline16, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconContextInjectionOutline16, IconCopyOutline16, IconCordisPluginOutline14, IconDarkOutline16, IconDataOutline16, IconDatabaseOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGlobeOutline14, 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, LinkIcon, MarkdownText, Menu, Modal, OnboardingSurface, Pill, ReadBlock, ReferenceIcon, RiskConfirmation, SearchBlock, StateDot, Switch, Tag, TerminalBlock, Toast, Tooltip, WebBlock, classifyLinkPath, diffTotals, extractMarkdownPlainText, fileSizeText, projectUserText, rankByName, relativeTime, useAnchoredMaxHeight, useAnchoredPosition, useDismissOnOutsidePointer, writeClipboard };
|
|
7339
|
+
export { BrandWordmark, Button, CodeBlock, ConnectionIndicator, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, DocumentFileIcon, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX, FishLogo, HoverCard, IconAgentPresetOutline16, IconAlarmClockOutline16, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconClockOutline16, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconContextInjectionOutline16, IconCopyOutline16, IconCordisPluginOutline14, IconDarkOutline16, IconDataOutline16, IconDatabaseOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGaugeOutline16, IconGlobeOutline14, 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, LinkIcon, MarkdownText, Menu, Modal, OnboardingSurface, Pill, ReadBlock, ReferenceIcon, RiskConfirmation, SearchBlock, StateDot, Switch, Tag, TerminalBlock, Toast, Tooltip, WebBlock, classifyLinkPath, diffTotals, extractMarkdownPlainText, fileSizeText, projectUserText, rankByName, relativeTime, useAnchoredMaxHeight, useAnchoredPosition, useDismissOnOutsidePointer, writeClipboard };
|
|
7265
7340
|
|
|
7266
7341
|
//# sourceMappingURL=index.js.map
|
package/lib/types/Menu.d.ts
CHANGED
|
@@ -76,6 +76,6 @@ export declare function Menu({ open, anchor, items, selectedId, selectedIds, onS
|
|
|
76
76
|
compact?: boolean;
|
|
77
77
|
selection?: 'check' | 'fill';
|
|
78
78
|
getAnchorRect?: () => DOMRect | null;
|
|
79
|
-
className?: string;
|
|
79
|
+
className?: string | undefined;
|
|
80
80
|
}): import("react").JSX.Element;
|
|
81
81
|
//# sourceMappingURL=Menu.d.ts.map
|
|
@@ -135,6 +135,10 @@ export declare const IconDataOutline16: ({ size, className }: IconProps) => impo
|
|
|
135
135
|
export declare const IconDatabaseOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
136
136
|
/** Thin-stroke clock: outlined dial with square-cut hour and minute hands. */
|
|
137
137
|
export declare const IconClockOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
138
|
+
/** Thin-stroke gauge: dial arc open at the bottom, filled hub, square-cut needle to the upper right.
|
|
139
|
+
* The dial center sits at y=8.75, not 8: the bottom opening leaves the glyph top-heavy, and the
|
|
140
|
+
* 0.75 drop optically centers the drawn extent in the 16 box. */
|
|
141
|
+
export declare const IconGaugeOutline16: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
138
142
|
/** ic_send_outline_14 (figma extract): thin-stroke upward send arrow. */
|
|
139
143
|
export declare const IconSendOutline14: ({ size, className }: IconProps) => import("react").JSX.Element;
|
|
140
144
|
/** ic_queue_outline_14 (figma extract): open chat bubble with two queued lines. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export { CodeBlock } from './markdown/CodeBlock.tsx';
|
|
|
58
58
|
export type { CodeBlockProps } from './markdown/CodeBlock.tsx';
|
|
59
59
|
export { JsonBlock } from './markdown/JsonBlock.tsx';
|
|
60
60
|
export { MarkdownText } from './markdown/MarkdownText.tsx';
|
|
61
|
-
export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels } from './markdown/MarkdownText.tsx';
|
|
61
|
+
export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels, MarkdownPathImages } from './markdown/MarkdownText.tsx';
|
|
62
62
|
export { extractMarkdownPlainText } from './markdown/plain-text.ts';
|
|
63
63
|
export type { MarkdownPlainTextMode, MarkdownPlainTextOptions } from './markdown/plain-text.ts';
|
|
64
64
|
export * from './icons/index.tsx';
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* other side of the freeze boundary renders literally until the settled
|
|
11
11
|
* full parse self-heals it.
|
|
12
12
|
*/
|
|
13
|
-
import type { MarkdownFileMentions, MarkdownLabels } from './render.tsx';
|
|
13
|
+
import type { MarkdownFileMentions, MarkdownLabels, MarkdownPathImages } from './render.tsx';
|
|
14
14
|
import 'katex/dist/katex.min.css';
|
|
15
|
-
export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels } from './render.tsx';
|
|
15
|
+
export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels, MarkdownPathImages } from './render.tsx';
|
|
16
16
|
/**
|
|
17
17
|
* Render untrusted assistant-authored Markdown as semantic React elements.
|
|
18
18
|
* @param props - Markdown source text preserved by the session projection;
|
|
@@ -22,18 +22,21 @@ export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels } from '.
|
|
|
22
22
|
* `labels` forwards localized fence and footnote chrome — pass a
|
|
23
23
|
* reference-stable object (memoized per locale revision), because a new
|
|
24
24
|
* identity discards the streaming render cache mid-message. `fileMentions`
|
|
25
|
-
* links inline-code tokens its resolver recognizes as real files
|
|
26
|
-
*
|
|
25
|
+
* links inline-code tokens its resolver recognizes as real files, and
|
|
26
|
+
* `pathImages` rewrites image destinations that are local file paths into
|
|
27
|
+
* displayable URLs its resolver vouches for; both vocabularies are the
|
|
28
|
+
* single streaming gate — they apply to settled renders only, because a
|
|
27
29
|
* streaming message's vocabulary is not final and frozen cached elements
|
|
28
30
|
* must not bake in handlers that could go stale.
|
|
29
31
|
* @returns A GFM document with TeX math rendered through KaTeX; raw HTML,
|
|
30
32
|
* relative links, and unsafe protocols are disabled, while absolute HTTP(S)
|
|
31
33
|
* images render directly.
|
|
32
34
|
*/
|
|
33
|
-
export declare const MarkdownText: import("react").MemoExoticComponent<({ text, streaming, labels, fileMentions }: {
|
|
35
|
+
export declare const MarkdownText: import("react").MemoExoticComponent<({ text, streaming, labels, fileMentions, pathImages }: {
|
|
34
36
|
text: string;
|
|
35
37
|
streaming?: boolean;
|
|
36
38
|
labels: MarkdownLabels;
|
|
37
39
|
fileMentions?: MarkdownFileMentions | undefined;
|
|
40
|
+
pathImages?: MarkdownPathImages | undefined;
|
|
38
41
|
}) => import("react").JSX.Element>;
|
|
39
42
|
//# sourceMappingURL=MarkdownText.d.ts.map
|
|
@@ -49,6 +49,23 @@ export declare function createReferenceTargets(): ReferenceTargets;
|
|
|
49
49
|
* @param targets - Accumulator, typically shared across incremental segments.
|
|
50
50
|
*/
|
|
51
51
|
export declare function collectReferenceTargets(nodes: readonly Md.RootContent[], targets: ReferenceTargets): void;
|
|
52
|
+
/**
|
|
53
|
+
* Local-path image vocabulary for image destinations: the owner maps an
|
|
54
|
+
* authored destination that fails the remote-URL allowlist (an absolute local
|
|
55
|
+
* file path, for example) to a displayable URL it can vouch for. Absent
|
|
56
|
+
* wherever no such vocabulary exists, authored local destinations keep their
|
|
57
|
+
* documented fallback (the image's alt text). Rewritten destinations must be
|
|
58
|
+
* absolute; the renderer re-checks their protocol before emitting them.
|
|
59
|
+
*/
|
|
60
|
+
export interface MarkdownPathImages {
|
|
61
|
+
/**
|
|
62
|
+
* Resolve one authored image destination.
|
|
63
|
+
* @param value - The destination exactly as the markdown author wrote it.
|
|
64
|
+
* @returns A displayable absolute URL, or undefined when the destination
|
|
65
|
+
* names no displayable image — it then stays inert alt text.
|
|
66
|
+
*/
|
|
67
|
+
resolve(value: string): string | undefined;
|
|
68
|
+
}
|
|
52
69
|
/**
|
|
53
70
|
* File-mention affordance for inline code: the owner resolves an authored
|
|
54
71
|
* token to the file it names, using its own vocabulary of real files — the
|
|
@@ -80,6 +97,8 @@ export interface MarkdownRenderContext {
|
|
|
80
97
|
readonly inBlockquote?: boolean;
|
|
81
98
|
/** Inline-code file mentions; absent wherever no opener vocabulary exists. */
|
|
82
99
|
readonly fileMentions: MarkdownFileMentions | undefined;
|
|
100
|
+
/** Local-path image vocabulary; absent wherever no rewriting owner exists. */
|
|
101
|
+
readonly pathImages: MarkdownPathImages | undefined;
|
|
83
102
|
/** Inside an anchor's children: interactive mentions must not nest there. */
|
|
84
103
|
readonly inLink?: boolean;
|
|
85
104
|
/** Reference targets visible to this pass. */
|
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.1.
|
|
4
|
+
"version": "0.1.5-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|