@openspecui/web 3.11.2 → 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-wEqYYKag.js → dist-B85dtRfx.js} +1 -1
  9. package/dist/assets/dist-B8IUilny.js +1 -0
  10. package/dist/assets/{dist-DHhbIMeO.js → dist-BRxF7Jkf.js} +1 -1
  11. package/dist/assets/{dist-Drgp5ero.js → dist-BZECNWGg.js} +1 -1
  12. package/dist/assets/{dist-B9Ltz3Au.js → dist-BfZLQFwN.js} +1 -1
  13. package/dist/assets/{dist-HIi-Q3U-.js → dist-CbJQUXs0.js} +1 -1
  14. package/dist/assets/{dist-B_UFDOda.js → dist-CmKsGuOc.js} +1 -1
  15. package/dist/assets/{dist-j6cthCRX.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-DRc-odji.js → dist-SfwxOhmW.js} +1 -1
  19. package/dist/assets/{dist-BggZ7cyQ.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-D41cyy2z.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-CzScJNYz.js → dist-B6MkJ-H7.js} +1 -1
  47. package/dist-ssg/client/assets/{dist-nDBVtvCj.js → dist-B9IiVt9G.js} +1 -1
  48. package/dist-ssg/client/assets/{dist-BJ0zzLee.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-CrdkjkLH.js → dist-CFjNhfqM.js} +1 -1
  51. package/dist-ssg/client/assets/{dist-lNUMJW4A.js → dist-CIMwXheF.js} +1 -1
  52. package/dist-ssg/client/assets/{dist-CuFabXJ8.js → dist-CNLvbQSx.js} +1 -1
  53. package/dist-ssg/client/assets/dist-CWcubuXa.js +1 -0
  54. package/dist-ssg/client/assets/{dist-Bg-C799P.js → dist-Ch_E0jle.js} +1 -1
  55. package/dist-ssg/client/assets/{dist-BZ35ju6s.js → dist-CjF4SWnb.js} +1 -1
  56. package/dist-ssg/client/assets/dist-D2pHEmiw.js +1 -0
  57. package/dist-ssg/client/assets/{dist-xprslEpB.js → dist-DgYslwM5.js} +1 -1
  58. package/dist-ssg/client/assets/{ghostty-web-D9IXftv-.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-L1BBuH-7.js → index.ssg-BWQbO3uy.js} +167 -152
  61. package/dist-ssg/client/assets/{init-C058TQcq.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 +897 -206
  73. package/package.json +6 -4
  74. package/dist/assets/CanvasRenderer-C0z1_uAq.js +0 -1
  75. package/dist/assets/WebGLRenderer-BgB0b-_m.js +0 -1
  76. package/dist/assets/WebGPURenderer-DUOyTSOY.js +0 -1
  77. package/dist/assets/audio-preview-DF-g2nKV.js +0 -1
  78. package/dist/assets/browserAll-DrkY7ahm.js +0 -1
  79. package/dist/assets/common-DiWYGJu_.js +0 -22
  80. package/dist/assets/dist-BLzDSe4i.js +0 -1
  81. package/dist/assets/dist-C2tzK2BJ.js +0 -1
  82. package/dist/assets/dist-LSzi3-xz.js +0 -1
  83. package/dist/assets/main-DpM-T-Pk.js +0 -1654
  84. package/dist/assets/main-fCQ7khWW.css +0 -1
  85. package/dist/assets/trpc-lJhbxi32.js +0 -1
  86. package/dist/assets/video-preview-CChWJOsP.js +0 -1
  87. package/dist/assets/webworkerAll-DXrA-UNG.js +0 -1
  88. package/dist-ssg/client/assets/CanvasRenderer-pScAgtDS.js +0 -1
  89. package/dist-ssg/client/assets/WebGLRenderer-BAJXWFrw.js +0 -1
  90. package/dist-ssg/client/assets/WebGPURenderer-BeNNGqxY.js +0 -1
  91. package/dist-ssg/client/assets/browserAll-CkiE7Jmg.js +0 -1
  92. package/dist-ssg/client/assets/dist-BJBQISim.js +0 -1
  93. package/dist-ssg/client/assets/dist-BPi1lJ-H.js +0 -1
  94. package/dist-ssg/client/assets/dist-CNPnfIVE.js +0 -1
  95. package/dist-ssg/client/assets/index-BtGAsAtP.css +0 -1
  96. package/dist-ssg/client/assets/trpc-BTuQHFNd.js +0 -1
  97. package/dist-ssg/client/assets/webworkerAll-ByOBLjhR.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,21 +32289,141 @@ 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",
32298
32409
  "local-ct2",
32410
+ "local-llama",
32299
32411
  "openai"
32300
32412
  ]);
32301
32413
  var DEFAULT_TRANSLATION_ENGINE_ID = "browser";
32302
32414
  function isManagedLocalTranslationEngineId(engineId) {
32415
+ return engineId === "local" || engineId === "local-ct2" || engineId === "local-llama";
32416
+ }
32417
+ function isDirectionalManagedLocalTranslationEngineId(engineId) {
32303
32418
  return engineId === "local" || engineId === "local-ct2";
32304
32419
  }
32305
32420
  enumType([
32306
32421
  "local",
32307
32422
  "local-ct2",
32423
+ "local-llama",
32308
32424
  "openai"
32309
32425
  ]);
