dsh-generative-ui 0.0.3 → 0.0.4
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/lib/client.js +313 -306
- package/lib/client.js.map +4 -4
- package/lib/index.js +11 -10
- package/lib/types/client/runtime/inline-fence.d.ts +5 -1
- package/lib/types/index.d.ts +1 -1
- package/package.json +42 -25
- package/src/client/index.ts +4 -1
- package/src/client/runtime/inline-fence.ts +10 -2
- package/src/index.ts +10 -11
package/lib/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import { join } from "node:path";
|
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
8
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
9
8
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
10
9
|
|
|
11
10
|
// src/contract-assets.ts
|
|
@@ -1727,7 +1726,7 @@ var EXEC_HISTORY = `**A history is a set too.** 最近改了啥, 梳理一下 gi
|
|
|
1727
1726
|
// src/index.ts
|
|
1728
1727
|
var name = "dsh-generative-ui";
|
|
1729
1728
|
var inject = ["systemPrompt"];
|
|
1730
|
-
var SETTINGS_NAMESPACE =
|
|
1729
|
+
var SETTINGS_NAMESPACE = name;
|
|
1731
1730
|
var Config = z.object({
|
|
1732
1731
|
allowExec: z.boolean().default(true).description("Let generated cards run shell commands through `$dsh/exec`, under this session's own sandbox mode. Cards use it to search (`rg`, `fd`), run `lint`/`check`, and read `git`. The sandbox still applies; what does not is the per-command approval prompt, so turn this off for a session where that matters.")
|
|
1733
1732
|
});
|
|
@@ -1763,8 +1762,8 @@ async function serveCanvas(liveWorkspaces, req, res) {
|
|
|
1763
1762
|
if (!liveWorkspaces().has(cwd))
|
|
1764
1763
|
return void res.writeHead(403).end();
|
|
1765
1764
|
if (id === null) {
|
|
1766
|
-
const ids = await readdir(join(cwd, CANVAS_DIR)).then((names) => names.flatMap((
|
|
1767
|
-
const found = canvasIdOf(`${CANVAS_DIR}/${
|
|
1765
|
+
const ids = await readdir(join(cwd, CANVAS_DIR)).then((names) => names.flatMap((name) => {
|
|
1766
|
+
const found = canvasIdOf(`${CANVAS_DIR}/${name}`);
|
|
1768
1767
|
return found === null ? [] : [found];
|
|
1769
1768
|
}), () => []);
|
|
1770
1769
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
@@ -1813,7 +1812,7 @@ async function serveFs(ctx, liveWorkspaces, req, res) {
|
|
|
1813
1812
|
if (req.method === "GET") {
|
|
1814
1813
|
if (url.searchParams.get("list") !== null) {
|
|
1815
1814
|
const entries = await ctx.fs.listDir(target);
|
|
1816
|
-
return json(200, { entries: entries.map(({ name
|
|
1815
|
+
return json(200, { entries: entries.map(({ name, type, size }) => ({ name, type, size })) });
|
|
1817
1816
|
}
|
|
1818
1817
|
if (url.searchParams.get("bytes") !== null) {
|
|
1819
1818
|
const bytes = await ctx.fs.readBytes(target, undefined, MAX_BINARY);
|
|
@@ -1986,11 +1985,13 @@ function apply(ctx, config = Config({})) {
|
|
|
1986
1985
|
configured = { allowExec, fiber: ctx.plugin({ name: "dsh-generative-ui:configured", apply: (scoped) => applyWith(scoped, allowExec) }) };
|
|
1987
1986
|
};
|
|
1988
1987
|
rebuild();
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1988
|
+
ctx.inject(["settings"], (sctx) => {
|
|
1989
|
+
sctx.settings.installSection(ctx, SETTINGS_NAMESPACE, Config, config, {
|
|
1990
|
+
setSource: (source) => {
|
|
1991
|
+
current = source;
|
|
1992
|
+
},
|
|
1993
|
+
onChange: rebuild
|
|
1994
|
+
});
|
|
1994
1995
|
});
|
|
1995
1996
|
ctx.effect(() => () => void configured?.fiber.dispose(), "dsh-generative-ui: settings scope");
|
|
1996
1997
|
}
|
|
@@ -20,6 +20,10 @@ export type InlineFenceOptions = {
|
|
|
20
20
|
streaming: boolean;
|
|
21
21
|
last: () => boolean;
|
|
22
22
|
}) => ReactElement;
|
|
23
|
+
/** 在预览渲染时读取当前语言,而不是捕获挂载时的翻译。 */
|
|
24
|
+
t: (key: "copy" | "copied") => string;
|
|
25
|
+
/** 语言或字典变化只刷新源码预览,不重新挂载卡片。 */
|
|
26
|
+
subscribeLocale: (refresh: () => void) => () => void;
|
|
23
27
|
scope?: HTMLElement;
|
|
24
28
|
};
|
|
25
29
|
/**
|
|
@@ -47,4 +51,4 @@ export type InlineFenceOptions = {
|
|
|
47
51
|
* store. The segment list is the transcript's own order and survives all three.
|
|
48
52
|
*/
|
|
49
53
|
export declare const isLastSegment: (segments: readonly Ui4aSegment[], code: string) => boolean;
|
|
50
|
-
export declare function claimInlineFences({ segments, render, scope }: InlineFenceOptions): () => void;
|
|
54
|
+
export declare function claimInlineFences({ segments, render, t, subscribeLocale, scope }: InlineFenceOptions): () => void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ declare module "@deepseek-ai/dsh-system-prompt" {
|
|
|
32
32
|
export declare const name = "dsh-generative-ui";
|
|
33
33
|
export declare const inject: string[];
|
|
34
34
|
/** The settings section this plugin owns; the key under `dsh-generative-ui:` in settings.yaml. */
|
|
35
|
-
export declare const SETTINGS_NAMESPACE
|
|
35
|
+
export declare const SETTINGS_NAMESPACE = "dsh-generative-ui";
|
|
36
36
|
/**
|
|
37
37
|
* Plugin settings. A schemastery schema, not a TypeScript type: the host validates the
|
|
38
38
|
* `settings.yaml` section against it and builds the settings UI from it, so a plain interface
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-generative-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Generative UI for DeepSeek Harness — the agent writes TSX, dsh web renders it live, inline in chat and in a canvas panel",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"build": "bun scripts/gen-standalone.ts && bun scripts/build.ts && tsc -p tsconfig.types.json",
|
|
57
57
|
"prepare": "bun scripts/gen-standalone.ts && bun scripts/build.ts && tsc -p tsconfig.types.json",
|
|
58
58
|
"typecheck": "tsc --noEmit",
|
|
59
|
-
"test": "bun test",
|
|
59
|
+
"test": "node scripts/primitives-deps.mjs && bun test",
|
|
60
60
|
"loads": "sh scripts/loads.sh",
|
|
61
61
|
"smoke": "bun scripts/smoke.ts",
|
|
62
62
|
"check": "bun run lint && bun run typecheck && bun run test && bun run test:shuffled && bun run audit && bun run replay && bun run cards && bun run paint && bun run build && bun run smoke && bun run loads",
|
|
@@ -81,45 +81,62 @@
|
|
|
81
81
|
"partial-tsx": "~0.0.3"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
85
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
86
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.
|
|
87
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
88
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
89
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
90
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
91
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
92
|
-
"@deepseek-ai/dsh-skill": "^0.1.
|
|
93
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
84
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
85
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-skill": "^0.1.5-rc.1",
|
|
93
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
94
94
|
"react": "^18.2.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@babel/parser": "^8.0.4",
|
|
98
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
99
|
-
"@deepseek-ai/dsh-client-locale": "0.1.
|
|
100
|
-
"@deepseek-ai/dsh-client-runtime": "0.1.
|
|
101
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.1.
|
|
102
|
-
"@deepseek-ai/dsh-client-ui-layout": "0.1.
|
|
103
|
-
"@deepseek-ai/dsh-client-ui-
|
|
104
|
-
"@deepseek-ai/dsh-
|
|
105
|
-
"@deepseek-ai/dsh-
|
|
106
|
-
"@deepseek-ai/dsh-
|
|
107
|
-
"@deepseek-ai/dsh-
|
|
108
|
-
"@deepseek-ai/dsh-
|
|
109
|
-
"@deepseek-ai/
|
|
98
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
99
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.1.5-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.1",
|
|
104
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.1",
|
|
105
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.1",
|
|
106
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.1",
|
|
107
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.1",
|
|
108
|
+
"@deepseek-ai/dsh-skill": "0.1.5-rc.1",
|
|
109
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.5-rc.1",
|
|
110
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
111
|
+
"@shikijs/langs": "^4.4.3",
|
|
110
112
|
"@types/node": "^26.3.0",
|
|
111
113
|
"@types/react": "~18.3.31",
|
|
112
114
|
"@types/react-dom": "~18.3.7",
|
|
113
115
|
"@types/scheduler": "^0.26.0",
|
|
116
|
+
"anser": "^2.3.5",
|
|
117
|
+
"clsx": "^2.1.1",
|
|
118
|
+
"katex": "^0.16.47",
|
|
119
|
+
"mdast-util-from-markdown": "^2.0.3",
|
|
120
|
+
"mdast-util-gfm": "^3.1.0",
|
|
121
|
+
"mdast-util-math": "^3.0.0",
|
|
122
|
+
"micromark-core-commonmark": "^2.0.3",
|
|
123
|
+
"micromark-extension-gfm": "^3.0.0",
|
|
124
|
+
"micromark-extension-math": "^3.1.0",
|
|
125
|
+
"micromark-factory-space": "^2.0.1",
|
|
126
|
+
"micromark-util-character": "^2.1.1",
|
|
127
|
+
"micromark-util-classify-character": "^2.0.1",
|
|
128
|
+
"micromark-util-sanitize-uri": "^2.0.1",
|
|
129
|
+
"micromark-util-symbol": "^2.0.1",
|
|
114
130
|
"micromatch": "^4.0.8",
|
|
115
131
|
"minimatch": "^10.2.6",
|
|
116
132
|
"motion": "^13.1.1",
|
|
117
|
-
"oxfmt": "^0.
|
|
133
|
+
"oxfmt": "^0.67.0",
|
|
118
134
|
"oxlint": "^1.80.0",
|
|
119
135
|
"picomatch": "^4.0.7",
|
|
120
136
|
"pkg-pr-new": "^0.0.88",
|
|
121
137
|
"react": "^18.3.1",
|
|
122
138
|
"react-dom": "^18.3.1",
|
|
139
|
+
"shiki": "^4.4.3",
|
|
123
140
|
"typescript": "^7.0.2"
|
|
124
141
|
},
|
|
125
142
|
"peerDependenciesMeta": {
|
package/src/client/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { createElement } from "react";
|
|
|
7
7
|
import type { ClientContext } from "@deepseek-ai/dsh-client-runtime/client";
|
|
8
8
|
import type {} from "@deepseek-ai/dsh-client-ui-layout/client";
|
|
9
9
|
import type {} from "@deepseek-ai/dsh-client-ui-conversation/client";
|
|
10
|
+
import type {} from "@deepseek-ai/dsh-client-locale/client";
|
|
10
11
|
import { GenUISurface } from "./runtime/GenUISurface.tsx";
|
|
11
12
|
import { cancelPendingReport, cardRendered, reportCardError } from "./runtime/report-error.ts";
|
|
12
13
|
import { disposeCompiler } from "./runtime/compiler.ts";
|
|
@@ -22,7 +23,7 @@ import { toolCallsOf, type CallBlock, type ToolCallView } from "./canvas/collect
|
|
|
22
23
|
import { canvasIdOf } from "../contract.ts";
|
|
23
24
|
import { CARD_ERROR_PATH } from "../contract-assets.ts";
|
|
24
25
|
|
|
25
|
-
export const inject = ["sessions"];
|
|
26
|
+
export const inject = ["sessions", "locale"];
|
|
26
27
|
|
|
27
28
|
/** Re-exported so `bun run smoke` can build the synthesized blob modules and parse them. */
|
|
28
29
|
export { localImports };
|
|
@@ -191,6 +192,8 @@ export function apply(ctx: ClientContext): void {
|
|
|
191
192
|
() =>
|
|
192
193
|
claimInlineFences({
|
|
193
194
|
segments,
|
|
195
|
+
t: ctx.locale.bind("common"),
|
|
196
|
+
subscribeLocale: (refresh) => ctx.locale.subscribe(refresh),
|
|
194
197
|
// The SAME gate on both callbacks. A card that may not report a failure may not retract
|
|
195
198
|
// one either — see `cardRendered`.
|
|
196
199
|
render: ({ code, streaming, last }) => createElement(GenUISurface, { code, streaming, onError: (error, phase) => reportCardError(sendToModel, error.message, phase, last), onRendered: (restored) => cardRendered(restored, last) }),
|
|
@@ -116,6 +116,10 @@ export type InlineFenceOptions = {
|
|
|
116
116
|
segments: () => readonly Ui4aSegment[];
|
|
117
117
|
/** `last` answers whether this card is still the transcript's newest — asked at report time, not render time. */
|
|
118
118
|
render: (props: { code: string; streaming: boolean; last: () => boolean }) => ReactElement;
|
|
119
|
+
/** 在预览渲染时读取当前语言,而不是捕获挂载时的翻译。 */
|
|
120
|
+
t: (key: "copy" | "copied") => string;
|
|
121
|
+
/** 语言或字典变化只刷新源码预览,不重新挂载卡片。 */
|
|
122
|
+
subscribeLocale: (refresh: () => void) => () => void;
|
|
119
123
|
scope?: HTMLElement;
|
|
120
124
|
};
|
|
121
125
|
|
|
@@ -158,7 +162,7 @@ export const isLastSegment = (segments: readonly Ui4aSegment[], code: string): b
|
|
|
158
162
|
*/
|
|
159
163
|
const NEAR_VIEWPORT = "100% 0px";
|
|
160
164
|
|
|
161
|
-
export function claimInlineFences({ segments, render, scope }: InlineFenceOptions): () => void {
|
|
165
|
+
export function claimInlineFences({ segments, render, t, subscribeLocale, scope }: InlineFenceOptions): () => void {
|
|
162
166
|
const claims = new Map<HTMLElement, Claim>();
|
|
163
167
|
const root = scope ?? document.body;
|
|
164
168
|
|
|
@@ -381,14 +385,18 @@ export function claimInlineFences({ segments, render, scope }: InlineFenceOption
|
|
|
381
385
|
// The preview follows the SEGMENT, not the block: mid-stream the snapshot runs ahead of
|
|
382
386
|
// what markdown has painted, so this is the newer text and the one the reader wants while
|
|
383
387
|
// waiting. Dropped the moment the card paints.
|
|
384
|
-
claim.preview?.root.render(createElement(CodeBlock, { code, lang: "tsx" }));
|
|
388
|
+
claim.preview?.root.render(createElement(CodeBlock, { code, lang: "tsx", copyLabel: t("copy"), copiedLabel: t("copied") }));
|
|
385
389
|
}
|
|
386
390
|
};
|
|
387
391
|
|
|
388
392
|
const stop = observeTranscript(sweep);
|
|
393
|
+
const stopLocale = subscribeLocale(() => {
|
|
394
|
+
for (const claim of claims.values()) claim.preview?.root.render(createElement(CodeBlock, { code: claim.code, lang: "tsx", copyLabel: t("copy"), copiedLabel: t("copied") }));
|
|
395
|
+
});
|
|
389
396
|
|
|
390
397
|
return () => {
|
|
391
398
|
stop();
|
|
399
|
+
stopLocale();
|
|
392
400
|
// `disconnect` rather than unobserving each: the observer is going away with us, and a
|
|
393
401
|
// parked block that is never claimed would otherwise keep it alive through its target list.
|
|
394
402
|
nearby?.disconnect();
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { createRequire } from "node:module";
|
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import type { Context } from "@deepseek-ai/cordis";
|
|
19
19
|
import z from "@deepseek-ai/schemastery";
|
|
20
|
-
import {
|
|
20
|
+
import type {} from "@deepseek-ai/dsh-settings";
|
|
21
21
|
import type {} from "@deepseek-ai/dsh-host-webserver";
|
|
22
22
|
import type {} from "@deepseek-ai/dsh-system-prompt";
|
|
23
23
|
import type {} from "@deepseek-ai/dsh-skill";
|
|
@@ -50,7 +50,7 @@ export const name = "dsh-generative-ui";
|
|
|
50
50
|
export const inject = ["systemPrompt"];
|
|
51
51
|
|
|
52
52
|
/** The settings section this plugin owns; the key under `dsh-generative-ui:` in settings.yaml. */
|
|
53
|
-
export const SETTINGS_NAMESPACE =
|
|
53
|
+
export const SETTINGS_NAMESPACE = name;
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Plugin settings. A schemastery schema, not a TypeScript type: the host validates the
|
|
@@ -541,16 +541,15 @@ export function apply(ctx: Context, config: Config = Config({})): void {
|
|
|
541
541
|
void configured?.fiber.dispose();
|
|
542
542
|
configured = { allowExec, fiber: ctx.plugin({ name: "dsh-generative-ui:configured", apply: (scoped: Context) => applyWith(scoped, allowExec) }) };
|
|
543
543
|
};
|
|
544
|
-
//
|
|
545
|
-
// `installSettingsSection` sits inside `ctx.inject(["settings"])`, so on a host with no settings
|
|
546
|
-
// service — `dsh --profile headless` is one — `onChange` never fires at all and nothing would
|
|
547
|
-
// ever mount. The `mounted` check above is what keeps this from double-mounting where it does.
|
|
544
|
+
// 无 settings 服务时下面的 inject 不执行,必须先用 entry 配置挂载;rebuild 内的值比较避免重复挂载。
|
|
548
545
|
rebuild();
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
546
|
+
ctx.inject(["settings"], (sctx) => {
|
|
547
|
+
sctx.settings.installSection(ctx, SETTINGS_NAMESPACE, Config, config, {
|
|
548
|
+
setSource: (source) => {
|
|
549
|
+
current = source;
|
|
550
|
+
},
|
|
551
|
+
onChange: rebuild,
|
|
552
|
+
});
|
|
554
553
|
});
|
|
555
554
|
ctx.effect(() => () => void configured?.fiber.dispose(), "dsh-generative-ui: settings scope");
|
|
556
555
|
}
|