dsh-better-display 0.3.6 → 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.
Files changed (79) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/docs/official-rendering-bridge.md +16 -0
  3. package/lib/client.js +564 -394
  4. package/lib/client.js.map +1 -1
  5. package/lib/dsh-better-display.js +139 -48
  6. package/lib/types/client/OfficialContent.d.ts.map +1 -1
  7. package/lib/types/client/OfficialContent.js +2 -1
  8. package/lib/types/client/OfficialContent.js.map +1 -1
  9. package/lib/types/client/Reader.d.ts.map +1 -1
  10. package/lib/types/client/Reader.js +96 -20
  11. package/lib/types/client/Reader.js.map +1 -1
  12. package/lib/types/client/ToolActivity.d.ts.map +1 -1
  13. package/lib/types/client/ToolActivity.js +10 -5
  14. package/lib/types/client/ToolActivity.js.map +1 -1
  15. package/lib/types/client/chat-seat.d.ts +3 -0
  16. package/lib/types/client/chat-seat.d.ts.map +1 -0
  17. package/lib/types/client/chat-seat.js +9 -0
  18. package/lib/types/client/chat-seat.js.map +1 -0
  19. package/lib/types/client/deliverables.d.ts.map +1 -1
  20. package/lib/types/client/deliverables.js +2 -0
  21. package/lib/types/client/deliverables.js.map +1 -1
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/index.js +9 -1
  24. package/lib/types/client/index.js.map +1 -1
  25. package/lib/types/client/motion.d.ts.map +1 -1
  26. package/lib/types/client/motion.js +9 -1
  27. package/lib/types/client/motion.js.map +1 -1
  28. package/lib/types/client/official-slots.d.ts.map +1 -1
  29. package/lib/types/client/official-slots.js +6 -3
  30. package/lib/types/client/official-slots.js.map +1 -1
  31. package/lib/types/client/open-file.d.ts.map +1 -1
  32. package/lib/types/client/open-file.js +1 -6
  33. package/lib/types/client/open-file.js.map +1 -1
  34. package/lib/types/client/styles.d.ts +3 -0
  35. package/lib/types/client/styles.d.ts.map +1 -0
  36. package/lib/types/client/styles.js +25 -0
  37. package/lib/types/client/styles.js.map +1 -0
  38. package/lib/types/client/tool-activity.d.ts +2 -2
  39. package/lib/types/client/tool-activity.d.ts.map +1 -1
  40. package/lib/types/client/tool-activity.js +12 -7
  41. package/lib/types/client/tool-activity.js.map +1 -1
  42. package/lib/types/dsh-better-display.d.ts.map +1 -1
  43. package/lib/types/dsh-better-display.js +2 -39
  44. package/lib/types/dsh-better-display.js.map +1 -1
  45. package/lib/types/reveal.d.ts +12 -0
  46. package/lib/types/reveal.d.ts.map +1 -0
  47. package/lib/types/reveal.js +147 -0
  48. package/lib/types/reveal.js.map +1 -0
  49. package/lib/types/workspace-path.d.ts +3 -0
  50. package/lib/types/workspace-path.d.ts.map +1 -0
  51. package/lib/types/workspace-path.js +5 -0
  52. package/lib/types/workspace-path.js.map +1 -0
  53. package/package.json +1 -1
  54. package/src/client/OfficialContent.tsx +2 -1
  55. package/src/client/Reader.module.css +41 -9
  56. package/src/client/Reader.tsx +108 -32
  57. package/src/client/ToolActivity.tsx +9 -5
  58. package/src/client/chat-seat.ts +10 -0
  59. package/src/client/deliverables.ts +1 -0
  60. package/src/client/index.tsx +10 -1
  61. package/src/client/motion.tsx +8 -1
  62. package/src/client/official-slots.tsx +6 -3
  63. package/src/client/open-file.ts +2 -8
  64. package/src/client/styles.ts +22 -0
  65. package/src/client/tool-activity.ts +18 -7
  66. package/src/dsh-better-display.ts +2 -38
  67. package/src/reveal.ts +90 -0
  68. package/src/workspace-path.ts +4 -0
  69. package/tests/chat-flow-hook.test.ts +13 -0
  70. package/tests/fixtures/reader-official-users.tsx +170 -0
  71. package/tests/footer-precedence.test.ts +8 -0
  72. package/tests/reader-glass-css.test.ts +28 -0
  73. package/tests/reader-official-users.test.ts +81 -0
  74. package/tests/retiring-content.test.ts +137 -0
  75. package/tests/reveal.test.ts +128 -0
  76. package/tests/stock-install.test.ts +8 -0
  77. package/tests/styles.test.ts +48 -0
  78. package/tests/terminal-input-preview.test.ts +113 -0
  79. package/tsdown.config.ts +55 -0