32426
+ var DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS = 15e3;
32310
32427
  var LocalModelDownloadStatusSchema = enumType([
32311
32428
  "not-downloaded",
32312
32429
  "queued",
@@ -32419,7 +32536,11 @@ var LocalModelProfileLoadStateSchema = objectType({
32419
32536
  updatedAt: numberType().int().nonnegative().optional()
32420
32537
  });
32421
32538
  objectType({
32422
- engineId: enumType(["local", "local-ct2"]),
32539
+ engineId: enumType([
32540
+ "local",
32541
+ "local-ct2",
32542
+ "local-llama"
32543
+ ]),
32423
32544
  modelId: stringType().min(1),
32424
32545
  selectedGroupId: stringType().min(1).optional(),
32425
32546
  groupId: stringType().min(1).optional(),
@@ -32468,6 +32589,11 @@ objectType({
32468
32589
  downloadedBytes: numberType().int().nonnegative().optional()
32469
32590
  })).default([])
32470
32591
  });
32592
+ enumType([
32593
+ "local",
32594
+ "network",
32595
+ "recommended"
32596
+ ]);
32471
32597
  var TranslationEngineDependencyStateSchema = enumType([
32472
32598
  "installed",
32473
32599
  "installing",
@@ -32542,17 +32668,27 @@ var TranslationOpenAISettingsSchema = objectType({
32542
32668
  var TranslationLocalSettingsSchema = objectType({
32543
32669
  model: stringType().default("Xenova/nllb-200-distilled-600M"),
32544
32670
  selectedGroupId: stringType().optional(),
32545
- hfEndpoint: stringType().default("")
32671
+ hfEndpoint: stringType().default(""),
32672
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32546
32673
  });
32547
32674
  var TranslationLocalCt2SettingsSchema = objectType({
32548
32675
  model: stringType().default("ooeoeo/opus-mt-en-zh-ct2-float16"),
32549
32676
  selectedGroupId: stringType().optional(),
32550
- hfEndpoint: stringType().default("")
32677
+ hfEndpoint: stringType().default(""),
32678
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32679
+ });
32680
+ var TranslationLocalLlamaSettingsSchema = objectType({
32681
+ model: stringType().default("bartowski/Qwen2.5-0.5B-Instruct-GGUF"),
32682
+ selectedGroupId: stringType().optional(),
32683
+ hfEndpoint: stringType().default(""),
32684
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32551
32685
  });
32552
32686
  objectType({
32687
+ engineId: TranslationEngineIdSchema.default(DEFAULT_TRANSLATION_ENGINE_ID),
32553
32688
  openai: TranslationOpenAISettingsSchema.default(TranslationOpenAISettingsSchema.parse({})),
32554
32689
  local: TranslationLocalSettingsSchema.default(TranslationLocalSettingsSchema.parse({})),
32555
- localCt2: TranslationLocalCt2SettingsSchema.default(TranslationLocalCt2SettingsSchema.parse({}))
32690
+ localCt2: TranslationLocalCt2SettingsSchema.default(TranslationLocalCt2SettingsSchema.parse({})),
32691
+ localLlama: TranslationLocalLlamaSettingsSchema.default(TranslationLocalLlamaSettingsSchema.parse({}))
32556
32692
  });
32557
32693
  objectType({
32558
32694
  engineId: TranslationEngineIdSchema,
@@ -32562,11 +32698,16 @@ objectType({
32562
32698
  selectedGroupId: stringType().min(1).optional(),
32563
32699
  inputs: arrayType(stringType()).min(1),
32564
32700
  instructions: stringType().optional(),
32565
- context: stringType().optional()
32701
+ context: stringType().optional(),
32702
+ timeoutMs: numberType().int().positive().default(DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS)
32566
32703
  });
32567
32704
  objectType({
32568
32705
  index: numberType().int().nonnegative(),
32569
- output: stringType()
32706
+ output: stringType().optional(),
32707
+ error: objectType({
32708
+ kind: enumType(BATCH_TRANSLATION_ERROR_KINDS),
32709
+ message: stringType().min(1)
32710
+ }).optional()
32570
32711
  });
32571
32712
  var DocumentTranslationDisplayModeSchema = enumType(["direct", "bilingual"]);
32572
32713
  var TranslationEngineProjectSettingsSchema = objectType({
@@ -32578,6 +32719,10 @@ var TranslationEngineProjectSettingsSchema = objectType({
32578
32719
  model: stringType().min(1).optional(),
32579
32720
  selectedGroupId: stringType().min(1).optional()
32580
32721
  }).default({}),
32722
+ localLlama: objectType({
32723
+ model: stringType().min(1).optional(),
32724
+ selectedGroupId: stringType().min(1).optional()
32725
+ }).default({}),
32581
32726
  openai: objectType({ model: stringType().min(1).optional() }).default({})
32582
32727
  }).default({});
32583
32728
  var DocumentTranslationConfigSchema = objectType({
@@ -68037,6 +68182,25 @@ var InputPanel = class extends i {
68037
68182
  }));
68038
68183
  this.requestUpdate();
68039
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
+ }
68040
68204
  _toggleLayout() {
68041
68205
  this.layout = this.layout === "fixed" ? "floating" : "fixed";
68042
68206
  if (this.layout === "fixed") this._closeFloatingDialog();
@@ -68273,17 +68437,32 @@ var InputPanel = class extends i {
68273
68437
  class="tab-btn"
68274
68438
  part="tab-btn"
68275
68439
  ?data-active=${this.activeTab === t.id}
68276
- @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)}
68277
68443
  >
68278
68444
  ${t.icon} ${this.activeTab === t.id ? t.label : ""}
68279
68445
  </button>
68280
68446
  `)}
68281
68447
  </div>
68282
68448
  <div class="action-group">
68283
- <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
+ >
68284
68456
  ${this.layout === "fixed" ? iconPin(14) : iconPinOff(14)}
68285
68457
  </button>
68286
- <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
+ >
68287
68466
  ${iconX(14)}
68288
68467
  </button>
68289
68468
  </div>
@@ -90823,30 +91002,12 @@ var ShortcutTab = class extends i {
90823
91002
  composed: true
90824
91003
  }));
90825
91004
  }
90826
- async _handleCommand(command) {
90827
- if (command === "copy") {
90828
- const selection = window.getSelection()?.toString() ?? "";
90829
- if (!selection) return;
90830
- try {
90831
- await navigator.clipboard.writeText(selection);
90832
- } catch {
90833
- document.execCommand("copy");
90834
- }
90835
- return;
90836
- }
90837
- if (command === "paste") {
90838
- try {
90839
- const text = await navigator.clipboard.readText();
90840
- if (text) this._send(text);
90841
- } catch {}
90842
- return;
90843
- }
90844
- const active = document.activeElement;
90845
- if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement) {
90846
- active.select();
90847
- return;
90848
- }
90849
- document.execCommand("selectAll");
91005
+ _handleCommand(command) {
91006
+ this.dispatchEvent(new CustomEvent("input-panel:command", {
91007
+ detail: { command },
91008
+ bubbles: true,
91009
+ composed: true
91010
+ }));
90850
91011
  }
90851
91012
  _dpadData(event, width, height, container) {
90852
91013
  const local = container.toLocal(event.global);
@@ -90856,7 +91017,7 @@ var ShortcutTab = class extends i {
90856
91017
  if (Math.abs(dx) > Math.abs(dy)) return dx < 0 ? "\x1B[D" : "\x1B[C";
90857
91018
  return dy < 0 ? "\x1B[A" : "\x1B[B";
90858
91019
  }
90859
- async _activateShortcut(item, event, width, height, container) {
91020
+ _activateShortcut(item, event, width, height, container) {
90860
91021
  if (item.kind === "dpad") {
90861
91022
  this._send(this._dpadData(event, width, height, container));
90862
91023
  return;
@@ -90871,7 +91032,7 @@ var ShortcutTab = class extends i {
90871
91032
  this._send(action.text);
90872
91033
  return;
90873
91034
  }
90874
- await this._handleCommand(action.command);
91035
+ this._handleCommand(action.command);
90875
91036
  }
90876
91037
  _setActivePage(pageId) {
90877
91038
  if (this.activePageId === pageId) return;
@@ -91420,8 +91581,11 @@ var VirtualKeyboardTab = class extends i {
91420
91581
  this._activeKey = null;
91421
91582
  this._activePointerId = null;
91422
91583
  }
91584
+ _hostPlatform() {
91585
+ return this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform();
91586
+ }
91423
91587
  getRows() {
91424
- return LAYOUTS[this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform()];
91588
+ return LAYOUTS[this._hostPlatform()];
91425
91589
  }
91426
91590
  async _initPixi() {
91427
91591
  const host = this.shadowRoot?.querySelector(".pixi-host");
@@ -91767,14 +91931,44 @@ var VirtualKeyboardTab = class extends i {
91767
91931
  if (this._isShifted(def, forceShift) && def.shift) return def.shift.data;
91768
91932
  return def.data;
91769
91933
  }
91770
- _sendKey(def, forceShift) {
91934
+ _resolveTerminalData(def, forceShift) {
91771
91935
  let data = this._resolveOutputData(def, forceShift);
91772
- if (!data) return;
91936
+ if (!data) return "";
91773
91937
  if (this._modifiers.ctrl && data.length === 1) {
91774
91938
  const code = data.toUpperCase().charCodeAt(0) - 64;
91775
91939
  if (code > 0 && code < 32) data = String.fromCharCode(code);
91776
91940
  }
91777
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
+ }
91778
91972
  this.dispatchEvent(new CustomEvent("input-panel:send", {
91779
91973
  detail: { data },
91780
91974
  bubbles: true,
@@ -92302,9 +92496,27 @@ var EDGE_SCROLL_OVERSHOOT = 15;
92302
92496
  function isTouchDevice() {
92303
92497
  return "ontouchstart" in window || navigator.maxTouchPoints > 0;
92304
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
+ }
92305
92514
  function isInputPanelTab(value) {
92306
92515
  return value === "input" || value === "keys" || value === "shortcuts" || value === "trackpad" || value === "settings";
92307
92516
  }
92517
+ function isInputPanelCommand(value) {
92518
+ return value === "copy" || value === "paste" || value === "select-all";
92519
+ }
92308
92520
  function isRecord$1(value) {
92309
92521
  return typeof value === "object" && value !== null;
92310
92522
  }
@@ -92551,6 +92763,7 @@ var InputPanelAddon = class InputPanelAddon {
92551
92763
  _persistentCleanups = [];
92552
92764
  _listenersAttached = false;
92553
92765
  _onInput;
92766
+ _onCommand;
92554
92767
  _onOpenCb;
92555
92768
  _onCloseCb;
92556
92769
  _getHistory;
@@ -92566,6 +92779,7 @@ var InputPanelAddon = class InputPanelAddon {
92566
92779
  _hasOwnPersistedState;
92567
92780
  constructor(opts) {
92568
92781
  this._onInput = opts?.onInput ?? (() => {});
92782
+ this._onCommand = opts?.onCommand ?? null;
92569
92783
  this._onOpenCb = opts?.onOpen ?? null;
92570
92784
  this._onCloseCb = opts?.onClose ?? null;
92571
92785
  this._getHistory = opts?.getHistory ?? null;
@@ -92601,6 +92815,9 @@ var InputPanelAddon = class InputPanelAddon {
92601
92815
  set onInput(fn) {
92602
92816
  this._onInput = fn;
92603
92817
  }
92818
+ set onCommand(fn) {
92819
+ this._onCommand = fn;
92820
+ }
92604
92821
  setPlatform(platform) {
92605
92822
  this._platform = platform;
92606
92823
  this._applyPlatformToPanel();
@@ -92757,6 +92974,18 @@ var InputPanelAddon = class InputPanelAddon {
92757
92974
  }).catch(() => {});
92758
92975
  }
92759
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
+ });
92760
92989
  this._on(inputTab, "input-panel:input-change", (e) => {
92761
92990
  const value = e.detail?.value;
92762
92991
  if (typeof value === "string") {
@@ -92974,9 +93203,7 @@ var InputPanelAddon = class InputPanelAddon {
92974
93203
  _resolveTarget(clientX, clientY) {
92975
93204
  const container = this._getTerminalHostElement();
92976
93205
  if (!container) return document.body;
92977
- const el = document.elementFromPoint(clientX, clientY);
92978
- if (el && container.contains(el)) return el;
92979
- return container.querySelector(".xterm-screen") ?? container;
93206
+ return resolveTerminalPointerTarget(container, clientX, clientY);
92980
93207
  }
92981
93208
  _dispatchMouse(type, opts = {}) {
92982
93209
  const coords = this._getClientCoords();
@@ -93131,6 +93358,55 @@ var InputPanelAddon = class InputPanelAddon {
93131
93358
  }
93132
93359
  };
93133
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
93134
93410
  //#region src/lib/user-gesture-audio-context.ts
93135
93411
  var audioContext = null;
93136
93412
  var audioContextPromise = null;
@@ -93534,6 +93810,146 @@ var TerminalInputHistoryStore = class {
93534
93810
  }
93535
93811
  };
93536
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
93537
93953
  //#region src/lib/terminal-theme.ts
93538
93954
  var TERMINAL_THEME_REGISTRY = {
93539
93955
  "default-light": {
@@ -93723,7 +94139,7 @@ function resolveTerminalTheme(input) {
93723
94139
  }
93724
94140
  //#endregion
93725
94141
  //#region src/lib/terminal-controller.ts
93726
- 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";
93727
94143
  var DEFAULT_TERMINAL_CONFIG = {
93728
94144
  fontSize: 13,
93729
94145
  fontFamily: "",
@@ -93772,6 +94188,10 @@ var GOOGLE_FONT_PRESETS = [
93772
94188
  "Ubuntu Mono",
93773
94189
  "Space Mono"
93774
94190
  ];
94191
+ var LOCAL_FONT_FAMILY_ALIASES = {
94192
+ "JetBrains Mono": "\"JetBrains Mono Variable\"",
94193
+ "Share Tech Mono": "\"Share Tech Mono\""
94194
+ };
93775
94195
  /** Already-injected font sources, used for deduplication */
93776
94196
  var loadedFontSources = /* @__PURE__ */ new Set();
93777
94197
  /** Extract a font name from a URL pathname (strip extension, replace dashes with spaces) */
@@ -93820,13 +94240,8 @@ async function loadFontSource(source) {
93820
94240
  function loadGoogleFont(fontName) {
93821
94241
  const key = `google:${fontName}`;
93822
94242
  if (loadedFontSources.has(key)) return;
93823
- const lang = navigator.language ?? "";
93824
- const apiHost = /^zh\b/i.test(lang) ? "https://fonts.googleapis.cn" : "https://fonts.googleapis.com";
93825
- const link = document.createElement("link");
93826
- link.rel = "stylesheet";
93827
- link.href = `${apiHost}/css2?family=${fontName.replace(/ /g, "+")}&display=swap`;
93828
- link.dataset.fontSrc = key;
93829
- document.head.appendChild(link);
94243
+ const link = loadGoogleFontsStylesheet({ families: [fontName] });
94244
+ if (link) link.dataset.fontSrc = key;
93830
94245
  loadedFontSources.add(key);
93831
94246
  }
93832
94247
  var TerminalController = class {
@@ -93838,6 +94253,7 @@ var TerminalController = class {
93838
94253
  snapshotCache = null;
93839
94254
  inputHistoryStore = new TerminalInputHistoryStore();
93840
94255
  bellSoundEngine = new TerminalBellSoundEngine();
94256
+ keybindings = new TerminalKeybindingRegistry();
93841
94257
  ghosttyModule = null;
93842
94258
  ghosttyInitPromise = null;
93843
94259
  appDarkMode = false;
@@ -94012,6 +94428,7 @@ var TerminalController = class {
94012
94428
  createInputPanelAddon(sessionId, platform) {
94013
94429
  return new InputPanelAddon({
94014
94430
  onInput: (data) => this.writeToSession(sessionId, data),
94431
+ onCommand: (command) => this.runInputPanelCommand(sessionId, command),
94015
94432
  getHistory: async () => this.inputHistoryStore.list(),
94016
94433
  addHistory: async (text) => this.inputHistoryStore.add(text),
94017
94434
  subscribeHistory: (listener) => this.inputHistoryStore.subscribe(listener),
@@ -94024,6 +94441,17 @@ var TerminalController = class {
94024
94441
  }
94025
94442
  });
94026
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
+ }
94027
94455
  applyGhosttyBackground(instance, container) {
94028
94456
  if (instance.rendererEngine !== "ghostty") return;
94029
94457
  const currentTheme = instance.terminal.options.theme ?? {};
@@ -94068,20 +94496,18 @@ var TerminalController = class {
94068
94496
  }
94069
94497
  const mod = event.metaKey || event.ctrlKey;
94070
94498
  if (event.type !== "keydown" || !mod) return this.getKeyEventResult(engine, "allow");
94071
- let handled = false;
94072
- if ((event.key === "=" || event.key === "+") && mod) {
94073
- this.zoomFont(1);
94074
- handled = true;
94075
- }
94076
- if (event.key === "-" && mod && !handled) {
94077
- this.zoomFont(-1);
94078
- handled = true;
94079
- }
94080
- if (event.key === "0" && mod && !handled) {
94081
- this.resetFontSize();
94082
- 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();
94083
94509
  }
94084
- return this.getKeyEventResult(engine, handled ? "block" : "allow");
94510
+ return this.getKeyEventResult(engine, keybindingResult);
94085
94511
  });
94086
94512
  }
94087
94513
  bindTerminalInput(instance) {
@@ -94359,12 +94785,16 @@ var TerminalController = class {
94359
94785
  }
94360
94786
  const entries = raw.split(/[,]+/).map((s) => s.trim()).filter(Boolean);
94361
94787
  const resolved = [];
94362
- for (const entry of entries) if (GOOGLE_FONT_PRESETS.includes(entry)) {
94363
- loadGoogleFont(entry);
94364
- resolved.push(entry);
94365
- } else {
94366
- const name = await loadFontSource(entry);
94367
- 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
+ }
94368
94798
  }
94369
94799
  resolved.push(DEFAULT_FONT_FAMILY);
94370
94800
  this._setFontFamily(resolved.join(", "));
@@ -95352,7 +95782,7 @@ function Dialog({ open, title, headerActions, onClose, onDismissRequest, onClose
95352
95782
  className: `flex h-full w-full items-center justify-center px-4 py-4 ${contentClassName} ${contentShellClassName}`,
95353
95783
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
95354
95784
  ref: panelRef,
95355
- 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}`,
95356
95786
  style: { maxHeight },
95357
95787
  children: [
95358
95788
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -114686,85 +115116,6 @@ function navigateHashAnchor(anchorElement, hash) {
114686
115116
  return true;
114687
115117
  }
114688
115118
  //#endregion
114689
- //#region src/lib/document-translation-session-state.ts
114690
- var DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY = "openspecui:document-translation:mode";
114691
- var DEFAULT_ACTIVATION = "source";
114692
- var ACTIVATION_STORAGE_EVENT = "openspecui:document-translation-session-change";
114693
- function isDocumentTranslationActivation(value) {
114694
- return value === "source" || value === "translated";
114695
- }
114696
- function readDocumentTranslationActivation() {
114697
- if (typeof window === "undefined") return DEFAULT_ACTIVATION;
114698
- try {
114699
- const value = window.sessionStorage.getItem(DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY);
114700
- return isDocumentTranslationActivation(value) ? value : DEFAULT_ACTIVATION;
114701
- } catch {
114702
- return DEFAULT_ACTIVATION;
114703
- }
114704
- }
114705
- function writeDocumentTranslationActivation(value) {
114706
- if (typeof window === "undefined") return;
114707
- try {
114708
- window.sessionStorage.setItem(DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY, value);
114709
- } catch {
114710
- return;
114711
- }
114712
- window.dispatchEvent(new CustomEvent(ACTIVATION_STORAGE_EVENT, { detail: value }));
114713
- }
114714
- function useDocumentTranslationActivation() {
114715
- const [activation, setActivationState] = (0, import_react.useState)(readDocumentTranslationActivation);
114716
- (0, import_react.useEffect)(() => {
114717
- const handleActivationChange = () => {
114718
- setActivationState(readDocumentTranslationActivation());
114719
- };
114720
- window.addEventListener(ACTIVATION_STORAGE_EVENT, handleActivationChange);
114721
- window.addEventListener("storage", handleActivationChange);
114722
- return () => {
114723
- window.removeEventListener(ACTIVATION_STORAGE_EVENT, handleActivationChange);
114724
- window.removeEventListener("storage", handleActivationChange);
114725
- };
114726
- }, []);
114727
- return {
114728
- activation,
114729
- setActivation: (0, import_react.useCallback)((value) => {
114730
- writeDocumentTranslationActivation(value);
114731
- setActivationState(value);
114732
- }, [])
114733
- };
114734
- }
114735
- //#endregion
114736
- //#region src/lib/resolve-document-translation-config.ts
114737
- function resolveDocumentTranslationConfig(translationConfig, globalSettings) {
114738
- if (!translationConfig) return void 0;
114739
- const local = translationConfig.engines?.local ?? {};
114740
- const localCt2 = translationConfig.engines?.localCt2 ?? {};
114741
- const openai = translationConfig.engines?.openai ?? {};
114742
- const resolvedLocalModel = local.model ?? globalSettings?.translationEngines.local?.model;
114743
- const resolvedLocalSelectedGroupId = local.selectedGroupId ?? globalSettings?.translationEngines.local?.selectedGroupId;
114744
- const resolvedLocalCt2Model = localCt2.model ?? globalSettings?.translationEngines.localCt2?.model;
114745
- const resolvedLocalCt2SelectedGroupId = localCt2.selectedGroupId ?? globalSettings?.translationEngines.localCt2?.selectedGroupId;
114746
- const resolvedOpenAIModel = openai.model ?? globalSettings?.translationEngines.openai?.model;
114747
- return {
114748
- ...translationConfig,
114749
- engines: {
114750
- local: {
114751
- ...local,
114752
- ...resolvedLocalModel ? { model: resolvedLocalModel } : {},
114753
- ...resolvedLocalSelectedGroupId ? { selectedGroupId: resolvedLocalSelectedGroupId } : {}
114754
- },
114755
- localCt2: {
114756
- ...localCt2,
114757
- ...resolvedLocalCt2Model ? { model: resolvedLocalCt2Model } : {},
114758
- ...resolvedLocalCt2SelectedGroupId ? { selectedGroupId: resolvedLocalCt2SelectedGroupId } : {}
114759
- },
114760
- openai: {
114761
- ...openai,
114762
- ...resolvedOpenAIModel ? { model: resolvedOpenAIModel } : {}
114763
- }
114764
- }
114765
- };
114766
- }
114767
- //#endregion
114768
115119
  //#region ../browser-translator/dist/index.mjs
114769
115120
  async function scanBrowserTranslationSupportTable(options) {
114770
115121
  const translator = (options.win ?? window).Translator;
@@ -114838,10 +115189,20 @@ var BrowserTranslatorFactory = class {
114838
115189
  }), options.signal);
114839
115190
  return {
114840
115191
  async *batchTranslate(inputs, batchOptions) {
114841
- for (const [index, input] of inputs.entries()) yield {
114842
- index,
114843
- output: await native.translate(input, batchOptions)
114844
- };
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
+ }
114845
115206
  },
114846
115207
  destroy() {
114847
115208
  native.destroy?.();
@@ -136508,6 +136869,23 @@ buildSearchIndex(SUPPORTED_TRANSLATION_LANGUAGES.map((language) => ({
136508
136869
  //#endregion
136509
136870
  //#region src/lib/browser-translation.ts
136510
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
+ }
136878
+ function isRenderableTranslationSegment(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);
136888
+ }
136511
136889
  function createBrowserTranslationExecution() {
136512
136890
  return {
136513
136891
  factory: createBrowserTranslatorFactory(),
@@ -136834,13 +137212,14 @@ async function translateMarkdownDocumentProgressively(args, onPatch) {
136834
137212
  sourceLanguage,
136835
137213
  targetLanguage: args.targetLanguage,
136836
137214
  signal: args.signal,
137215
+ timeoutMs: args.timeoutMs,
136837
137216
  cache: args.cache,
136838
137217
  jobs,
136839
137218
  translatedSegments,
136840
137219
  onPatch
136841
137220
  })));
136842
137221
  return {
136843
- segments: translatedSegments,
137222
+ segments: normalizeTranslationSegments(translatedSegments),
136844
137223
  displayMode: args.displayMode,
136845
137224
  sourceLanguage: languageDetection.documentLanguage,
136846
137225
  targetLanguage: args.targetLanguage
@@ -136919,9 +137298,25 @@ async function translatePendingJobsBySourceLanguage(input) {
136919
137298
  startWorkersToDesired();
136920
137299
  }
136921
137300
  };
136922
- const applyBatchResult = async (batch, outputs) => {
137301
+ const applyBatchResult = async (batch, result) => {
136923
137302
  for (const [offset, job] of batch.jobs.entries()) {
136924
- 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) ?? "";
136925
137320
  const restoredTarget = job.segment.placeholderProtocol ? restoreTranslatedPlaceholderFragment(target, job.segment.placeholderProtocol) : { target: job.protectedInput.restore(target).trim() };
136926
137321
  const translatedSegment = {
136927
137322
  ...job.segment,
@@ -136960,7 +137355,10 @@ async function translatePendingJobsBySourceLanguage(input) {
136960
137355
  const batch = batches[batchIndex];
136961
137356
  const startedAt = getCurrentTimeMs();
136962
137357
  try {
136963
- 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));
136964
137362
  completedBatches += 1;
136965
137363
  const elapsedMs = Math.max(1, getCurrentTimeMs() - startedAt);
136966
137364
  appendTranslationAdaptiveConcurrencyLog({
@@ -137000,8 +137398,85 @@ async function translatePendingJobsBySourceLanguage(input) {
137000
137398
  startWorkersToDesired();
137001
137399
  while (workerPromises.size > 0) await Promise.race(workerPromises);
137002
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
+ }
137003
137478
  function getUnsupportedEngineLanguagePairMessage(input) {
137004
- if (!isManagedLocalTranslationEngineId(input.engine.cacheIdentity.engineId)) return null;
137479
+ if (!isDirectionalManagedLocalTranslationEngineId(input.engine.cacheIdentity.engineId)) return null;
137005
137480
  const directionCheck = checkLocalDirectionalModelLanguagePair({
137006
137481
  model: input.engine.cacheIdentity.model,
137007
137482
  sourceLanguage: input.sourceLanguage,
@@ -137010,6 +137485,9 @@ function getUnsupportedEngineLanguagePairMessage(input) {
137010
137485
  if (directionCheck.supported) return null;
137011
137486
  return directionCheck.message ?? "Selected local model does not support the detected translation direction.";
137012
137487
  }
137488
+ function normalizeTranslationSegments(segments) {
137489
+ return segments.filter(isRenderableTranslationSegment);
137490
+ }
137013
137491
  function summarizeTranslationLogThroughput(logs) {
137014
137492
  if (logs.length === 0) return 0;
137015
137493
  const totalTokens = logs.reduce((total, log) => total + log.estimatedTokens, 0);
@@ -137061,12 +137539,17 @@ function packTranslationJobs(jobs) {
137061
137539
  }
137062
137540
  async function collectBatchTranslationOutputs(stream, expectedCount) {
137063
137541
  const outputs = /* @__PURE__ */ new Map();
137542
+ const errors = /* @__PURE__ */ new Map();
137064
137543
  for await (const item of stream) {
137065
137544
  if (item.index < 0 || item.index >= expectedCount) throw new Error(`Translator yielded output for unexpected index ${item.index}.`);
137066
- 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);
137067
137547
  }
137068
- if (outputs.size !== expectedCount) throw new Error(`Translator returned ${outputs.size} outputs for ${expectedCount} inputs.`);
137069
- 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
+ };
137070
137553
  }
137071
137554
  function estimateTranslationTokens(input) {
137072
137555
  const trimmed = input.trim();
@@ -137562,6 +138045,104 @@ function getErrorMessage(error) {
137562
138045
  return error instanceof Error ? error.message : "Unknown translation error.";
137563
138046
  }
137564
138047
  //#endregion
138048
+ //#region src/lib/document-translation-session-state.ts
138049
+ var DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY = "openspecui:document-translation:mode";
138050
+ var DEFAULT_ACTIVATION = "source";
138051
+ var ACTIVATION_STORAGE_EVENT = "openspecui:document-translation-session-change";
138052
+ function isDocumentTranslationActivation(value) {
138053
+ return value === "source" || value === "translated";
138054
+ }
138055
+ function readDocumentTranslationActivation() {
138056
+ if (typeof window === "undefined") return DEFAULT_ACTIVATION;
138057
+ try {
138058
+ const value = window.sessionStorage.getItem(DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY);
138059
+ return isDocumentTranslationActivation(value) ? value : DEFAULT_ACTIVATION;
138060
+ } catch {
138061
+ return DEFAULT_ACTIVATION;
138062
+ }
138063
+ }
138064
+ function writeDocumentTranslationActivation(value) {
138065
+ if (typeof window === "undefined") return;
138066
+ try {
138067
+ window.sessionStorage.setItem(DOCUMENT_TRANSLATION_SESSION_STORAGE_KEY, value);
138068
+ } catch {
138069
+ return;
138070
+ }
138071
+ window.dispatchEvent(new CustomEvent(ACTIVATION_STORAGE_EVENT, { detail: value }));
138072
+ }
138073
+ function useDocumentTranslationActivation() {
138074
+ const [activation, setActivationState] = (0, import_react.useState)(readDocumentTranslationActivation);
138075
+ (0, import_react.useEffect)(() => {
138076
+ const handleActivationChange = () => {
138077
+ setActivationState(readDocumentTranslationActivation());
138078
+ };
138079
+ window.addEventListener(ACTIVATION_STORAGE_EVENT, handleActivationChange);
138080
+ window.addEventListener("storage", handleActivationChange);
138081
+ return () => {
138082
+ window.removeEventListener(ACTIVATION_STORAGE_EVENT, handleActivationChange);
138083
+ window.removeEventListener("storage", handleActivationChange);
138084
+ };
138085
+ }, []);
138086
+ return {
138087
+ activation,
138088
+ setActivation: (0, import_react.useCallback)((value) => {
138089
+ writeDocumentTranslationActivation(value);
138090
+ setActivationState(value);
138091
+ }, [])
138092
+ };
138093
+ }
138094
+ //#endregion
138095
+ //#region src/lib/resolve-document-translation-config.ts
138096
+ function resolveDocumentTranslationConfig(translationConfig, globalSettings, configPresence) {
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;
138104
+ const local = translationConfig.engines?.local ?? {};
138105
+ const localCt2 = translationConfig.engines?.localCt2 ?? {};
138106
+ const localLlama = translationConfig.engines?.localLlama ?? {};
138107
+ const openai = translationConfig.engines?.openai ?? {};
138108
+ const resolvedLocalModel = local.model ?? globalSettings?.translationEngines.local?.model;
138109
+ const resolvedLocalSelectedGroupId = local.selectedGroupId ?? globalSettings?.translationEngines.local?.selectedGroupId;
138110
+ const resolvedLocalCt2Model = localCt2.model ?? globalSettings?.translationEngines.localCt2?.model;
138111
+ const resolvedLocalCt2SelectedGroupId = localCt2.selectedGroupId ?? globalSettings?.translationEngines.localCt2?.selectedGroupId;
138112
+ const resolvedLocalLlamaModel = localLlama.model ?? globalSettings?.translationEngines.localLlama?.model;
138113
+ const resolvedLocalLlamaSelectedGroupId = localLlama.selectedGroupId ?? globalSettings?.translationEngines.localLlama?.selectedGroupId;
138114
+ const resolvedOpenAIModel = openai.model ?? globalSettings?.translationEngines.openai?.model;
138115
+ return {
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,
138122
+ engines: {
138123
+ local: {
138124
+ ...local,
138125
+ ...resolvedLocalModel ? { model: resolvedLocalModel } : {},
138126
+ ...resolvedLocalSelectedGroupId ? { selectedGroupId: resolvedLocalSelectedGroupId } : {}
138127
+ },
138128
+ localCt2: {
138129
+ ...localCt2,
138130
+ ...resolvedLocalCt2Model ? { model: resolvedLocalCt2Model } : {},
138131
+ ...resolvedLocalCt2SelectedGroupId ? { selectedGroupId: resolvedLocalCt2SelectedGroupId } : {}
138132
+ },
138133
+ localLlama: {
138134
+ ...localLlama,
138135
+ ...resolvedLocalLlamaModel ? { model: resolvedLocalLlamaModel } : {},
138136
+ ...resolvedLocalLlamaSelectedGroupId ? { selectedGroupId: resolvedLocalLlamaSelectedGroupId } : {}
138137
+ },
138138
+ openai: {
138139
+ ...openai,
138140
+ ...resolvedOpenAIModel ? { model: resolvedOpenAIModel } : {}
138141
+ }
138142
+ }
138143
+ };
138144
+ }
138145
+ //#endregion
137565
138146
  //#region ../core/src/local-download-profiles.ts
137566
138147
  function selectLocalDownloadGroup(plan, selectedGroupId) {
137567
138148
  if (!plan?.groups?.length) return null;
@@ -137635,6 +138216,11 @@ function projectTranslateServiceStatus(input) {
137635
138216
  engineId: input.engineId,
137636
138217
  message: getTranslationEngineLifecycleMessage(input.engineLifecycle) ?? "Translation engine runtime is not ready."
137637
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
+ };
137638
138224
  }
137639
138225
  if (isManagedLocalTranslationEngineId(input.engineId)) {
137640
138226
  if (!input.localModel?.trim()) return {
@@ -137719,15 +138305,17 @@ async function resolveTranslateServiceState(input) {
137719
138305
  localModel: model,
137720
138306
  localSelectedGroupId: localEngineConfig.selectedGroupId
137721
138307
  }) });
137722
- const directionCheck = checkLocalDirectionalModelLanguagePair({
137723
- model,
137724
- targetLanguage: config.targetLanguage
137725
- });
137726
- if (!directionCheck.supported) return emitTranslateServiceState(input.onUpdate, { status: {
137727
- state: "unavailable",
137728
- engineId: config.engineId,
137729
- message: directionCheck.message ?? "Selected local model does not support the configured target language."
137730
- } });
138308
+ if (isDirectionalManagedLocalTranslationEngineId(config.engineId)) {
138309
+ const directionCheck = checkLocalDirectionalModelLanguagePair({
138310
+ model,
138311
+ targetLanguage: config.targetLanguage
138312
+ });
138313
+ if (!directionCheck.supported) return emitTranslateServiceState(input.onUpdate, { status: {
138314
+ state: "unavailable",
138315
+ engineId: config.engineId,
138316
+ message: directionCheck.message ?? "Selected local model does not support the configured target language."
138317
+ } });
138318
+ }
137731
138319
  input.onUpdate?.(createTranslateServiceState({ status: projectTranslateServiceStatus({
137732
138320
  enabled: config.enabled,
137733
138321
  hasSource: input.hasSource,
@@ -137897,13 +138485,16 @@ function getManagedLocalEngineConfig(config) {
137897
138485
  return config.engineId === "local-ct2" ? {
137898
138486
  model: config.engines.localCt2.model,
137899
138487
  selectedGroupId: config.engines.localCt2.selectedGroupId
138488
+ } : config.engineId === "local-llama" ? {
138489
+ model: config.engines.localLlama.model,
138490
+ selectedGroupId: config.engines.localLlama.selectedGroupId
137900
138491
  } : {
137901
138492
  model: config.engines.local.model,
137902
138493
  selectedGroupId: config.engines.local.selectedGroupId
137903
138494
  };
137904
138495
  }
137905
138496
  async function queryManagedLocalPanelState(engineId, input) {
137906
- return engineId === "local" ? trpcClient.localModels.panelState.query(input) : trpcClient.localCt2Models.panelState.query(input);
138497
+ return engineId === "local" ? trpcClient.localModels.panelState.query(input) : engineId === "local-ct2" ? trpcClient.localCt2Models.panelState.query(input) : trpcClient.localLlamaModels.panelState.query(input);
137907
138498
  }
137908
138499
  var TrpcTranslator = class {
137909
138500
  constructor(options) {
@@ -137922,7 +138513,8 @@ var TrpcTranslator = class {
137922
138513
  selectedGroupId: this.options.selectedGroupId,
137923
138514
  inputs,
137924
138515
  instructions: options?.instructions,
137925
- context: options?.context
138516
+ context: options?.context,
138517
+ timeoutMs: options?.timeoutMs ?? 15e3
137926
138518
  }, {
137927
138519
  onData(event) {
137928
138520
  queue.push(event);
@@ -137978,11 +138570,13 @@ function useDocumentTranslation(markdown, config) {
137978
138570
  const abortRef = (0, import_react.useRef)(null);
137979
138571
  const generationRef = (0, import_react.useRef)(0);
137980
138572
  const latestStartRef = (0, import_react.useRef)(null);
138573
+ const segmentPatchMapRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
137981
138574
  const { activation } = useDocumentTranslationActivation();
137982
138575
  const cancel = (0, import_react.useCallback)(() => {
137983
138576
  generationRef.current += 1;
137984
138577
  abortRef.current?.abort();
137985
138578
  abortRef.current = null;
138579
+ segmentPatchMapRef.current.clear();
137986
138580
  setStatus("source");
137987
138581
  setResult(null);
137988
138582
  setError(null);
@@ -137991,6 +138585,7 @@ function useDocumentTranslation(markdown, config) {
137991
138585
  generationRef.current += 1;
137992
138586
  abortRef.current?.abort();
137993
138587
  abortRef.current = null;
138588
+ segmentPatchMapRef.current.clear();
137994
138589
  setStatus("source");
137995
138590
  setResult(null);
137996
138591
  setError(null);
@@ -138000,6 +138595,7 @@ function useDocumentTranslation(markdown, config) {
138000
138595
  generationRef.current += 1;
138001
138596
  setCapability(null);
138002
138597
  setBrowserSupportTable(null);
138598
+ segmentPatchMapRef.current.clear();
138003
138599
  setResult(null);
138004
138600
  setStatus("source");
138005
138601
  setError(null);
@@ -138012,6 +138608,8 @@ function useDocumentTranslation(markdown, config) {
138012
138608
  config?.engines.local.selectedGroupId,
138013
138609
  config?.engines.localCt2.model,
138014
138610
  config?.engines.localCt2.selectedGroupId,
138611
+ config?.engines.localLlama.model,
138612
+ config?.engines.localLlama.selectedGroupId,
138015
138613
  config?.engines.openai.model,
138016
138614
  config?.targetLanguage
138017
138615
  ]);
@@ -138054,6 +138652,8 @@ function useDocumentTranslation(markdown, config) {
138054
138652
  config?.engines.local.selectedGroupId,
138055
138653
  config?.engines.localCt2.model,
138056
138654
  config?.engines.localCt2.selectedGroupId,
138655
+ config?.engines.localLlama.model,
138656
+ config?.engines.localLlama.selectedGroupId,
138057
138657
  config?.targetLanguage,
138058
138658
  markdown.length
138059
138659
  ]);
@@ -138064,6 +138664,7 @@ function useDocumentTranslation(markdown, config) {
138064
138664
  const generationId = generationRef.current + 1;
138065
138665
  generationRef.current = generationId;
138066
138666
  abortRef.current = controller;
138667
+ segmentPatchMapRef.current.clear();
138067
138668
  setError(null);
138068
138669
  setStatus("initializing");
138069
138670
  try {
@@ -138098,6 +138699,7 @@ function useDocumentTranslation(markdown, config) {
138098
138699
  targetLanguage: config.targetLanguage,
138099
138700
  displayMode: config.displayMode,
138100
138701
  signal: controller.signal,
138702
+ timeoutMs: DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS,
138101
138703
  engine: createTranslationEngineExecution(config),
138102
138704
  cache: config.cacheEnabled && !isStaticMode() ? {
138103
138705
  read: (keyHash) => trpcClient.translationCache.read.query({ keyHash }),
@@ -138108,11 +138710,11 @@ function useDocumentTranslation(markdown, config) {
138108
138710
  setResult((current) => applyDocumentTranslationPatch(current, patch, {
138109
138711
  displayMode: config.displayMode,
138110
138712
  targetLanguage: config.targetLanguage
138111
- }));
138713
+ }, segmentPatchMapRef.current));
138112
138714
  });
138113
138715
  if (controller.signal.aborted || abortRef.current !== controller || generationRef.current !== generationId) return;
138114
138716
  const documentFailure = getDocumentTranslationFailureMessage(nextResult);
138115
- setResult(nextResult);
138717
+ setResult(normalizeDocumentTranslationResult(nextResult));
138116
138718
  if (documentFailure) {
138117
138719
  setError(documentFailure);
138118
138720
  setStatus("error");
@@ -138138,9 +138740,62 @@ function useDocumentTranslation(markdown, config) {
138138
138740
  config?.engines.local.selectedGroupId,
138139
138741
  config?.engines.localCt2.model,
138140
138742
  config?.engines.localCt2.selectedGroupId,
138743
+ config?.engines.localLlama.model,
138744
+ config?.engines.localLlama.selectedGroupId,
138141
138745
  markdown,
138142
138746
  serviceStatus
138143
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]);
138144
138799
  (0, import_react.useEffect)(() => {
138145
138800
  latestStartRef.current = start;
138146
138801
  }, [start]);
@@ -138163,28 +138818,46 @@ function useDocumentTranslation(markdown, config) {
138163
138818
  error,
138164
138819
  result,
138165
138820
  start,
138821
+ retrySegment,
138166
138822
  cancel,
138167
138823
  reset
138168
138824
  };
138169
138825
  }
138826
+ function isDocumentTranslationSegment(segment) {
138827
+ return isRenderableTranslationSegment(segment);
138828
+ }
138170
138829
  function getDocumentTranslationFailureMessage(result) {
138171
- const segments = (Array.isArray(result.segments) ? result.segments : []).filter((segment) => segment !== void 0);
138830
+ const segments = (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment);
138172
138831
  if (segments.length === 0) return null;
138173
138832
  if (segments.some((segment) => segment.status !== "error" && typeof segment.target === "string")) return null;
138174
138833
  const errors = segments.map((segment) => segment.status === "error" ? segment.error : void 0).filter((message) => typeof message === "string" && message.length > 0);
138175
138834
  if (errors.length === 0) return null;
138176
138835
  return errors[0] ?? "Translation failed.";
138177
138836
  }
138178
- function applyDocumentTranslationPatch(current, patch, fallback) {
138179
- const segments = current?.segments.slice() ?? [];
138180
- segments[patch.segmentIndex] = patch.segment;
138181
- return {
138837
+ function applyDocumentTranslationPatch(current, patch, fallback, patchMap) {
138838
+ patchMap.set(patch.segmentIndex, patch.segment);
138839
+ return buildPatchedDocumentTranslationResult(current, fallback, patchMap);
138840
+ }
138841
+ function buildPatchedDocumentTranslationResult(current, fallback, patchMap) {
138842
+ return normalizeDocumentTranslationResult({
138182
138843
  displayMode: current?.displayMode ?? fallback.displayMode,
138183
138844
  sourceLanguage: current?.sourceLanguage,
138184
138845
  targetLanguage: current?.targetLanguage ?? fallback.targetLanguage,
138185
- segments
138846
+ segments: [...patchMap.entries()].sort((left, right) => left[0] - right[0]).map(([, segment]) => segment)
138847
+ });
138848
+ }
138849
+ function normalizeDocumentTranslationResult(result) {
138850
+ return {
138851
+ ...result,
138852
+ segments: (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment)
138186
138853
  };
138187
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
+ }
138188
138861
  //#endregion
138189
138862
  //#region ../../node_modules/.pnpm/comma-separated-tokens@2.0.3/node_modules/comma-separated-tokens/index.js
138190
138863
  /**
@@ -148790,8 +149463,9 @@ function transformSafeMarkdownUrl(value) {
148790
149463
  }
148791
149464
  //#endregion
148792
149465
  //#region src/components/document-translation-segment-render.tsx
148793
- function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className }) {
149466
+ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className, onRetry }) {
148794
149467
  const target = segment.target ?? "";
149468
+ const showRetry = segment.status === "error" && typeof onRetry === "function";
148795
149469
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
148796
149470
  className: mergeClassName$1("document-translation-segment", className),
148797
149471
  children: [displayMode === "bilingual" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -148804,7 +149478,19 @@ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode
148804
149478
  title: displayMode === "direct" ? segment.source : void 0,
148805
149479
  lang: segment.targetLanguage,
148806
149480
  "data-translation-target": "",
148807
- 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 }))
148808
149494
  })]
148809
149495
  });
148810
149496
  }
@@ -148829,13 +149515,13 @@ var OPENSPEC_SECTION_TARGET_LABELS = {
148829
149515
  overview: "目的",
148830
149516
  requirements: "需求"
148831
149517
  };
148832
- function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode) {
149518
+ function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetry) {
148833
149519
  const kind = getCurrentDataAttribute(input, "data-openspec-kind");
148834
- if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode);
149520
+ if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry);
148835
149521
  if (kind !== "requirement" && kind !== "scenario") return void 0;
148836
- return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind);
149522
+ return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry);
148837
149523
  }
148838
- function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode) {
149524
+ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry) {
148839
149525
  const sectionKind = getCurrentDataAttribute(input, "data-openspec-section-kind");
148840
149526
  if (!sectionKind) return void 0;
148841
149527
  const targetTitle = OPENSPEC_SECTION_TARGET_LABELS[sectionKind];
@@ -148853,11 +149539,12 @@ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displa
148853
149539
  },
148854
149540
  displayMode,
148855
149541
  targetChildren: targetTitle,
148856
- className: "document-translation-heading-segment"
149542
+ className: "document-translation-heading-segment",
149543
+ onRetry
148857
149544
  })
148858
149545
  };
148859
149546
  }
148860
- function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind) {
149547
+ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry) {
148861
149548
  const sourceParts = splitOpenSpecHeadingText(segment.source, kind);
148862
149549
  const targetParts = splitOpenSpecHeadingText(segment.target ?? "", kind);
148863
149550
  const visualLabel = getCurrentDataAttribute(input, "data-openspec-visual-label") ?? getCurrentDataAttribute(input, "data-openspec-label");
@@ -148902,7 +149589,8 @@ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, disp
148902
149589
  segment: titleSegment,
148903
149590
  displayMode,
148904
149591
  targetChildren: targetNodes ? renderTranslatedHastNodes(targetNodes) : void 0,
148905
- className: "document-translation-heading-segment"
149592
+ className: "document-translation-heading-segment",
149593
+ onRetry
148906
149594
  })
148907
149595
  })] }),
148908
149596
  target: targetTitle
@@ -148963,7 +149651,7 @@ function useDocumentTranslationRenderPlugin({ markdown, translationConfig }) {
148963
149651
  const resolvedTranslationConfig = (0, import_react.useMemo)(() => translationConfig === void 0 ? void 0 : DocumentTranslationConfigSchema.parse(resolveDocumentTranslationConfig(translationConfig, globalSettings)), [globalSettings, translationConfig]);
148964
149652
  const session = useDocumentTranslation(markdown ?? "", resolvedTranslationConfig);
148965
149653
  const canTranslate = resolvedTranslationConfig !== void 0 && typeof markdown === "string" && markdown.length > 0;
148966
- 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]);
148967
149655
  const translationAction = (0, import_react.useMemo)(() => canTranslate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DocumentTranslationAction, {
148968
149656
  enabled: resolvedTranslationConfig?.enabled ?? false,
148969
149657
  session
@@ -149034,7 +149722,7 @@ function hashString(value) {
149034
149722
  for (let index = 0; index < value.length; index++) hash = hash * 31 + value.charCodeAt(index) | 0;
149035
149723
  return hash.toString(36);
149036
149724
  }
149037
- function createTranslationProjection(result) {
149725
+ function createTranslationProjection(result, onRetrySegment) {
149038
149726
  if (!result) return { blockAnnotations: [] };
149039
149727
  const segments = getRenderableTranslationSegments(result);
149040
149728
  const segmentByOffset = new Map(segments.filter((segment) => segment.target).map((segment) => [segment.sourceStartOffset, segment]));
@@ -149056,7 +149744,7 @@ function createTranslationProjection(result) {
149056
149744
  transformHeading(input) {
149057
149745
  const segment = input.sourceStartOffset === void 0 ? void 0 : segmentByOffset.get(input.sourceStartOffset);
149058
149746
  if (!segment?.target || segment.kind !== "heading") return void 0;
149059
- return createTranslatedHeadingTransform(input, segment, result.displayMode);
149747
+ return createTranslatedHeadingTransform(input, segment, result.displayMode, onRetrySegment);
149060
149748
  }
149061
149749
  },
149062
149750
  blockAnnotations: segments.filter((segment) => segment.target && segment.kind !== "heading").map((segment) => ({
@@ -149075,17 +149763,18 @@ function createTranslationProjection(result) {
149075
149763
  renderChildren: (children) => segment.targetNodes && result.displayMode === "direct" ? children : renderTranslationSegmentChildren({
149076
149764
  sourceChildren: children,
149077
149765
  segment,
149078
- displayMode: result.displayMode
149766
+ displayMode: result.displayMode,
149767
+ onRetry: onRetrySegment
149079
149768
  })
149080
149769
  }))
149081
149770
  };
149082
149771
  }
149083
149772
  function getRenderableTranslationSegments(result) {
149084
- return (Array.isArray(result.segments) ? result.segments : []).filter((segment) => segment !== void 0);
149773
+ return (Array.isArray(result.segments) ? result.segments : []).filter(isRenderableTranslationSegment);
149085
149774
  }
149086
- function createTranslatedHeadingTransform(input, segment, displayMode) {
149775
+ function createTranslatedHeadingTransform(input, segment, displayMode, onRetrySegment) {
149087
149776
  const projectedTarget = segment.target ?? "";
149088
- const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode);
149777
+ const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetrySegment);
149089
149778
  if (openSpecHeading) return {
149090
149779
  tocDataLabel: displayMode === "direct" ? openSpecHeading.tocDataLabel : input.current?.tocLabel ?? input.text,
149091
149780
  children: openSpecHeading.children,
@@ -149100,7 +149789,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149100
149789
  segment,
149101
149790
  displayMode,
149102
149791
  targetChildren,
149103
- className: "document-translation-heading-segment"
149792
+ className: "document-translation-heading-segment",
149793
+ onRetry: onRetrySegment
149104
149794
  }),
149105
149795
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149106
149796
  };
@@ -149111,7 +149801,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149111
149801
  segment,
149112
149802
  displayMode,
149113
149803
  targetChildren,
149114
- className: "document-translation-heading-segment"
149804
+ className: "document-translation-heading-segment",
149805
+ onRetry: onRetrySegment
149115
149806
  }),
149116
149807
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149117
149808
  };