@mirrowel/opencode-openshare 0.1.0 → 0.2.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dialogs.js CHANGED
@@ -435,6 +435,36 @@ export function MenuDialog(props) {
435
435
  props.onDone(undefined);
436
436
  } })] }));
437
437
  }
438
+ /** Guard against stacked plugin dialogs. `dialog.replace` is documented as
439
+ * an atomic swap, but on some host builds a second replace while a dialog is
440
+ * still pending leaves BOTH renderers mounted - the reader sees two menus
441
+ * with two highlighted rows and keys go to a stale layer ("/share does
442
+ * nothing"). Cancelling the pending dialog and clearing before the next
443
+ * replace keeps exactly one dialog alive. The normal single-dialog path is
444
+ * untouched, so the esc-to-parent flicker fix stays intact. */
445
+ let pendingDialog;
446
+ function claimDialog(api) {
447
+ if (pendingDialog) {
448
+ try {
449
+ pendingDialog();
450
+ }
451
+ catch { }
452
+ try {
453
+ api.ui.dialog.clear();
454
+ }
455
+ catch { }
456
+ }
457
+ let released = false;
458
+ const release = () => {
459
+ if (released)
460
+ return;
461
+ released = true;
462
+ if (pendingDialog === release)
463
+ pendingDialog = undefined;
464
+ };
465
+ pendingDialog = release;
466
+ return release;
467
+ }
438
468
  /** showMenuOnce, family discipline with the flicker fix (2026-10-19):
439
469
  * done() does NOT clear the host stack — the next dialog's replace() is an
440
470
  * atomic swap, so clearing first paints one empty frame (the esc-to-parent
@@ -443,10 +473,12 @@ export function MenuDialog(props) {
443
473
  export function showMenuOnce(api, props) {
444
474
  return new Promise((resolve) => {
445
475
  let settled = false;
476
+ const release = claimDialog(api);
446
477
  const done = (value, _clear = false) => {
447
478
  if (settled)
448
479
  return;
449
480
  settled = true;
481
+ release();
450
482
  dbgLog(`menu(${props.title}) -> ${value === undefined ? "cancel" : String(value)}`);
451
483
  resolve(value);
452
484
  };
@@ -457,10 +489,12 @@ export function showMenuOnce(api, props) {
457
489
  export function showInfoDialog(api, props) {
458
490
  return new Promise((resolve) => {
459
491
  let settled = false;
492
+ const release = claimDialog(api);
460
493
  const done = (value) => {
461
494
  if (settled)
462
495
  return;
463
496
  settled = true;
497
+ release();
464
498
  resolve(value);
465
499
  };
466
500
  api.ui.dialog.replace(() => _jsx(InfoDialog, { api: api, title: props.title, message: props.message, actions: props.actions, onDone: done }), () => done(undefined));
@@ -596,10 +630,12 @@ export function PromptDialog(props) {
596
630
  export function showPromptOnce(api, props) {
597
631
  return new Promise((resolve) => {
598
632
  let settled = false;
633
+ const release = claimDialog(api);
599
634
  const done = (value, _clear = false) => {
600
635
  if (settled)
601
636
  return;
602
637
  settled = true;
638
+ release();
603
639
  dbgLog(`prompt(${props.title}) -> ${value === undefined ? "cancel" : `len=${value.length}`}`);
604
640
  resolve(value);
605
641
  };
@@ -614,10 +650,12 @@ export function toMenuOptions(options) {
614
650
  export function showSizePickerOnce(api, current) {
615
651
  return new Promise((resolve) => {
616
652
  let settled = false;
653
+ const release = claimDialog(api);
617
654
  const done = (value) => {
618
655
  if (settled)
619
656
  return;
620
657
  settled = true;
658
+ release();
621
659
  resolve(value);
622
660
  };
623
661
  api.ui.dialog.replace(() => _jsx(SizePickerDialog, { api: api, current: current, onDone: done }), () => done(undefined));
@@ -1,4 +1,4 @@
1
- // OpenShare share flows — host-agnostic logic behind /share, /unshare and
1
+ // OpenShare share flows — host-agnostic logic behind /share and
2
2
  // the settings dialog's "this session" actions. Runs against the
3
3
  // ShareHostApi facade (tui-host.ts) and the shared config module, so the
4
4
  // same compiled code serves the v1 TUI and the v2 adapter.
@@ -9,7 +9,7 @@
9
9
  // (with per-session opts when present) -> wait for the server
10
10
  // plugin to publish the binding URL into session metadata
11
11
  // (~seconds; worker-down surfaces as a timeout toast) -> copy.
12
- // /unshare resolve root -> confirm -> read binding -> clear flag ->
12
+ // stop-sharing resolve root -> confirm -> read binding -> clear flag ->
13
13
  // DELETE the share directly via the worker (belt and braces:
14
14
  // works even if the server plugin never saw the flag event) ->
15
15
  // the server-side flag-removal path cleans up on its own.
package/dist/sidebar.d.ts CHANGED
@@ -3,7 +3,9 @@ import type { JSX } from "@opentui/solid";
3
3
  import type { TuiPluginApi, TuiSlotContext } from "@opencode-ai/plugin/tui";
4
4
  /** value -> `kind:preview` so the log shows TYPES (the crash was a type bug) */