@@ -1,9 +1,9 @@
1
- import { spawn } from "node:child_process";
2
1
  import { access, readdir } from "node:fs/promises";
3
2
  import { existsSync } from "node:fs";
4
3
  import { homedir } from "node:os";
5
- import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
6
5
  import { fileURLToPath } from "node:url";
6
+ import { spawn } from "node:child_process";
7
7
  //#region src/skill-status.ts
8
8
  /** Shared generative-mcpapps identity. Plugin-tree presence is not installation. */
9
9
  const GENERATIVE_MCPAPPS_SKILL = "generative-mcpapps";
@@ -156,6 +156,142 @@ async function scanGenerativeMcpappsStatus(options = {}) {
156
156
  };
157
157
  }
158
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
159
295
  //#region src/dsh-better-display.ts
160
296
  const name = "dsh-better-display";
161
297
  const inject = ["webServer"];
@@ -175,52 +311,7 @@ function apply(ctx) {
175
311
  const disposeReveal = ctx.webServer.register({
176
312
  kind: "exact",
177
313
  path: "/better-display/reveal",
178
- handler: async (req, res) => {
179
- if (req.method !== "POST") {
180
- res.statusCode = 405;
181
- res.end();
182
- return;
183
- }
184
- let body = "";
185
- req.on("data", (chunk) => {
186
- body += chunk;
187
- });
188
- req.on("end", () => {
189
- try {
190
- const data = JSON.parse(body);
191
- const targetPath = typeof data.path === "string" ? data.path.trim() : "";
192
- if (!targetPath) {
193
- res.statusCode = 400;
194
- res.end(JSON.stringify({
195
- ok: false,
196
- error: "Empty path"
197
- }));
198
- return;
199
- }
200
- if (process.platform === "darwin") spawn("open", ["-R", targetPath], {
201
- detached: true,
202
- stdio: "ignore"
203
- });
204
- else if (process.platform === "win32") spawn("explorer.exe", [`/select,${targetPath}`], {
205
- detached: true,
206
- stdio: "ignore"
207
- });
208
- else spawn("xdg-open", [targetPath], {
209
- detached: true,
210
- stdio: "ignore"
211
- });
212
- res.setHeader("Content-Type", "application/json");
213
- res.statusCode = 200;
214
- res.end(JSON.stringify({ ok: true }));
215
- } catch (err) {
216
- res.statusCode = 400;
217
- res.end(JSON.stringify({
218
- ok: false,
219
- error: String(err)
220
- }));
221
- }
222
- });
223
- }
314
+ handler: createRevealHandler({ roots: (sessionId) => revealRoots(ctx, sessionId) })
224
315
  });
225
316
  const disposeSkill = ctx.webServer.register({
226
317
  kind: "exact",
@@ -1 +1 @@
1
- {"version":3,"file":"OfficialContent.d.ts","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAE,wBAAwB,EAAsB,kBAAkB,EAAiB,MAAM,wCAAwC,CAAC;AAE9I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACxF,KAAK,SAAS,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,sCAKtH;AASD,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,gBAAgB,GAAG;IACtG,QAAQ,EAAE,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;CAClH,+BAYA;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,oGAsBrI;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,sCAK3J"}
1
+ {"version":3,"file":"OfficialContent.d.ts","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAE,wBAAwB,EAAsB,kBAAkB,EAAiB,MAAM,wCAAwC,CAAC;AAE9I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACxF,KAAK,SAAS,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,sCAKtH;AASD,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,gBAAgB,GAAG;IACtG,QAAQ,EAAE,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;CAClH,+BAYA;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,oGAsBrI;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,sCAK3J"}
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useState, useSyncExternalStore } from 'react';
3
3
  import { createSnapshotStore } from '@deepseek-ai/dsh-client-store';
4
4
  import { OFFICIAL_SEATS } from './official-slots.js';
5
+ import { chatSeatProps } from './chat-seat.js';
5
6
  export function OfficialActions({ official, messageId }) {
6
7
  if (!official || !messageId)
7
8
  return null;
@@ -44,7 +45,7 @@ export function OfficialNode({ node, fallback, ...render }) {
44
45
  const turn = node.location.kind === 'turn' || node.location.kind === 'step' ? node.location.turn : undefined;
45
46
  // The runtime node domain is open; the public SlotMap enumerates the known kinds.
46
47
  const renderNode = official.renderSlot;
47
- return _jsx("div", { "data-reader-official-node": node.kind, "data-chat-anchor-key": node.key, "data-chat-flow-kind": node.kind, children: renderNode(OFFICIAL_SEATS.nodes, { ...owner, node }, { entryKey: node.kind, hookContext: { turnData: turn?.data, disclosureReset }, fallback }) });
48
+ return _jsx("div", { "data-reader-official-node": node.kind, ...(node.kind === 'user' || node.kind === 'steering' ? chatSeatProps(node.kind, node.key) : {}), children: renderNode(OFFICIAL_SEATS.nodes, { ...owner, node }, { entryKey: node.kind, hookContext: { turnData: turn?.data, disclosureReset }, fallback }) });
48
49
  }
49
50
  export function OfficialTail({ official, owner, produced }) {
50
51
  const tailOwner = owner && official ? { ...owner, openFile: official.officialPreviewFile, readerProducedPaths: produced } : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"OfficialContent.js","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OfficialContent.js","sourceRoot":"","sources":["../../../src/client/OfficialContent.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Reader.d.ts","sourceRoot":"","sources":["../../../src/client/Reader.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,YAAY,CAAC;AA8kBhE,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAiQxC"}
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';
@@ -25,9 +27,31 @@ import { ClosedProcessSummary } from './ClosedProcessSummary.js';
25
27
  import { StickyLane } from './StickyLane.js';
26
28
  import css from './Reader.module.css';
27
29
  import { markdownLabels, truncatedJsonLabel } from './primitive-labels.js';
30
+ import { chatSeatProps } from './chat-seat.js';
31
+ import { ensureStyles } from './styles.js';
28
32
  function isNode(node, kind) {
29
33
  return node.kind === kind;
30
34
  }
35
+ function observedInputIds(order, getNode) {
36
+ const ids = new Set();
37
+ for (const key of order) {
38
+ const node = getNode(key);
39
+ if (!node || (node.kind !== 'user' && node.kind !== 'steering' && node.kind !== 'turn-trigger'))
40
+ continue;
41
+ const source = node.data.source;
42
+ if (source?.kind === 'user' && typeof source.rpcId === 'string')
43
+ ids.add(source.rpcId);
44
+ }
45
+ return ids;
46
+ }
47
+ function inboxText(item) {
48
+ return (item.content ?? []).filter((block) => {
49
+ return typeof block === 'object' && block !== null && block.type === 'text' && typeof block.text === 'string';
50
+ }).map(block => block.text).join('');
51
+ }
52
+ function inboxAttachmentCount(item) {
53
+ return (item.content ?? []).filter(block => typeof block === 'object' && block !== null && block.type !== 'text').length;
54
+ }
31
55
  function cleanErrorMessage(raw) {
32
56
  if (!raw)
33
57
  return '模型服务暂时无响应或连接中断,请稍后重试。';
@@ -120,6 +144,7 @@ const AssistantNode = memo(function AssistantNode({ useChat, nodeKey, boundary,
120
144
  });
121
145
  const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen = false, ...render }) {
122
146
  const node = useChat(snapshot => snapshot.nodes.get(nodeKey));
147
+ const [disclosureReset] = useState(() => createSnapshotStore(0));
123
148
  if (!node || node.visibility === 'hidden')
124
149
  return null;
125
150
  if (isNode(node, 'user') || isNode(node, 'steering')) {
@@ -128,7 +153,30 @@ const MainNode = memo(function MainNode({ useChat, nodeKey, boundary, pinned, pr
128
153
  const otherBlocks = blocks.filter(b => b.kind !== 'image');
129
154
  const text = otherBlocks.filter((block) => block.kind === 'text').map(block => block.text).join('\n\n');
130
155
  const time = node.data.time;
131
- return _jsxs("div", { className: css.userCluster, "data-reader-anchor": true, "data-reader-key": nodeKey, 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 })] });
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 });
132
180
  }
133
181
  if (isNode(node, 'assistant-step'))
134
182
  return null;
@@ -294,6 +342,8 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
294
342
  const liveItems = useMemo(() => presentLiveTurn(steps, boundary, autoFold), [steps, boundary, autoFold]);
295
343
  const openMode = deliverableOpenModeOf(useSyncExternalStore(props.openPrefs?.subscribe ?? ((fn) => { void fn; return () => { }; }), () => props.openPrefs?.getSnapshot()?.deliverableOpenMode, () => 'external'));
296
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;
297
347
  // Only a real, still-active text selection delays folding. Merely clicking,
298
348
  // focusing or scrolling the live card does not create a permanent override.
299
349
  const holdingSelection = selectedProcessKeys.some(key => flow.some(item => item.key === key)
@@ -362,6 +412,7 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
362
412
  fileMentions,
363
413
  metrics,
364
414
  official,
415
+ sessionId: props.sessionId,
365
416
  cwd,
366
417
  };
367
418
  const terminal = terminalLabel(boundary.reason);
@@ -383,18 +434,19 @@ const TurnGroup = memo(function TurnGroup({ group, motion, autoFold, pinnedKeys,
383
434
  return _jsx(BlockBoundary, { children: _jsx(MainNode, { ...shared, boundary: boundary, nodeKey: step.nodeKey }) });
384
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 }) })] });
385
436
  };
