dsh-better-display 0.3.7 → 0.3.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/CHANGELOG.md +11 -1
- package/docs/official-rendering-bridge.md +12 -0
- package/lib/client.js +221 -105
- package/lib/client.js.map +1 -1
- package/lib/dsh-better-display.js +297 -53
- package/lib/types/client/Reader.d.ts.map +1 -1
- package/lib/types/client/Reader.js +43 -12
- package/lib/types/client/Reader.js.map +1 -1
- package/lib/types/client/ToolActivity.d.ts.map +1 -1
- package/lib/types/client/ToolActivity.js +10 -5
- package/lib/types/client/ToolActivity.js.map +1 -1
- package/lib/types/client/deliverables.d.ts.map +1 -1
- package/lib/types/client/deliverables.js +2 -0
- package/lib/types/client/deliverables.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +9 -1
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/motion.d.ts.map +1 -1
- package/lib/types/client/motion.js +9 -1
- package/lib/types/client/motion.js.map +1 -1
- package/lib/types/client/official-slots.d.ts.map +1 -1
- package/lib/types/client/official-slots.js +6 -3
- package/lib/types/client/official-slots.js.map +1 -1
- package/lib/types/client/open-file.d.ts.map +1 -1
- package/lib/types/client/open-file.js +1 -6
- package/lib/types/client/open-file.js.map +1 -1
- package/lib/types/client/styles.d.ts +3 -0
- package/lib/types/client/styles.d.ts.map +1 -0
- package/lib/types/client/styles.js +25 -0
- package/lib/types/client/styles.js.map +1 -0
- package/lib/types/client/tool-activity.d.ts +2 -2
- package/lib/types/client/tool-activity.d.ts.map +1 -1
- package/lib/types/client/tool-activity.js +12 -7
- package/lib/types/client/tool-activity.js.map +1 -1
- package/lib/types/dsh-better-display.d.ts.map +1 -1
- package/lib/types/dsh-better-display.js +2 -39
- package/lib/types/dsh-better-display.js.map +1 -1
- package/lib/types/reveal.d.ts +12 -0
- package/lib/types/reveal.d.ts.map +1 -0
- package/lib/types/reveal.js +147 -0
- package/lib/types/reveal.js.map +1 -0
- package/lib/types/workspace-path.d.ts +3 -0
- package/lib/types/workspace-path.d.ts.map +1 -0
- package/lib/types/workspace-path.js +5 -0
- package/lib/types/workspace-path.js.map +1 -0
- package/package.json +1 -1
- package/src/client/Reader.module.css +41 -9
- package/src/client/Reader.tsx +56 -21
- package/src/client/ToolActivity.tsx +9 -5
- package/src/client/deliverables.ts +1 -0
- package/src/client/index.tsx +10 -1
- package/src/client/motion.tsx +8 -1
- package/src/client/official-slots.tsx +6 -3
- package/src/client/open-file.ts +2 -8
- package/src/client/styles.ts +22 -0
- package/src/client/tool-activity.ts +18 -7
- package/src/dsh-better-display.ts +2 -38
- package/src/reveal.ts +90 -0
- package/src/workspace-path.ts +4 -0
- package/tests/fixtures/reader-official-users.tsx +170 -0
- package/tests/footer-precedence.test.ts +8 -0
- package/tests/reader-glass-css.test.ts +28 -0
- package/tests/reader-official-users.test.ts +81 -0
- package/tests/retiring-content.test.ts +137 -0
- package/tests/reveal.test.ts +128 -0
- package/tests/stock-install.test.ts +8 -0
- package/tests/styles.test.ts +48 -0
- package/tests/terminal-input-preview.test.ts +113 -0
- package/tsdown.config.ts +49 -1
|
@@ -1,6 +1,297 @@
|
|
|
1
|
+
import { access, readdir } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
1
6
|
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
|
|
7
|
+
//#region src/skill-status.ts
|
|
8
|
+
/** Shared generative-mcpapps identity. Plugin-tree presence is not installation. */
|
|
9
|
+
const GENERATIVE_MCPAPPS_SKILL = "generative-mcpapps";
|
|
10
|
+
/** Conventional relative roots only. Never expand $HOME or dump host `root.path`. */
|
|
11
|
+
const CONVENTIONAL_SKILL_ROOTS = [".dsh/skills", ".agents/skills"];
|
|
12
|
+
function skillNameMatches(name) {
|
|
13
|
+
return typeof name === "string" && name === "generative-mcpapps";
|
|
14
|
+
}
|
|
15
|
+
function skillListIncludes(entries) {
|
|
16
|
+
return Array.isArray(entries) && entries.some((entry) => skillNameMatches(entry?.name));
|
|
17
|
+
}
|
|
18
|
+
/** Directory bundle `name/SKILL.md` or flat `name.md` at a scanned root. */
|
|
19
|
+
function skillRootEntryMatches(entryName) {
|
|
20
|
+
return entryName === "generative-mcpapps" || entryName === `generative-mcpapps.md`;
|
|
21
|
+
}
|
|
22
|
+
function skillsFromListResult(result) {
|
|
23
|
+
if (Array.isArray(result)) return result;
|
|
24
|
+
if (result === null || typeof result !== "object") return [];
|
|
25
|
+
const record = result;
|
|
26
|
+
if (record.ok === false) return [];
|
|
27
|
+
if (Array.isArray(record.skills)) return record.skills;
|
|
28
|
+
if (record.value && typeof record.value === "object") {
|
|
29
|
+
const value = record.value;
|
|
30
|
+
if (Array.isArray(value.skills)) return value.skills;
|
|
31
|
+
if (Array.isArray(record.value)) return record.value;
|
|
32
|
+
}
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
function publicSkillRoots() {
|
|
36
|
+
return CONVENTIONAL_SKILL_ROOTS.map((path) => ({
|
|
37
|
+
source: "conventional",
|
|
38
|
+
path
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip host home / pack absolutes before anything user-facing (HTTP or Settings).
|
|
43
|
+
* Detection still uses the raw scan; this is the public face.
|
|
44
|
+
*/
|
|
45
|
+
function toPublicSkillStatus(status) {
|
|
46
|
+
return {
|
|
47
|
+
name: status.name,
|
|
48
|
+
installed: status.installed,
|
|
49
|
+
via: status.via,
|
|
50
|
+
roots: publicSkillRoots()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/skill-roots.ts
|
|
55
|
+
function expandHomePath(path, home = homedir()) {
|
|
56
|
+
if (path === "~") return home;
|
|
57
|
+
if (path.startsWith("~/") || path.startsWith("~\\")) return join(home, path.slice(2));
|
|
58
|
+
return path;
|
|
59
|
+
}
|
|
60
|
+
/** `$DSH_HOME` or `~/.dsh`, matching official `resolveDshHome`. */
|
|
61
|
+
function resolveDshHome(env = process.env, home = homedir()) {
|
|
62
|
+
const fromEnv = env.DSH_HOME;
|
|
63
|
+
const selected = fromEnv !== void 0 && fromEnv.trim().length > 0 ? fromEnv : join(home, ".dsh");
|
|
64
|
+
return resolve(expandHomePath(selected, home));
|
|
65
|
+
}
|
|
66
|
+
/** `$DSH_AGENTS_HOME` or `~/.agents`. */
|
|
67
|
+
function resolveAgentsHome(env = process.env, home = homedir()) {
|
|
68
|
+
const fromEnv = env.DSH_AGENTS_HOME;
|
|
69
|
+
const selected = fromEnv !== void 0 && fromEnv.trim().length > 0 ? fromEnv : join(home, ".agents");
|
|
70
|
+
return resolve(expandHomePath(selected, home));
|
|
71
|
+
}
|
|
72
|
+
function userSkillRoots(env = process.env, home = homedir()) {
|
|
73
|
+
return [{
|
|
74
|
+
source: "user-dsh",
|
|
75
|
+
path: join(resolveDshHome(env, home), "skills")
|
|
76
|
+
}, {
|
|
77
|
+
source: "user-agents",
|
|
78
|
+
path: join(resolveAgentsHome(env, home), "skills")
|
|
79
|
+
}];
|
|
80
|
+
}
|
|
81
|
+
function projectSkillRoots(projectRoot) {
|
|
82
|
+
return [{
|
|
83
|
+
source: "project-dsh",
|
|
84
|
+
path: join(projectRoot, ".dsh", "skills")
|
|
85
|
+
}, {
|
|
86
|
+
source: "project-agents",
|
|
87
|
+
path: join(projectRoot, ".agents", "skills")
|
|
88
|
+
}];
|
|
89
|
+
}
|
|
90
|
+
async function findProjectRoot(cwd) {
|
|
91
|
+
let current = resolve(cwd);
|
|
92
|
+
while (true) {
|
|
93
|
+
if (existsSync(join(current, ".git"))) return current;
|
|
94
|
+
const parent = dirname(current);
|
|
95
|
+
if (parent === current) return resolve(cwd);
|
|
96
|
+
current = parent;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function rootHasGenerativeMcpapps(rootPath) {
|
|
100
|
+
let entries;
|
|
101
|
+
try {
|
|
102
|
+
entries = await readdir(rootPath, { withFileTypes: true });
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
if (entry.name === ".system") continue;
|
|
108
|
+
if (!skillRootEntryMatches(entry.name)) continue;
|
|
109
|
+
if (entry.isDirectory()) try {
|
|
110
|
+
await access(join(rootPath, entry.name, "SKILL.md"));
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (entry.isFile()) return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
/** Resolve the plugin-shipped pack. This path is never treated as a skill root. */
|
|
120
|
+
function pluginSkillPackPath(moduleUrl = import.meta.url) {
|
|
121
|
+
const here = dirname(fileURLToPath(moduleUrl));
|
|
122
|
+
return [
|
|
123
|
+
join(here, "../skills", GENERATIVE_MCPAPPS_SKILL),
|
|
124
|
+
join(here, "../../skills", GENERATIVE_MCPAPPS_SKILL),
|
|
125
|
+
join(here, "skills", GENERATIVE_MCPAPPS_SKILL)
|
|
126
|
+
].find((path) => existsSync(join(path, "SKILL.md")));
|
|
127
|
+
}
|
|
128
|
+
async function scanGenerativeMcpappsStatus(options = {}) {
|
|
129
|
+
const roots = [...userSkillRoots(options.env ?? process.env, options.home ?? homedir())];
|
|
130
|
+
const cwd = typeof options.cwd === "string" && options.cwd.trim().length > 0 ? isAbsolute(options.cwd) ? options.cwd : resolve(options.cwd) : void 0;
|
|
131
|
+
if (cwd !== void 0) roots.unshift(...projectSkillRoots(await findProjectRoot(cwd)));
|
|
132
|
+
const inspected = [];
|
|
133
|
+
let rootHit = false;
|
|
134
|
+
for (const root of roots) {
|
|
135
|
+
const present = await rootHasGenerativeMcpapps(root.path);
|
|
136
|
+
inspected.push({
|
|
137
|
+
...root,
|
|
138
|
+
present
|
|
139
|
+
});
|
|
140
|
+
if (present) rootHit = true;
|
|
141
|
+
}
|
|
142
|
+
let via = null;
|
|
143
|
+
let listHit = false;
|
|
144
|
+
if (options.listSkills) try {
|
|
145
|
+
listHit = skillListIncludes(await options.listSkills());
|
|
146
|
+
if (listHit) via = "skills.list";
|
|
147
|
+
} catch {}
|
|
148
|
+
if (!listHit && rootHit) via = "skill-root";
|
|
149
|
+
const packPath = options.packPath ?? pluginSkillPackPath();
|
|
150
|
+
return {
|
|
151
|
+
name: GENERATIVE_MCPAPPS_SKILL,
|
|
152
|
+
installed: listHit || rootHit,
|
|
153
|
+
via,
|
|
154
|
+
roots: inspected,
|
|
155
|
+
...packPath !== void 0 ? { packPath } : {}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/workspace-path.ts
|
|
160
|
+
/** Absolute paths accepted by both POSIX and Windows hosts. */
|
|
161
|
+
function isAbsoluteWorkspacePath(path) {
|
|
162
|
+
return /^(?:\/|[A-Za-z]:[/\\]|\\\\)/.test(path);
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region \0@oxc-project+runtime@0.153.0/helpers/esm/usingCtx.js
|
|
166
|
+
function _usingCtx() {
|
|
167
|
+
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
168
|
+
var n = Error();
|
|
169
|
+
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
170
|
+
}, e = {}, n = [];
|
|
171
|
+
function using(r, e) {
|
|
172
|
+
if (null != e) {
|
|
173
|
+
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
174
|
+
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
175
|
+
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
176
|
+
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
177
|
+
t && (o = function o() {
|
|
178
|
+
try {
|
|
179
|
+
t.call(e);
|
|
180
|
+
} catch (r) {
|
|
181
|
+
return Promise.reject(r);
|
|
182
|
+
}
|
|
183
|
+
}), n.push({
|
|
184
|
+
v: e,
|
|
185
|
+
d: o,
|
|
186
|
+
a: r
|
|
187
|
+
});
|
|
188
|
+
} else r && n.push({
|
|
189
|
+
d: e,
|
|
190
|
+
a: r
|
|
191
|
+
});
|
|
192
|
+
return e;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
e,
|
|
196
|
+
u: using.bind(null, !1),
|
|
197
|
+
a: using.bind(null, !0),
|
|
198
|
+
d: function d() {
|
|
199
|
+
var o, t = this.e, s = 0;
|
|
200
|
+
function next() {
|
|
201
|
+
for (; o = n.pop();) try {
|
|
202
|
+
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
203
|
+
if (o.d) {
|
|
204
|
+
var r = o.d.call(o.v);
|
|
205
|
+
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
206
|
+
} else s |= 1;
|
|
207
|
+
} catch (r) {
|
|
208
|
+
return err(r);
|
|
209
|
+
}
|
|
210
|
+
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
211
|
+
if (t !== e) throw t;
|
|
212
|
+
}
|
|
213
|
+
function err(n) {
|
|
214
|
+
return t = t !== e ? new r(n, t) : n, next();
|
|
215
|
+
}
|
|
216
|
+
return next();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/reveal.ts
|
|
222
|
+
async function revealRoots(ctx, sessionId) {
|
|
223
|
+
if (sessionId !== void 0) {
|
|
224
|
+
const cwd = (ctx.get?.("sessions"))?.get(sessionId)?.header.cwd;
|
|
225
|
+
if (cwd) return [cwd];
|
|
226
|
+
const query = ctx.get?.("sessionQuery");
|
|
227
|
+
if (query) try {
|
|
228
|
+
var _usingCtx$1 = _usingCtx();
|
|
229
|
+
const observation = _usingCtx$1.u(await query.observeSession(sessionId, { projectionMode: "none" }));
|
|
230
|
+
if (observation.header.cwd) return [observation.header.cwd];
|
|
231
|
+
} catch (_) {
|
|
232
|
+
_usingCtx$1.e = _;
|
|
233
|
+
} finally {
|
|
234
|
+
_usingCtx$1.d();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return ((ctx.get?.("workspaceRegistry"))?.list() ?? []).filter((workspace) => sessionId === void 0 || workspace.sessionIds.includes(sessionId)).map((workspace) => workspace.path);
|
|
238
|
+
}
|
|
239
|
+
function resolveRevealPath(path, roots, exists = existsSync) {
|
|
240
|
+
if (isAbsoluteWorkspacePath(path)) return path;
|
|
241
|
+
if (/^[A-Za-z]:|^\\/.test(path)) throw new Error("Path is not workspace-relative");
|
|
242
|
+
const matches = /* @__PURE__ */ new Set();
|
|
243
|
+
for (const root of roots) {
|
|
244
|
+
if (!isAbsoluteWorkspacePath(root)) continue;
|
|
245
|
+
const candidate = (/^[A-Za-z]:[/\\]|^\\\\/.test(root) ? win32 : posix).resolve(root, path);
|
|
246
|
+
if (exists(candidate)) matches.add(candidate);
|
|
247
|
+
}
|
|
248
|
+
if (matches.size !== 1) throw new Error(matches.size === 0 ? "Workspace path not found" : "Workspace path is ambiguous");
|
|
249
|
+
return [...matches][0];
|
|
250
|
+
}
|
|
251
|
+
function revealPath(path, platform = process.platform, spawnProcess = spawn) {
|
|
252
|
+
const options = {
|
|
253
|
+
detached: true,
|
|
254
|
+
stdio: "ignore"
|
|
255
|
+
};
|
|
256
|
+
if (platform === "darwin") spawnProcess("open", ["-R", path], options);
|
|
257
|
+
else if (platform === "win32") spawnProcess("explorer.exe", [`/select,"${path}"`], {
|
|
258
|
+
...options,
|
|
259
|
+
windowsVerbatimArguments: true
|
|
260
|
+
});
|
|
261
|
+
else spawnProcess("xdg-open", [path], options);
|
|
262
|
+
}
|
|
263
|
+
function createRevealHandler({ roots, reveal = revealPath }) {
|
|
264
|
+
return (req, res) => {
|
|
265
|
+
if (req.method !== "POST") {
|
|
266
|
+
res.statusCode = 405;
|
|
267
|
+
res.end();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let body = "";
|
|
271
|
+
req.on("data", (chunk) => {
|
|
272
|
+
body += chunk;
|
|
273
|
+
});
|
|
274
|
+
req.on("end", async () => {
|
|
275
|
+
res.setHeader("Content-Type", "application/json");
|
|
276
|
+
try {
|
|
277
|
+
const data = JSON.parse(body);
|
|
278
|
+
const path = typeof data === "object" && data !== null && "path" in data && typeof data.path === "string" ? data.path.trim() : "";
|
|
279
|
+
if (!path) throw new Error("Empty path");
|
|
280
|
+
const sessionId = typeof data === "object" && data !== null && "sessionId" in data && typeof data.sessionId === "string" ? data.sessionId : void 0;
|
|
281
|
+
reveal(resolveRevealPath(path, isAbsoluteWorkspacePath(path) ? [] : await roots(sessionId)));
|
|
282
|
+
res.statusCode = 200;
|
|
283
|
+
res.end(JSON.stringify({ ok: true }));
|
|
284
|
+
} catch (err) {
|
|
285
|
+
res.statusCode = 400;
|
|
286
|
+
res.end(JSON.stringify({
|
|
287
|
+
ok: false,
|
|
288
|
+
error: String(err)
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
4
295
|
//#region src/dsh-better-display.ts
|
|
5
296
|
const name = "dsh-better-display";
|
|
6
297
|
const inject = ["webServer"];
|
|
@@ -20,52 +311,7 @@ function apply(ctx) {
|
|
|
20
311
|
const disposeReveal = ctx.webServer.register({
|
|
21
312
|
kind: "exact",
|
|
22
313
|
path: "/better-display/reveal",
|
|
23
|
-
handler:
|
|
24
|
-
if (req.method !== "POST") {
|
|
25
|
-
res.statusCode = 405;
|
|
26
|
-
res.end();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
let body = "";
|
|
30
|
-
req.on("data", (chunk) => {
|
|
31
|
-
body += chunk;
|
|
32
|
-
});
|
|
33
|
-
req.on("end", () => {
|
|
34
|
-
try {
|
|
35
|
-
const data = JSON.parse(body);
|
|
36
|
-
const targetPath = typeof data.path === "string" ? data.path.trim() : "";
|
|
37
|
-
if (!targetPath) {
|
|
38
|
-
res.statusCode = 400;
|
|
39
|
-
res.end(JSON.stringify({
|
|
40
|
-
ok: false,
|
|
41
|
-
error: "Empty path"
|
|
42
|
-
}));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (process.platform === "darwin") spawn("open", ["-R", targetPath], {
|
|
46
|
-
detached: true,
|
|
47
|
-
stdio: "ignore"
|
|
48
|
-
});
|
|
49
|
-
else if (process.platform === "win32") spawn("explorer.exe", [`/select,${targetPath}`], {
|
|
50
|
-
detached: true,
|
|
51
|
-
stdio: "ignore"
|
|
52
|
-
});
|
|
53
|
-
else spawn("xdg-open", [targetPath], {
|
|
54
|
-
detached: true,
|
|
55
|
-
stdio: "ignore"
|
|
56
|
-
});
|
|
57
|
-
res.setHeader("Content-Type", "application/json");
|
|
58
|
-
res.statusCode = 200;
|
|
59
|
-
res.end(JSON.stringify({ ok: true }));
|
|
60
|
-
} catch (err) {
|
|
61
|
-
res.statusCode = 400;
|
|
62
|
-
res.end(JSON.stringify({
|
|
63
|
-
ok: false,
|
|
64
|
-
error: String(err)
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
314
|
+
handler: createRevealHandler({ roots: (sessionId) => revealRoots(ctx, sessionId) })
|
|
69
315
|
});
|
|
70
316
|
const disposeSkill = ctx.webServer.register({
|
|
71
317
|
kind: "exact",
|
|
@@ -77,12 +323,10 @@ function apply(ctx) {
|
|
|
77
323
|
return;
|
|
78
324
|
}
|
|
79
325
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
cwd,
|
|
326
|
+
writeJson(res, 200, toPublicSkillStatus(await scanGenerativeMcpappsStatus({
|
|
327
|
+
cwd: new URL(req.url ?? "", "http://127.0.0.1").searchParams.get("cwd") ?? void 0,
|
|
83
328
|
listSkills: skillLister(ctx)
|
|
84
|
-
});
|
|
85
|
-
writeJson(res, 200, toPublicSkillStatus(status));
|
|
329
|
+
})));
|
|
86
330
|
} catch (err) {
|
|
87
331
|
writeJson(res, 500, {
|
|
88
332
|
ok: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reader.d.ts","sourceRoot":"","sources":["../../../src/client/Reader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Reader.d.ts","sourceRoot":"","sources":["../../../src/client/Reader.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,YAAY,CAAC;AAooBhE,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAoRxC"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
2
|
+
import { Fragment, memo, useCallback, useEffect, useId, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
|
+
import { createSnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
3
4
|
import { JsonBlock, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
5
|
import { BlockBoundary, Blocks, contentBlocks, CopyAnswer, UserMessageActions } from './Blocks.js';
|
|
5
6
|
import { ReasoningCard } from './ReasoningCard.js';
|
|
6
7
|
import { ToolActivity, ToolMedia } from './ToolActivity.js';
|
|
7
8
|
import { OfficialActions, OfficialNode, OfficialTail } from './OfficialContent.js';
|
|
9
|
+
import { OFFICIAL_SEATS } from './official-slots.js';
|
|
8
10
|
import { preparingLabel, readerFlow } from './tool-activity.js';
|
|
9
11
|
import { Disclosure, ProcessFragment, RetiringContent, StatusText, useMotionAllowed, usePinnedSelection, useReadingScroll } from './motion.js';
|
|
10
12
|
import { StreamMotionContext } from './streaming.js';
|
|
@@ -26,6 +28,7 @@ import { StickyLane } from './StickyLane.js';
|
|
|
26
28
|
import css from './Reader.module.css';
|
|
27
29
|
import { markdownLabels, truncatedJsonLabel } from './primitive-labels.js';
|
|
28
30
|
import { chatSeatProps } from './chat-seat.js';
|
|
31
|
+
import { ensureStyles } from './styles.js';
|
|
29
32
|
function isNode(node, kind) {
|
|
30
33
|
return node.kind === kind;
|
|
31
34
|
}
|
|
@@ -141,6 +144,7 @@ const AssistantNode = memo(function AssistantNode({ useChat, nodeKey, boundary,
|
|
|
141
144
|
});
|
|
142
145
|
const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen = false, ...render }) {
|
|
143
146
|
const node = useChat(snapshot => snapshot.nodes.get(nodeKey));
|
|
147
|
+
const [disclosureReset] = useState(() => createSnapshotStore(0));
|
|
144
148
|
if (!node || node.visibility === 'hidden')
|
|
145
149
|
return null;
|
|
146
150
|
if (isNode(node, 'user') || isNode(node, 'steering')) {
|
|
@@ -149,7 +153,30 @@ const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, pr
|
|
|
149
153
|
const otherBlocks = blocks.filter(b => b.kind !== 'image');
|
|
150
154
|
const text = otherBlocks.filter((block) => block.kind === 'text').map(block => block.text).join('\n\n');
|
|
151
155
|
const time = node.data.time;
|
|
152
|
-
|
|
156
|
+
const fallback = _jsxs(_Fragment, { children: [node.kind === 'steering' && _jsx("p", { className: css.meta, children: "\u8865\u5145\u6D88\u606F" }), imageBlocks.length > 0 && _jsx("div", { className: css.userImages, children: _jsx(Blocks, { ...render, blocks: imageBlocks, source: "user" }) }), otherBlocks.length > 0 && _jsx("div", { className: css.user, children: _jsx(Blocks, { ...render, blocks: otherBlocks, source: "user" }) }), _jsx(UserMessageActions, { text: text, time: time })] });
|
|
157
|
+
const official = render.official;
|
|
158
|
+
let rendered = fallback;
|
|
159
|
+
if (official) {
|
|
160
|
+
const turn = node.location.kind === 'turn' || node.location.kind === 'step' ? node.location.turn : undefined;
|
|
161
|
+
const owner = {
|
|
162
|
+
cwd: render.cwd,
|
|
163
|
+
openFile: render.openFile ?? (() => { }),
|
|
164
|
+
openSkill: () => { },
|
|
165
|
+
forkAt: render.forkAt ?? (() => { }),
|
|
166
|
+
inspectCall: callId => official.openView('trajectory', callId),
|
|
167
|
+
loadImage: official.officialImageLoader,
|
|
168
|
+
renderMessageImages: images => official.renderSlot(OFFICIAL_SEATS.images, {
|
|
169
|
+
...images, loadImage: official.officialImageLoader,
|
|
170
|
+
}),
|
|
171
|
+
fileMentions: official.officialFileMentions,
|
|
172
|
+
sessionId: render.sessionId,
|
|
173
|
+
};
|
|
174
|
+
const options = { hookContext: { turnData: turn?.data, disclosureReset }, fallback };
|
|
175
|
+
rendered = node.kind === 'user'
|
|
176
|
+
? official.renderSlot(OFFICIAL_SEATS.nodes, { ...owner, node }, { ...options, entryKey: 'user' })
|
|
177
|
+
: official.renderSlot(OFFICIAL_SEATS.nodes, { ...owner, node }, { ...options, entryKey: 'steering' });
|
|
178
|
+
}
|
|
179
|
+
return _jsx("div", { className: css.userCluster, "data-reader-anchor": true, "data-reader-key": nodeKey, ...chatSeatProps(node.kind, nodeKey), children: rendered });
|
|
153
180
|
}
|
|
154
181
|
if (isNode(node, 'assistant-step'))
|
|
155
182
|
return null;
|
|
@@ -315,6 +342,8 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
315
342
|
const liveItems = useMemo(() => presentLiveTurn(steps, boundary, autoFold), [steps, boundary, autoFold]);
|
|
316
343
|
const openMode = deliverableOpenModeOf(useSyncExternalStore(props.openPrefs?.subscribe ?? ((fn) => { void fn; return () => { }; }), () => props.openPrefs?.getSnapshot()?.deliverableOpenMode, () => 'external'));
|
|
317
344
|
const hasProcess = flow.some(item => item.kind === 'tool' || hasProcessContent(nodes.get(item.nodeKey), boundary));
|
|
345
|
+
const showStatusLane = hasProcess && !isAwaitingModel;
|
|
346
|
+
const showClosedSummary = boundary.status === 'closed' && hasProcess && autoFold;
|
|
318
347
|
// Only a real, still-active text selection delays folding. Merely clicking,
|
|
319
348
|
// focusing or scrolling the live card does not create a permanent override.
|
|
320
349
|
const holdingSelection = selectedProcessKeys.some(key => flow.some(item => item.key === key)
|
|
@@ -383,6 +412,7 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
383
412
|
fileMentions,
|
|
384
413
|
metrics,
|
|
385
414
|
official,
|
|
415
|
+
sessionId: props.sessionId,
|
|
386
416
|
cwd,
|
|
387
417
|
};
|
|
388
418
|
const terminal = terminalLabel(boundary.reason);
|
|
@@ -404,18 +434,19 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
|
|
|
404
434
|
return _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, boundary: boundary, nodeKey: step.nodeKey }) });
|
|
405
435
|
return _jsxs(Fragment, { children: [_jsx(BlockBoundary, { children: _jsx(ProcessNode, { useChat: useFlowChat, t: props.t, nodeKey: step.nodeKey, open: processOpen, motion: motion, onRead: pinProcess, returnFocusTo: processButton }) }), _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, boundary: boundary, nodeKey: step.nodeKey, pinned: pinnedKeys.includes(step.nodeKey), processOpen: processOpen }) })] });
|
|
406
436
|
};
|
|
407
|
-
return _jsxs("section", { className: css.turn, "data-reader-turn": group.turn ?? 'unresolved', "data-reader-turn-state": boundary.status, "data-reader-turn-result": boundary.reason ?? undefined, children: [startsWithUser && _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, useChat: props.useChat, boundary: boundary, nodeKey: group.keys[0] }) }),
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
437
|
+
return _jsxs("section", { className: css.turn, "data-reader-turn": group.turn ?? 'unresolved', "data-reader-turn-state": boundary.status, "data-reader-turn-result": boundary.reason ?? undefined, children: [startsWithUser && _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, useChat: props.useChat, boundary: boundary, nodeKey: group.keys[0] }) }), _jsxs("div", { className: css.turnStickyRegion, "data-reader-sticky-region": true, children: [(showStatusLane || showClosedSummary) && _jsxs("div", { className: css.turnStickyLanes, children: [showStatusLane && _jsx(StickyLane, { kind: "status", className: css.turnProcessSticky, children: _jsx(Disclosure, { open: expanded, onChange: setExpanded, controls: flowId, buttonRef: processButton, label: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, pending: interaction, motion: motion }) }) }), showClosedSummary && _jsx(ClosedProcessSummary, { open: expanded, onChange: setExpanded, controls: flowId, steps: steps.filter(step => {
|
|
438
|
+
if (step.kind === 'user')
|
|
439
|
+
return false;
|
|
440
|
+
if (step.kind !== 'body')
|
|
441
|
+
return true;
|
|
442
|
+
const node = nodes.get(step.nodeKey);
|
|
443
|
+
return !!node && isNode(node, 'assistant-step') && (isEarlierNarration(node.data, boundary)
|
|
444
|
+
|| node.data.blocks.some(block => block.kind === 'tool-call')
|
|
445
|
+
|| (boundary.latestStep > 0 && node.data.step < boundary.latestStep));
|
|
446
|
+
}) })] }), _jsx(ChoreographedFlow, { id: flowId, frame: presentation, motion: motion, enabled: autoFold && boundary.status === 'open' && !holdingSelection, urgent: hasTurnError || interaction !== undefined || !sessionRunning, open: foldOpenByKey, processOpen: expanded, onOpenChange: (key, value) => { pinProcess(); setFoldOpenByKey(current => ({ ...current, [key]: value })); }, renderStep: renderStep })] }), !hasProcess && boundary.status === 'open' && !isAwaitingModel && _jsx("div", { className: css.disclosure, "data-reader-status-only": true, children: _jsx(GroupStatus, { group: group, sessionId: props.sessionId, useChat: props.useChat, pending: interaction, motion: motion }) }), showDeliverablesRow(boundary.status, deliverables) && _jsx(DeliverablesRow, { deliverables: deliverables, openFile: props.openFile, revealFile: props.revealFile, openMode: openMode }), boundary.status === 'closed' && _jsx(BlockBoundary, { children: _jsx(OfficialTail, { official: official, owner: tailOwner, produced: deliverables }) }), showTerminalNotice && _jsx("div", { className: css.notice, "data-reader-terminal": true, children: terminal })] });
|
|
417
447
|
});
|
|
418
448
|
export function Reader(props) {
|
|
449
|
+
useInsertionEffect(() => { ensureStyles(); }, []);
|
|
419
450
|
const root = useRef(null);
|
|
420
451
|
const activatedAt = useRef(Date.now());
|
|
421
452
|
const order = props.useChat(snapshot => snapshot.order);
|