dsh-generative-ui 0.0.2 → 0.0.3

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 CHANGED
@@ -3392,6 +3392,279 @@ var require_server_browser = __commonJS(function(exports2) {
3392
3392
  exports2.renderToReadableStream = s.renderToReadableStream;
3393
3393
  });
3394
3394
 
3395
+ // node_modules/scheduler/cjs/scheduler.production.min.js
3396
+ var require_scheduler_production_min = __commonJS(function(exports2) {
3397
+ function f(a, b) {
3398
+ var c = a.length;
3399
+ a.push(b);
3400
+ a:
3401
+ for (;0 < c; ) {
3402
+ var d = c - 1 >>> 1, e = a[d];
3403
+ if (0 < g(e, b))
3404
+ a[d] = b, a[c] = e, c = d;
3405
+ else
3406
+ break a;
3407
+ }
3408
+ }
3409
+ function h(a) {
3410
+ return a.length === 0 ? null : a[0];
3411
+ }
3412
+ function k(a) {
3413
+ if (a.length === 0)
3414
+ return null;
3415
+ var b = a[0], c = a.pop();
3416
+ if (c !== b) {
3417
+ a[0] = c;
3418
+ a:
3419
+ for (var d = 0, e = a.length, w = e >>> 1;d < w; ) {
3420
+ var m = 2 * (d + 1) - 1, C = a[m], n = m + 1, x = a[n];
3421
+ if (0 > g(C, c))
3422
+ n < e && 0 > g(x, C) ? (a[d] = x, a[n] = c, d = n) : (a[d] = C, a[m] = c, d = m);
3423
+ else if (n < e && 0 > g(x, c))
3424
+ a[d] = x, a[n] = c, d = n;
3425
+ else
3426
+ break a;
3427
+ }
3428
+ }
3429
+ return b;
3430
+ }
3431
+ function g(a, b) {
3432
+ var c = a.sortIndex - b.sortIndex;
3433
+ return c !== 0 ? c : a.id - b.id;
3434
+ }
3435
+ if (typeof performance === "object" && typeof performance.now === "function") {
3436
+ l = performance;
3437
+ exports2.unstable_now = function() {
3438
+ return l.now();
3439
+ };
3440
+ } else {
3441
+ p = Date, q = p.now();
3442
+ exports2.unstable_now = function() {
3443
+ return p.now() - q;
3444
+ };
3445
+ }
3446
+ var l;
3447
+ var p;
3448
+ var q;
3449
+ var r = [];
3450
+ var t = [];
3451
+ var u = 1;
3452
+ var v = null;
3453
+ var y = 3;
3454
+ var z = false;
3455
+ var A = false;
3456
+ var B = false;
3457
+ var D = typeof setTimeout === "function" ? setTimeout : null;
3458
+ var E = typeof clearTimeout === "function" ? clearTimeout : null;
3459
+ var F = typeof setImmediate !== "undefined" ? setImmediate : null;
3460
+ typeof navigator !== "undefined" && navigator.scheduling !== undefined && navigator.scheduling.isInputPending !== undefined && navigator.scheduling.isInputPending.bind(navigator.scheduling);
3461
+ function G(a) {
3462
+ for (var b = h(t);b !== null; ) {
3463
+ if (b.callback === null)
3464
+ k(t);
3465
+ else if (b.startTime <= a)
3466
+ k(t), b.sortIndex = b.expirationTime, f(r, b);
3467
+ else
3468
+ break;
3469
+ b = h(t);
3470
+ }
3471
+ }
3472
+ function H(a) {
3473
+ B = false;
3474
+ G(a);
3475
+ if (!A)
3476
+ if (h(r) !== null)
3477
+ A = true, I(J);
3478
+ else {
3479
+ var b = h(t);
3480
+ b !== null && K(H, b.startTime - a);
3481
+ }
3482
+ }
3483
+ function J(a, b) {
3484
+ A = false;
3485
+ B && (B = false, E(L), L = -1);
3486
+ z = true;
3487
+ var c = y;
3488
+ try {
3489
+ G(b);
3490
+ for (v = h(r);v !== null && (!(v.expirationTime > b) || a && !M()); ) {
3491
+ var d = v.callback;
3492
+ if (typeof d === "function") {
3493
+ v.callback = null;
3494
+ y = v.priorityLevel;
3495
+ var e = d(v.expirationTime <= b);
3496
+ b = exports2.unstable_now();
3497
+ typeof e === "function" ? v.callback = e : v === h(r) && k(r);
3498
+ G(b);
3499
+ } else
3500
+ k(r);
3501
+ v = h(r);
3502
+ }
3503
+ if (v !== null)
3504
+ var w = true;
3505
+ else {
3506
+ var m = h(t);
3507
+ m !== null && K(H, m.startTime - b);
3508
+ w = false;
3509
+ }
3510
+ return w;
3511
+ } finally {
3512
+ v = null, y = c, z = false;
3513
+ }
3514
+ }
3515
+ var N = false;
3516
+ var O = null;
3517
+ var L = -1;
3518
+ var P = 5;
3519
+ var Q = -1;
3520
+ function M() {
3521
+ return exports2.unstable_now() - Q < P ? false : true;
3522
+ }
3523
+ function R() {
3524
+ if (O !== null) {
3525
+ var a = exports2.unstable_now();
3526
+ Q = a;
3527
+ var b = true;
3528
+ try {
3529
+ b = O(true, a);
3530
+ } finally {
3531
+ b ? S() : (N = false, O = null);
3532
+ }
3533
+ } else
3534
+ N = false;
3535
+ }
3536
+ var S;
3537
+ if (typeof F === "function")
3538
+ S = function() {
3539
+ F(R);
3540
+ };
3541
+ else if (typeof MessageChannel !== "undefined") {
3542
+ T = new MessageChannel, U = T.port2;
3543
+ T.port1.onmessage = R;
3544
+ S = function() {
3545
+ U.postMessage(null);
3546
+ };
3547
+ } else
3548
+ S = function() {
3549
+ D(R, 0);
3550
+ };
3551
+ var T;
3552
+ var U;
3553
+ function I(a) {
3554
+ O = a;
3555
+ N || (N = true, S());
3556
+ }
3557
+ function K(a, b) {
3558
+ L = D(function() {
3559
+ a(exports2.unstable_now());
3560
+ }, b);
3561
+ }
3562
+ exports2.unstable_IdlePriority = 5;
3563
+ exports2.unstable_ImmediatePriority = 1;
3564
+ exports2.unstable_LowPriority = 4;
3565
+ exports2.unstable_NormalPriority = 3;
3566
+ exports2.unstable_Profiling = null;
3567
+ exports2.unstable_UserBlockingPriority = 2;
3568
+ exports2.unstable_cancelCallback = function(a) {
3569
+ a.callback = null;
3570
+ };
3571
+ exports2.unstable_continueExecution = function() {
3572
+ A || z || (A = true, I(J));
3573
+ };
3574
+ exports2.unstable_forceFrameRate = function(a) {
3575
+ 0 > a || 125 < a ? console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported") : P = 0 < a ? Math.floor(1000 / a) : 5;
3576
+ };
3577
+ exports2.unstable_getCurrentPriorityLevel = function() {
3578
+ return y;
3579
+ };
3580
+ exports2.unstable_getFirstCallbackNode = function() {
3581
+ return h(r);
3582
+ };
3583
+ exports2.unstable_next = function(a) {
3584
+ switch (y) {
3585
+ case 1:
3586
+ case 2:
3587
+ case 3:
3588
+ var b = 3;
3589
+ break;
3590
+ default:
3591
+ b = y;
3592
+ }
3593
+ var c = y;
3594
+ y = b;
3595
+ try {
3596
+ return a();
3597
+ } finally {
3598
+ y = c;
3599
+ }
3600
+ };
3601
+ exports2.unstable_pauseExecution = function() {};
3602
+ exports2.unstable_requestPaint = function() {};
3603
+ exports2.unstable_runWithPriority = function(a, b) {
3604
+ switch (a) {
3605
+ case 1:
3606
+ case 2:
3607
+ case 3:
3608
+ case 4:
3609
+ case 5:
3610
+ break;
3611
+ default:
3612
+ a = 3;
3613
+ }
3614
+ var c = y;
3615
+ y = a;
3616
+ try {
3617
+ return b();
3618
+ } finally {
3619
+ y = c;
3620
+ }
3621
+ };
3622
+ exports2.unstable_scheduleCallback = function(a, b, c) {
3623
+ var d = exports2.unstable_now();
3624
+ typeof c === "object" && c !== null ? (c = c.delay, c = typeof c === "number" && 0 < c ? d + c : d) : c = d;
3625
+ switch (a) {
3626
+ case 1:
3627
+ var e = -1;
3628
+ break;
3629
+ case 2:
3630
+ e = 250;
3631
+ break;
3632
+ case 5:
3633
+ e = 1073741823;
3634
+ break;
3635
+ case 4:
3636
+ e = 1e4;
3637
+ break;
3638
+ default:
3639
+ e = 5000;
3640
+ }
3641
+ e = c + e;
3642
+ a = { id: u++, callback: b, priorityLevel: a, startTime: c, expirationTime: e, sortIndex: -1 };
3643
+ c > d ? (a.sortIndex = c, f(t, a), h(r) === null && a === h(t) && (B ? (E(L), L = -1) : B = true, K(H, c - d))) : (a.sortIndex = e, f(r, a), A || z || (A = true, I(J)));
3644
+ return a;
3645
+ };
3646
+ exports2.unstable_shouldYield = M;
3647
+ exports2.unstable_wrapCallback = function(a) {
3648
+ var b = y;
3649
+ return function() {
3650
+ var c = y;
3651
+ y = b;
3652
+ try {
3653
+ return a.apply(this, arguments);
3654
+ } finally {
3655
+ y = c;
3656
+ }
3657
+ };
3658
+ };
3659
+ });
3660
+
3661
+ // node_modules/scheduler/index.js
3662
+ var require_scheduler = __commonJS(function(exports2, module2) {
3663
+ if (true) {
3664
+ module2.exports = require_scheduler_production_min();
3665
+ }
3666
+ });
3667
+
3395
3668
  // src/client/index.ts