386
- 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] }) }), hasProcess && !isAwaitingModel && _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 }) }) }), boundary.status === 'closed' && hasProcess && autoFold && _jsx(ClosedProcessSummary, { open: expanded, onChange: setExpanded, controls: flowId, steps: steps.filter(step => {
387
- if (step.kind === 'user')
388
- return false;
389
- if (step.kind !== 'body')
390
- return true;
391
- const node = nodes.get(step.nodeKey);
392
- return !!node && isNode(node, 'assistant-step') && (isEarlierNarration(node.data, boundary)
393
- || node.data.blocks.some(block => block.kind === 'tool-call')
394
- || (boundary.latestStep > 0 && node.data.step < boundary.latestStep));
395
- }) }), _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 })] });
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 })] });
396
447
  });
397
448
  export function Reader(props) {
449
+ useInsertionEffect(() => { ensureStyles(); }, []);
398
450
  const root = useRef(null);
399
451
  const activatedAt = useRef(Date.now());
400
452
  const order = props.useChat(snapshot => snapshot.order);
@@ -408,6 +460,31 @@ export function Reader(props) {
408
460
  const loadingOlder = props.useSession(snapshot => snapshot.loadingOlder);
409
461
  const pendingSubmissions = props.useSession(snapshot => snapshot.pendingSubmissions);
410
462
  const pendingList = asReadonlyArray(pendingSubmissions);
463
+ const inbox = props.useProjection?.('inbox');
464
+ const inboxSteering = asReadonlyArray(inbox?.['next-step']).filter(item => item.source?.kind === 'user');
465
+ const observed = useMemo(() => observedInputIds(order, key => nodes.get(key)), [order, nodes]);
466
+ const visibleSubmissions = useMemo(() => pendingList.filter(sub => sub.placement !== 'queued' && !observed.has(sub.requestId)), [pendingList, observed]);
467
+ const pendingInputs = useMemo(() => {
468
+ const local = new Map(visibleSubmissions.map(sub => [sub.requestId, sub]));
469
+ const localIds = new Set(pendingList.filter(sub => sub.placement !== 'queued').map(sub => sub.requestId));
470
+ const rows = [];
471
+ for (const item of inboxSteering) {
472
+ const rpcId = typeof item.source?.rpcId === 'string' ? item.source.rpcId : undefined;
473
+ if (!rpcId) {
474
+ rows.push(item);
475
+ continue;
476
+ }
477
+ const submission = local.get(rpcId);
478
+ if (submission) {
479
+ local.delete(rpcId);
480
+ rows.push(submission);
481
+ }
482
+ else if (!localIds.has(rpcId))
483
+ rows.push(item);
484
+ }
485
+ rows.push(...local.values());
486
+ return rows;
487
+ }, [inboxSteering, pendingList, visibleSubmissions]);
411
488
  const waitAnchor = waitingAnchor(order, key => nodes.get(key), pendingList);
412
489
  const motionPreference = props.useStore(state => state.motion);
413
490
  const motion = useMotionAllowed(motionPreference);
@@ -576,11 +653,6 @@ export function Reader(props) {
576
653
  scroll.jump();
577
654
  }
578
655
  }, [lastKey, lastNode?.kind, lastSubmissionId, scroll]);