5
5
  export declare function dbg(v: unknown, depth?: number): string;
6
- export declare function dbgLog(line: string): void;
6
+ /** no-op: the TUI debug log file was a crash-hunt aid and must not write
7
+ * to temp any more (owner request 2026-10-21) */
8
+ export declare function dbgLog(_line: string): void;
7
9
  type SidebarTitleProps = {
8
10
  session_id: string;
9
11
  title: string;
package/dist/sidebar.js CHANGED
@@ -1,9 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/solid/jsx-runtime";
2
2
  import { bindingsOf, bindingOf, verifyShare } from "./share-flow.js";
3
- import { appendFileSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- const DBG_FILE = join(tmpdir(), "openshare-tui-debug.log");
7
3
  /** value -> `kind:preview` so the log shows TYPES (the crash was a type bug) */
8
4
  export function dbg(v, depth = 0) {
9
5
  const t = typeof v;
@@ -50,13 +46,9 @@ function dbgEl(el, depth = 0) {
50
46
  return `el?-${dbg(e)}`;
51
47
  }
52
48
  }
53
- export function dbgLog(line) {
54
- try {
55
- appendFileSync(DBG_FILE, `${new Date().toISOString()} ${line}\n`);
56
- }
57
- catch { }
58
- }
59
- let renderSeq = 0;
49
+ /** no-op: the TUI debug log file was a crash-hunt aid and must not write
50
+ * to temp any more (owner request 2026-10-21) */
51
+ export function dbgLog(_line) { }
60
52
  const GONE_TTL_MS = 60_000;
61
53
  const goneCache = new Map();
62
54
  function goneVerdict(sid, server, shareId) {
@@ -70,13 +62,10 @@ function goneVerdict(sid, server, shareId) {
70
62
  return cached;
71
63
  }
72
64
  export function createSidebarSlot(api, opts) {
73
- dbgLog(`createSidebarSlot called; api.state=${!!api.state} api.theme=${!!api.theme} api.version=${dbg(api.app?.version)}`);
74
65
  return {
75
66
  slots: {
76
67
  sidebar_title: (ctx, props) => {
77
- const seq = ++renderSeq;
78
68
  try {
79
- dbgLog(`render#${seq} ctx=${dbg(ctx)} props=${dbg(props)}`);
80
69
  const sid = typeof props?.session_id === "string" ? props.session_id : "";
81
70
  const title = String(props?.title ?? "") || "Session";
82
71
  const session = sid ? api.state.session.get(sid) : undefined;
@@ -90,7 +79,6 @@ export function createSidebarSlot(api, opts) {
90
79
  const anyGone = verdicts.some((v) => v?.status === "gone");
91
80
  const multi = liveCount > 1 ? `·${liveCount} ` : "";
92
81
  const theme = ctx?.theme?.current ?? api.theme.current;
93
- dbgLog(`render#${seq} sid=${dbg(sid)} title=${dbg(title)} session=${session ? "found" : "MISSING"} binding=${binding ? `id=${dbg(binding.id)} enabled=${dbg(binding.enabled)}` : "none"} status=${dbg(status)} theme=${dbg(theme)}`);
94
82
  // chip on its own line under the title (takes the blank line that
95
83
  // separated the title block from the context blocks); clicking it
96
84
  // copies the share link — proven pattern: onMouseDown on a box.
@@ -131,11 +119,9 @@ export function createSidebarSlot(api, opts) {
131
119
  : status === "live"
132
120
  ? `◉ re:share ${multi}— click to copy link`
133
121
  : "◉ re:share (dormant)" }) })) : null] }));
134
- dbgLog(`render#${seq} RETURNS ${dbgEl(el)}`);
135
122
  return el;
136
123
  }