3396
3669
  var exports_client = {};
3397
3670
  __export(exports_client, {
@@ -7002,6 +7275,7 @@ var AI_STREAM_PATH = "/dsh-generative-ui/ai";
7002
7275
  var FS_PATH = "/dsh-generative-ui/fs";
7003
7276
  var EXEC_PATH = "/dsh-generative-ui/exec";
7004
7277
  var WEB_SEARCH_PATH = "/dsh-generative-ui/web-search";
7278
+ var CARD_ERROR_PATH = "/dsh-generative-ui/card-error";
7005
7279
 
7006
7280
  // src/client/runtime/compiler.ts
7007
7281
  var initPromise2 = null;
@@ -7022,6 +7296,7 @@ var warmCompiler = () => {
7022
7296
  function disposeCompiler() {
7023
7297
  initPromise2 = null;
7024
7298
  }
7299
+ var hasDefaultExport = (source) => /^\s*export\s+default\s/m.test(source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "")) || /\bexport\s*\{[^}]*\bas\s+default\b/.test(source);
7025
7300
  function createBrowserTsxCompiler() {
7026
7301
  return {
7027
7302
  async compile(code, options = {}) {
@@ -7037,8 +7312,11 @@ function createBrowserTsxCompiler() {
7037
7312
  return build(normalizeGeneratedTsx(code, { mode: "streaming" }));
7038
7313
  try {
7039
7314
  return build(normalizeGeneratedTsx(code, { mode: "final" }));
7040
- } catch {
7041
- return build(normalizeGeneratedTsx(code, { mode: "streaming" }));
7315
+ } catch (error) {
7316
+ const cut = normalizeGeneratedTsx(code, { mode: "streaming" });
7317
+ if (!hasDefaultExport(cut))
7318
+ throw error;
7319
+ return build(cut);
7042
7320
  }
7043
7321
  }
7044
7322
  };
@@ -7124,6 +7402,7 @@ var React = __toESM(require("react"));
7124
7402
  var ReactJsxRuntime = __toESM(require("react/jsx-runtime"));
7125
7403
  var ReactDom = __toESM(require("react-dom"));
7126
7404
  var ReactDomClient = __toESM(require("react-dom/client"));
7405
+ var Scheduler = __toESM(require_scheduler(), 1);
7127
7406
  var installed = false;
7128
7407
  function installDocumentImportMap() {
7129
7408
  if (installed || typeof document === "undefined")
@@ -7144,7 +7423,8 @@ function registerRuntimeModules() {
7144
7423
  "react/jsx-runtime": ReactJsxRuntime,
7145
7424
  "react/jsx-dev-runtime": ReactJsxRuntime,
7146
7425
  "react-dom": ReactDom,
7147
- "react-dom/client": ReactDomClient
7426
+ "react-dom/client": ReactDomClient,
7427
+ scheduler: Scheduler
7148
7428
  });
7149
7429
  installDocumentImportMap();
7150
7430
  }
@@ -7260,7 +7540,7 @@ async function request(method, path, content, extra) {
7260
7540
  const response = await fetch(`${FS_PATH}?${query}`, content === undefined ? { method } : { method, headers: { "content-type": "application/json" }, body: JSON.stringify({ content }) });
7261
7541
  const body = await response.json().catch(() => ({}));
7262
7542
  if (!response.ok)
7263
- throw new Error(`[dsh-generative-ui] $dsh/fs ${path}: ${body.error ?? response.statusText}`);
7543
+ throw Object.assign(new Error(`[dsh-generative-ui] $dsh/fs ${path}: ${body.error ?? response.statusText}`), { code: body.error, denied: response.status === 403 });
7264
7544
  return body;
7265
7545
  }
7266
7546
  async function execRequest(cwd, sessionId, command, signal) {
@@ -17067,6 +17347,9 @@ var unoConfig = (scope2) => ({
17067
17347
  {
17068
17348
  getCSS: () => `${scope2} *, ${scope2} *::before, ${scope2} *::after { box-sizing: border-box; }
17069
17349
  ${scope2} ul, ${scope2} ol { list-style: none; margin: 0; padding: 0; }
17350
+ ${scope2} h1, ${scope2} h2, ${scope2} h3, ${scope2} h4, ${scope2} h5, ${scope2} h6,
17351
+ ${scope2} p, ${scope2} blockquote, ${scope2} figure, ${scope2} figcaption,
17352
+ ${scope2} dl, ${scope2} dd, ${scope2} pre, ${scope2} hr { margin: 0; }
17070
17353
  ${scope2} button, ${scope2} input, ${scope2} select, ${scope2} textarea {
17071
17354
  background: transparent; color: inherit; font: inherit; border: 0 solid; cursor: pointer;
17072
17355
  }
@@ -17167,10 +17450,8 @@ var compiler = () => {
17167
17450
  }
17168
17451
  return sharedCompiler;
17169
17452
  };
17170
- var TRANSIENT = /No default export found|Unexpected (end of|eof)/i;
17171
17453
  var TRANSIENT_LOAD = /failed to fetch|failed to load|networkerror|load failed/i;
17172
17454
  var MAX_RETRIES = 3;
17173
- var isUnfinishedFrame = (message, phase, streaming) => streaming && phase !== "render" && TRANSIENT.test(message);
17174
17455
  var shouldRetry = (message, phase, streaming, attempts) => phase === "compile" && !streaming && TRANSIENT_LOAD.test(message) && attempts < MAX_RETRIES;
17175
17456
  var deliveryFor = (code, delivered, streaming) => {
17176
17457
  if (!streaming)
@@ -17194,16 +17475,20 @@ var deliver = (renderer, delivery) => {
17194
17475
  return true;
17195
17476
  };
17196
17477
  var importSignature = (code) => [...code.matchAll(/from\s+["']([^"']+)["']/g)].map((match) => match[1]).join(" ");
17478
+ var reportStranded = (streaming, stranded, code, reportedFor) => !streaming && stranded !== null && reportedFor !== code;
17197
17479
  var errorAction = (message, phase, streaming, attempts) => {
17198
- if (isUnfinishedFrame(message, phase, streaming))
17480
+ if (streaming)
17199
17481
  return "ignore";
17200
17482
  return shouldRetry(message, phase, streaming, attempts) ? "retry" : "report";
17201
17483
  };
17202
- var RETRY_BACKOFF_MS = 400;
17203
- var probeOutcome = (signature, current, streaming, delivered) => {
17484
+ var RETRY_BACKOFF_MS = 500;
17485
+ var RETRY_FACTOR = 4;
17486
+ var probeOutcome = (signature, current, streaming, delivered, probed) => {
17204
17487
  if (signature !== current)
17205
17488
  return "stale";
17206
- return !streaming && delivered !== "" ? "redeliver" : "store";
17489
+ if (delivered === "")
17490
+ return "store";
17491
+ return !streaming || delivered === probed ? "redeliver" : "store";
17207
17492
  };
17208
17493
  var dispatchError = (action, effects) => {
17209
17494
  if (action === "ignore")
@@ -17211,17 +17496,28 @@ var dispatchError = (action, effects) => {
17211
17496
  if (action === "retry") {
17212
17497
  const next = effects.attempts() + 1;
17213
17498
  effects.setAttempts(next);
17214
- effects.schedule(RETRY_BACKOFF_MS * next);
17499
+ effects.schedule(RETRY_BACKOFF_MS * RETRY_FACTOR ** (next - 1));
17215
17500
  return;
17216
17501
  }
17217
17502
  effects.report();
17218
17503
  };
17504
+ var unbundleFetchedImports = (imports) => Object.fromEntries(Object.entries(imports).map(([key, url]) => {
17505
+ if (!url.startsWith("https://esm.sh/"))
17506
+ return [key, url];
17507
+ const parsed = new URL(url);
17508
+ parsed.searchParams.delete("bundle");
17509
+ return [key, parsed.toString()];
17510
+ }));
17219
17511
  var bustFetchedImports = (imports, attempt) => Object.fromEntries(Object.entries(imports).map(([key, url]) => [key, url.startsWith("https://esm.sh/") ? `${url}${url.includes("?") ? "&" : "?"}ui4a-retry=${attempt}` : url]));
17220
17512
  function GenUISurface({ code, streaming = false, preserveState = true, onError, onRendered, className }) {
17221
17513
  const hostRef = import_react4.useRef(null);
17222
17514
  const [renderer, setRenderer] = import_react4.useState(null);
17223
17515
  const onErrorRef = useLatest(onError);
17224
17516
  const onRenderedRef = useLatest(onRendered);
17517
+ const threwRef = import_react4.useRef(false);
17518
+ const strandedRef = import_react4.useRef(null);
17519
+ const strandedReportedRef = import_react4.useRef("");
17520
+ const retryTimers = import_react4.useRef(new Set);
17225
17521
  const streamingRef = useLatest(streaming);
17226
17522
  const preserveStateRef = import_react4.useRef(preserveState);
17227
17523
  const retriesRef = import_react4.useRef(0);
@@ -17230,7 +17526,8 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17230
17526
  return;
17231
17527
  const attempt = retriesRef.current;
17232
17528
  mergeFallbackImports(localImports(), code).then((imports) => {
17233
- renderer.setImportMap({ imports: bustFetchedImports(imports, attempt) });
17529
+ const fresh = attempt > 0 ? unbundleFetchedImports(imports) : imports;
17530
+ renderer.setImportMap({ imports: bustFetchedImports(fresh, attempt) });
17234
17531
  renderer.clear({ preserveVisualState: true });
17235
17532
  renderer.render(code);
17236
17533
  });
@@ -17247,18 +17544,27 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17247
17544
  preserveStateOnUpdate: preserveStateRef.current,
17248
17545
  callbacks: {
17249
17546
  onError: (error, phase) => {
17250
- dispatchError(errorAction(error.message, phase, streamingRef.current, retriesRef.current), {
17547
+ if (phase === "render")
17548
+ threwRef.current = true;
17549
+ const action = errorAction(error.message, phase, streamingRef.current, retriesRef.current);
17550
+ if (action === "ignore")
17551
+ strandedRef.current = { error, phase };
17552
+ dispatchError(action, {
17251
17553
  attempts: () => retriesRef.current,
17252
17554
  setAttempts: (n2) => {
17253
17555
  retriesRef.current = n2;
17254
17556
  },
17255
- schedule: (ms) => setTimeout(() => retryRef.current(), ms),
17557
+ schedule: (ms) => void retryTimers.current.add(setTimeout(() => retryRef.current(), ms)),
17256
17558
  report: () => onErrorRef.current?.(error, phase)
17257
17559
  });
17258
17560
  },
17259
17561
  onRendered: () => {
17260
17562
  retriesRef.current = 0;
17261
- onRenderedRef.current?.();
17563
+ const restored = threwRef.current;
17564
+ threwRef.current = false;
17565
+ if (!restored)
17566
+ strandedRef.current = null;
17567
+ onRenderedRef.current?.(restored);
17262
17568
  }
17263
17569
  }
17264
17570
  }).then((created) => {
@@ -17272,6 +17578,9 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17272
17578
  attached?.detach();
17273
17579
  attached = null;
17274
17580
  setRenderer(null);
17581
+ for (const timer of retryTimers.current)
17582
+ clearTimeout(timer);
17583
+ retryTimers.current.clear();
17275
17584
  };
17276
17585
  }, []);
17277
17586
  const importedRef = import_react4.useRef("");
@@ -17283,7 +17592,7 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17283
17592
  if (signature !== importedRef.current) {
17284
17593
  importedRef.current = signature;
17285
17594
  mergeFallbackImports(localImports(), code).then((imports) => {
17286
- const settle = probeOutcome(signature, importedRef.current, streamingRef.current, deliveredRef.current);
17595
+ const settle = probeOutcome(signature, importedRef.current, streamingRef.current, deliveredRef.current, code);
17287
17596
  if (settle === "stale")
17288
17597
  return;
17289
17598
  renderer.setImportMap({ imports });
@@ -17293,8 +17602,14 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17293
17602
  }
17294
17603
  retriesRef.current = 0;
17295
17604
  ensureUnoStyles(code, streaming);
17296
- if (!deliver(renderer, deliveryFor(code, deliveredRef.current, streaming)))
17605
+ if (!deliver(renderer, deliveryFor(code, deliveredRef.current, streaming))) {
17606
+ const stranded = strandedRef.current;
17607
+ if (stranded !== null && reportStranded(streaming, stranded, code, strandedReportedRef.current)) {
17608
+ strandedReportedRef.current = code;
17609
+ onErrorRef.current?.(stranded.error, stranded.phase);
17610
+ }
17297
17611
  return;
17612
+ }
17298
17613
  deliveredRef.current = code;
17299
17614
  }, [renderer, code, streaming]);
17300
17615
  return /* @__PURE__ */ jsx_runtime.jsx("div", {
@@ -17305,32 +17620,39 @@ function GenUISurface({ code, streaming = false, preserveState = true, onError,
17305
17620
  }
17306
17621
 
17307
17622
  // src/client/runtime/report-error.ts
17308
- var sent = new Set;
17309
- var reportBody = (message, phase) => `[automatic] The card you just wrote did not render. It failed at the ${phase} step:
17310
-
17311
- ${message}
17312
-
17313
- This was sent by the renderer, not by the user — nobody typed it, so do not apologise or address it as a request. If the error names the correct usage (the available exports, for instance), fix the card and send it again. If it does not, look it up before you change anything, and answer in the language the user has been writing in.`;
17314
17623
  var SETTLE_MS = 1000;
17315
17624
  var pending = null;
17316
- function cardRendered() {
17625
+ var outstanding = null;
17626
+ function cancelPendingReport() {
17317
17627
  if (pending === null)
17318
17628
  return;
17319
- clearTimeout(pending);
17629
+ clearTimeout(pending.timer);
17320
17630
  pending = null;
17321
17631
  }
17322
- function reportCardError(send, message, phase) {
17632
+ function cardRendered(restored = false, current = () => true) {
17633
+ if (restored)
17634
+ return;
17635
+ cancelPendingReport();
17636
+ if (!current())
17637
+ return;
17638
+ const send = outstanding;
17639
+ outstanding = null;
17640
+ send?.(null);
17641
+ }
17642
+ function reportCardError(send, message, phase, current = () => true) {
17323
17643
  if (send === undefined)
17324
17644
  return;
17325
- if (sent.has(message))
17645
+ if (!current())
17326
17646
  return;
17327
- sent.add(message);
17328
17647
  if (pending !== null)
17329
- clearTimeout(pending);
17330
- pending = setTimeout(() => {
17648
+ clearTimeout(pending.timer);
17649
+ pending = { timer: setTimeout(() => {
17331
17650
  pending = null;
17332
- send(reportBody(message, phase));
17333
- }, SETTLE_MS);
17651
+ if (!current())
17652
+ return;
17653
+ outstanding = send;
17654
+ send({ message, phase });
17655
+ }, SETTLE_MS), message };
17334
17656
  }
17335
17657
 
17336
17658
  // src/client/runtime/inline-fence.ts
@@ -17338,6 +17660,55 @@ var import_client2 = require("react-dom/client");
17338
17660
  var import_react5 = require("react");
17339
17661
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
17340
17662
 
17663
+ // src/client/runtime/segments.ts
17664
+ var FENCE = new RegExp(String.raw`(?<!\x60)(\x60{3,})${FENCE_LANG.replace("/", "\\/")}([^\n]*)(\n|$)`);
17665
+ function findClose(body, fence) {
17666
+ const anchored = body.indexOf(`
17667
+ ${fence}`);
17668
+ if (anchored >= 0 && /^[^\S\n]*(?:\n|$)/.test(body.slice(anchored + 1 + fence.length)) && body[anchored + 1 + fence.length] !== "`")
17669
+ return anchored + 1;
17670
+ for (let at = body.indexOf(fence);at >= 0; at = body.indexOf(fence, at + 1)) {
17671
+ if (body[at - 1] === "`" || body[at + fence.length] === "`")
17672
+ continue;
17673
+ if (/^[^\S\n]*(?:\n|$)/.test(body.slice(at + fence.length)))
17674
+ return at;
17675
+ }
17676
+ const short = /(?:^|\n)[^\S\n]*(`{3,})[^\S\n]*(?:\n|$)/.exec(body);
17677
+ return short === undefined || short === null ? -1 : short.index + (short[0].startsWith(`
17678
+ `) ? 1 : 0);
17679
+ }
17680
+ var TOOL_CALL_MARKUP = /\n?(?:<\/(?:antml:|||DSML||)?(?:parameter|invoke|tool_calls)>\s*)+$/;
17681
+ var CODE_LINE = /^(import|export|const|function|type|interface|let|\/\/)\b/;
17682
+ var FENCE_META = /^[\w-]+=/;
17683
+ function parseUi4aSegments(text2) {
17684
+ const segments = [];
17685
+ let rest = text2;
17686
+ while (true) {
17687
+ const open = FENCE.exec(rest);
17688
+ if (open === null)
17689
+ return segments;
17690
+ const trailing = open[2].trim();
17691
+ const bodyStart = open.index + open[0].length;
17692
+ if (trailing !== "" && !CODE_LINE.test(trailing) && !FENCE_META.test(trailing)) {
17693
+ rest = rest.slice(bodyStart);
17694
+ continue;
17695
+ }
17696
+ if (/^[^\S\n]*`{3,}ui4a\/tsx/.test(rest.slice(bodyStart))) {
17697
+ rest = rest.slice(bodyStart);
17698
+ continue;
17699
+ }
17700
+ const inlineCode = CODE_LINE.test(trailing) ? `${trailing}
17701
+ ` : "";
17702
+ const closeIndex = findClose(rest.slice(bodyStart), open[1]);
17703
+ if (closeIndex === -1) {
17704
+ segments.push({ code: (inlineCode + rest.slice(bodyStart)).replace(TOOL_CALL_MARKUP, ""), complete: false });
17705
+ return segments;
17706
+ }
17707
+ segments.push({ code: (inlineCode + rest.slice(bodyStart, bodyStart + closeIndex)).replace(TOOL_CALL_MARKUP, ""), complete: true });
17708
+ rest = rest.slice(bodyStart + closeIndex).replace(new RegExp(String.raw`^${open[1]}[^\n]*\n?`), "");
17709
+ }
17710
+ }
17711
+
17341
17712
  // src/client/runtime/observe.ts
17342
17713
  var listeners = new Set;
17343
17714
  var observer = null;
@@ -17405,12 +17776,56 @@ var hasPainted = (mount) => {
17405
17776
  }
17406
17777
  return false;
17407
17778
  };
17408
- var codeOf = (block) => block.querySelector("pre")?.textContent ?? "";
17779
+ var LEADING_UI4A_FENCE = new RegExp(String.raw`^\s*\x60{3,}${FENCE_LANG.replace("/", "\\/")}(?:[^\n]*)(?:\n|$)`);
17780
+ var codeOf = (block) => {
17781
+ const rendered = block.querySelector("pre")?.textContent ?? "";
17782
+ if (!LEADING_UI4A_FENCE.test(rendered))
17783
+ return rendered;
17784
+ return parseUi4aSegments(rendered)[0]?.code ?? rendered;
17785
+ };
17409
17786
  var sameCode = (a, b) => a.trimEnd() === b.trimEnd();
17410
17787
  var matchSegment = (segments, rendered) => segments.find((segment) => sameCode(segment.code, rendered) || segment.code.startsWith(rendered));
17788
+ var isLastSegment = (segments, code) => {
17789
+ const last = segments.at(-1);
17790
+ return last !== undefined && (sameCode(last.code, code) || last.code.startsWith(code));
17791
+ };
17792
+ var NEAR_VIEWPORT = "100% 0px";
17411
17793
  function claimInlineFences({ segments, render, scope: scope2 }) {
17412
17794
  const claims = new Map;
17413
17795
  const root = scope2 ?? document.body;
17796
+ const parked = new Set;
17797
+ const woken = new Set;
17798
+ const nearby = typeof IntersectionObserver === "undefined" ? null : new IntersectionObserver((entries) => {
17799
+ let woke = false;
17800
+ for (const entry of entries) {
17801
+ if (!entry.isIntersecting)
17802
+ continue;
17803
+ const block = entry.target;
17804
+ nearby?.unobserve(block);
17805
+ parked.delete(block);
17806
+ woken.add(block);
17807
+ woke = true;
17808
+ }
17809
+ if (woke)
17810
+ sweep();
17811
+ }, { rootMargin: NEAR_VIEWPORT });
17812
+ const defer = (block, segment) => {
17813
+ if (nearby === null || !segment.complete)
17814
+ return false;
17815
+ if (woken.delete(block))
17816
+ return false;
17817
+ if (parked.has(block))
17818
+ return true;
17819
+ const box = block.getBoundingClientRect();
17820
+ const limit = globalThis.innerHeight || 0 || 0;
17821
+ if (limit === 0 || box.height === 0 && box.width === 0)
17822
+ return false;
17823
+ if (box.bottom > -limit && box.top < limit * 2)
17824
+ return false;
17825
+ parked.add(block);
17826
+ nearby.observe(block);
17827
+ return true;
17828
+ };
17414
17829
  const release = (claim, restore) => {
17415
17830
  claim.painted?.disconnect();
17416
17831
  claim.root.unmount();
@@ -17424,13 +17839,22 @@ function claimInlineFences({ segments, render, scope: scope2 }) {
17424
17839
  };
17425
17840
  const sweep = () => {
17426
17841
  const current = segments();
17842
+ for (const claim of claims.values())
17843
+ if (!claim.block.isConnected)
17844
+ release(claim, false);
17845
+ const taken = new Set([...claims.values()].map((claim) => claim.reserved));
17427
17846
  for (const block of root.querySelectorAll(`.md-code-block:not([${CLAIMED}])`)) {
17847
+ if (block.closest(`[${PREVIEW}]`) !== null || block.closest(`[${MOUNT}]`) !== null)
17848
+ continue;
17428
17849
  const code = codeOf(block);
17429
17850
  if (code === "")
17430
17851
  continue;
17431
- const segment = matchSegment(current, code);
17852
+ const segment = matchSegment(current.filter((candidate) => !taken.has(candidate.code)), code);
17432
17853
  if (segment === undefined)
17433
17854
  continue;
17855
+ taken.add(segment.code);
17856
+ if (defer(block, segment))
17857
+ continue;
17434
17858
  block.setAttribute(CLAIMED, "");
17435
17859
  const mount = document.createElement("div");
17436
17860
  mount.setAttribute(MOUNT, "");
@@ -17439,7 +17863,7 @@ function claimInlineFences({ segments, render, scope: scope2 }) {
17439
17863
  previewHost.setAttribute(PREVIEW, "");
17440
17864
  block.parentElement?.insertBefore(previewHost, block);
17441
17865
  block.style.display = "none";
17442
- const claim = { block, mount, root: import_client2.createRoot(mount), code: "", complete: false, rendered: "", painted: null, preview: { host: previewHost, root: import_client2.createRoot(previewHost) } };
17866
+ const claim = { block, mount, root: import_client2.createRoot(mount), code: "", reserved: segment.code, complete: false, rendered: "", painted: null, preview: { host: previewHost, root: import_client2.createRoot(previewHost) } };
17443
17867
  let queued = 0;
17444
17868
  claim.painted = new MutationObserver(() => {
17445
17869
  if (queued !== 0)
@@ -17465,7 +17889,7 @@ function claimInlineFences({ segments, render, scope: scope2 }) {
17465
17889
  if (rendered === claim.rendered && claim.code !== "" && claim.complete)
17466
17890
  continue;
17467
17891
  claim.rendered = rendered;
17468
- const segment = matchSegment(current, rendered);
17892
+ const segment = matchSegment(current.filter((candidate) => candidate.code === claim.reserved || !taken.has(candidate.code)), rendered);
17469
17893
  if (segment === undefined) {
17470
17894
  if (claim.code.startsWith(rendered))
17471
17895
  continue;
@@ -17473,80 +17897,41 @@ function claimInlineFences({ segments, render, scope: scope2 }) {
17473
17897
  continue;
17474
17898
  }
17475
17899
  const { code, complete } = segment;
17900
+ taken.delete(claim.reserved);
17901
+ taken.add(code);
17902
+ claim.reserved = code;
17476
17903
  if (code === claim.code && complete === claim.complete)
17477
17904
  continue;
17478
17905
  claim.code = code;
17479
17906
  claim.complete = complete;
17480
- claim.root.render(render({ code, streaming: !complete }));
17907
+ claim.root.render(render({ code, streaming: !complete, last: () => isLastSegment(segments(), code) }));
17481
17908
  claim.preview?.root.render(import_react5.createElement(import_dsh_client_ui_primitives.CodeBlock, { code, lang: "tsx" }));
17482
17909
  }
17483
17910
  };
17484
17911
  const stop = observeTranscript(sweep);
17485
17912
  return () => {
17486
17913
  stop();
17914
+ nearby?.disconnect();
17915
+ parked.clear();
17916
+ woken.clear();
17487
17917
  for (const claim of claims.values())
17488
17918
  release(claim, true);
17489
17919
  };
17490
17920
  }
17491
17921
 
17492
- // src/client/runtime/segments.ts
17493
- var FENCE = new RegExp(String.raw`(?<!\x60)(\x60{3,})${FENCE_LANG.replace("/", "\\/")}([^\n]*)(\n|$)`);
17494
- function findClose(body, fence) {
17495
- const anchored = body.indexOf(`
17496
- ${fence}`);
17497
- if (anchored >= 0 && /^[^\S\n]*(?:\n|$)/.test(body.slice(anchored + 1 + fence.length)) && body[anchored + 1 + fence.length] !== "`")
17498
- return anchored + 1;
17499
- for (let at = body.indexOf(fence);at >= 0; at = body.indexOf(fence, at + 1)) {
17500
- if (body[at - 1] === "`" || body[at + fence.length] === "`")
17501
- continue;
17502
- if (/^[^\S\n]*(?:\n|$)/.test(body.slice(at + fence.length)))
17503
- return at;
17504
- }
17505
- const short = /(?:^|\n)[^\S\n]*(`{3,})[^\S\n]*(?:\n|$)/.exec(body);
17506
- return short === undefined || short === null ? -1 : short.index + (short[0].startsWith(`
17507
- `) ? 1 : 0);
17508
- }
17509
- var TOOL_CALL_MARKUP = /\n?(?:<\/(?:antml:|||DSML||)?(?:parameter|invoke|tool_calls)>\s*)+$/;
17510
- var CODE_LINE = /^(import|export|const|function|type|interface|let|\/\/)\b/;
17511
- var FENCE_META = /^[\w-]+=/;
17512
- function parseUi4aSegments(text2) {
17513
- const segments = [];
17514
- let rest = text2;
17515
- while (true) {
17516
- const open = FENCE.exec(rest);
17517
- if (open === null)
17518
- return segments;
17519
- const trailing = open[2].trim();
17520
- const bodyStart = open.index + open[0].length;
17521
- if (trailing !== "" && !CODE_LINE.test(trailing) && !FENCE_META.test(trailing)) {
17522
- rest = rest.slice(bodyStart);
17523
- continue;
17524
- }
17525
- if (/^[^\S\n]*`{3,}ui4a\/tsx/.test(rest.slice(bodyStart))) {
17526
- rest = rest.slice(bodyStart);
17527
- continue;
17528
- }
17529
- const inlineCode = CODE_LINE.test(trailing) ? `${trailing}
17530
- ` : "";
17531
- const closeIndex = findClose(rest.slice(bodyStart), open[1]);
17532
- if (closeIndex === -1) {
17533
- segments.push({ code: (inlineCode + rest.slice(bodyStart)).replace(TOOL_CALL_MARKUP, ""), complete: false });
17534
- return segments;
17535
- }
17536
- segments.push({ code: (inlineCode + rest.slice(bodyStart, bodyStart + closeIndex)).replace(TOOL_CALL_MARKUP, ""), complete: true });
17537
- rest = rest.slice(bodyStart + closeIndex).replace(new RegExp(String.raw`^${open[1]}[^\n]*\n?`), "");
17538
- }
17539
- }
17540
-
17541
17922
  // src/client/session.ts
17542
17923
  function chatNodes(ctx) {
17543
17924
  const sessionId = ctx.sessions.list.getSnapshot().current;
17544
17925
  if (sessionId === undefined)
17545
17926
  return [];
17546
- const snapshot = ctx.sessions.binding(sessionId)?.session.getSnapshot();
17547
- if (snapshot === undefined)
17927
+ const binding = ctx.sessions.binding(sessionId);
17928
+ if (binding === undefined)
17929
+ return [];
17930
+ const uiConversation = ctx.get("uiConversation");
17931
+ const chat = uiConversation === undefined ? binding.session.getSnapshot()?.chat : uiConversation.binding(binding).target("chat").getSnapshot();
17932
+ if (chat === undefined)
17548
17933
  return [];
17549
- return [...snapshot.chat.nodes.values()];
17934
+ return [...chat.nodes.values()];
17550
17935
  }
17551
17936
  function perNode(key, derive) {
17552
17937
  let cache = new Map;
@@ -17853,7 +18238,6 @@ function CanvasPanel({ canvases, offerable, cwd, onOpen, onClose, onWidth, onCar
17853
18238
  }) : /* @__PURE__ */ jsx_runtime2.jsx(GenUISurface, {
17854
18239
  code: resolved,
17855
18240
  streaming: active.streaming,
17856
- preserveState: false,
17857
18241
  onError: (error, phase) => onCardError?.(error.message, phase),
17858
18242
  onRendered: onCardRendered
17859
18243
  }, active.id)
@@ -18474,6 +18858,7 @@ function apply(ctx) {
18474
18858
  const currentSession = () => ctx.sessions.list.getSnapshot().current;
18475
18859
  const sessionId = () => currentSession() ?? "";
18476
18860
  ctx.effect(() => disposeRegistry, "dsh-generative-ui: blob module URLs");
18861
+ ctx.effect(() => () => cancelPendingReport(), "dsh-generative-ui: pending error report");
18477
18862
  ctx.effect(() => () => {
18478
18863
  disposeCompiler();
18479
18864
  dropSharedCompiler();
@@ -18499,14 +18884,15 @@ function apply(ctx) {
18499
18884
  };
18500
18885
  }, "dsh-generative-ui: $dsh host");
18501
18886
  });
18502
- const sendToModel = (text2) => {
18887
+ const sendToModel = (report) => {
18503
18888
  const id = currentSession();
18504
- const session = id === undefined ? undefined : ctx.sessions.scope(id);
18505
- if (session === undefined)
18889
+ if (id === undefined)
18506
18890
  return;
18507
- session.inject(["conversation"], (addressed) => {
18508
- addressed.conversation.send(text2).catch((error) => console.error("[dsh-generative-ui] card error report failed", error));
18509
- });
18891
+ fetch(`${CARD_ERROR_PATH}?session=${encodeURIComponent(id)}`, {
18892
+ method: "POST",
18893
+ headers: { "content-type": "application/json" },
18894
+ body: JSON.stringify(report ?? {})
18895
+ }).catch((error) => console.error("[dsh-generative-ui] card error report failed", error));
18510
18896
  };
18511
18897
  let showCanvas = null;
18512
18898
  ctx.effect(() => {
@@ -18536,7 +18922,7 @@ function apply(ctx) {
18536
18922
  });
18537
18923
  ctx.effect(() => claimInlineFences({
18538
18924
  segments,
18539
- render: ({ code, streaming }) => import_react9.createElement(GenUISurface, { code, streaming, onError: (error, phase) => reportCardError(sendToModel, error.message, phase), onRendered: cardRendered })
18925
+ render: ({ code, streaming, last }) => import_react9.createElement(GenUISurface, { code, streaming, onError: (error, phase) => reportCardError(sendToModel, error.message, phase, last), onRendered: (restored) => cardRendered(restored, last) })
18540
18926
  }), "dsh-generative-ui: inline fences");
18541
18927
  }
18542
18928
  function textOf(node) {
@@ -18564,5 +18950,5 @@ function callsKeyOf(node) {
18564
18950
 
18565
18951
  return module.exports; } });
18566
18952
 
18567
- //# debugId=86F35A693577D63964756E2164756E21
18953
+ //# debugId=5ED6C7BECA8B837364756E2164756E21
18568
18954
  //# sourceMappingURL=client.js.map