579
- const visibleSubmissions = useMemo(() => {
580
- if (pendingList.length === 0)
581
- return [];
582
- return pendingList.filter(sub => sub.placement !== 'queued');
583
- }, [pendingList]);
584
656
  // ChatView publishes data-chat-flow="" on its column. Skins treat a
585
657
  // scrollport without that hook as inspect-only and hide [data-composer-seat].
586
658
  return _jsx(StreamMotionContext.Provider, { value: streamMotion, children: _jsxs("div", { ref: root, className: css.root, "data-reader-build": "0.3.4", "data-dsh-better-display": "0.3.4", "data-reader-wait-clock-version": "input-v1", "data-reader-wait-start": waitAnchor.time ?? undefined, "data-motion": motion ? 'on' : 'off', "data-reader-glass": frostedGlass || undefined, "data-reader-auto-fold": autoFold ? 'on' : 'off', children: [_jsx(TimelineRail, { items: timelineItems, activeTurn: activeTurn, busyTurn: busyTurn, onNavigate: onNavigateTurn }), _jsxs("div", { className: css.column, "data-chat-flow": "", children: [_jsx(StickyLane, { kind: "toolbar", className: css.toolbar, children: _jsx("button", { type: "button", className: css.textButton, "aria-pressed": autoFold, onClick: () => {
@@ -594,9 +666,13 @@ export function Reader(props) {
594
666
  catch {
595
667
  setHistoryError(true);
596
668
  }
597
- }, children: loadingOlder ? '正在加载更早记录' : '加载更早记录' }), historyError && _jsx("div", { className: css.notice, children: "\u5386\u53F2\u8BB0\u5F55\u52A0\u8F7D\u5931\u8D25\uFF0C\u53EF\u518D\u6B21\u5C1D\u8BD5\uFF1B\u73B0\u6709\u5185\u5BB9\u672A\u6539\u53D8\u3002" }), openError && _jsxs("div", { className: css.error, role: "alert", children: ["\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\uFF1A", openError.message] }), loading && groups.length === 0 && _jsx("p", { className: css.empty, role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026" }), groups.map(group => _jsx(TurnGroup, { ...props, group: group, motion: motion, autoFold: autoFold, pinnedKeys: pinnedKeys, selectedProcessKeys: selectedProcessKeys, isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key }, group.key)), visibleSubmissions.map(submission => {
598
- const images = pendingSubmissionImages(submission);
599
- return (_jsxs("div", { className: css.userCluster, "data-reader-pending-submission": true, children: [images.length > 0 && (_jsx("div", { className: css.userImages, children: images.map((item, idx) => (_jsx("figure", { className: css.imageFigure, children: _jsx("div", { className: css.imageFrame, style: { aspectRatio: `${item.width || 4} / ${item.height || 3}` }, children: _jsx("img", { src: item.previewUrl, alt: item.name ?? '发送的图片', className: css.pendingImage }) }) }, idx))) })), submission.text ? (_jsx("div", { className: css.user, children: _jsx("div", { className: css.blocks, children: submission.text }) })) : null, _jsx(UserMessageActions, { text: submission.text ?? '', time: submission.time })] }, submission.requestId));
669
+ }, children: loadingOlder ? '正在加载更早记录' : '加载更早记录' }), historyError && _jsx("div", { className: css.notice, children: "\u5386\u53F2\u8BB0\u5F55\u52A0\u8F7D\u5931\u8D25\uFF0C\u53EF\u518D\u6B21\u5C1D\u8BD5\uFF1B\u73B0\u6709\u5185\u5BB9\u672A\u6539\u53D8\u3002" }), openError && _jsxs("div", { className: css.error, role: "alert", children: ["\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\uFF1A", openError.message] }), loading && groups.length === 0 && _jsx("p", { className: css.empty, role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026" }), groups.map(group => _jsx(TurnGroup, { ...props, group: group, motion: motion, autoFold: autoFold, pinnedKeys: pinnedKeys, selectedProcessKeys: selectedProcessKeys, isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key }, group.key)), pendingInputs.map((item, index) => {
670
+ const submission = 'requestId' in item ? item : undefined;
671
+ const text = submission?.text ?? inboxText(item);
672
+ const images = submission ? pendingSubmissionImages(submission) : [];
673
+ const attachmentCount = images.length;
674
+ const requestId = submission?.requestId ?? `inbox-${index}`;
675
+ return (_jsxs("div", { className: css.userCluster, "data-reader-pending-submission": true, "data-pending-steering": submission ? undefined : '', "data-pending-attachment-count": attachmentCount, children: [images.length > 0 && (_jsx("div", { className: css.userImages, children: images.map((item, idx) => (_jsx("figure", { className: css.imageFigure, children: _jsx("div", { className: css.imageFrame, style: { aspectRatio: `${item.width || 4} / ${item.height || 3}` }, children: _jsx("img", { src: item.previewUrl, alt: item.name ?? '发送的图片', className: css.pendingImage }) }) }, idx))) })), text ? (_jsx("div", { className: css.user, children: _jsx("div", { className: css.blocks, children: text }) })) : null, _jsx(UserMessageActions, { text: text, time: submission?.time })] }, requestId));
600
676
  }), isAwaitingModel && _jsx(WaitingStatus, { anchor: waitAnchor, label: props.t ? props.t('chat.deepDiving') : '深度求索中...' }), pending !== undefined && _jsxs("div", { className: css.attention, role: "alert", "data-reader-attention": true, children: [_jsx("strong", { children: pending.kind === 'question' ? '需要你回答一个问题' : '需要你的确认' }), _jsx("span", { children: "\u8BF7\u5728\u4E0B\u65B9\u539F\u751F\u64CD\u4F5C\u533A\u5904\u7406\u3002\u6B64\u63D0\u793A\u4E0D\u4F1A\u6536\u8FDB\u6267\u884C\u8FC7\u7A0B\u3002" })] }), scroll.detached && _jsx("div", { className: css.jumpDock, children: _jsx("button", { type: "button", className: css.jump, "aria-label": "\u56DE\u5230\u5E95\u90E8", title: "\u56DE\u5230\u5E95\u90E8", onClick: scroll.jump, children: _jsx("svg", { viewBox: "0 0 14 14", width: "14", height: "14", "aria-hidden": "true", children: _jsx("path", { d: "M3 5.5 7 9.5 11 5.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })] })] }) });
601
677
  }
602
678
  //# sourceMappingURL=Reader.js.map