@openspecui/web 3.11.3 → 3.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/assets/CanvasRenderer-CaB1iANG.js +1 -0
  2. package/dist/assets/WebGLRenderer-BQkM8hJI.js +1 -0
  3. package/dist/assets/WebGPURenderer-DL2luov0.js +1 -0
  4. package/dist/assets/audio-preview-EEAGPjut.js +1 -0
  5. package/dist/assets/browserAll-77yDI0Dz.js +1 -0
  6. package/dist/assets/common-Cv1DC55Y.js +22 -0
  7. package/dist/assets/common-tDRAnupk.css +1 -0
  8. package/dist/assets/{dist-Bl4uejAA.js → dist-B85dtRfx.js} +1 -1
  9. package/dist/assets/dist-B8IUilny.js +1 -0
  10. package/dist/assets/{dist-CkEGbci5.js → dist-BRxF7Jkf.js} +1 -1
  11. package/dist/assets/{dist-DDPVC2MF.js → dist-BZECNWGg.js} +1 -1
  12. package/dist/assets/{dist-BVKuVv2H.js → dist-BfZLQFwN.js} +1 -1
  13. package/dist/assets/{dist-Caq73PXT.js → dist-CbJQUXs0.js} +1 -1
  14. package/dist/assets/{dist-rlrlm1HB.js → dist-CmKsGuOc.js} +1 -1
  15. package/dist/assets/{dist-YXnB8lV1.js → dist-DiwHgET9.js} +1 -1
  16. package/dist/assets/dist-Djq8Qfgl.js +1 -0
  17. package/dist/assets/dist-DpKtO7Rd.js +1 -0
  18. package/dist/assets/{dist-CZh2GIVt.js → dist-SfwxOhmW.js} +1 -1
  19. package/dist/assets/{dist-BKY1RG9s.js → dist-zpg9m3aC.js} +1 -1
  20. package/dist/assets/{image-preview-BvROjYK5.js → image-preview-D763uzFX.js} +1 -1
  21. package/dist/assets/{init-sSrVfvjg.js → init-fMWSa5Px.js} +1 -1
  22. package/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  23. package/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  24. package/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  25. package/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  26. package/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  27. package/dist/assets/main-C9sK_hBV.js +1669 -0
  28. package/dist/assets/main-X9jaWXie.css +1 -0
  29. package/dist/assets/{media-preview-DWkSTGaX.js → media-preview-CKyl7d05.js} +1 -1
  30. package/dist/assets/{pdf-preview-C7gTFuZg.js → pdf-preview-CZGViMN9.js} +1 -1
  31. package/dist/assets/share-tech-mono-latin-400-normal-15MgdvEM.woff +0 -0
  32. package/dist/assets/share-tech-mono-latin-400-normal-CCqdbj5z.woff2 +0 -0
  33. package/dist/assets/trpc-CYA_MIjc.js +1 -0
  34. package/dist/assets/video-preview-zIpjT8bC.js +1 -0
  35. package/dist/assets/webworkerAll-BDQFYrM_.js +1 -0
  36. package/dist/audio-preview.html +4 -3
  37. package/dist/image-preview.html +3 -2
  38. package/dist/index.html +2 -29
  39. package/dist/pdf-preview.html +3 -2
  40. package/dist/video-preview.html +4 -3
  41. package/dist-ssg/client/.vite/ssr-manifest.json +8565 -1080
  42. package/dist-ssg/client/assets/CanvasRenderer-tPU4vV1i.js +1 -0
  43. package/dist-ssg/client/assets/WebGLRenderer-8XaEJZ8d.js +1 -0
  44. package/dist-ssg/client/assets/WebGPURenderer-D_U45dp8.js +1 -0
  45. package/dist-ssg/client/assets/browserAll-6a90eROj.js +1 -0
  46. package/dist-ssg/client/assets/{dist-BKTT7Vdm.js → dist-B6MkJ-H7.js} +1 -1
  47. package/dist-ssg/client/assets/{dist-upAmtw9_.js → dist-B9IiVt9G.js} +1 -1
  48. package/dist-ssg/client/assets/{dist-CX1-5rfl.js → dist-BCjKNKOg.js} +1 -1
  49. package/dist-ssg/client/assets/dist-Bqpf_8HF.js +1 -0
  50. package/dist-ssg/client/assets/{dist-DJkx9TIh.js → dist-CFjNhfqM.js} +1 -1
  51. package/dist-ssg/client/assets/{dist-BPUIHtZ8.js → dist-CIMwXheF.js} +1 -1
  52. package/dist-ssg/client/assets/{dist-CVizFqBj.js → dist-CNLvbQSx.js} +1 -1
  53. package/dist-ssg/client/assets/dist-CWcubuXa.js +1 -0
  54. package/dist-ssg/client/assets/{dist-Cm5Vx6W8.js → dist-Ch_E0jle.js} +1 -1
  55. package/dist-ssg/client/assets/{dist-CLrPzzut.js → dist-CjF4SWnb.js} +1 -1
  56. package/dist-ssg/client/assets/dist-D2pHEmiw.js +1 -0
  57. package/dist-ssg/client/assets/{dist-HHQTTSi-.js → dist-DgYslwM5.js} +1 -1
  58. package/dist-ssg/client/assets/{ghostty-web-BKQcHDq5.js → ghostty-web-BYk-wneR.js} +1 -1
  59. package/dist-ssg/client/assets/index-8OoZ339S.css +1 -0
  60. package/dist-ssg/client/assets/{index.ssg-DBq9_pDs.js → index.ssg-BWQbO3uy.js} +164 -149
  61. package/dist-ssg/client/assets/{init-brQ3KbKB.js → init-DnSi5N8V.js} +1 -1
  62. package/dist-ssg/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  63. package/dist-ssg/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  64. package/dist-ssg/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  65. package/dist-ssg/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  66. package/dist-ssg/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  67. package/dist-ssg/client/assets/share-tech-mono-latin-400-normal-15MgdvEM.woff +0 -0
  68. package/dist-ssg/client/assets/share-tech-mono-latin-400-normal-CCqdbj5z.woff2 +0 -0
  69. package/dist-ssg/client/assets/trpc-BhXIjp-3.js +1 -0
  70. package/dist-ssg/client/assets/webworkerAll-Dp42RyhF.js +1 -0
  71. package/dist-ssg/client/index.ssg.html +2 -29
  72. package/dist-ssg/server/entry-server.js +734 -108
  73. package/package.json +6 -4
  74. package/dist/assets/CanvasRenderer-DGnQw8uB.js +0 -1
  75. package/dist/assets/WebGLRenderer-DHRbteVS.js +0 -1
  76. package/dist/assets/WebGPURenderer-BnFcGKnW.js +0 -1
  77. package/dist/assets/audio-preview-DF-g2nKV.js +0 -1
  78. package/dist/assets/browserAll-BB7WVzo0.js +0 -1
  79. package/dist/assets/common-DiWYGJu_.js +0 -22
  80. package/dist/assets/dist-B0Que_Lt.js +0 -1
  81. package/dist/assets/dist-B8m1wxBF.js +0 -1
  82. package/dist/assets/dist-DP_rT4tp.js +0 -1
  83. package/dist/assets/main-DThx1JaQ.js +0 -1654
  84. package/dist/assets/main-fCQ7khWW.css +0 -1
  85. package/dist/assets/trpc-Dko2Zn9y.js +0 -1
  86. package/dist/assets/video-preview-CChWJOsP.js +0 -1
  87. package/dist/assets/webworkerAll-Bfbn1LyG.js +0 -1
  88. package/dist-ssg/client/assets/CanvasRenderer-Dpr5EYjp.js +0 -1
  89. package/dist-ssg/client/assets/WebGLRenderer-DCzH263Z.js +0 -1
  90. package/dist-ssg/client/assets/WebGPURenderer-DgF_8B9g.js +0 -1
  91. package/dist-ssg/client/assets/browserAll-CPl4rDmN.js +0 -1
  92. package/dist-ssg/client/assets/dist-BuFVGK1x.js +0 -1
  93. package/dist-ssg/client/assets/dist-DlrmChto.js +0 -1
  94. package/dist-ssg/client/assets/dist-bnfyzoQu.js +0 -1
  95. package/dist-ssg/client/assets/index-BtGAsAtP.css +0 -1
  96. package/dist-ssg/client/assets/trpc-DGapI9Ny.js +0 -1
  97. package/dist-ssg/client/assets/webworkerAll-BKMUeO96.js +0 -1
@@ -28325,13 +28325,13 @@ function reduceKernel(state, event) {
28325
28325
  persist: "none"
28326
28326
  };