137
124
  catch (error) {
138
- dbgLog(`render#${seq} THREW ${error?.stack ?? dbg(error)} — rethrowing`);
139
125
  throw error;
140
126
  }
141
127
  },
package/dist/tui.js CHANGED
@@ -1,19 +1,15 @@
1
1
  // @bun
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
2
+ var __esm = (fn, res, err) => () => {
3
+ if (fn)
4
+ try {
5
+ res = fn(fn = 0);
6
+ } catch (e) {
7
+ err = [e];
8
+ }
9
+ if (err)
10
+ throw err[0];
11
+ return res;
15
12
  };
16
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
13
 
18
14
  // src/config.ts
19
15
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -165,8 +161,8 @@ function resolveServer(cfg) {
165
161
  if (cfg.workerUrl)
166
162
  servers[cfg.workerUrl] = { token: cfg.createToken || "", adminToken: cfg.createToken || "", active: true };
167
163
  if (cfg.servers)
168
- for (const [url2, entry] of Object.entries(cfg.servers))
169
- servers[url2] = entry;
164
+ for (const [url, entry] of Object.entries(cfg.servers))
165
+ servers[url] = entry;
170
166
  const urls = Object.keys(servers);
171
167
  if (!urls.length)
172
168
  return;
@@ -318,9 +314,6 @@ import { insertNode as _$insertNode } from "@opentui/solid";
318
314
  import { insert as _$insert } from "@opentui/solid";
319
315
  import { setProp as _$setProp } from "@opentui/solid";
320
316
  import { createElement as _$createElement } from "@opentui/solid";
321
- import { appendFileSync } from "fs";
322
- import { tmpdir } from "os";
323
- import { join as join2 } from "path";
324
317
  function dbg(v, depth = 0) {
325
318
  const t = typeof v;
326
319
  if (v === null)
@@ -344,31 +337,7 @@ function dbg(v, depth = 0) {
344
337
  return "obj:?}";
345
338
  }
346
339
  }
347
- function dbgEl(el, depth = 0) {
348
- if (el === null || el === undefined || typeof el === "string" || typeof el === "number" || typeof el === "boolean")
349
- return dbg(el);
350
- if (Array.isArray(el))
351
- return `[${el.map((x) => dbgEl(x, depth)).join(",")}]`;
352
- if (typeof el === "function")
353
- return "fn-node";
354
- if (depth >= 4)
355
- return "\u2026";
356
- try {
357
- const rec = el;
358
- const t = dbg(rec.t ?? rec.type, 2);
359
- const kids = rec.children;
360
- const props = Object.keys(rec).filter((k) => !["t", "type", "children"].includes(k));
361
- return `<${t} ${props.join(" ")}>${kids === undefined ? "" : dbgEl(kids, depth + 1)}</${t}>`;
362
- } catch (e) {
363
- return `el?-${dbg(e)}`;
364
- }
365
- }
366
- function dbgLog(line) {
367
- try {
368
- appendFileSync(DBG_FILE, `${new Date().toISOString()} ${line}
369
- `);
370
- } catch {}
371
- }
340
+ function dbgLog(_line) {}
372
341
  function goneVerdict(sid, server, shareId) {
373
342
  const key = `${sid}|${server}`;
374
343
  const cached = goneCache.get(key);
@@ -383,13 +352,10 @@ function goneVerdict(sid, server, shareId) {
383
352
  return cached;
384
353
  }
385
354
  function createSidebarSlot(api, opts) {
386
- dbgLog(`createSidebarSlot called; api.state=${!!api.state} api.theme=${!!api.theme} api.version=${dbg(api.app?.version)}`);
387
355
  return {
388
356
  slots: {
389
357
  sidebar_title: (ctx, props) => {
390
- const seq = ++renderSeq;
391
358
  try {
392
- dbgLog(`render#${seq} ctx=${dbg(ctx)} props=${dbg(props)}`);
393
359
  const sid = typeof props?.session_id === "string" ? props.session_id : "";
394
360
  const title = String(props?.title ?? "") || "Session";
395
361
  const session = sid ? api.state.session.get(sid) : undefined;
@@ -402,7 +368,6 @@ function createSidebarSlot(api, opts) {
402
368
  const anyGone = verdicts.some((v) => v?.status === "gone");
403
369
  const multi = liveCount > 1 ? `\xB7${liveCount} ` : "";
404
370
  const theme = ctx?.theme?.current ?? api.theme.current;
405
- dbgLog(`render#${seq} sid=${dbg(sid)} title=${dbg(title)} session=${session ? "found" : "MISSING"} binding=${binding ? `id=${dbg(binding.id)} enabled=${dbg(binding.enabled)}` : "none"} status=${dbg(status)} theme=${dbg(theme)}`);
406
371
  const copyLink = () => {
407
372
  if (anyGone) {
408
373
  opts?.onShareCommand?.();
@@ -449,64 +414,22 @@ function createSidebarSlot(api, opts) {
449
414
  _$effect((_$p) => _$setProp(_el$2, "fg", theme.text, _$p));
450
415
  return _el$;
451
416
  })();
452
- dbgLog(`render#${seq} RETURNS ${dbgEl(el)}`);
453
417
  return el;
454
418
  } catch (error) {
455
- dbgLog(`render#${seq} THREW ${error?.stack ?? dbg(error)} \u2014 rethrowing`);
456
419
  throw error;
457
420
  }
458
421
  }
459
422
  }
460
423
  };
461
424
  }
462
- var DBG_FILE, renderSeq = 0, GONE_TTL_MS = 60000, goneCache;
425
+ var GONE_TTL_MS = 60000, goneCache;
463
426
  var init_sidebar = __esm(() => {
464
427
  init_share_flow();
465
- DBG_FILE = join2(tmpdir(), "openshare-tui-debug.log");
466
428
  goneCache = new Map;
467
429
  });
468
430
 
469
431
  // src/share-flow.ts
470
- var exports_share_flow = {};
471
- __export(exports_share_flow, {
472
- workerDeleteShare: () => workerDeleteShare,
473
- waitForBinding: () => waitForBinding,
474
- verifyShare: () => verifyShare,
475
- setShareFlag: () => setShareFlag,
476
- serverEntries: () => serverEntries,
477
- saveServerEntry: () => saveServerEntry,
478
- saveConfig: () => saveConfig,
479
- runUnshareFlow: () => runUnshareFlow,
480
- runShareFlow: () => runShareFlow,
481
- rotateAtServer: () => rotateAtServer,
482
- resolveRoot: () => resolveRoot,
483
- readBindings: () => readBindings,
484
- readBinding: () => readBinding,
485
- probeWorker: () => probeWorker,
486
- probeAuthMode: () => probeAuthMode,
487
- pluginConfigPaths: () => pluginConfigPaths,
488
- patchSessionBinding: () => patchSessionBinding,
489
- normServerUrl: () => normServerUrl,
490
- isConfigured: () => isConfigured,
491
- hostOf: () => hostOf,
492
- enrollAtServer: () => enrollAtServer,
493
- disconnectAtServer: () => disconnectAtServer,
494
- deleteServerEntry: () => deleteServerEntry,
495
- currentConfig: () => currentConfig,
496
- confirmShare: () => confirmShare,
497
- clearShareFlag: () => clearShareFlag,
498
- clearGoneBindingEntries: () => clearGoneBindingEntries,
499
- bindingsOf: () => bindingsOf,
500
- bindingOf: () => bindingOf,
501
- adminTokenFor: () => adminTokenFor,
502
- adminClient: () => adminClient,
503
- activeServer: () => activeServer,
504
- CONSENT_KV_KEY: () => CONSENT_KV_KEY
505
- });
506
432
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
507
- function pluginConfigPaths(baseDir = moduleDir()) {
508
- return configPaths(baseDir);
509
- }
510
433
  function currentConfig(baseDir = moduleDir()) {
511
434
  return loadConfig(baseDir);
512
435
  }
@@ -1169,14 +1092,14 @@ async function editKey(host, cfg, key) {
1169
1092
  });
1170
1093
  if (picked === undefined)
1171
1094
  return;
1172
- const next2 = { ...cfg };
1095
+ const next = { ...cfg };
1173
1096
  if (key === "shareMode")
1174
- next2[key] = picked === "auto" ? undefined : picked;
1097
+ next[key] = picked === "auto" ? undefined : picked;
1175
1098
  else if (key === "openTasksInNewTab" || key === "announceStderr")
1176
- next2[key] = picked === "true";
1099
+ next[key] = picked === "true";
1177
1100
  else
1178
- next2[key] = picked;
1179
- return next2;
1101
+ next[key] = picked;
1102
+ return next;
1180
1103
  }
1181
1104
  const raw = await showPrompt(host.ui, {
1182
1105
  title: `${meta.label} \u2014 ${displayValue(key, cfg)}`,
@@ -1681,15 +1604,15 @@ async function adminCodesMenu(host, client, codes) {
1681
1604
  continue;
1682
1605
  const n = Number(raw.trim() === "" ? "1" : raw.trim());
1683
1606
  const activations = Number.isFinite(n) && n >= 1 ? Math.floor(n) : -1;
1684
- const res2 = await client.createCode(label.trim() || "invite", activations);
1685
- if (res2.ok && res2.data?.code) {
1686
- await host.clipboard.write(res2.data.code);
1687
- await showInfo(host.ui, { title: "Code created", message: `${label.trim() || "invite"}: ${res2.data.code}
1607
+ const res = await client.createCode(label.trim() || "invite", activations);
1608
+ if (res.ok && res.data?.code) {
1609
+ await host.clipboard.write(res.data.code);
1610
+ await showInfo(host.ui, { title: "Code created", message: `${label.trim() || "invite"}: ${res.data.code}
1688
1611
  (${activations === -1 ? "unlimited" : activations} activation${activations === 1 ? "" : "s"})
1689
1612
 
1690
1613
  (copied to clipboard \u2014 shown once)` });
1691
1614
  } else {
1692
- await showInfo(host.ui, { title: "Create failed", message: res2.error ?? "unknown error" });
1615
+ await showInfo(host.ui, { title: "Create failed", message: res.error ?? "unknown error" });
1693
1616
  }
1694
1617
  continue;
1695
1618
  }
@@ -2215,7 +2138,7 @@ async function showShareManager(host, sessionID) {
2215
2138
  all = all.filter((b) => !gone.some((g) => g.server === b.server));
2216
2139
  const list = gone.map((b) => `\u2022 ${hostOf(b.url || b.server)} \u2014 ${b.url}`).join(`
2217
2140
  `);
2218
- const choice2 = await host.ui.showMenu({
2141
+ const choice = await host.ui.showMenu({
2219
2142
  title: "Share is gone",
2220
2143
  hint: "The page was removed, expired, or purged on the server \u2014 sharing disabled for it.",
2221
2144
  options: [
@@ -2229,7 +2152,7 @@ Gone:
2229
2152
  ${list}` }
2230
2153
  ]
2231
2154
  });
2232
- if (choice2 === "again") {
2155
+ if (choice === "again") {
2233
2156
  finish(host, await runShareFlow(host, sid));
2234
2157
  return;
2235
2158
  }
@@ -2266,13 +2189,13 @@ Anyone with the link can read the page (plus the login, if read auth is set).`
2266
2189
  },
2267
2190
  { title: "Cancel", value: "cancel" }
2268
2191
  ]
2269
- }).then(async (choice2) => {
2270
- if (choice2 === "copy") {
2192
+ }).then(async (choice) => {
2193
+ if (choice === "copy") {
2271
2194
  const ok = await host.clipboard.write(b.url);
2272
2195
  finish(host, ok ? { ok: true, message: `Share URL copied to clipboard: ${b.url}`, url: b.url } : { ok: false, message: `Could not write the clipboard \u2014 link: ${b.url}`, url: b.url });
2273
2196
  return;
2274
2197
  }
2275
- if (choice2 === "stop") {
2198
+ if (choice === "stop") {
2276
2199
  const how = await host.ui.showMenu({
2277
2200
  title: "Stop sharing",
2278
2201
  hint: b.url,
@@ -2411,18 +2334,18 @@ ${otherServers.map((s) => `\u2022 ${s.url}`).join(`
2411
2334
  });
2412
2335
  if (!pick || pick === "__cancel")
2413
2336
  return;
2414
- const { saveServerEntry: saveServerEntry2 } = await Promise.resolve().then(() => (init_share_flow(), exports_share_flow));
2337
+ await Promise.resolve().then(() => init_share_flow());
2415
2338
  for (const s of serverEntries())
2416
2339
  if (s.url !== pick)
2417
- saveServerEntry2(s.url, { active: undefined });
2418
- saveServerEntry2(pick, { active: true });
2340
+ saveServerEntry(s.url, { active: undefined });
2341
+ saveServerEntry(pick, { active: true });
2419
2342
  finish(host, { ok: true, message: `${hostOf(pick)} activated \u2014 this session mirrors there within seconds (new sessions share to it too).` });
2420
2343
  return;
2421
2344
  }
2422
2345
  if (choice === "manage") {
2423
2346
  const pick = await host.ui.showMenu({
2424
2347
  title: "Manage server",
2425
- options: [...all.map((b2) => ({ title: hostOf(b2.url || b2.server), value: b2.server, description: b2.url })), { title: "Cancel", value: "__cancel" }]
2348
+ options: [...all.map((b) => ({ title: hostOf(b.url || b.server), value: b.server, description: b.url })), { title: "Cancel", value: "__cancel" }]
2426
2349
  });
2427
2350
  if (!pick || pick === "__cancel")
2428
2351
  return;
@@ -2952,20 +2875,20 @@ function MenuDialog(props) {
2952
2875
  else
2953
2876
  sections.at(-1)?.lines.push(line);
2954
2877
  }
2955
- for (const section2 of sections) {
2956
- while (section2.lines.length && section2.lines.at(-1) === "")
2957
- section2.lines.pop();
2958
- while (section2.lines.length && section2.lines[0] === "")
2959
- section2.lines.shift();
2878
+ for (const section of sections) {
2879
+ while (section.lines.length && section.lines.at(-1) === "")
2880
+ section.lines.pop();
2881
+ while (section.lines.length && section.lines[0] === "")
2882
+ section.lines.shift();
2960
2883
  }
2961
2884
  const contentWidth = dialogContentWidth(props.api) - 2;
2962
- return sections.filter((section2, index) => index === 0 ? section2.lines.length > 0 : true).map((section2) => ({
2963
- title: section2.title,
2964
- lines: preWrap(section2.lines.join(`
2885
+ return sections.filter((section, index) => index === 0 ? section.lines.length > 0 : true).map((section) => ({
2886
+ title: section.title,
2887
+ lines: preWrap(section.lines.join(`
2965
2888
  `), contentWidth)
2966
2889
  }));
2967
2890
  });
2968
- const infoHeight = createMemo(() => cappedHeight(helpSections().reduce((rows, section2) => rows + section2.lines.length + (section2.title ? 2 : 0), 0) + 1, maxRows(props.api, dimensions().height, 8, 4), 4));
2891
+ const infoHeight = createMemo(() => cappedHeight(helpSections().reduce((rows, section) => rows + section.lines.length + (section.title ? 2 : 0), 0) + 1, maxRows(props.api, dimensions().height, 8, 4), 4));
2969
2892
  const [section, setSection] = createSignal(0);
2970
2893
  const jumpSection = (delta) => {
2971
2894
  const count = helpSections().length;
@@ -3380,13 +3303,36 @@ function MenuDialog(props) {
3380
3303
  }
3381
3304
  });
3382
3305
  }
3306
+ var pendingDialog;
3307
+ function claimDialog(api) {
3308
+ if (pendingDialog) {
3309
+ try {
3310
+ pendingDialog();
3311
+ } catch {}
3312
+ try {
3313
+ api.ui.dialog.clear();
3314
+ } catch {}
3315
+ }
3316
+ let released = false;
3317
+ const release = () => {
3318
+ if (released)
3319
+ return;
3320
+ released = true;
3321
+ if (pendingDialog === release)
3322
+ pendingDialog = undefined;
3323
+ };
3324
+ pendingDialog = release;
3325
+ return release;
3326
+ }
3383
3327
  function showMenuOnce(api, props) {
3384
3328
  return new Promise((resolve) => {
3385
3329
  let settled = false;
3330
+ const release = claimDialog(api);
3386
3331
  const done = (value, _clear = false) => {
3387
3332
  if (settled)
3388
3333
  return;
3389
3334
  settled = true;
3335
+ release();
3390
3336
  dbgLog(`menu(${props.title}) -> ${value === undefined ? "cancel" : String(value)}`);
3391
3337
  resolve(value);
3392
3338
  };
@@ -3412,10 +3358,12 @@ function showMenuOnce(api, props) {
3412
3358
  function showInfoDialog(api, props) {
3413
3359
  return new Promise((resolve) => {
3414
3360
  let settled = false;
3361
+ const release = claimDialog(api);
3415
3362
  const done = (value) => {
3416
3363
  if (settled)
3417
3364
  return;
3418
3365
  settled = true;
3366
+ release();
3419
3367
  resolve(value);
3420
3368
  };
3421
3369
  api.ui.dialog.replace(() => _$createComponent(InfoDialog, {
@@ -3810,10 +3758,12 @@ function PromptDialog(props) {
3810
3758
  function showPromptOnce(api, props) {
3811
3759
  return new Promise((resolve) => {
3812
3760
  let settled = false;
3761
+ const release = claimDialog(api);
3813
3762
  const done = (value, _clear = false) => {
3814
3763
  if (settled)
3815
3764
  return;
3816
3765
  settled = true;
3766
+ release();
3817
3767
  dbgLog(`prompt(${props.title}) -> ${value === undefined ? "cancel" : `len=${value.length}`}`);
3818
3768
  resolve(value);
3819
3769
  };
@@ -3848,10 +3798,12 @@ function toMenuOptions(options) {
3848
3798
  function showSizePickerOnce(api, current) {
3849
3799
  return new Promise((resolve) => {
3850
3800
  let settled = false;
3801
+ const release = claimDialog(api);
3851
3802
  const done = (value) => {
3852
3803
  if (settled)
3853
3804
  return;
3854
3805
  settled = true;
3806
+ release();
3855
3807
  resolve(value);
3856
3808
  };
3857
3809
  api.ui.dialog.replace(() => _$createComponent(SizePickerDialog, {
@@ -4757,6 +4709,6 @@ var tui_default = {
4757
4709
  setup: createV2TuiSetup()
4758
4710
  };
4759
4711
  export {
4760
- tui_default as default,
4761
- buildV1HostApi
4712
+ buildV1HostApi,
4713
+ tui_default as default
4762
4714
  };
@@ -1,30 +1,8 @@
1
- import { type MessageInfo, type PartInfo } from "./core.js";
2
1
  type V2Event = {
3
2
  type: string;
4
3
  data?: Record<string, unknown>;
5
4
  [key: string]: unknown;
6
5
  };
7
- type V2Message = {
8
- id: string;
9
- type: string;
10
- time?: {
11
- created?: number;
12
- completed?: number;
13
- streamed?: number;
14
- };
15
- text?: string;
16
- agent?: string;
17
- model?: {
18
- providerID: string;
19
- id?: string;
20
- modelID?: string;
21
- };
22
- content?: Array<Record<string, unknown>>;
23
- error?: {
24
- message?: string;
25
- } | null;
26
- metadata?: Record<string, unknown>;
27
- };
28
6
  type V2SessionClient = {
29
7
  get(input: {
30
8
  sessionID: string;
@@ -72,10 +50,5 @@ type V2Context = {
72
50
  };
73
51
  signal?: AbortSignal;
74
52
  };
75
- /** v2 flat message -> { info, parts } in the v1 shapes the viewer speaks. */
76
- export declare function convertV2Message(message: V2Message, sessionID: string): {
77
- info: MessageInfo;
78
- parts: PartInfo[];
79
- } | undefined;
80
53
  export declare function createV2ServerSetup(): (context: V2Context) => Promise<(() => void) | void>;
81
54
  export {};