28327
28327
  case "APPLY_LAYOUT": {
28328
- if (event.layout.updatedAt <= state.updatedAt) return {
28328
+ const merged = mergeLayout(event.layout);
28329
+ if (!(event.layout.updatedAt !== state.updatedAt || !areTabsEqual(state.mainTabs, merged.mainTabs) || !areTabsEqual(state.bottomTabs, merged.bottomTabs)) || !event.force && event.layout.updatedAt <= state.updatedAt) return {
28329
28330
  nextState: state,
28330
28331
  changed: false,
28331
28332
  notify: [],
28332
28333
  persist: "none"
28333
28334
  };
28334
- const merged = mergeLayout(event.layout);
28335
28335
  return {
28336
28336
  nextState: {
28337
28337
  ...state,
@@ -28688,14 +28688,11 @@ var NavController = class {
28688
28688
  rebindProjectScopedLayout(storageKey) {
28689
28689
  const scopedLocal = readLocalStorageByKey(storageKey);
28690
28690
  if (scopedLocal) {
28691
- const merged = mergeLayout(scopedLocal);
28692
- this.state = normalizeState({
28693
- ...this.state,
28694
- mainTabs: merged.mainTabs,
28695
- bottomTabs: merged.bottomTabs,
28696
- updatedAt: scopedLocal.updatedAt
28691
+ this.dispatch({
28692
+ type: "APPLY_LAYOUT",
28693
+ layout: scopedLocal,
28694
+ force: true
28697
28695
  });
28698
- this.normalizeUrl();
28699
28696
  return;
28700
28697
  }
28701
28698
  this.state = normalizeState({
@@ -32292,6 +32289,120 @@ ZodOptional.create;
32292
32289
  ZodNullable.create;
32293
32290
  var preprocessType = ZodEffects.createWithPreprocess;
32294
32291
  ZodPipeline.create;
32292
+ //#endregion
32293
+ //#region ../core/src/translation-task-control.ts
32294
+ var BATCH_TRANSLATION_ERROR_KINDS = [
32295
+ "timeout",
32296
+ "memory-limit",
32297
+ "runtime",
32298
+ "abort"
32299
+ ];
32300
+ async function runControlledTranslationTask(task, options = {}) {
32301
+ const controller = new AbortController();
32302
+ const cleanupCallbacks = [];
32303
+ const parentSignal = options.signal;
32304
+ const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
32305
+ if (parentSignal) if (parentSignal.aborted) controller.abort(parentSignal.reason);
32306
+ else {
32307
+ const onAbort = () => controller.abort(parentSignal.reason);
32308
+ parentSignal.addEventListener("abort", onAbort, { once: true });
32309
+ cleanupCallbacks.push(() => parentSignal.removeEventListener("abort", onAbort));
32310
+ }
32311
+ if (controller.signal.aborted) {
32312
+ cleanupCallbacks.forEach((callback) => callback());
32313
+ return {
32314
+ ok: false,
32315
+ error: normalizeBatchTranslationError(controller.signal.reason, parentSignal)
32316
+ };
32317
+ }
32318
+ let timer;
32319
+ let timeoutError;
32320
+ const taskPromise = Promise.resolve().then(() => task(controller.signal));
32321
+ taskPromise.catch(() => void 0);
32322
+ const races = [taskPromise];
32323
+ if (!controller.signal.aborted) races.push(new Promise((_, reject) => {
32324
+ const onAbort = () => reject(new DOMException("Translation cancelled.", "AbortError"));
32325
+ controller.signal.addEventListener("abort", onAbort, { once: true });
32326
+ cleanupCallbacks.push(() => controller.signal.removeEventListener("abort", onAbort));
32327
+ }));
32328
+ if (timeoutMs !== void 0) races.push(new Promise((_, reject) => {
32329
+ timer = setTimeout(() => {
32330
+ timeoutError = /* @__PURE__ */ new Error(`Translation task timed out after ${timeoutMs}ms.`);
32331
+ controller.abort(timeoutError);
32332
+ reject(timeoutError);
32333
+ }, timeoutMs);
32334
+ }));
32335
+ try {
32336
+ return {
32337
+ ok: true,
32338
+ value: await Promise.race(races)
32339
+ };
32340
+ } catch (error) {
32341
+ controller.abort(error);
32342
+ if (timeoutError && controller.signal.reason === timeoutError) return {
32343
+ ok: false,
32344
+ error: {
32345
+ kind: "timeout",
32346
+ message: timeoutError.message
32347
+ }
32348
+ };
32349
+ return {
32350
+ ok: false,
32351
+ error: normalizeBatchTranslationError(error, parentSignal)
32352
+ };
32353
+ } finally {
32354
+ if (timer) clearTimeout(timer);
32355
+ cleanupCallbacks.forEach((callback) => callback());
32356
+ }
32357
+ }
32358
+ function normalizeBatchTranslationError(error, parentSignal) {
32359
+ if (isBatchTranslationError(error)) return error;
32360
+ if (parentSignal?.aborted) return {
32361
+ kind: "abort",
32362
+ message: readAbortMessage(parentSignal.reason) ?? "Translation cancelled."
32363
+ };
32364
+ if (error instanceof DOMException && error.name === "AbortError") return {
32365
+ kind: "abort",
32366
+ message: readAbortMessage(error) ?? "Translation cancelled."
32367
+ };
32368
+ const message = getErrorMessage$2(error);
32369
+ if (isTimeoutMessage(message)) return {
32370
+ kind: "timeout",
32371
+ message
32372
+ };
32373
+ if (isMemoryLimitMessage(message)) return {
32374
+ kind: "memory-limit",
32375
+ message
32376
+ };
32377
+ return {
32378
+ kind: "runtime",
32379
+ message
32380
+ };
32381
+ }
32382
+ function normalizeTimeoutMs(timeoutMs) {
32383
+ return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.floor(timeoutMs) : void 0;
32384
+ }
32385
+ function getErrorMessage$2(error) {
32386
+ if (error instanceof Error && error.message.trim().length > 0) return error.message;
32387
+ if (typeof error === "string" && error.trim().length > 0) return error;
32388
+ return "Translation failed.";
32389
+ }
32390
+ function readAbortMessage(reason) {
32391
+ if (reason instanceof Error && reason.message.trim().length > 0) return reason.message;
32392
+ if (typeof reason === "string" && reason.trim().length > 0) return reason;
32393
+ }
32394
+ function isTimeoutMessage(message) {
32395
+ return /\btime(?:d)?\s*out\b/i.test(message);
32396
+ }
32397
+ function isMemoryLimitMessage(message) {
32398
+ return /out of memory/i.test(message) || /memory limit/i.test(message) || /insufficient memory/i.test(message) || /ERR_WORKER_OUT_OF_MEMORY/i.test(message);
32399
+ }
32400
+ function isBatchTranslationError(value) {
32401
+ if (typeof value !== "object" || value === null) return false;
32402
+ const kind = Reflect.get(value, "kind");
32403
+ const message = Reflect.get(value, "message");
32404
+ return typeof kind === "string" && BATCH_TRANSLATION_ERROR_KINDS.includes(kind) && typeof message === "string" && message.length > 0;
32405
+ }
32295
32406
  var TranslationEngineIdSchema = enumType([
32296
32407
  "browser",
32297
32408
  "local",
@@ -32312,6 +32423,7 @@ enumType([
32312
32423
  "local-llama",
32313
32424
  "openai"
32314
32425
  ]);
32426
+ var DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS = 15e3;
32315
32427
  var LocalModelDownloadStatusSchema = enumType([
32316
32428
  "not-downloaded",
32317
32429
  "queued",
@@ -32556,19 +32668,23 @@ var TranslationOpenAISettingsSchema = objectType({
32556
32668
  var TranslationLocalSettingsSchema = objectType({
32557
32669
  model: stringType().default("Xenova/nllb-200-distilled-600M"),
32558
32670
  selectedGroupId: stringType().optional(),
32559
- hfEndpoint: stringType().default("")
32671
+ hfEndpoint: stringType().default(""),
32672
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32560
32673
  });
32561
32674
  var TranslationLocalCt2SettingsSchema = objectType({
32562
32675
  model: stringType().default("ooeoeo/opus-mt-en-zh-ct2-float16"),
32563
32676
  selectedGroupId: stringType().optional(),
32564
- hfEndpoint: stringType().default("")
32677
+ hfEndpoint: stringType().default(""),
32678
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32565
32679
  });
32566
32680
  var TranslationLocalLlamaSettingsSchema = objectType({
32567
32681
  model: stringType().default("bartowski/Qwen2.5-0.5B-Instruct-GGUF"),
32568
32682
  selectedGroupId: stringType().optional(),
32569
- hfEndpoint: stringType().default("")
32683
+ hfEndpoint: stringType().default(""),
32684
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32570
32685
  });
32571
32686
  objectType({
32687
+ engineId: TranslationEngineIdSchema.default(DEFAULT_TRANSLATION_ENGINE_ID),
32572
32688
  openai: TranslationOpenAISettingsSchema.default(TranslationOpenAISettingsSchema.parse({})),
32573
32689
  local: TranslationLocalSettingsSchema.default(TranslationLocalSettingsSchema.parse({})),
32574
32690
  localCt2: TranslationLocalCt2SettingsSchema.default(TranslationLocalCt2SettingsSchema.parse({})),
@@ -32582,11 +32698,16 @@ objectType({
32582
32698
  selectedGroupId: stringType().min(1).optional(),
32583
32699
  inputs: arrayType(stringType()).min(1),
32584
32700
  instructions: stringType().optional(),
32585
- context: stringType().optional()
32701
+ context: stringType().optional(),
32702
+ timeoutMs: numberType().int().positive().default(DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS)
32586
32703
  });
32587
32704
  objectType({
32588
32705
  index: numberType().int().nonnegative(),
32589
- output: stringType()
32706
+ output: stringType().optional(),
32707
+ error: objectType({
32708
+ kind: enumType(BATCH_TRANSLATION_ERROR_KINDS),
32709
+ message: stringType().min(1)
32710
+ }).optional()
32590
32711
  });
32591
32712
  var DocumentTranslationDisplayModeSchema = enumType(["direct", "bilingual"]);
32592
32713
  var TranslationEngineProjectSettingsSchema = objectType({
@@ -68061,6 +68182,25 @@ var InputPanel = class extends i {
68061
68182
  }));
68062
68183
  this.requestUpdate();
68063
68184
  }
68185
+ _stopToolbarControlPointer(e) {
68186
+ e.stopPropagation();
68187
+ e.preventDefault();
68188
+ }
68189
+ _stopToolbarControlEvent(e) {
68190
+ e.stopPropagation();
68191
+ }
68192
+ _switchTabFromToolbar(e, tab) {
68193
+ this._stopToolbarControlEvent(e);
68194
+ this._switchTab(tab);
68195
+ }
68196
+ _toggleLayoutFromToolbar(e) {
68197
+ this._stopToolbarControlEvent(e);
68198
+ this._toggleLayout();
68199
+ }
68200
+ _closeFromToolbar(e) {
68201
+ this._stopToolbarControlEvent(e);
68202
+ this._close();
68203
+ }
68064
68204
  _toggleLayout() {
68065
68205
  this.layout = this.layout === "fixed" ? "floating" : "fixed";
68066
68206
  if (this.layout === "fixed") this._closeFloatingDialog();
@@ -68297,17 +68437,32 @@ var InputPanel = class extends i {
68297
68437
  class="tab-btn"
68298
68438
  part="tab-btn"
68299
68439
  ?data-active=${this.activeTab === t.id}
68300
- @click=${() => this._switchTab(t.id)}
68440
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68441
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68442
+ @click=${(e) => this._switchTabFromToolbar(e, t.id)}
68301
68443
  >
68302
68444
  ${t.icon} ${this.activeTab === t.id ? t.label : ""}
68303
68445
  </button>
68304
68446
  `)}
68305
68447
  </div>
68306
68448
  <div class="action-group">
68307
- <button class="icon-btn" @click=${this._toggleLayout} title="Toggle layout mode">
68449
+ <button
68450
+ class="icon-btn"
68451
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68452
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68453
+ @click=${(e) => this._toggleLayoutFromToolbar(e)}
68454
+ title="Toggle layout mode"
68455
+ >
68308
68456
  ${this.layout === "fixed" ? iconPin(14) : iconPinOff(14)}
68309
68457
  </button>
68310
- <button class="icon-btn" part="close-btn" @click=${this._close} title="Close panel">
68458
+ <button
68459
+ class="icon-btn"
68460
+ part="close-btn"
68461
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68462
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68463
+ @click=${(e) => this._closeFromToolbar(e)}
68464
+ title="Close panel"
68465
+ >
68311
68466
  ${iconX(14)}
68312
68467
  </button>
68313
68468
  </div>
@@ -90847,30 +91002,12 @@ var ShortcutTab = class extends i {
90847
91002
  composed: true
90848
91003
  }));
90849
91004
  }
90850
- async _handleCommand(command) {
90851
- if (command === "copy") {
90852
- const selection = window.getSelection()?.toString() ?? "";
90853
- if (!selection) return;
90854
- try {
90855
- await navigator.clipboard.writeText(selection);
90856
- } catch {
90857
- document.execCommand("copy");
90858
- }
90859
- return;
90860
- }
90861
- if (command === "paste") {
90862
- try {
90863
- const text = await navigator.clipboard.readText();
90864
- if (text) this._send(text);
90865
- } catch {}
90866
- return;
90867
- }
90868
- const active = document.activeElement;
90869
- if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement) {
90870
- active.select();
90871
- return;
90872
- }
90873
- document.execCommand("selectAll");
91005
+ _handleCommand(command) {
91006
+ this.dispatchEvent(new CustomEvent("input-panel:command", {
91007
+ detail: { command },
91008
+ bubbles: true,
91009
+ composed: true
91010
+ }));
90874
91011
  }
90875
91012
  _dpadData(event, width, height, container) {
90876
91013
  const local = container.toLocal(event.global);
@@ -90880,7 +91017,7 @@ var ShortcutTab = class extends i {
90880
91017
  if (Math.abs(dx) > Math.abs(dy)) return dx < 0 ? "\x1B[D" : "\x1B[C";
90881
91018
  return dy < 0 ? "\x1B[A" : "\x1B[B";
90882
91019
  }
90883
- async _activateShortcut(item, event, width, height, container) {
91020
+ _activateShortcut(item, event, width, height, container) {
90884
91021
  if (item.kind === "dpad") {
90885
91022
  this._send(this._dpadData(event, width, height, container));
90886
91023
  return;
@@ -90895,7 +91032,7 @@ var ShortcutTab = class extends i {
90895
91032
  this._send(action.text);
90896
91033
  return;
90897
91034
  }
90898
- await this._handleCommand(action.command);
91035
+ this._handleCommand(action.command);
90899
91036
  }
90900
91037
  _setActivePage(pageId) {
90901
91038
  if (this.activePageId === pageId) return;
@@ -91444,8 +91581,11 @@ var VirtualKeyboardTab = class extends i {
91444
91581
  this._activeKey = null;
91445
91582
  this._activePointerId = null;
91446
91583
  }
91584
+ _hostPlatform() {
91585
+ return this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform();
91586
+ }
91447
91587
  getRows() {
91448
- return LAYOUTS[this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform()];
91588
+ return LAYOUTS[this._hostPlatform()];
91449
91589
  }
91450
91590
  async _initPixi() {
91451
91591
  const host = this.shadowRoot?.querySelector(".pixi-host");
@@ -91791,14 +91931,44 @@ var VirtualKeyboardTab = class extends i {
91791
91931
  if (this._isShifted(def, forceShift) && def.shift) return def.shift.data;
91792
91932
  return def.data;
91793
91933
  }
91794
- _sendKey(def, forceShift) {
91934
+ _resolveTerminalData(def, forceShift) {
91795
91935
  let data = this._resolveOutputData(def, forceShift);
91796
- if (!data) return;
91936
+ if (!data) return "";
91797
91937
  if (this._modifiers.ctrl && data.length === 1) {
91798
91938
  const code = data.toUpperCase().charCodeAt(0) - 64;
91799
91939
  if (code > 0 && code < 32) data = String.fromCharCode(code);
91800
91940
  }
91801
91941
  if (this._modifiers.alt || this._modifiers.meta) data = `\x1b${data}`;
91942
+ return data;
91943
+ }
91944
+ _resolvePrimaryCommand(def) {
91945
+ if (this._modifiers.alt || this._modifiers.shift) return null;
91946
+ if (!(this._hostPlatform() === "macos" ? this._modifiers.meta && !this._modifiers.ctrl : this._modifiers.ctrl && !this._modifiers.meta)) return null;
91947
+ const key = def.data.toLowerCase();
91948
+ if (key === "c") return "copy";
91949
+ if (key === "v") return "paste";
91950
+ if (key === "a") return "select-all";
91951
+ return null;
91952
+ }
91953
+ _sendCommand(command, fallbackData) {
91954
+ this.dispatchEvent(new CustomEvent("input-panel:command", {
91955
+ detail: {
91956
+ command,
91957
+ fallbackData
91958
+ },
91959
+ bubbles: true,
91960
+ composed: true
91961
+ }));
91962
+ }
91963
+ _sendKey(def, forceShift) {
91964
+ const data = this._resolveTerminalData(def, forceShift);
91965
+ if (!data) return;
91966
+ const command = this._resolvePrimaryCommand(def);
91967
+ if (command) {
91968
+ const fallbackData = this._hostPlatform() === "macos" ? void 0 : data;
91969
+ this._sendCommand(command, fallbackData);
91970
+ return;
91971
+ }
91802
91972
  this.dispatchEvent(new CustomEvent("input-panel:send", {
91803
91973
  detail: { data },
91804
91974
  bubbles: true,
@@ -92326,9 +92496,27 @@ var EDGE_SCROLL_OVERSHOOT = 15;
92326
92496
  function isTouchDevice() {
92327
92497
  return "ontouchstart" in window || navigator.maxTouchPoints > 0;
92328
92498
  }
92499
+ function isTerminalTouchMouseOverlay(element) {
92500
+ return element instanceof HTMLElement && element.classList.contains("terminal-touch-mouse-overlay");
92501
+ }
92502
+ function resolveTerminalPointerTarget(container, clientX, clientY) {
92503
+ let element = document.elementFromPoint(clientX, clientY);
92504
+ if (element && isTerminalTouchMouseOverlay(element)) {
92505
+ const overlayElement = element;
92506
+ const previousPointerEvents = overlayElement.style.pointerEvents;
92507
+ overlayElement.style.pointerEvents = "none";
92508
+ element = document.elementFromPoint(clientX, clientY);
92509
+ overlayElement.style.pointerEvents = previousPointerEvents;
92510
+ }
92511
+ if (element && container.contains(element) && !isTerminalTouchMouseOverlay(element)) return element;
92512
+ return container.querySelector(".xterm-screen") ?? container;
92513
+ }
92329
92514
  function isInputPanelTab(value) {
92330
92515
  return value === "input" || value === "keys" || value === "shortcuts" || value === "trackpad" || value === "settings";
92331
92516
  }
92517
+ function isInputPanelCommand(value) {
92518
+ return value === "copy" || value === "paste" || value === "select-all";
92519
+ }
92332
92520
  function isRecord$1(value) {
92333
92521
  return typeof value === "object" && value !== null;
92334
92522
  }
@@ -92575,6 +92763,7 @@ var InputPanelAddon = class InputPanelAddon {
92575
92763
  _persistentCleanups = [];
92576
92764
  _listenersAttached = false;
92577
92765
  _onInput;
92766
+ _onCommand;
92578
92767
  _onOpenCb;
92579
92768
  _onCloseCb;
92580
92769
  _getHistory;
@@ -92590,6 +92779,7 @@ var InputPanelAddon = class InputPanelAddon {
92590
92779
  _hasOwnPersistedState;
92591
92780
  constructor(opts) {
92592
92781
  this._onInput = opts?.onInput ?? (() => {});
92782
+ this._onCommand = opts?.onCommand ?? null;
92593
92783
  this._onOpenCb = opts?.onOpen ?? null;
92594
92784
  this._onCloseCb = opts?.onClose ?? null;
92595
92785
  this._getHistory = opts?.getHistory ?? null;
@@ -92625,6 +92815,9 @@ var InputPanelAddon = class InputPanelAddon {
92625
92815
  set onInput(fn) {
92626
92816
  this._onInput = fn;
92627
92817
  }
92818
+ set onCommand(fn) {
92819
+ this._onCommand = fn;
92820
+ }
92628
92821
  setPlatform(platform) {
92629
92822
  this._platform = platform;
92630
92823
  this._applyPlatformToPanel();
@@ -92781,6 +92974,18 @@ var InputPanelAddon = class InputPanelAddon {
92781
92974
  }).catch(() => {});
92782
92975
  }
92783
92976
  });
92977
+ this._on(panel, "input-panel:command", (e) => {
92978
+ const detail = e.detail;
92979
+ if (!isRecord$1(detail)) return;
92980
+ const command = detail.command;
92981
+ if (!isInputPanelCommand(command)) return;
92982
+ const fallbackData = typeof detail.fallbackData === "string" ? detail.fallbackData : void 0;
92983
+ Promise.resolve(this._onCommand?.(command, { fallbackData }) ?? false).then((handled) => {
92984
+ if (!handled && fallbackData) this._onInput(fallbackData);
92985
+ }).catch(() => {
92986
+ if (fallbackData) this._onInput(fallbackData);
92987
+ });
92988
+ });
92784
92989
  this._on(inputTab, "input-panel:input-change", (e) => {
92785
92990
  const value = e.detail?.value;
92786
92991
  if (typeof value === "string") {
@@ -92998,9 +93203,7 @@ var InputPanelAddon = class InputPanelAddon {
92998
93203
  _resolveTarget(clientX, clientY) {
92999
93204
  const container = this._getTerminalHostElement();
93000
93205
  if (!container) return document.body;
93001
- const el = document.elementFromPoint(clientX, clientY);
93002
- if (el && container.contains(el)) return el;
93003
- return container.querySelector(".xterm-screen") ?? container;
93206
+ return resolveTerminalPointerTarget(container, clientX, clientY);
93004
93207
  }
93005
93208
  _dispatchMouse(type, opts = {}) {
93006
93209
  const coords = this._getClientCoords();
@@ -93155,6 +93358,55 @@ var InputPanelAddon = class InputPanelAddon {
93155
93358
  }
93156
93359
  };
93157
93360
  //#endregion
93361
+ //#region src/lib/google-font-loader.ts
93362
+ function getPreferredGoogleFontsHosts(lang) {
93363
+ const useCnCdn = /^zh\b/i.test(lang);
93364
+ return {
93365
+ apiHost: useCnCdn ? "https://fonts.googleapis.cn" : "https://fonts.googleapis.com",
93366
+ staticHost: useCnCdn ? "https://fonts.gstatic.cn" : "https://fonts.gstatic.com"
93367
+ };
93368
+ }
93369
+ function encodeGoogleFontFamily(family) {
93370
+ return family.trim().replace(/ /g, "+");
93371
+ }
93372
+ function buildGoogleFontsStylesheetHref(options) {
93373
+ const families = options.families.map(encodeGoogleFontFamily).filter(Boolean);
93374
+ const { apiHost } = getPreferredGoogleFontsHosts(options.lang ?? "");
93375
+ const familyParams = families.map((family) => `family=${family}`);
93376
+ const displayParam = `display=${options.display ?? "swap"}`;
93377
+ return `${apiHost}/css2?${[...familyParams, displayParam].join("&")}`;
93378
+ }
93379
+ /**
93380
+ * Legacy Google Fonts loader retained for optional large CJK/JCK font families.
93381
+ * The app shell uses local @fontsource assets by default.
93382
+ */
93383
+ function loadGoogleFontsStylesheet(options) {
93384
+ const documentRef = options.documentRef ?? (typeof document === "undefined" ? null : document);
93385
+ if (!documentRef || options.families.length === 0) return null;
93386
+ const lang = options.lang ?? (typeof navigator === "undefined" ? "" : navigator.languages && navigator.languages[0] || navigator.language || navigator.userLanguage || "");
93387
+ const hosts = getPreferredGoogleFontsHosts(lang);
93388
+ const href = buildGoogleFontsStylesheetHref({
93389
+ ...options,
93390
+ lang
93391
+ });
93392
+ if (documentRef.head.querySelector(`link[href="${href}"]`)) return null;
93393
+ const preconnectApi = documentRef.createElement("link");
93394
+ preconnectApi.rel = "preconnect";
93395
+ preconnectApi.href = hosts.apiHost;
93396
+ preconnectApi.dataset.openspecGoogleFonts = "preconnect-api";
93397
+ const preconnectStatic = documentRef.createElement("link");
93398
+ preconnectStatic.rel = "preconnect";
93399
+ preconnectStatic.href = hosts.staticHost;
93400
+ preconnectStatic.crossOrigin = "anonymous";
93401
+ preconnectStatic.dataset.openspecGoogleFonts = "preconnect-static";
93402
+ const stylesheet = documentRef.createElement("link");
93403
+ stylesheet.rel = "stylesheet";
93404
+ stylesheet.href = href;
93405
+ stylesheet.dataset.openspecGoogleFonts = "stylesheet";
93406
+ documentRef.head.append(preconnectApi, preconnectStatic, stylesheet);
93407
+ return stylesheet;
93408
+ }
93409
+ //#endregion
93158
93410
  //#region src/lib/user-gesture-audio-context.ts
93159
93411
  var audioContext = null;
93160
93412
  var audioContextPromise = null;
@@ -93558,6 +93810,146 @@ var TerminalInputHistoryStore = class {
93558
93810
  }
93559
93811
  };
93560
93812
  //#endregion
93813
+ //#region src/lib/terminal-keybindings.ts
93814
+ function isKeydown(event) {
93815
+ return event.type === "keydown";
93816
+ }
93817
+ function keyEquals(event, key) {
93818
+ return event.key.toLowerCase() === key;
93819
+ }
93820
+ function hasPrimaryModifier(event) {
93821
+ return event.metaKey || event.ctrlKey;
93822
+ }
93823
+ function hasPlainPrimaryModifier(event) {
93824
+ return hasPrimaryModifier(event) && !event.altKey;
93825
+ }
93826
+ function resolveClipboard(context) {
93827
+ if (context.clipboard) return context.clipboard;
93828
+ if (typeof navigator === "undefined") return null;
93829
+ return navigator.clipboard ?? null;
93830
+ }
93831
+ function runAsync(task, context) {
93832
+ task.catch((error) => {
93833
+ context.onAsyncError?.(error);
93834
+ });
93835
+ }
93836
+ async function copySelection(context, selection) {
93837
+ const clipboard = resolveClipboard(context);
93838
+ if (!clipboard?.writeText) return;
93839
+ await clipboard.writeText(selection);
93840
+ }
93841
+ async function pasteClipboard(context) {
93842
+ const clipboard = resolveClipboard(context);
93843
+ if (!clipboard?.readText) return;
93844
+ const text = await clipboard.readText();
93845
+ if (!text) return;
93846
+ try {
93847
+ context.terminal.paste?.(text);
93848
+ if (context.terminal.paste) return;
93849
+ } catch {}
93850
+ context.writeInput(text);
93851
+ }
93852
+ var defaultTerminalKeybindings = [
93853
+ {
93854
+ id: "terminal.copy-selection",
93855
+ handle: (event, context) => {
93856
+ if (!isKeydown(event) || !hasPlainPrimaryModifier(event) || !keyEquals(event, "c")) return null;
93857
+ if (!context.terminal.hasSelection?.()) return "allow";
93858
+ return copySelectionCommand(context);
93859
+ }
93860
+ },
93861
+ {
93862
+ id: "terminal.paste-clipboard",
93863
+ handle: (event, context) => {
93864
+ if (!isKeydown(event) || !hasPlainPrimaryModifier(event) || !keyEquals(event, "v")) return null;
93865
+ return pasteClipboardCommand(context);
93866
+ }
93867
+ },
93868
+ {
93869
+ id: "terminal.select-all",
93870
+ handle: (event, context) => {
93871
+ if (!isKeydown(event) || !event.metaKey || event.ctrlKey || event.altKey) return null;
93872
+ if (!keyEquals(event, "a")) return null;
93873
+ return selectAllCommand(context);
93874
+ }
93875
+ },
93876
+ {
93877
+ id: "terminal.zoom-in",
93878
+ handle: (event, context) => {
93879
+ if (!isKeydown(event) || !hasPrimaryModifier(event)) return null;
93880
+ if (event.key !== "=" && event.key !== "+") return null;
93881
+ context.zoomFont(1);
93882
+ return "block";
93883
+ }
93884
+ },
93885
+ {
93886
+ id: "terminal.zoom-out",
93887
+ handle: (event, context) => {
93888
+ if (!isKeydown(event) || !hasPrimaryModifier(event) || event.key !== "-") return null;
93889
+ context.zoomFont(-1);
93890
+ return "block";
93891
+ }
93892
+ },
93893
+ {
93894
+ id: "terminal.zoom-reset",
93895
+ handle: (event, context) => {
93896
+ if (!isKeydown(event) || !hasPrimaryModifier(event) || event.key !== "0") return null;
93897
+ context.resetFontSize();
93898
+ return "block";
93899
+ }
93900
+ }
93901
+ ];
93902
+ function copySelectionCommand(context) {
93903
+ if (!context.terminal.hasSelection?.()) return "allow";
93904
+ const selection = context.terminal.getSelection?.() ?? "";
93905
+ if (!selection) return "allow";
93906
+ runAsync(copySelection(context, selection), context);
93907
+ return "block";
93908
+ }
93909
+ function pasteClipboardCommand(context) {
93910
+ if (!resolveClipboard(context)?.readText) return "allow";
93911
+ runAsync(pasteClipboard(context), context);
93912
+ return "block";
93913
+ }
93914
+ function selectAllCommand(context) {
93915
+ if (!context.terminal.selectAll) return "allow";
93916
+ context.terminal.selectAll();
93917
+ return "block";
93918
+ }
93919
+ var defaultTerminalCommandBindings = [
93920
+ {
93921
+ id: "terminal.command.copy-selection",
93922
+ command: "copy",
93923
+ run: copySelectionCommand
93924
+ },
93925
+ {
93926
+ id: "terminal.command.paste-clipboard",
93927
+ command: "paste",
93928
+ run: pasteClipboardCommand
93929
+ },
93930
+ {
93931
+ id: "terminal.command.select-all",
93932
+ command: "select-all",
93933
+ run: selectAllCommand
93934
+ }
93935
+ ];
93936
+ var TerminalKeybindingRegistry = class {
93937
+ constructor(bindings = defaultTerminalKeybindings, commandBindings = defaultTerminalCommandBindings) {
93938
+ this.bindings = bindings;
93939
+ this.commandBindings = commandBindings;
93940
+ }
93941
+ handleKeyEvent(event, context) {
93942
+ for (const binding of this.bindings) {
93943
+ const result = binding.handle(event, context);
93944
+ if (result) return result;
93945
+ }
93946
+ return "allow";
93947
+ }
93948
+ runCommand(command, context) {
93949
+ return this.commandBindings.find((item) => item.command === command)?.run(context) ?? "allow";
93950
+ }
93951
+ };
93952
+ //#endregion
93561
93953
  //#region src/lib/terminal-theme.ts
93562
93954
  var TERMINAL_THEME_REGISTRY = {
93563
93955
  "default-light": {
@@ -93747,7 +94139,7 @@ function resolveTerminalTheme(input) {
93747
94139
  }
93748
94140
  //#endregion
93749
94141
  //#region src/lib/terminal-controller.ts
93750
- var DEFAULT_FONT_FAMILY = "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace";
94142
+ var DEFAULT_FONT_FAMILY = "\"JetBrains Mono Variable\", \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace";
93751
94143
  var DEFAULT_TERMINAL_CONFIG = {
93752
94144
  fontSize: 13,
93753
94145
  fontFamily: "",
@@ -93796,6 +94188,10 @@ var GOOGLE_FONT_PRESETS = [
93796
94188
  "Ubuntu Mono",
93797
94189
  "Space Mono"
93798
94190
  ];
94191
+ var LOCAL_FONT_FAMILY_ALIASES = {
94192
+ "JetBrains Mono": "\"JetBrains Mono Variable\"",
94193
+ "Share Tech Mono": "\"Share Tech Mono\""
94194
+ };
93799
94195
  /** Already-injected font sources, used for deduplication */
93800
94196
  var loadedFontSources = /* @__PURE__ */ new Set();
93801
94197
  /** Extract a font name from a URL pathname (strip extension, replace dashes with spaces) */
@@ -93844,13 +94240,8 @@ async function loadFontSource(source) {
93844
94240
  function loadGoogleFont(fontName) {
93845
94241
  const key = `google:${fontName}`;
93846
94242
  if (loadedFontSources.has(key)) return;
93847
- const lang = navigator.language ?? "";
93848
- const apiHost = /^zh\b/i.test(lang) ? "https://fonts.googleapis.cn" : "https://fonts.googleapis.com";
93849
- const link = document.createElement("link");
93850
- link.rel = "stylesheet";
93851
- link.href = `${apiHost}/css2?family=${fontName.replace(/ /g, "+")}&display=swap`;
93852
- link.dataset.fontSrc = key;
93853
- document.head.appendChild(link);
94243
+ const link = loadGoogleFontsStylesheet({ families: [fontName] });
94244
+ if (link) link.dataset.fontSrc = key;
93854
94245
  loadedFontSources.add(key);
93855
94246
  }
93856
94247
  var TerminalController = class {
@@ -93862,6 +94253,7 @@ var TerminalController = class {
93862
94253
  snapshotCache = null;
93863
94254
  inputHistoryStore = new TerminalInputHistoryStore();
93864
94255
  bellSoundEngine = new TerminalBellSoundEngine();
94256
+ keybindings = new TerminalKeybindingRegistry();
93865
94257
  ghosttyModule = null;
93866
94258
  ghosttyInitPromise = null;
93867
94259
  appDarkMode = false;
@@ -94036,6 +94428,7 @@ var TerminalController = class {
94036
94428
  createInputPanelAddon(sessionId, platform) {
94037
94429
  return new InputPanelAddon({
94038
94430
  onInput: (data) => this.writeToSession(sessionId, data),
94431
+ onCommand: (command) => this.runInputPanelCommand(sessionId, command),
94039
94432
  getHistory: async () => this.inputHistoryStore.list(),
94040
94433
  addHistory: async (text) => this.inputHistoryStore.add(text),
94041
94434
  subscribeHistory: (listener) => this.inputHistoryStore.subscribe(listener),
@@ -94048,6 +94441,17 @@ var TerminalController = class {
94048
94441
  }
94049
94442
  });
94050
94443
  }
94444
+ runInputPanelCommand(sessionId, command) {
94445
+ const instance = this.instances.get(sessionId);
94446
+ if (!instance) return false;
94447
+ return this.keybindings.runCommand(command, {
94448
+ terminal: instance.terminal,
94449
+ writeInput: (data) => this.writeToSession(sessionId, data),
94450
+ zoomFont: (delta) => this.zoomFont(delta),
94451
+ resetFontSize: () => this.resetFontSize(),
94452
+ onAsyncError: (error) => console.error("[terminal] input panel command failed:", error)
94453
+ }) === "block";
94454
+ }
94051
94455
  applyGhosttyBackground(instance, container) {
94052
94456
  if (instance.rendererEngine !== "ghostty") return;
94053
94457
  const currentTheme = instance.terminal.options.theme ?? {};
@@ -94092,20 +94496,18 @@ var TerminalController = class {
94092
94496
  }
94093
94497
  const mod = event.metaKey || event.ctrlKey;
94094
94498
  if (event.type !== "keydown" || !mod) return this.getKeyEventResult(engine, "allow");
94095
- let handled = false;
94096
- if ((event.key === "=" || event.key === "+") && mod) {
94097
- this.zoomFont(1);
94098
- handled = true;
94099
- }
94100
- if (event.key === "-" && mod && !handled) {
94101
- this.zoomFont(-1);
94102
- handled = true;
94103
- }
94104
- if (event.key === "0" && mod && !handled) {
94105
- this.resetFontSize();
94106
- handled = true;
94499
+ const keybindingResult = this.keybindings.handleKeyEvent(event, {
94500
+ terminal,
94501
+ writeInput,
94502
+ zoomFont: (delta) => this.zoomFont(delta),
94503
+ resetFontSize: () => this.resetFontSize(),
94504
+ onAsyncError: (error) => console.error("[terminal] keybinding failed:", error)
94505
+ });
94506
+ if (keybindingResult === "block") {
94507
+ event.preventDefault();
94508
+ event.stopPropagation();
94107
94509
  }
94108
- return this.getKeyEventResult(engine, handled ? "block" : "allow");
94510
+ return this.getKeyEventResult(engine, keybindingResult);
94109
94511
  });
94110
94512
  }
94111
94513
  bindTerminalInput(instance) {
@@ -94383,12 +94785,16 @@ var TerminalController = class {
94383
94785
  }
94384
94786
  const entries = raw.split(/[,]+/).map((s) => s.trim()).filter(Boolean);
94385
94787
  const resolved = [];
94386
- for (const entry of entries) if (GOOGLE_FONT_PRESETS.includes(entry)) {
94387
- loadGoogleFont(entry);
94388
- resolved.push(entry);
94389
- } else {
94390
- const name = await loadFontSource(entry);
94391
- if (name) resolved.push(name);
94788
+ for (const entry of entries) {
94789
+ const localFontFamily = LOCAL_FONT_FAMILY_ALIASES[entry];
94790
+ if (localFontFamily) resolved.push(localFontFamily);
94791
+ else if (GOOGLE_FONT_PRESETS.includes(entry)) {
94792
+ loadGoogleFont(entry);
94793
+ resolved.push(entry);
94794
+ } else {
94795
+ const name = await loadFontSource(entry);
94796
+ if (name) resolved.push(name);
94797
+ }
94392
94798
  }
94393
94799
  resolved.push(DEFAULT_FONT_FAMILY);
94394
94800
  this._setFontFamily(resolved.join(", "));
@@ -95376,7 +95782,7 @@ function Dialog({ open, title, headerActions, onClose, onDismissRequest, onClose
95376
95782
  className: `flex h-full w-full items-center justify-center px-4 py-4 ${contentClassName} ${contentShellClassName}`,
95377
95783
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
95378
95784
  ref: panelRef,
95379
- className: `bg-background text-foreground relative flex h-fit w-[calc(100%-0.5rem)] max-w-2xl flex-col overflow-hidden rounded-[var(--openspec-dialog-radius,0.75rem)] border shadow-xl ${borderClass} ${className}`,
95785
+ className: `@container/dialog bg-background text-foreground relative flex h-fit w-[calc(100%-0.5rem)] max-w-2xl flex-col overflow-hidden rounded-[var(--openspec-dialog-radius,0.75rem)] border shadow-xl ${borderClass} ${className}`,
95380
95786
  style: { maxHeight },
95381
95787
  children: [
95382
95788
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -114783,10 +115189,20 @@ var BrowserTranslatorFactory = class {
114783
115189
  }), options.signal);
114784
115190
  return {
114785
115191
  async *batchTranslate(inputs, batchOptions) {
114786
- for (const [index, input] of inputs.entries()) yield {
114787
- index,
114788
- output: await native.translate(input, batchOptions)
114789
- };
115192
+ for (const [index, input] of inputs.entries()) {
115193
+ const controlled = await runControlledTranslationTask((signal) => native.translate(input, { signal }), batchOptions);
115194
+ if (controlled.ok) {
115195
+ yield {
115196
+ index,
115197
+ output: controlled.value
115198
+ };
115199
+ continue;
115200
+ }
115201
+ yield {
115202
+ index,
115203
+ error: controlled.error
115204
+ };
115205
+ }
114790
115206
  },
114791
115207
  destroy() {
114792
115208
  native.destroy?.();
@@ -136453,8 +136869,22 @@ buildSearchIndex(SUPPORTED_TRANSLATION_LANGUAGES.map((language) => ({
136453
136869
  //#endregion
136454
136870
  //#region src/lib/browser-translation.ts
136455
136871
  var browserSupportTableCache = /* @__PURE__ */ new Map();
136872
+ function isFiniteTranslationOffset(value) {
136873
+ return typeof value === "number" && Number.isFinite(value);
136874
+ }
136875
+ function isTranslationSegmentKind(value) {
136876
+ return value === "heading" || value === "listItem" || value === "paragraph" || value === "blockquote" || value === "text";
136877
+ }
136456
136878
  function isRenderableTranslationSegment(segment) {
136457
- return typeof segment === "object" && segment !== null && !Array.isArray(segment);
136879
+ if (typeof segment !== "object" || segment === null || Array.isArray(segment)) return false;
136880
+ const id = Reflect.get(segment, "id");
136881
+ const sourceStartOffset = Reflect.get(segment, "sourceStartOffset");
136882
+ const sourceEndOffset = Reflect.get(segment, "sourceEndOffset");
136883
+ const sourceKind = Reflect.get(segment, "sourceKind");
136884
+ const source = Reflect.get(segment, "source");
136885
+ const translatorInput = Reflect.get(segment, "translatorInput");
136886
+ const kind = Reflect.get(segment, "kind");
136887
+ return typeof id === "string" && isFiniteTranslationOffset(sourceStartOffset) && isFiniteTranslationOffset(sourceEndOffset) && typeof sourceKind === "string" && typeof source === "string" && typeof translatorInput === "string" && isTranslationSegmentKind(kind);
136458
136888
  }
136459
136889
  function createBrowserTranslationExecution() {
136460
136890
  return {
@@ -136782,6 +137212,7 @@ async function translateMarkdownDocumentProgressively(args, onPatch) {
136782
137212
  sourceLanguage,
136783
137213
  targetLanguage: args.targetLanguage,
136784
137214
  signal: args.signal,
137215
+ timeoutMs: args.timeoutMs,
136785
137216
  cache: args.cache,
136786
137217
  jobs,
136787
137218
  translatedSegments,
@@ -136867,9 +137298,25 @@ async function translatePendingJobsBySourceLanguage(input) {
136867
137298
  startWorkersToDesired();
136868
137299
  }
136869
137300
  };
136870
- const applyBatchResult = async (batch, outputs) => {
137301
+ const applyBatchResult = async (batch, result) => {
136871
137302
  for (const [offset, job] of batch.jobs.entries()) {
136872
- const target = outputs[offset] ?? "";
137303
+ const error = result.errors.get(offset);
137304
+ if (error) {
137305
+ const failedSegment = {
137306
+ ...job.segment,
137307
+ sourceLanguage: job.sourceLanguage,
137308
+ targetLanguage: input.targetLanguage,
137309
+ status: "error",
137310
+ error
137311
+ };
137312
+ input.translatedSegments[job.segmentIndex] = failedSegment;
137313
+ input.onPatch({
137314
+ segmentIndex: job.segmentIndex,
137315
+ segment: failedSegment
137316
+ });
137317
+ continue;
137318
+ }
137319
+ const target = result.outputs.get(offset) ?? "";
136873
137320
  const restoredTarget = job.segment.placeholderProtocol ? restoreTranslatedPlaceholderFragment(target, job.segment.placeholderProtocol) : { target: job.protectedInput.restore(target).trim() };
136874
137321
  const translatedSegment = {
136875
137322
  ...job.segment,
@@ -136908,7 +137355,10 @@ async function translatePendingJobsBySourceLanguage(input) {
136908
137355
  const batch = batches[batchIndex];
136909
137356
  const startedAt = getCurrentTimeMs();
136910
137357
  try {
136911
- await applyBatchResult(batch, await collectBatchTranslationOutputs(translator.batchTranslate(batch.jobs.map((job) => job.protectedInput.text), { signal: input.signal }), batch.jobs.length));
137358
+ await applyBatchResult(batch, await collectBatchTranslationOutputs(translator.batchTranslate(batch.jobs.map((job) => job.protectedInput.text), {
137359
+ signal: input.signal,
137360
+ timeoutMs: input.timeoutMs ?? 15e3
137361
+ }), batch.jobs.length));
136912
137362
  completedBatches += 1;
136913
137363
  const elapsedMs = Math.max(1, getCurrentTimeMs() - startedAt);
136914
137364
  appendTranslationAdaptiveConcurrencyLog({
@@ -136948,6 +137398,83 @@ async function translatePendingJobsBySourceLanguage(input) {
136948
137398
  startWorkersToDesired();
136949
137399
  while (workerPromises.size > 0) await Promise.race(workerPromises);
136950
137400
  }
137401
+ async function retryTranslationSegment(input) {
137402
+ const engine = input.engine ?? createBrowserTranslationExecution();
137403
+ const sourceLanguage = input.sourceLanguage ?? input.segment.sourceLanguage ?? DEFAULT_SOURCE_LANGUAGE;
137404
+ if (areEquivalentTranslationLanguages(sourceLanguage, input.targetLanguage)) return {
137405
+ ...input.segment,
137406
+ target: input.segment.source,
137407
+ sourceLanguage,
137408
+ targetLanguage: input.targetLanguage,
137409
+ status: "translated",
137410
+ error: void 0
137411
+ };
137412
+ const unsupportedLanguagePairMessage = getUnsupportedEngineLanguagePairMessage({
137413
+ engine,
137414
+ sourceLanguage,
137415
+ targetLanguage: input.targetLanguage
137416
+ });
137417
+ if (unsupportedLanguagePairMessage) return {
137418
+ ...input.segment,
137419
+ sourceLanguage,
137420
+ targetLanguage: input.targetLanguage,
137421
+ status: "error",
137422
+ error: unsupportedLanguagePairMessage
137423
+ };
137424
+ const cacheKey = createSegmentCacheKey(input.segment, sourceLanguage, input.targetLanguage, engine.cacheIdentity);
137425
+ const cachedSegment = cacheKey ? await readCachedTranslationSegment(input.cache, cacheKey, input.segment, {
137426
+ sourceLanguage,
137427
+ targetLanguage: input.targetLanguage
137428
+ }) : null;
137429
+ if (cachedSegment) return cachedSegment;
137430
+ const protectedInput = input.segment.placeholderProtocol ? {
137431
+ text: input.segment.translatorInput,
137432
+ restore: (output) => output
137433
+ } : protectTranslatorInput(input.segment.translatorInput);
137434
+ let translator = null;
137435
+ try {
137436
+ translator = await engine.factory.create({
137437
+ sourceLanguage,
137438
+ targetLanguage: input.targetLanguage,
137439
+ signal: input.signal
137440
+ });
137441
+ const result = await collectBatchTranslationOutputs(translator.batchTranslate([protectedInput.text], {
137442
+ signal: input.signal,
137443
+ timeoutMs: input.timeoutMs ?? 15e3
137444
+ }), 1);
137445
+ const error = result.errors.get(0);
137446
+ if (error) return {
137447
+ ...input.segment,
137448
+ sourceLanguage,
137449
+ targetLanguage: input.targetLanguage,
137450
+ status: "error",
137451
+ error
137452
+ };
137453
+ const target = result.outputs.get(0) ?? "";
137454
+ const restoredTarget = input.segment.placeholderProtocol ? restoreTranslatedPlaceholderFragment(target, input.segment.placeholderProtocol) : { target: protectedInput.restore(target).trim() };
137455
+ const translatedSegment = {
137456
+ ...input.segment,
137457
+ ...restoredTarget,
137458
+ sourceLanguage,
137459
+ targetLanguage: input.targetLanguage,
137460
+ status: "translated",
137461
+ error: void 0
137462
+ };
137463
+ if (cacheKey) writeCachedTranslationSegment(input.cache, cacheKey, translatedSegment);
137464
+ return translatedSegment;
137465
+ } catch (error) {
137466
+ if (input.signal.aborted) throw error;
137467
+ return {
137468
+ ...input.segment,
137469
+ sourceLanguage,
137470
+ targetLanguage: input.targetLanguage,
137471
+ status: "error",
137472
+ error: getErrorMessage(error)
137473
+ };
137474
+ } finally {
137475
+ translator?.destroy?.();
137476
+ }
137477
+ }
136951
137478
  function getUnsupportedEngineLanguagePairMessage(input) {
136952
137479
  if (!isDirectionalManagedLocalTranslationEngineId(input.engine.cacheIdentity.engineId)) return null;
136953
137480
  const directionCheck = checkLocalDirectionalModelLanguagePair({
@@ -137012,12 +137539,17 @@ function packTranslationJobs(jobs) {
137012
137539
  }
137013
137540
  async function collectBatchTranslationOutputs(stream, expectedCount) {
137014
137541
  const outputs = /* @__PURE__ */ new Map();
137542
+ const errors = /* @__PURE__ */ new Map();
137015
137543
  for await (const item of stream) {
137016
137544
  if (item.index < 0 || item.index >= expectedCount) throw new Error(`Translator yielded output for unexpected index ${item.index}.`);
137017
- if (!outputs.has(item.index)) outputs.set(item.index, item.output);
137545
+ if (item.output !== void 0 && !outputs.has(item.index)) outputs.set(item.index, item.output);
137546
+ if (item.error && !errors.has(item.index)) errors.set(item.index, item.error.message);
137018
137547
  }
137019
- if (outputs.size !== expectedCount) throw new Error(`Translator returned ${outputs.size} outputs for ${expectedCount} inputs.`);
137020
- return Array.from({ length: expectedCount }, (_, index) => outputs.get(index) ?? "");
137548
+ if (outputs.size + errors.size !== expectedCount) throw new Error(`Translator returned ${outputs.size + errors.size} results for ${expectedCount} inputs.`);
137549
+ return {
137550
+ outputs,
137551
+ errors
137552
+ };
137021
137553
  }
137022
137554
  function estimateTranslationTokens(input) {
137023
137555
  const trimmed = input.trim();
@@ -137561,8 +138093,14 @@ function useDocumentTranslationActivation() {
137561
138093
  }
137562
138094
  //#endregion
137563
138095
  //#region src/lib/resolve-document-translation-config.ts
137564
- function resolveDocumentTranslationConfig(translationConfig, globalSettings) {
138096
+ function resolveDocumentTranslationConfig(translationConfig, globalSettings, configPresence) {
137565
138097
  if (!translationConfig) return void 0;
138098
+ const translationPresence = configPresence?.translation;
138099
+ const projectOwnsEnabled = translationPresence?.enabled ?? false;
138100
+ const projectOwnsTargetLanguage = translationPresence?.targetLanguage ?? false;
138101
+ const projectOwnsDisplayMode = translationPresence?.displayMode ?? false;
138102
+ const projectOwnsCacheEnabled = translationPresence?.cacheEnabled ?? false;
138103
+ const projectOwnsEngineId = translationPresence?.engineId ?? false;
137566
138104
  const local = translationConfig.engines?.local ?? {};
137567
138105
  const localCt2 = translationConfig.engines?.localCt2 ?? {};
137568
138106
  const localLlama = translationConfig.engines?.localLlama ?? {};
@@ -137576,6 +138114,11 @@ function resolveDocumentTranslationConfig(translationConfig, globalSettings) {
137576
138114
  const resolvedOpenAIModel = openai.model ?? globalSettings?.translationEngines.openai?.model;
137577
138115
  return {
137578
138116
  ...translationConfig,
138117
+ enabled: projectOwnsEnabled ? translationConfig.enabled : globalSettings?.translation?.enabled ?? translationConfig.enabled,
138118
+ targetLanguage: projectOwnsTargetLanguage ? translationConfig.targetLanguage : globalSettings?.translation?.targetLanguage ?? translationConfig.targetLanguage,
138119
+ displayMode: projectOwnsDisplayMode ? translationConfig.displayMode : globalSettings?.translation?.displayMode ?? translationConfig.displayMode,
138120
+ cacheEnabled: projectOwnsCacheEnabled ? translationConfig.cacheEnabled : globalSettings?.translation?.cacheEnabled ?? translationConfig.cacheEnabled,
138121
+ engineId: projectOwnsEngineId ? translationConfig.engineId : globalSettings?.translationEngines.engineId ?? translationConfig.engineId,
137579
138122
  engines: {
137580
138123
  local: {
137581
138124
  ...local,
@@ -137673,6 +138216,11 @@ function projectTranslateServiceStatus(input) {
137673
138216
  engineId: input.engineId,
137674
138217
  message: getTranslationEngineLifecycleMessage(input.engineLifecycle) ?? "Translation engine runtime is not ready."
137675
138218
  };
138219
+ if (input.engineLifecycle.assets.state === "error") return {
138220
+ state: "unavailable",
138221
+ engineId: input.engineId,
138222
+ message: input.engineLifecycle.assets.message ?? input.engineLifecycle.assets.error ?? "Selected local model is incompatible with the current translation runtime."
138223
+ };
137676
138224
  }
137677
138225
  if (isManagedLocalTranslationEngineId(input.engineId)) {
137678
138226
  if (!input.localModel?.trim()) return {
@@ -137965,7 +138513,8 @@ var TrpcTranslator = class {
137965
138513
  selectedGroupId: this.options.selectedGroupId,
137966
138514
  inputs,
137967
138515
  instructions: options?.instructions,
137968
- context: options?.context
138516
+ context: options?.context,
138517
+ timeoutMs: options?.timeoutMs ?? 15e3
137969
138518
  }, {
137970
138519
  onData(event) {
137971
138520
  queue.push(event);
@@ -138150,6 +138699,7 @@ function useDocumentTranslation(markdown, config) {
138150
138699
  targetLanguage: config.targetLanguage,
138151
138700
  displayMode: config.displayMode,
138152
138701
  signal: controller.signal,
138702
+ timeoutMs: DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS,
138153
138703
  engine: createTranslationEngineExecution(config),
138154
138704
  cache: config.cacheEnabled && !isStaticMode() ? {
138155
138705
  read: (keyHash) => trpcClient.translationCache.read.query({ keyHash }),
@@ -138195,6 +138745,57 @@ function useDocumentTranslation(markdown, config) {
138195
138745
  markdown,
138196
138746
  serviceStatus
138197
138747
  ]);
138748
+ const retrySegment = (0, import_react.useCallback)(async (segmentId) => {
138749
+ if (!config?.enabled || !result) return;
138750
+ const segmentIndex = result.segments.findIndex((segment) => segment.id === segmentId);
138751
+ const segment = result.segments[segmentIndex];
138752
+ if (segmentIndex < 0 || !segment || segment.status !== "error") return;
138753
+ const controller = new AbortController();
138754
+ const retryingSegment = {
138755
+ ...segment,
138756
+ error: void 0,
138757
+ status: "pending"
138758
+ };
138759
+ setResult((current) => current ? {
138760
+ ...current,
138761
+ segments: current.segments.map((entry, index) => index === segmentIndex ? retryingSegment : entry)
138762
+ } : current);
138763
+ try {
138764
+ const nextSegment = await retryTranslationSegment({
138765
+ segment,
138766
+ sourceLanguage: segment.sourceLanguage,
138767
+ targetLanguage: config.targetLanguage,
138768
+ signal: controller.signal,
138769
+ timeoutMs: DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS,
138770
+ engine: createTranslationEngineExecution(config),
138771
+ cache: config.cacheEnabled && !isStaticMode() ? {
138772
+ read: (keyHash) => trpcClient.translationCache.read.query({ keyHash }),
138773
+ write: (input) => trpcClient.translationCache.write.mutate(input)
138774
+ } : void 0
138775
+ });
138776
+ setResult((current) => current ? {
138777
+ ...current,
138778
+ segments: current.segments.map((entry, index) => index === segmentIndex ? nextSegment : entry)
138779
+ } : current);
138780
+ setError((current) => {
138781
+ if (nextSegment.status === "error") return nextSegment.error ?? current;
138782
+ return getDocumentTranslationFailureMessage(buildRetriedDocumentResult(result, segmentIndex, nextSegment));
138783
+ });
138784
+ setStatus((current) => {
138785
+ if (nextSegment.status === "error") return current === "translated" ? "translated" : "error";
138786
+ return getDocumentTranslationFailureMessage(buildRetriedDocumentResult(result, segmentIndex, nextSegment)) ? "error" : "translated";
138787
+ });
138788
+ } catch (retryError) {
138789
+ setResult((current) => current ? {
138790
+ ...current,
138791
+ segments: current.segments.map((entry, index) => index === segmentIndex ? {
138792
+ ...segment,
138793
+ status: "error",
138794
+ error: retryError instanceof Error ? retryError.message : "Translation retry failed."
138795
+ } : entry)
138796
+ } : current);
138797
+ }
138798
+ }, [config, result]);
138198
138799
  (0, import_react.useEffect)(() => {
138199
138800
  latestStartRef.current = start;
138200
138801
  }, [start]);
@@ -138217,12 +138818,13 @@ function useDocumentTranslation(markdown, config) {
138217
138818
  error,
138218
138819
  result,
138219
138820
  start,
138821
+ retrySegment,
138220
138822
  cancel,
138221
138823
  reset
138222
138824
  };
138223
138825
  }
138224
138826
  function isDocumentTranslationSegment(segment) {
138225
- return typeof segment === "object" && segment !== null && !Array.isArray(segment);
138827
+ return isRenderableTranslationSegment(segment);
138226
138828
  }
138227
138829
  function getDocumentTranslationFailureMessage(result) {
138228
138830
  const segments = (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment);
@@ -138250,6 +138852,12 @@ function normalizeDocumentTranslationResult(result) {
138250
138852
  segments: (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment)
138251
138853
  };
138252
138854
  }
138855
+ function buildRetriedDocumentResult(current, segmentIndex, nextSegment) {
138856
+ return normalizeDocumentTranslationResult({
138857
+ ...current,
138858
+ segments: current.segments.map((entry, index) => index === segmentIndex ? nextSegment : entry)
138859
+ });
138860
+ }
138253
138861
  //#endregion
138254
138862
  //#region ../../node_modules/.pnpm/comma-separated-tokens@2.0.3/node_modules/comma-separated-tokens/index.js
138255
138863
  /**
@@ -148855,8 +149463,9 @@ function transformSafeMarkdownUrl(value) {
148855
149463
  }
148856
149464
  //#endregion
148857
149465
  //#region src/components/document-translation-segment-render.tsx
148858
- function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className }) {
149466
+ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className, onRetry }) {
148859
149467
  const target = segment.target ?? "";
149468
+ const showRetry = segment.status === "error" && typeof onRetry === "function";
148860
149469
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
148861
149470
  className: mergeClassName$1("document-translation-segment", className),
148862
149471
  children: [displayMode === "bilingual" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -148869,7 +149478,19 @@ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode
148869
149478
  title: displayMode === "direct" ? segment.source : void 0,
148870
149479
  lang: segment.targetLanguage,
148871
149480
  "data-translation-target": "",
148872
- children: targetChildren ?? (segment.targetNodes ? renderTranslatedHastNodes(segment.targetNodes) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownInlineContent, { markdown: target }))
149481
+ children: showRetry ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
149482
+ className: "inline-flex items-center gap-2",
149483
+ children: [displayMode === "direct" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
149484
+ className: "text-muted-foreground",
149485
+ children: segment.source
149486
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
149487
+ size: "sm",
149488
+ variant: "secondary",
149489
+ "aria-label": "Retry translation",
149490
+ onClick: () => onRetry(segment.id),
149491
+ children: "Retry"
149492
+ })]
149493
+ }) : targetChildren ?? (segment.targetNodes ? renderTranslatedHastNodes(segment.targetNodes) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownInlineContent, { markdown: target }))
148873
149494
  })]
148874
149495
  });
148875
149496
  }
@@ -148894,13 +149515,13 @@ var OPENSPEC_SECTION_TARGET_LABELS = {
148894
149515
  overview: "目的",
148895
149516
  requirements: "需求"
148896
149517
  };
148897
- function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode) {
149518
+ function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetry) {
148898
149519
  const kind = getCurrentDataAttribute(input, "data-openspec-kind");
148899
- if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode);
149520
+ if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry);
148900
149521
  if (kind !== "requirement" && kind !== "scenario") return void 0;
148901
- return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind);
149522
+ return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry);
148902
149523
  }
148903
- function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode) {
149524
+ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry) {
148904
149525
  const sectionKind = getCurrentDataAttribute(input, "data-openspec-section-kind");
148905
149526
  if (!sectionKind) return void 0;
148906
149527
  const targetTitle = OPENSPEC_SECTION_TARGET_LABELS[sectionKind];
@@ -148918,11 +149539,12 @@ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displa
148918
149539
  },
148919
149540
  displayMode,
148920
149541
  targetChildren: targetTitle,
148921
- className: "document-translation-heading-segment"
149542
+ className: "document-translation-heading-segment",
149543
+ onRetry
148922
149544
  })
148923
149545
  };
148924
149546
  }
148925
- function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind) {
149547
+ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry) {
148926
149548
  const sourceParts = splitOpenSpecHeadingText(segment.source, kind);
148927
149549
  const targetParts = splitOpenSpecHeadingText(segment.target ?? "", kind);
148928
149550
  const visualLabel = getCurrentDataAttribute(input, "data-openspec-visual-label") ?? getCurrentDataAttribute(input, "data-openspec-label");
@@ -148967,7 +149589,8 @@ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, disp
148967
149589
  segment: titleSegment,
148968
149590
  displayMode,
148969
149591
  targetChildren: targetNodes ? renderTranslatedHastNodes(targetNodes) : void 0,
148970
- className: "document-translation-heading-segment"
149592
+ className: "document-translation-heading-segment",
149593
+ onRetry
148971
149594
  })
148972
149595
  })] }),
148973
149596
  target: targetTitle
@@ -149028,7 +149651,7 @@ function useDocumentTranslationRenderPlugin({ markdown, translationConfig }) {
149028
149651
  const resolvedTranslationConfig = (0, import_react.useMemo)(() => translationConfig === void 0 ? void 0 : DocumentTranslationConfigSchema.parse(resolveDocumentTranslationConfig(translationConfig, globalSettings)), [globalSettings, translationConfig]);
149029
149652
  const session = useDocumentTranslation(markdown ?? "", resolvedTranslationConfig);
149030
149653
  const canTranslate = resolvedTranslationConfig !== void 0 && typeof markdown === "string" && markdown.length > 0;
149031
- const translationProjection = (0, import_react.useMemo)(() => createTranslationProjection(session.result), [session.result]);
149654
+ const translationProjection = (0, import_react.useMemo)(() => createTranslationProjection(session.result, session.retrySegment), [session.result, session.retrySegment]);
149032
149655
  const translationAction = (0, import_react.useMemo)(() => canTranslate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DocumentTranslationAction, {
149033
149656
  enabled: resolvedTranslationConfig?.enabled ?? false,
149034
149657
  session
@@ -149099,7 +149722,7 @@ function hashString(value) {
149099
149722
  for (let index = 0; index < value.length; index++) hash = hash * 31 + value.charCodeAt(index) | 0;
149100
149723
  return hash.toString(36);
149101
149724
  }
149102
- function createTranslationProjection(result) {
149725
+ function createTranslationProjection(result, onRetrySegment) {
149103
149726
  if (!result) return { blockAnnotations: [] };
149104
149727
  const segments = getRenderableTranslationSegments(result);
149105
149728
  const segmentByOffset = new Map(segments.filter((segment) => segment.target).map((segment) => [segment.sourceStartOffset, segment]));
@@ -149121,7 +149744,7 @@ function createTranslationProjection(result) {
149121
149744
  transformHeading(input) {
149122
149745
  const segment = input.sourceStartOffset === void 0 ? void 0 : segmentByOffset.get(input.sourceStartOffset);
149123
149746
  if (!segment?.target || segment.kind !== "heading") return void 0;
149124
- return createTranslatedHeadingTransform(input, segment, result.displayMode);
149747
+ return createTranslatedHeadingTransform(input, segment, result.displayMode, onRetrySegment);
149125
149748
  }
149126
149749
  },
149127
149750
  blockAnnotations: segments.filter((segment) => segment.target && segment.kind !== "heading").map((segment) => ({
@@ -149140,7 +149763,8 @@ function createTranslationProjection(result) {
149140
149763
  renderChildren: (children) => segment.targetNodes && result.displayMode === "direct" ? children : renderTranslationSegmentChildren({
149141
149764
  sourceChildren: children,
149142
149765
  segment,
149143
- displayMode: result.displayMode
149766
+ displayMode: result.displayMode,
149767
+ onRetry: onRetrySegment
149144
149768
  })
149145
149769
  }))
149146
149770
  };
@@ -149148,9 +149772,9 @@ function createTranslationProjection(result) {
149148
149772
  function getRenderableTranslationSegments(result) {
149149
149773
  return (Array.isArray(result.segments) ? result.segments : []).filter(isRenderableTranslationSegment);
149150
149774
  }
149151
- function createTranslatedHeadingTransform(input, segment, displayMode) {
149775
+ function createTranslatedHeadingTransform(input, segment, displayMode, onRetrySegment) {
149152
149776
  const projectedTarget = segment.target ?? "";
149153
- const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode);
149777
+ const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetrySegment);
149154
149778
  if (openSpecHeading) return {
149155
149779
  tocDataLabel: displayMode === "direct" ? openSpecHeading.tocDataLabel : input.current?.tocLabel ?? input.text,
149156
149780
  children: openSpecHeading.children,
@@ -149165,7 +149789,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149165
149789
  segment,
149166
149790
  displayMode,
149167
149791
  targetChildren,
149168
- className: "document-translation-heading-segment"
149792
+ className: "document-translation-heading-segment",
149793
+ onRetry: onRetrySegment
149169
149794
  }),
149170
149795
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149171
149796
  };
@@ -149176,7 +149801,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149176
149801
  segment,
149177
149802
  displayMode,
149178
149803
  targetChildren,
149179
- className: "document-translation-heading-segment"
149804
+ className: "document-translation-heading-segment",
149805
+ onRetry: onRetrySegment
149180
149806
  }),
149181
149807
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149182
149808
  };