@openspecui/web 3.11.3 → 3.11.5

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-B4xoxAIc.js +1 -0
  2. package/dist/assets/WebGLRenderer-B0Jtk0nh.js +1 -0
  3. package/dist/assets/WebGPURenderer-Cfl-sqIJ.js +1 -0
  4. package/dist/assets/audio-preview-EEAGPjut.js +1 -0
  5. package/dist/assets/browserAll-BAtKDoiA.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-B1OAaHtN.js} +1 -1
  9. package/dist/assets/{dist-DDPVC2MF.js → dist-BD5mncFZ.js} +1 -1
  10. package/dist/assets/{dist-CkEGbci5.js → dist-BPXRU7xA.js} +1 -1
  11. package/dist/assets/dist-BPb0nckX.js +1 -0
  12. package/dist/assets/{dist-Caq73PXT.js → dist-BoE24RpQ.js} +1 -1
  13. package/dist/assets/{dist-CZh2GIVt.js → dist-C4vm2Q_T.js} +1 -1
  14. package/dist/assets/dist-C5Si4P75.js +1 -0
  15. package/dist/assets/{dist-rlrlm1HB.js → dist-CM2xvCkf.js} +1 -1
  16. package/dist/assets/dist-CyBfJ6hc.js +1 -0
  17. package/dist/assets/{dist-BKY1RG9s.js → dist-DHe7I_KZ.js} +1 -1
  18. package/dist/assets/{dist-BVKuVv2H.js → dist-DSuLZBKC.js} +1 -1
  19. package/dist/assets/{dist-YXnB8lV1.js → dist-iayOqDE6.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-DUi_seZz.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-BBjD638P.css +1 -0
  28. package/dist/assets/main-CWtuI7Nz.js +1669 -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-C0ZfTMd0.js +1 -0
  34. package/dist/assets/video-preview-zIpjT8bC.js +1 -0
  35. package/dist/assets/webworkerAll-s8l06Z4r.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-C0SJkAx_.js +1 -0
  43. package/dist-ssg/client/assets/WebGLRenderer-_kPaznMR.js +1 -0
  44. package/dist-ssg/client/assets/WebGPURenderer-BxiyOM-9.js +1 -0
  45. package/dist-ssg/client/assets/browserAll-gJgi68_s.js +1 -0
  46. package/dist-ssg/client/assets/{dist-CX1-5rfl.js → dist-BHOFHtBx.js} +1 -1
  47. package/dist-ssg/client/assets/{dist-HHQTTSi-.js → dist-BZziKvqb.js} +1 -1
  48. package/dist-ssg/client/assets/{dist-upAmtw9_.js → dist-BjFWEmHB.js} +1 -1
  49. package/dist-ssg/client/assets/{dist-BKTT7Vdm.js → dist-BnZ2HTAx.js} +1 -1
  50. package/dist-ssg/client/assets/{dist-CLrPzzut.js → dist-Bv4vhBN7.js} +1 -1
  51. package/dist-ssg/client/assets/{dist-DJkx9TIh.js → dist-CIuFQ_JU.js} +1 -1
  52. package/dist-ssg/client/assets/dist-CrxbKAeY.js +1 -0
  53. package/dist-ssg/client/assets/{dist-Cm5Vx6W8.js → dist-CsJzUP3e.js} +1 -1
  54. package/dist-ssg/client/assets/dist-DI0_1FlR.js +1 -0
  55. package/dist-ssg/client/assets/{dist-BPUIHtZ8.js → dist-DMB6LAIC.js} +1 -1
  56. package/dist-ssg/client/assets/dist-DtUEqoN7.js +1 -0
  57. package/dist-ssg/client/assets/{dist-CVizFqBj.js → dist-n4MxMk4A.js} +1 -1
  58. package/dist-ssg/client/assets/{ghostty-web-BKQcHDq5.js → ghostty-web-BcYHa-ju.js} +1 -1
  59. package/dist-ssg/client/assets/index-CinxwuFk.css +1 -0
  60. package/dist-ssg/client/assets/{index.ssg-DBq9_pDs.js → index.ssg-VKLGiOJL.js} +164 -149
  61. package/dist-ssg/client/assets/{init-brQ3KbKB.js → init-BotiXPO-.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-C4pu2wyc.js +1 -0
  70. package/dist-ssg/client/assets/webworkerAll-DBwLgZ3e.js +1 -0
  71. package/dist-ssg/client/index.ssg.html +2 -29
  72. package/dist-ssg/server/entry-server.js +1054 -118
  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",
@@ -32553,27 +32665,61 @@ var TranslationOpenAISettingsSchema = objectType({
32553
32665
  token: stringType().default(""),
32554
32666
  model: stringType().default("gpt-4.1-mini")
32555
32667
  });
32668
+ var TranslationOpenAISettingsUpdateSchema = objectType({
32669
+ baseUrl: stringType().optional(),
32670
+ token: stringType().optional(),
32671
+ model: stringType().optional()
32672
+ });
32556
32673
  var TranslationLocalSettingsSchema = objectType({
32557
32674
  model: stringType().default("Xenova/nllb-200-distilled-600M"),
32558
32675
  selectedGroupId: stringType().optional(),
32559
- hfEndpoint: stringType().default("")
32676
+ hfEndpoint: stringType().default(""),
32677
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32678
+ });
32679
+ var TranslationLocalSettingsUpdateSchema = objectType({
32680
+ model: stringType().min(1).optional(),
32681
+ selectedGroupId: stringType().min(1).nullable().optional(),
32682
+ hfEndpoint: stringType().optional(),
32683
+ memoryBudgetPercent: numberType().min(0).max(100).optional()
32560
32684
  });
32561
32685
  var TranslationLocalCt2SettingsSchema = objectType({
32562
32686
  model: stringType().default("ooeoeo/opus-mt-en-zh-ct2-float16"),
32563
32687
  selectedGroupId: stringType().optional(),
32564
- hfEndpoint: stringType().default("")
32688
+ hfEndpoint: stringType().default(""),
32689
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32690
+ });
32691
+ var TranslationLocalCt2SettingsUpdateSchema = objectType({
32692
+ model: stringType().min(1).optional(),
32693
+ selectedGroupId: stringType().min(1).nullable().optional(),
32694
+ hfEndpoint: stringType().optional(),
32695
+ memoryBudgetPercent: numberType().min(0).max(100).optional()
32565
32696
  });
32566
32697
  var TranslationLocalLlamaSettingsSchema = objectType({
32567
32698
  model: stringType().default("bartowski/Qwen2.5-0.5B-Instruct-GGUF"),
32568
32699
  selectedGroupId: stringType().optional(),
32569
- hfEndpoint: stringType().default("")
32700
+ hfEndpoint: stringType().default(""),
32701
+ memoryBudgetPercent: numberType().min(0).max(100).default(25)
32702
+ });
32703
+ var TranslationLocalLlamaSettingsUpdateSchema = objectType({
32704
+ model: stringType().min(1).optional(),
32705
+ selectedGroupId: stringType().min(1).nullable().optional(),
32706
+ hfEndpoint: stringType().optional(),
32707
+ memoryBudgetPercent: numberType().min(0).max(100).optional()
32570
32708
  });
32571
32709
  objectType({
32710
+ engineId: TranslationEngineIdSchema.default(DEFAULT_TRANSLATION_ENGINE_ID),
32572
32711
  openai: TranslationOpenAISettingsSchema.default(TranslationOpenAISettingsSchema.parse({})),
32573
32712
  local: TranslationLocalSettingsSchema.default(TranslationLocalSettingsSchema.parse({})),
32574
32713
  localCt2: TranslationLocalCt2SettingsSchema.default(TranslationLocalCt2SettingsSchema.parse({})),
32575
32714
  localLlama: TranslationLocalLlamaSettingsSchema.default(TranslationLocalLlamaSettingsSchema.parse({}))
32576
32715
  });
32716
+ objectType({
32717
+ engineId: TranslationEngineIdSchema.optional(),
32718
+ openai: TranslationOpenAISettingsUpdateSchema.optional(),
32719
+ local: TranslationLocalSettingsUpdateSchema.optional(),
32720
+ localCt2: TranslationLocalCt2SettingsUpdateSchema.optional(),
32721
+ localLlama: TranslationLocalLlamaSettingsUpdateSchema.optional()
32722
+ });
32577
32723
  objectType({
32578
32724
  engineId: TranslationEngineIdSchema,
32579
32725
  sourceLanguage: stringType().min(1),
@@ -32582,11 +32728,16 @@ objectType({
32582
32728
  selectedGroupId: stringType().min(1).optional(),
32583
32729
  inputs: arrayType(stringType()).min(1),
32584
32730
  instructions: stringType().optional(),
32585
- context: stringType().optional()
32731
+ context: stringType().optional(),
32732
+ timeoutMs: numberType().int().positive().default(DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS)
32586
32733
  });
32587
32734
  objectType({
32588
32735
  index: numberType().int().nonnegative(),
32589
- output: stringType()
32736
+ output: stringType().optional(),
32737
+ error: objectType({
32738
+ kind: enumType(BATCH_TRANSLATION_ERROR_KINDS),
32739
+ message: stringType().min(1)
32740
+ }).optional()
32590
32741
  });
32591
32742
  var DocumentTranslationDisplayModeSchema = enumType(["direct", "bilingual"]);
32592
32743
  var TranslationEngineProjectSettingsSchema = objectType({
@@ -32604,6 +32755,21 @@ var TranslationEngineProjectSettingsSchema = objectType({
32604
32755
  }).default({}),
32605
32756
  openai: objectType({ model: stringType().min(1).optional() }).default({})
32606
32757
  }).default({});
32758
+ var TranslationEngineProjectSettingsUpdateSchema = objectType({
32759
+ local: objectType({
32760
+ model: stringType().min(1).optional(),
32761
+ selectedGroupId: stringType().min(1).nullable().optional()
32762
+ }).optional(),
32763
+ localCt2: objectType({
32764
+ model: stringType().min(1).optional(),
32765
+ selectedGroupId: stringType().min(1).nullable().optional()
32766
+ }).optional(),
32767
+ localLlama: objectType({
32768
+ model: stringType().min(1).optional(),
32769
+ selectedGroupId: stringType().min(1).nullable().optional()
32770
+ }).optional(),
32771
+ openai: objectType({ model: stringType().min(1).optional() }).optional()
32772
+ });
32607
32773
  var DocumentTranslationConfigSchema = objectType({
32608
32774
  enabled: booleanType().default(false),
32609
32775
  targetLanguage: stringType().min(1).default("zh"),
@@ -32612,7 +32778,18 @@ var DocumentTranslationConfigSchema = objectType({
32612
32778
  engineId: TranslationEngineIdSchema.default(DEFAULT_TRANSLATION_ENGINE_ID),
32613
32779
  engines: TranslationEngineProjectSettingsSchema
32614
32780
  });
32615
- objectType({ entryLimit: numberType().int().min(100).max(2e5).default(1e4) });
32781
+ objectType({
32782
+ enabled: booleanType().optional(),
32783
+ targetLanguage: stringType().min(1).optional(),
32784
+ displayMode: DocumentTranslationDisplayModeSchema.optional(),
32785
+ cacheEnabled: booleanType().optional(),
32786
+ engineId: TranslationEngineIdSchema.optional(),
32787
+ engines: TranslationEngineProjectSettingsUpdateSchema.optional()
32788
+ });
32789
+ var DEFAULT_TRANSLATION_CACHE_ENTRY_LIMIT = 1e4;
32790
+ var MAX_TRANSLATION_CACHE_ENTRY_LIMIT = 2e5;
32791
+ objectType({ entryLimit: numberType().int().min(100).max(MAX_TRANSLATION_CACHE_ENTRY_LIMIT).default(DEFAULT_TRANSLATION_CACHE_ENTRY_LIMIT) });
32792
+ objectType({ entryLimit: numberType().int().min(100).max(MAX_TRANSLATION_CACHE_ENTRY_LIMIT).optional() });
32616
32793
  objectType({
32617
32794
  key: stringType().min(1),
32618
32795
  keyHash: stringType().min(1),
@@ -68061,6 +68238,25 @@ var InputPanel = class extends i {
68061
68238
  }));
68062
68239
  this.requestUpdate();
68063
68240
  }
68241
+ _stopToolbarControlPointer(e) {
68242
+ e.stopPropagation();
68243
+ e.preventDefault();
68244
+ }
68245
+ _stopToolbarControlEvent(e) {
68246
+ e.stopPropagation();
68247
+ }
68248
+ _switchTabFromToolbar(e, tab) {
68249
+ this._stopToolbarControlEvent(e);
68250
+ this._switchTab(tab);
68251
+ }
68252
+ _toggleLayoutFromToolbar(e) {
68253
+ this._stopToolbarControlEvent(e);
68254
+ this._toggleLayout();
68255
+ }
68256
+ _closeFromToolbar(e) {
68257
+ this._stopToolbarControlEvent(e);
68258
+ this._close();
68259
+ }
68064
68260
  _toggleLayout() {
68065
68261
  this.layout = this.layout === "fixed" ? "floating" : "fixed";
68066
68262
  if (this.layout === "fixed") this._closeFloatingDialog();
@@ -68297,17 +68493,32 @@ var InputPanel = class extends i {
68297
68493
  class="tab-btn"
68298
68494
  part="tab-btn"
68299
68495
  ?data-active=${this.activeTab === t.id}
68300
- @click=${() => this._switchTab(t.id)}
68496
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68497
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68498
+ @click=${(e) => this._switchTabFromToolbar(e, t.id)}
68301
68499
  >
68302
68500
  ${t.icon} ${this.activeTab === t.id ? t.label : ""}
68303
68501
  </button>
68304
68502
  `)}
68305
68503
  </div>
68306
68504
  <div class="action-group">
68307
- <button class="icon-btn" @click=${this._toggleLayout} title="Toggle layout mode">
68505
+ <button
68506
+ class="icon-btn"
68507
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68508
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68509
+ @click=${(e) => this._toggleLayoutFromToolbar(e)}
68510
+ title="Toggle layout mode"
68511
+ >
68308
68512
  ${this.layout === "fixed" ? iconPin(14) : iconPinOff(14)}
68309
68513
  </button>
68310
- <button class="icon-btn" part="close-btn" @click=${this._close} title="Close panel">
68514
+ <button
68515
+ class="icon-btn"
68516
+ part="close-btn"
68517
+ @pointerdown=${(e) => this._stopToolbarControlPointer(e)}
68518
+ @mousedown=${(e) => this._stopToolbarControlPointer(e)}
68519
+ @click=${(e) => this._closeFromToolbar(e)}
68520
+ title="Close panel"
68521
+ >
68311
68522
  ${iconX(14)}
68312
68523
  </button>
68313
68524
  </div>
@@ -90847,30 +91058,12 @@ var ShortcutTab = class extends i {
90847
91058
  composed: true
90848
91059
  }));
90849
91060
  }
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");
91061
+ _handleCommand(command) {
91062
+ this.dispatchEvent(new CustomEvent("input-panel:command", {
91063
+ detail: { command },
91064
+ bubbles: true,
91065
+ composed: true
91066
+ }));
90874
91067
  }
90875
91068
  _dpadData(event, width, height, container) {
90876
91069
  const local = container.toLocal(event.global);
@@ -90880,7 +91073,7 @@ var ShortcutTab = class extends i {
90880
91073
  if (Math.abs(dx) > Math.abs(dy)) return dx < 0 ? "\x1B[D" : "\x1B[C";
90881
91074
  return dy < 0 ? "\x1B[A" : "\x1B[B";
90882
91075
  }
90883
- async _activateShortcut(item, event, width, height, container) {
91076
+ _activateShortcut(item, event, width, height, container) {
90884
91077
  if (item.kind === "dpad") {
90885
91078
  this._send(this._dpadData(event, width, height, container));
90886
91079
  return;
@@ -90895,7 +91088,7 @@ var ShortcutTab = class extends i {
90895
91088
  this._send(action.text);
90896
91089
  return;
90897
91090
  }
90898
- await this._handleCommand(action.command);
91091
+ this._handleCommand(action.command);
90899
91092
  }
90900
91093
  _setActivePage(pageId) {
90901
91094
  if (this.activePageId === pageId) return;
@@ -91444,8 +91637,11 @@ var VirtualKeyboardTab = class extends i {
91444
91637
  this._activeKey = null;
91445
91638
  this._activePointerId = null;
91446
91639
  }
91640
+ _hostPlatform() {
91641
+ return this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform();
91642
+ }
91447
91643
  getRows() {
91448
- return LAYOUTS[this.platform === "windows" || this.platform === "macos" || this.platform === "common" ? this.platform : detectHostPlatform()];
91644
+ return LAYOUTS[this._hostPlatform()];
91449
91645
  }
91450
91646
  async _initPixi() {
91451
91647
  const host = this.shadowRoot?.querySelector(".pixi-host");
@@ -91791,14 +91987,44 @@ var VirtualKeyboardTab = class extends i {
91791
91987
  if (this._isShifted(def, forceShift) && def.shift) return def.shift.data;
91792
91988
  return def.data;
91793
91989
  }
91794
- _sendKey(def, forceShift) {
91990
+ _resolveTerminalData(def, forceShift) {
91795
91991
  let data = this._resolveOutputData(def, forceShift);
91796
- if (!data) return;
91992
+ if (!data) return "";
91797
91993
  if (this._modifiers.ctrl && data.length === 1) {
91798
91994
  const code = data.toUpperCase().charCodeAt(0) - 64;
91799
91995
  if (code > 0 && code < 32) data = String.fromCharCode(code);
91800
91996
  }
91801
91997
  if (this._modifiers.alt || this._modifiers.meta) data = `\x1b${data}`;
91998
+ return data;
91999
+ }
92000
+ _resolvePrimaryCommand(def) {
92001
+ if (this._modifiers.alt || this._modifiers.shift) return null;
92002
+ if (!(this._hostPlatform() === "macos" ? this._modifiers.meta && !this._modifiers.ctrl : this._modifiers.ctrl && !this._modifiers.meta)) return null;
92003
+ const key = def.data.toLowerCase();
92004
+ if (key === "c") return "copy";
92005
+ if (key === "v") return "paste";
92006
+ if (key === "a") return "select-all";
92007
+ return null;
92008
+ }
92009
+ _sendCommand(command, fallbackData) {
92010
+ this.dispatchEvent(new CustomEvent("input-panel:command", {
92011
+ detail: {
92012
+ command,
92013
+ fallbackData
92014
+ },
92015
+ bubbles: true,
92016
+ composed: true
92017
+ }));
92018
+ }
92019
+ _sendKey(def, forceShift) {
92020
+ const data = this._resolveTerminalData(def, forceShift);
92021
+ if (!data) return;
92022
+ const command = this._resolvePrimaryCommand(def);
92023
+ if (command) {
92024
+ const fallbackData = this._hostPlatform() === "macos" ? void 0 : data;
92025
+ this._sendCommand(command, fallbackData);
92026
+ return;
92027
+ }
91802
92028
  this.dispatchEvent(new CustomEvent("input-panel:send", {
91803
92029
  detail: { data },
91804
92030
  bubbles: true,
@@ -92326,9 +92552,27 @@ var EDGE_SCROLL_OVERSHOOT = 15;
92326
92552
  function isTouchDevice() {
92327
92553
  return "ontouchstart" in window || navigator.maxTouchPoints > 0;
92328
92554
  }
92555
+ function isTerminalTouchMouseOverlay(element) {
92556
+ return element instanceof HTMLElement && element.classList.contains("terminal-touch-mouse-overlay");
92557
+ }
92558
+ function resolveTerminalPointerTarget(container, clientX, clientY) {
92559
+ let element = document.elementFromPoint(clientX, clientY);
92560
+ if (element && isTerminalTouchMouseOverlay(element)) {
92561
+ const overlayElement = element;
92562
+ const previousPointerEvents = overlayElement.style.pointerEvents;
92563
+ overlayElement.style.pointerEvents = "none";
92564
+ element = document.elementFromPoint(clientX, clientY);
92565
+ overlayElement.style.pointerEvents = previousPointerEvents;
92566
+ }
92567
+ if (element && container.contains(element) && !isTerminalTouchMouseOverlay(element)) return element;
92568
+ return container.querySelector(".xterm-screen") ?? container;
92569
+ }
92329
92570
  function isInputPanelTab(value) {
92330
92571
  return value === "input" || value === "keys" || value === "shortcuts" || value === "trackpad" || value === "settings";
92331
92572
  }
92573
+ function isInputPanelCommand(value) {
92574
+ return value === "copy" || value === "paste" || value === "select-all";
92575
+ }
92332
92576
  function isRecord$1(value) {
92333
92577
  return typeof value === "object" && value !== null;
92334
92578
  }
@@ -92575,6 +92819,7 @@ var InputPanelAddon = class InputPanelAddon {
92575
92819
  _persistentCleanups = [];
92576
92820
  _listenersAttached = false;
92577
92821
  _onInput;
92822
+ _onCommand;
92578
92823
  _onOpenCb;
92579
92824
  _onCloseCb;
92580
92825
  _getHistory;
@@ -92590,6 +92835,7 @@ var InputPanelAddon = class InputPanelAddon {
92590
92835
  _hasOwnPersistedState;
92591
92836
  constructor(opts) {
92592
92837
  this._onInput = opts?.onInput ?? (() => {});
92838
+ this._onCommand = opts?.onCommand ?? null;
92593
92839
  this._onOpenCb = opts?.onOpen ?? null;
92594
92840
  this._onCloseCb = opts?.onClose ?? null;
92595
92841
  this._getHistory = opts?.getHistory ?? null;
@@ -92625,6 +92871,9 @@ var InputPanelAddon = class InputPanelAddon {
92625
92871
  set onInput(fn) {
92626
92872
  this._onInput = fn;
92627
92873
  }
92874
+ set onCommand(fn) {
92875
+ this._onCommand = fn;
92876
+ }
92628
92877
  setPlatform(platform) {
92629
92878
  this._platform = platform;
92630
92879
  this._applyPlatformToPanel();
@@ -92781,6 +93030,18 @@ var InputPanelAddon = class InputPanelAddon {
92781
93030
  }).catch(() => {});
92782
93031
  }
92783
93032
  });
93033
+ this._on(panel, "input-panel:command", (e) => {
93034
+ const detail = e.detail;
93035
+ if (!isRecord$1(detail)) return;
93036
+ const command = detail.command;
93037
+ if (!isInputPanelCommand(command)) return;
93038
+ const fallbackData = typeof detail.fallbackData === "string" ? detail.fallbackData : void 0;
93039
+ Promise.resolve(this._onCommand?.(command, { fallbackData }) ?? false).then((handled) => {
93040
+ if (!handled && fallbackData) this._onInput(fallbackData);
93041
+ }).catch(() => {
93042
+ if (fallbackData) this._onInput(fallbackData);
93043
+ });
93044
+ });
92784
93045
  this._on(inputTab, "input-panel:input-change", (e) => {
92785
93046
  const value = e.detail?.value;
92786
93047
  if (typeof value === "string") {
@@ -92998,9 +93259,7 @@ var InputPanelAddon = class InputPanelAddon {
92998
93259
  _resolveTarget(clientX, clientY) {
92999
93260
  const container = this._getTerminalHostElement();
93000
93261
  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;
93262
+ return resolveTerminalPointerTarget(container, clientX, clientY);
93004
93263
  }
93005
93264
  _dispatchMouse(type, opts = {}) {
93006
93265
  const coords = this._getClientCoords();
@@ -93155,6 +93414,55 @@ var InputPanelAddon = class InputPanelAddon {
93155
93414
  }
93156
93415
  };
93157
93416
  //#endregion
93417
+ //#region src/lib/google-font-loader.ts
93418
+ function getPreferredGoogleFontsHosts(lang) {
93419
+ const useCnCdn = /^zh\b/i.test(lang);
93420
+ return {
93421
+ apiHost: useCnCdn ? "https://fonts.googleapis.cn" : "https://fonts.googleapis.com",
93422
+ staticHost: useCnCdn ? "https://fonts.gstatic.cn" : "https://fonts.gstatic.com"
93423
+ };
93424
+ }
93425
+ function encodeGoogleFontFamily(family) {
93426
+ return family.trim().replace(/ /g, "+");
93427
+ }
93428
+ function buildGoogleFontsStylesheetHref(options) {
93429
+ const families = options.families.map(encodeGoogleFontFamily).filter(Boolean);
93430
+ const { apiHost } = getPreferredGoogleFontsHosts(options.lang ?? "");
93431
+ const familyParams = families.map((family) => `family=${family}`);
93432
+ const displayParam = `display=${options.display ?? "swap"}`;
93433
+ return `${apiHost}/css2?${[...familyParams, displayParam].join("&")}`;
93434
+ }
93435
+ /**
93436
+ * Legacy Google Fonts loader retained for optional large CJK/JCK font families.
93437
+ * The app shell uses local @fontsource assets by default.
93438
+ */
93439
+ function loadGoogleFontsStylesheet(options) {
93440
+ const documentRef = options.documentRef ?? (typeof document === "undefined" ? null : document);
93441
+ if (!documentRef || options.families.length === 0) return null;
93442
+ const lang = options.lang ?? (typeof navigator === "undefined" ? "" : navigator.languages && navigator.languages[0] || navigator.language || navigator.userLanguage || "");
93443
+ const hosts = getPreferredGoogleFontsHosts(lang);
93444
+ const href = buildGoogleFontsStylesheetHref({
93445
+ ...options,
93446
+ lang
93447
+ });
93448
+ if (documentRef.head.querySelector(`link[href="${href}"]`)) return null;
93449
+ const preconnectApi = documentRef.createElement("link");
93450
+ preconnectApi.rel = "preconnect";
93451
+ preconnectApi.href = hosts.apiHost;
93452
+ preconnectApi.dataset.openspecGoogleFonts = "preconnect-api";
93453
+ const preconnectStatic = documentRef.createElement("link");
93454
+ preconnectStatic.rel = "preconnect";
93455
+ preconnectStatic.href = hosts.staticHost;
93456
+ preconnectStatic.crossOrigin = "anonymous";
93457
+ preconnectStatic.dataset.openspecGoogleFonts = "preconnect-static";
93458
+ const stylesheet = documentRef.createElement("link");
93459
+ stylesheet.rel = "stylesheet";
93460
+ stylesheet.href = href;
93461
+ stylesheet.dataset.openspecGoogleFonts = "stylesheet";
93462
+ documentRef.head.append(preconnectApi, preconnectStatic, stylesheet);
93463
+ return stylesheet;
93464
+ }
93465
+ //#endregion
93158
93466
  //#region src/lib/user-gesture-audio-context.ts
93159
93467
  var audioContext = null;
93160
93468
  var audioContextPromise = null;
@@ -93558,6 +93866,146 @@ var TerminalInputHistoryStore = class {
93558
93866
  }
93559
93867
  };
93560
93868
  //#endregion
93869
+ //#region src/lib/terminal-keybindings.ts
93870
+ function isKeydown(event) {
93871
+ return event.type === "keydown";
93872
+ }
93873
+ function keyEquals(event, key) {
93874
+ return event.key.toLowerCase() === key;
93875
+ }
93876
+ function hasPrimaryModifier(event) {
93877
+ return event.metaKey || event.ctrlKey;
93878
+ }
93879
+ function hasPlainPrimaryModifier(event) {
93880
+ return hasPrimaryModifier(event) && !event.altKey;
93881
+ }
93882
+ function resolveClipboard(context) {
93883
+ if (context.clipboard) return context.clipboard;
93884
+ if (typeof navigator === "undefined") return null;
93885
+ return navigator.clipboard ?? null;
93886
+ }
93887
+ function runAsync(task, context) {
93888
+ task.catch((error) => {
93889
+ context.onAsyncError?.(error);
93890
+ });
93891
+ }
93892
+ async function copySelection(context, selection) {
93893
+ const clipboard = resolveClipboard(context);
93894
+ if (!clipboard?.writeText) return;
93895
+ await clipboard.writeText(selection);
93896
+ }
93897
+ async function pasteClipboard(context) {
93898
+ const clipboard = resolveClipboard(context);
93899
+ if (!clipboard?.readText) return;
93900
+ const text = await clipboard.readText();
93901
+ if (!text) return;
93902
+ try {
93903
+ context.terminal.paste?.(text);
93904
+ if (context.terminal.paste) return;
93905
+ } catch {}
93906
+ context.writeInput(text);
93907
+ }
93908
+ var defaultTerminalKeybindings = [
93909
+ {
93910
+ id: "terminal.copy-selection",
93911
+ handle: (event, context) => {
93912
+ if (!isKeydown(event) || !hasPlainPrimaryModifier(event) || !keyEquals(event, "c")) return null;
93913
+ if (!context.terminal.hasSelection?.()) return "allow";
93914
+ return copySelectionCommand(context);
93915
+ }
93916
+ },
93917
+ {
93918
+ id: "terminal.paste-clipboard",
93919
+ handle: (event, context) => {
93920
+ if (!isKeydown(event) || !hasPlainPrimaryModifier(event) || !keyEquals(event, "v")) return null;
93921
+ return pasteClipboardCommand(context);
93922
+ }
93923
+ },
93924
+ {
93925
+ id: "terminal.select-all",
93926
+ handle: (event, context) => {
93927
+ if (!isKeydown(event) || !event.metaKey || event.ctrlKey || event.altKey) return null;
93928
+ if (!keyEquals(event, "a")) return null;
93929
+ return selectAllCommand(context);
93930
+ }
93931
+ },
93932
+ {
93933
+ id: "terminal.zoom-in",
93934
+ handle: (event, context) => {
93935
+ if (!isKeydown(event) || !hasPrimaryModifier(event)) return null;
93936
+ if (event.key !== "=" && event.key !== "+") return null;
93937
+ context.zoomFont(1);
93938
+ return "block";
93939
+ }
93940
+ },
93941
+ {
93942
+ id: "terminal.zoom-out",
93943
+ handle: (event, context) => {
93944
+ if (!isKeydown(event) || !hasPrimaryModifier(event) || event.key !== "-") return null;
93945
+ context.zoomFont(-1);
93946
+ return "block";
93947
+ }
93948
+ },
93949
+ {
93950
+ id: "terminal.zoom-reset",
93951
+ handle: (event, context) => {
93952
+ if (!isKeydown(event) || !hasPrimaryModifier(event) || event.key !== "0") return null;
93953
+ context.resetFontSize();
93954
+ return "block";
93955
+ }
93956
+ }
93957
+ ];
93958
+ function copySelectionCommand(context) {
93959
+ if (!context.terminal.hasSelection?.()) return "allow";
93960
+ const selection = context.terminal.getSelection?.() ?? "";
93961
+ if (!selection) return "allow";
93962
+ runAsync(copySelection(context, selection), context);
93963
+ return "block";
93964
+ }
93965
+ function pasteClipboardCommand(context) {
93966
+ if (!resolveClipboard(context)?.readText) return "allow";
93967
+ runAsync(pasteClipboard(context), context);
93968
+ return "block";
93969
+ }
93970
+ function selectAllCommand(context) {
93971
+ if (!context.terminal.selectAll) return "allow";
93972
+ context.terminal.selectAll();
93973
+ return "block";
93974
+ }
93975
+ var defaultTerminalCommandBindings = [
93976
+ {
93977
+ id: "terminal.command.copy-selection",
93978
+ command: "copy",
93979
+ run: copySelectionCommand
93980
+ },
93981
+ {
93982
+ id: "terminal.command.paste-clipboard",
93983
+ command: "paste",
93984
+ run: pasteClipboardCommand
93985
+ },
93986
+ {
93987
+ id: "terminal.command.select-all",
93988
+ command: "select-all",
93989
+ run: selectAllCommand
93990
+ }
93991
+ ];
93992
+ var TerminalKeybindingRegistry = class {
93993
+ constructor(bindings = defaultTerminalKeybindings, commandBindings = defaultTerminalCommandBindings) {
93994
+ this.bindings = bindings;
93995
+ this.commandBindings = commandBindings;
93996
+ }
93997
+ handleKeyEvent(event, context) {
93998
+ for (const binding of this.bindings) {
93999
+ const result = binding.handle(event, context);
94000
+ if (result) return result;
94001
+ }
94002
+ return "allow";
94003
+ }
94004
+ runCommand(command, context) {
94005
+ return this.commandBindings.find((item) => item.command === command)?.run(context) ?? "allow";
94006
+ }
94007
+ };
94008
+ //#endregion
93561
94009
  //#region src/lib/terminal-theme.ts
93562
94010
  var TERMINAL_THEME_REGISTRY = {
93563
94011
  "default-light": {
@@ -93747,7 +94195,7 @@ function resolveTerminalTheme(input) {
93747
94195
  }
93748
94196
  //#endregion
93749
94197
  //#region src/lib/terminal-controller.ts
93750
- var DEFAULT_FONT_FAMILY = "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace";
94198
+ var DEFAULT_FONT_FAMILY = "\"JetBrains Mono Variable\", \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace";
93751
94199
  var DEFAULT_TERMINAL_CONFIG = {
93752
94200
  fontSize: 13,
93753
94201
  fontFamily: "",
@@ -93796,6 +94244,10 @@ var GOOGLE_FONT_PRESETS = [
93796
94244
  "Ubuntu Mono",
93797
94245
  "Space Mono"
93798
94246
  ];
94247
+ var LOCAL_FONT_FAMILY_ALIASES = {
94248
+ "JetBrains Mono": "\"JetBrains Mono Variable\"",
94249
+ "Share Tech Mono": "\"Share Tech Mono\""
94250
+ };
93799
94251
  /** Already-injected font sources, used for deduplication */
93800
94252
  var loadedFontSources = /* @__PURE__ */ new Set();
93801
94253
  /** Extract a font name from a URL pathname (strip extension, replace dashes with spaces) */
@@ -93844,13 +94296,8 @@ async function loadFontSource(source) {
93844
94296
  function loadGoogleFont(fontName) {
93845
94297
  const key = `google:${fontName}`;
93846
94298
  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);
94299
+ const link = loadGoogleFontsStylesheet({ families: [fontName] });
94300
+ if (link) link.dataset.fontSrc = key;
93854
94301
  loadedFontSources.add(key);
93855
94302
  }
93856
94303
  var TerminalController = class {
@@ -93862,6 +94309,7 @@ var TerminalController = class {
93862
94309
  snapshotCache = null;
93863
94310
  inputHistoryStore = new TerminalInputHistoryStore();
93864
94311
  bellSoundEngine = new TerminalBellSoundEngine();
94312
+ keybindings = new TerminalKeybindingRegistry();
93865
94313
  ghosttyModule = null;
93866
94314
  ghosttyInitPromise = null;
93867
94315
  appDarkMode = false;
@@ -94036,6 +94484,7 @@ var TerminalController = class {
94036
94484
  createInputPanelAddon(sessionId, platform) {
94037
94485
  return new InputPanelAddon({
94038
94486
  onInput: (data) => this.writeToSession(sessionId, data),
94487
+ onCommand: (command) => this.runInputPanelCommand(sessionId, command),
94039
94488
  getHistory: async () => this.inputHistoryStore.list(),
94040
94489
  addHistory: async (text) => this.inputHistoryStore.add(text),
94041
94490
  subscribeHistory: (listener) => this.inputHistoryStore.subscribe(listener),
@@ -94048,6 +94497,17 @@ var TerminalController = class {
94048
94497
  }
94049
94498
  });
94050
94499
  }
94500
+ runInputPanelCommand(sessionId, command) {
94501
+ const instance = this.instances.get(sessionId);
94502
+ if (!instance) return false;
94503
+ return this.keybindings.runCommand(command, {
94504
+ terminal: instance.terminal,
94505
+ writeInput: (data) => this.writeToSession(sessionId, data),
94506
+ zoomFont: (delta) => this.zoomFont(delta),
94507
+ resetFontSize: () => this.resetFontSize(),
94508
+ onAsyncError: (error) => console.error("[terminal] input panel command failed:", error)
94509
+ }) === "block";
94510
+ }
94051
94511
  applyGhosttyBackground(instance, container) {
94052
94512
  if (instance.rendererEngine !== "ghostty") return;
94053
94513
  const currentTheme = instance.terminal.options.theme ?? {};
@@ -94092,20 +94552,18 @@ var TerminalController = class {
94092
94552
  }
94093
94553
  const mod = event.metaKey || event.ctrlKey;
94094
94554
  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;
94555
+ const keybindingResult = this.keybindings.handleKeyEvent(event, {
94556
+ terminal,
94557
+ writeInput,
94558
+ zoomFont: (delta) => this.zoomFont(delta),
94559
+ resetFontSize: () => this.resetFontSize(),
94560
+ onAsyncError: (error) => console.error("[terminal] keybinding failed:", error)
94561
+ });
94562
+ if (keybindingResult === "block") {
94563
+ event.preventDefault();
94564
+ event.stopPropagation();
94107
94565
  }
94108
- return this.getKeyEventResult(engine, handled ? "block" : "allow");
94566
+ return this.getKeyEventResult(engine, keybindingResult);
94109
94567
  });
94110
94568
  }
94111
94569
  bindTerminalInput(instance) {
@@ -94383,12 +94841,16 @@ var TerminalController = class {
94383
94841
  }
94384
94842
  const entries = raw.split(/[,]+/).map((s) => s.trim()).filter(Boolean);
94385
94843
  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);
94844
+ for (const entry of entries) {
94845
+ const localFontFamily = LOCAL_FONT_FAMILY_ALIASES[entry];
94846
+ if (localFontFamily) resolved.push(localFontFamily);
94847
+ else if (GOOGLE_FONT_PRESETS.includes(entry)) {
94848
+ loadGoogleFont(entry);
94849
+ resolved.push(entry);
94850
+ } else {
94851
+ const name = await loadFontSource(entry);
94852
+ if (name) resolved.push(name);
94853
+ }
94392
94854
  }
94393
94855
  resolved.push(DEFAULT_FONT_FAMILY);
94394
94856
  this._setFontFamily(resolved.join(", "));
@@ -95376,7 +95838,7 @@ function Dialog({ open, title, headerActions, onClose, onDismissRequest, onClose
95376
95838
  className: `flex h-full w-full items-center justify-center px-4 py-4 ${contentClassName} ${contentShellClassName}`,
95377
95839
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
95378
95840
  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}`,
95841
+ 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
95842
  style: { maxHeight },
95381
95843
  children: [
95382
95844
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -114711,6 +115173,25 @@ function navigateHashAnchor(anchorElement, hash) {
114711
115173
  }
114712
115174
  //#endregion
114713
115175
  //#region ../browser-translator/dist/index.mjs
115176
+ var DEFAULT_SOURCE_LANGUAGE$1 = "en";
115177
+ async function probeBrowserTranslator(targetLanguage, sourceLanguage = DEFAULT_SOURCE_LANGUAGE$1, win = window) {
115178
+ const translator = win.Translator;
115179
+ if (!translator) return {
115180
+ availability: "missing",
115181
+ message: "Browser Translator API is not exposed."
115182
+ };
115183
+ try {
115184
+ return { availability: normalizeAvailability$1(await translator.availability({
115185
+ sourceLanguage,
115186
+ targetLanguage
115187
+ })) };
115188
+ } catch (error) {
115189
+ return {
115190
+ availability: "error",
115191
+ message: getErrorMessage$1(error)
115192
+ };
115193
+ }
115194
+ }
114714
115195
  async function scanBrowserTranslationSupportTable(options) {
114715
115196
  const translator = (options.win ?? window).Translator;
114716
115197
  if (!translator) return {
@@ -114768,6 +115249,63 @@ async function scanBrowserTranslationSupportTable(options) {
114768
115249
  rows
114769
115250
  };
114770
115251
  }
115252
+ async function prepareBrowserTranslator(targetLanguage, options) {
115253
+ const sourceLanguage = options.sourceLanguage ?? DEFAULT_SOURCE_LANGUAGE$1;
115254
+ const win = options.win ?? window;
115255
+ const translator = win.Translator;
115256
+ if (!translator) return {
115257
+ availability: "missing",
115258
+ message: "Browser Translator API is not exposed."
115259
+ };
115260
+ try {
115261
+ const initialStatus = await probeBrowserTranslator(targetLanguage, sourceLanguage, win);
115262
+ if (initialStatus.availability === "missing" || initialStatus.availability === "unavailable" || initialStatus.availability === "error") {
115263
+ options.onStatus?.(initialStatus);
115264
+ return initialStatus;
115265
+ }
115266
+ if (initialStatus.availability === "available") {
115267
+ options.onStatus?.(initialStatus);
115268
+ return initialStatus;
115269
+ }
115270
+ options.onStatus?.({
115271
+ availability: "downloading",
115272
+ message: "Downloading browser translation support."
115273
+ });
115274
+ (await raceAbort$1(translator.create({
115275
+ sourceLanguage,
115276
+ targetLanguage,
115277
+ signal: options.signal,
115278
+ monitor: (monitor) => monitorDownload(monitor, { setStatus(input) {
115279
+ options.onStatus?.({
115280
+ availability: "downloading",
115281
+ progress: input.progress,
115282
+ message: input.message
115283
+ });
115284
+ } })
115285
+ }), options.signal)).destroy?.();
115286
+ const finalStatus = {
115287
+ availability: "available",
115288
+ message: "Browser translator is ready."
115289
+ };
115290
+ options.onStatus?.(finalStatus);
115291
+ return finalStatus;
115292
+ } catch (error) {
115293
+ if (options.signal.aborted) {
115294
+ const cancelledStatus = {
115295
+ availability: "downloadable",
115296
+ message: "Browser translation download was cancelled."
115297
+ };
115298
+ options.onStatus?.(cancelledStatus);
115299
+ return cancelledStatus;
115300
+ }
115301
+ const failureStatus = {
115302
+ availability: "error",
115303
+ message: getErrorMessage$1(error)
115304
+ };
115305
+ options.onStatus?.(failureStatus);
115306
+ return failureStatus;
115307
+ }
115308
+ }
114771
115309
  var BrowserTranslatorFactory = class {
114772
115310
  constructor(win = window) {
114773
115311
  this.win = win;
@@ -114783,10 +115321,20 @@ var BrowserTranslatorFactory = class {
114783
115321
  }), options.signal);
114784
115322
  return {
114785
115323
  async *batchTranslate(inputs, batchOptions) {
114786
- for (const [index, input] of inputs.entries()) yield {
114787
- index,
114788
- output: await native.translate(input, batchOptions)
114789
- };
115324
+ for (const [index, input] of inputs.entries()) {
115325
+ const controlled = await runControlledTranslationTask((signal) => native.translate(input, { signal }), batchOptions);
115326
+ if (controlled.ok) {
115327
+ yield {
115328
+ index,
115329
+ output: controlled.value
115330
+ };
115331
+ continue;
115332
+ }
115333
+ yield {
115334
+ index,
115335
+ error: controlled.error
115336
+ };
115337
+ }
114790
115338
  },
114791
115339
  destroy() {
114792
115340
  native.destroy?.();
@@ -136453,8 +137001,22 @@ buildSearchIndex(SUPPORTED_TRANSLATION_LANGUAGES.map((language) => ({
136453
137001
  //#endregion
136454
137002
  //#region src/lib/browser-translation.ts
136455
137003
  var browserSupportTableCache = /* @__PURE__ */ new Map();
137004
+ function isFiniteTranslationOffset(value) {
137005
+ return typeof value === "number" && Number.isFinite(value);
137006
+ }
137007
+ function isTranslationSegmentKind(value) {
137008
+ return value === "heading" || value === "listItem" || value === "paragraph" || value === "blockquote" || value === "text";
137009
+ }
136456
137010
  function isRenderableTranslationSegment(segment) {
136457
- return typeof segment === "object" && segment !== null && !Array.isArray(segment);
137011
+ if (typeof segment !== "object" || segment === null || Array.isArray(segment)) return false;
137012
+ const id = Reflect.get(segment, "id");
137013
+ const sourceStartOffset = Reflect.get(segment, "sourceStartOffset");
137014
+ const sourceEndOffset = Reflect.get(segment, "sourceEndOffset");
137015
+ const sourceKind = Reflect.get(segment, "sourceKind");
137016
+ const source = Reflect.get(segment, "source");
137017
+ const translatorInput = Reflect.get(segment, "translatorInput");
137018
+ const kind = Reflect.get(segment, "kind");
137019
+ return typeof id === "string" && isFiniteTranslationOffset(sourceStartOffset) && isFiniteTranslationOffset(sourceEndOffset) && typeof sourceKind === "string" && typeof source === "string" && typeof translatorInput === "string" && isTranslationSegmentKind(kind);
136458
137020
  }
136459
137021
  function createBrowserTranslationExecution() {
136460
137022
  return {
@@ -136474,6 +137036,18 @@ var SUPPORTED_TRANSLATION_LANGUAGE_CODES = new Set(SUPPORTED_TRANSLATION_LANGUAG
136474
137036
  function isBrowserTranslationSupported() {
136475
137037
  return typeof window !== "undefined" && !!window.Translator;
136476
137038
  }
137039
+ async function prepareBrowserTranslation(targetLanguage, input) {
137040
+ if (typeof window === "undefined") return {
137041
+ availability: "missing",
137042
+ message: "Browser translation is not available."
137043
+ };
137044
+ return prepareBrowserTranslator(targetLanguage, {
137045
+ sourceLanguage: input.sourceLanguage ?? DEFAULT_SOURCE_LANGUAGE,
137046
+ signal: input.signal,
137047
+ onStatus: input.onStatus,
137048
+ win: window
137049
+ });
137050
+ }
136477
137051
  function getBrowserSupportTableState(targetLanguage) {
136478
137052
  return browserSupportTableCache.get(normalizeBrowserSupportTargetKey(targetLanguage)) ?? null;
136479
137053
  }
@@ -136782,6 +137356,7 @@ async function translateMarkdownDocumentProgressively(args, onPatch) {
136782
137356
  sourceLanguage,
136783
137357
  targetLanguage: args.targetLanguage,
136784
137358
  signal: args.signal,
137359
+ timeoutMs: args.timeoutMs,
136785
137360
  cache: args.cache,
136786
137361
  jobs,
136787
137362
  translatedSegments,
@@ -136867,9 +137442,25 @@ async function translatePendingJobsBySourceLanguage(input) {
136867
137442
  startWorkersToDesired();
136868
137443
  }
136869
137444
  };
136870
- const applyBatchResult = async (batch, outputs) => {
137445
+ const applyBatchResult = async (batch, result) => {
136871
137446
  for (const [offset, job] of batch.jobs.entries()) {
136872
- const target = outputs[offset] ?? "";
137447
+ const error = result.errors.get(offset);
137448
+ if (error) {
137449
+ const failedSegment = {
137450
+ ...job.segment,
137451
+ sourceLanguage: job.sourceLanguage,
137452
+ targetLanguage: input.targetLanguage,
137453
+ status: "error",
137454
+ error
137455
+ };
137456
+ input.translatedSegments[job.segmentIndex] = failedSegment;
137457
+ input.onPatch({
137458
+ segmentIndex: job.segmentIndex,
137459
+ segment: failedSegment
137460
+ });
137461
+ continue;
137462
+ }
137463
+ const target = result.outputs.get(offset) ?? "";
136873
137464
  const restoredTarget = job.segment.placeholderProtocol ? restoreTranslatedPlaceholderFragment(target, job.segment.placeholderProtocol) : { target: job.protectedInput.restore(target).trim() };
136874
137465
  const translatedSegment = {
136875
137466
  ...job.segment,
@@ -136908,7 +137499,10 @@ async function translatePendingJobsBySourceLanguage(input) {
136908
137499
  const batch = batches[batchIndex];
136909
137500
  const startedAt = getCurrentTimeMs();
136910
137501
  try {
136911
- await applyBatchResult(batch, await collectBatchTranslationOutputs(translator.batchTranslate(batch.jobs.map((job) => job.protectedInput.text), { signal: input.signal }), batch.jobs.length));
137502
+ await applyBatchResult(batch, await collectBatchTranslationOutputs(translator.batchTranslate(batch.jobs.map((job) => job.protectedInput.text), {
137503
+ signal: input.signal,
137504
+ timeoutMs: input.timeoutMs ?? 15e3
137505
+ }), batch.jobs.length));
136912
137506
  completedBatches += 1;
136913
137507
  const elapsedMs = Math.max(1, getCurrentTimeMs() - startedAt);
136914
137508
  appendTranslationAdaptiveConcurrencyLog({
@@ -136948,6 +137542,83 @@ async function translatePendingJobsBySourceLanguage(input) {
136948
137542
  startWorkersToDesired();
136949
137543
  while (workerPromises.size > 0) await Promise.race(workerPromises);
136950
137544
  }
137545
+ async function retryTranslationSegment(input) {
137546
+ const engine = input.engine ?? createBrowserTranslationExecution();
137547
+ const sourceLanguage = input.sourceLanguage ?? input.segment.sourceLanguage ?? DEFAULT_SOURCE_LANGUAGE;
137548
+ if (areEquivalentTranslationLanguages(sourceLanguage, input.targetLanguage)) return {
137549
+ ...input.segment,
137550
+ target: input.segment.source,
137551
+ sourceLanguage,
137552
+ targetLanguage: input.targetLanguage,
137553
+ status: "translated",
137554
+ error: void 0
137555
+ };
137556
+ const unsupportedLanguagePairMessage = getUnsupportedEngineLanguagePairMessage({
137557
+ engine,
137558
+ sourceLanguage,
137559
+ targetLanguage: input.targetLanguage
137560
+ });
137561
+ if (unsupportedLanguagePairMessage) return {
137562
+ ...input.segment,
137563
+ sourceLanguage,
137564
+ targetLanguage: input.targetLanguage,
137565
+ status: "error",
137566
+ error: unsupportedLanguagePairMessage
137567
+ };
137568
+ const cacheKey = createSegmentCacheKey(input.segment, sourceLanguage, input.targetLanguage, engine.cacheIdentity);
137569
+ const cachedSegment = cacheKey ? await readCachedTranslationSegment(input.cache, cacheKey, input.segment, {
137570
+ sourceLanguage,
137571
+ targetLanguage: input.targetLanguage
137572
+ }) : null;
137573
+ if (cachedSegment) return cachedSegment;
137574
+ const protectedInput = input.segment.placeholderProtocol ? {
137575
+ text: input.segment.translatorInput,
137576
+ restore: (output) => output
137577
+ } : protectTranslatorInput(input.segment.translatorInput);
137578
+ let translator = null;
137579
+ try {
137580
+ translator = await engine.factory.create({
137581
+ sourceLanguage,
137582
+ targetLanguage: input.targetLanguage,
137583
+ signal: input.signal
137584
+ });
137585
+ const result = await collectBatchTranslationOutputs(translator.batchTranslate([protectedInput.text], {
137586
+ signal: input.signal,
137587
+ timeoutMs: input.timeoutMs ?? 15e3
137588
+ }), 1);
137589
+ const error = result.errors.get(0);
137590
+ if (error) return {
137591
+ ...input.segment,
137592
+ sourceLanguage,
137593
+ targetLanguage: input.targetLanguage,
137594
+ status: "error",
137595
+ error
137596
+ };
137597
+ const target = result.outputs.get(0) ?? "";
137598
+ const restoredTarget = input.segment.placeholderProtocol ? restoreTranslatedPlaceholderFragment(target, input.segment.placeholderProtocol) : { target: protectedInput.restore(target).trim() };
137599
+ const translatedSegment = {
137600
+ ...input.segment,
137601
+ ...restoredTarget,
137602
+ sourceLanguage,
137603
+ targetLanguage: input.targetLanguage,
137604
+ status: "translated",
137605
+ error: void 0
137606
+ };
137607
+ if (cacheKey) writeCachedTranslationSegment(input.cache, cacheKey, translatedSegment);
137608
+ return translatedSegment;
137609
+ } catch (error) {
137610
+ if (input.signal.aborted) throw error;
137611
+ return {
137612
+ ...input.segment,
137613
+ sourceLanguage,
137614
+ targetLanguage: input.targetLanguage,
137615
+ status: "error",
137616
+ error: getErrorMessage(error)
137617
+ };
137618
+ } finally {
137619
+ translator?.destroy?.();
137620
+ }
137621
+ }
136951
137622
  function getUnsupportedEngineLanguagePairMessage(input) {
136952
137623
  if (!isDirectionalManagedLocalTranslationEngineId(input.engine.cacheIdentity.engineId)) return null;
136953
137624
  const directionCheck = checkLocalDirectionalModelLanguagePair({
@@ -137012,12 +137683,17 @@ function packTranslationJobs(jobs) {
137012
137683
  }
137013
137684
  async function collectBatchTranslationOutputs(stream, expectedCount) {
137014
137685
  const outputs = /* @__PURE__ */ new Map();
137686
+ const errors = /* @__PURE__ */ new Map();
137015
137687
  for await (const item of stream) {
137016
137688
  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);
137689
+ if (item.output !== void 0 && !outputs.has(item.index)) outputs.set(item.index, item.output);
137690
+ if (item.error && !errors.has(item.index)) errors.set(item.index, item.error.message);
137018
137691
  }
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) ?? "");
137692
+ if (outputs.size + errors.size !== expectedCount) throw new Error(`Translator returned ${outputs.size + errors.size} results for ${expectedCount} inputs.`);
137693
+ return {
137694
+ outputs,
137695
+ errors
137696
+ };
137021
137697
  }
137022
137698
  function estimateTranslationTokens(input) {
137023
137699
  const trimmed = input.trim();
@@ -137245,6 +137921,7 @@ function toMarkdownFactKindFromHast(tagName) {
137245
137921
  if (/^h[1-6]$/.test(tagName)) return "heading";
137246
137922
  if (tagName === "li") return "listItem";
137247
137923
  if (tagName === "blockquote") return "blockquote";
137924
+ if (tagName === "td" || tagName === "th") return "tableCell";
137248
137925
  return "paragraph";
137249
137926
  }
137250
137927
  function toTranslationSegmentKindFromHast(tagName) {
@@ -137561,8 +138238,14 @@ function useDocumentTranslationActivation() {
137561
138238
  }
137562
138239
  //#endregion
137563
138240
  //#region src/lib/resolve-document-translation-config.ts
137564
- function resolveDocumentTranslationConfig(translationConfig, globalSettings) {
138241
+ function resolveDocumentTranslationConfig(translationConfig, globalSettings, configPresence) {
137565
138242
  if (!translationConfig) return void 0;
138243
+ const translationPresence = configPresence?.translation;
138244
+ const projectOwnsEnabled = translationPresence?.enabled ?? false;
138245
+ const projectOwnsTargetLanguage = translationPresence?.targetLanguage ?? false;
138246
+ const projectOwnsDisplayMode = translationPresence?.displayMode ?? false;
138247
+ const projectOwnsCacheEnabled = translationPresence?.cacheEnabled ?? false;
138248
+ const projectOwnsEngineId = translationPresence?.engineId ?? false;
137566
138249
  const local = translationConfig.engines?.local ?? {};
137567
138250
  const localCt2 = translationConfig.engines?.localCt2 ?? {};
137568
138251
  const localLlama = translationConfig.engines?.localLlama ?? {};
@@ -137576,6 +138259,11 @@ function resolveDocumentTranslationConfig(translationConfig, globalSettings) {
137576
138259
  const resolvedOpenAIModel = openai.model ?? globalSettings?.translationEngines.openai?.model;
137577
138260
  return {
137578
138261
  ...translationConfig,
138262
+ enabled: projectOwnsEnabled ? translationConfig.enabled : globalSettings?.translation?.enabled ?? translationConfig.enabled,
138263
+ targetLanguage: projectOwnsTargetLanguage ? translationConfig.targetLanguage : globalSettings?.translation?.targetLanguage ?? translationConfig.targetLanguage,
138264
+ displayMode: projectOwnsDisplayMode ? translationConfig.displayMode : globalSettings?.translation?.displayMode ?? translationConfig.displayMode,
138265
+ cacheEnabled: projectOwnsCacheEnabled ? translationConfig.cacheEnabled : globalSettings?.translation?.cacheEnabled ?? translationConfig.cacheEnabled,
138266
+ engineId: projectOwnsEngineId ? translationConfig.engineId : globalSettings?.translationEngines.engineId ?? translationConfig.engineId,
137579
138267
  engines: {
137580
138268
  local: {
137581
138269
  ...local,
@@ -137646,12 +138334,20 @@ function projectTranslateServiceStatus(input) {
137646
138334
  message: input.browserCapabilityLoading ? "Browser translation capability is being checked." : "Browser translator will be checked before translation starts."
137647
138335
  };
137648
138336
  switch (input.browserCapability.availability) {
137649
- case "available":
137650
- case "downloadable":
138337
+ case "available": return {
138338
+ state: "ready",
138339
+ engineId: "browser",
138340
+ message: input.browserCapability.message ?? "Browser translator is ready."
138341
+ };
138342
+ case "downloadable": return {
138343
+ state: "ready",
138344
+ engineId: "browser",
138345
+ message: input.browserCapability.message ?? "Browser translation support will be downloaded when translation starts."
138346
+ };
137651
138347
  case "downloading": return {
137652
138348
  state: "ready",
137653
138349
  engineId: "browser",
137654
- message: "Browser translator is ready."
138350
+ message: input.browserCapability.message ?? "Browser translation support is downloading and will continue when ready."
137655
138351
  };
137656
138352
  case "missing":
137657
138353
  case "unavailable":
@@ -137673,6 +138369,11 @@ function projectTranslateServiceStatus(input) {
137673
138369
  engineId: input.engineId,
137674
138370
  message: getTranslationEngineLifecycleMessage(input.engineLifecycle) ?? "Translation engine runtime is not ready."
137675
138371
  };
138372
+ if (input.engineLifecycle.assets.state === "error") return {
138373
+ state: "unavailable",
138374
+ engineId: input.engineId,
138375
+ message: input.engineLifecycle.assets.message ?? input.engineLifecycle.assets.error ?? "Selected local model is incompatible with the current translation runtime."
138376
+ };
137676
138377
  }
137677
138378
  if (isManagedLocalTranslationEngineId(input.engineId)) {
137678
138379
  if (!input.localModel?.trim()) return {
@@ -137715,6 +138416,7 @@ function isLocalAssetReady(asset, selectedGroupId) {
137715
138416
  }
137716
138417
  //#endregion
137717
138418
  //#region src/lib/translate-service.ts
138419
+ var DEFAULT_BROWSER_SOURCE_LANGUAGE = "en";
137718
138420
  async function resolveTranslateServiceState(input) {
137719
138421
  const config = input.config;
137720
138422
  if (!config?.enabled || !input.hasSource) return emitTranslateServiceState(input.onUpdate, { status: projectTranslateServiceStatus({
@@ -137876,7 +138578,7 @@ function prepareTranslateServiceRun(input) {
137876
138578
  hasSource: input.hasSource,
137877
138579
  engineId: input.config.engineId
137878
138580
  }) });
137879
- const preferredRow = input.browserSupportTable?.table?.rows.find((row) => row.availability === "available") ?? input.browserSupportTable?.table?.rows.find((row) => row.availability === "downloading") ?? input.browserSupportTable?.table?.rows.find((row) => row.availability === "downloadable") ?? null;
138581
+ const preferredRow = selectPreferredBrowserSupportRow(input.browserSupportTable);
137880
138582
  if (!preferredRow) return createTranslateServiceState({
137881
138583
  browserSupportTable: input.browserSupportTable,
137882
138584
  status: projectTranslateServiceStatus({
@@ -137904,6 +138606,51 @@ function prepareTranslateServiceRun(input) {
137904
138606
  })
137905
138607
  });
137906
138608
  }
138609
+ async function ensureBrowserTranslationReady(input) {
138610
+ const preferredRow = selectPreferredBrowserSupportRow(input.browserSupportTable, input.sourceLanguage);
138611
+ const sourceLanguage = input.sourceLanguage?.trim() || preferredRow?.sourceLanguage || DEFAULT_BROWSER_SOURCE_LANGUAGE;
138612
+ if (!sourceLanguage) throw new Error("No browser translation language pair is available to prepare.");
138613
+ if (preferredRow?.availability === "available" && normalizeBrowserLanguageTag(preferredRow.sourceLanguage) === normalizeBrowserLanguageTag(sourceLanguage)) return {
138614
+ capability: {
138615
+ availability: "available",
138616
+ message: preferredRow.message
138617
+ },
138618
+ browserSupportTable: input.browserSupportTable,
138619
+ sourceLanguage
138620
+ };
138621
+ let nextTable = input.browserSupportTable;
138622
+ const emit = (capability) => {
138623
+ nextTable = patchBrowserSupportTableRow(input.targetLanguage, {
138624
+ sourceLanguage,
138625
+ targetLanguage: input.targetLanguage,
138626
+ availability: capability.availability,
138627
+ progress: capability.progress,
138628
+ message: capability.message
138629
+ }, { state: capability.availability === "error" ? "error" : "ready" });
138630
+ input.onUpdate?.({
138631
+ capability,
138632
+ browserSupportTable: nextTable,
138633
+ sourceLanguage
138634
+ });
138635
+ };
138636
+ const finalStatus = await prepareBrowserTranslation(input.targetLanguage, {
138637
+ sourceLanguage,
138638
+ signal: input.signal ?? new AbortController().signal,
138639
+ onStatus: emit
138640
+ });
138641
+ if (input.signal?.aborted) throw new DOMException("The operation was aborted.", "AbortError");
138642
+ if (finalStatus.availability !== "available") throw new Error(finalStatus.message ?? "Browser translator is not ready.");
138643
+ const readyCapability = {
138644
+ availability: "available",
138645
+ message: finalStatus.message
138646
+ };
138647
+ emit(readyCapability);
138648
+ return {
138649
+ capability: readyCapability,
138650
+ browserSupportTable: nextTable,
138651
+ sourceLanguage
138652
+ };
138653
+ }
137907
138654
  function createTranslationEngineExecution(config) {
137908
138655
  if (config.engineId === "browser" || isStaticMode()) return createBrowserTranslationExecution();
137909
138656
  const model = config.engineId === "openai" ? config.engines.openai.model : getManagedLocalEngineConfig(config).model;
@@ -137945,6 +138692,18 @@ function getManagedLocalEngineConfig(config) {
137945
138692
  selectedGroupId: config.engines.local.selectedGroupId
137946
138693
  };
137947
138694
  }
138695
+ function selectPreferredBrowserSupportRow(browserSupportTable, sourceLanguage) {
138696
+ const rows = browserSupportTable?.table?.rows ?? [];
138697
+ if (sourceLanguage) {
138698
+ const normalizedSourceLanguage = normalizeBrowserLanguageTag(sourceLanguage);
138699
+ const matchingRow = rows.find((row) => normalizeBrowserLanguageTag(row.sourceLanguage) === normalizedSourceLanguage);
138700
+ if (matchingRow) return matchingRow;
138701
+ }
138702
+ return rows.find((row) => row.availability === "available") ?? rows.find((row) => row.availability === "downloading") ?? rows.find((row) => row.availability === "downloadable") ?? null;
138703
+ }
138704
+ function normalizeBrowserLanguageTag(language) {
138705
+ return language.trim().toLowerCase();
138706
+ }
137948
138707
  async function queryManagedLocalPanelState(engineId, input) {
137949
138708
  return engineId === "local" ? trpcClient.localModels.panelState.query(input) : engineId === "local-ct2" ? trpcClient.localCt2Models.panelState.query(input) : trpcClient.localLlamaModels.panelState.query(input);
137950
138709
  }
@@ -137965,7 +138724,8 @@ var TrpcTranslator = class {
137965
138724
  selectedGroupId: this.options.selectedGroupId,
137966
138725
  inputs,
137967
138726
  instructions: options?.instructions,
137968
- context: options?.context
138727
+ context: options?.context,
138728
+ timeoutMs: options?.timeoutMs ?? 15e3
137969
138729
  }, {
137970
138730
  onData(event) {
137971
138731
  queue.push(event);
@@ -138044,8 +138804,6 @@ function useDocumentTranslation(markdown, config) {
138044
138804
  (0, import_react.useEffect)(() => reset, [reset]);
138045
138805
  (0, import_react.useEffect)(() => {
138046
138806
  generationRef.current += 1;
138047
- setCapability(null);
138048
- setBrowserSupportTable(null);
138049
138807
  segmentPatchMapRef.current.clear();
138050
138808
  setResult(null);
138051
138809
  setStatus("source");
@@ -138064,6 +138822,21 @@ function useDocumentTranslation(markdown, config) {
138064
138822
  config?.engines.openai.model,
138065
138823
  config?.targetLanguage
138066
138824
  ]);
138825
+ (0, import_react.useEffect)(() => {
138826
+ setCapability(null);
138827
+ setBrowserSupportTable(null);
138828
+ }, [
138829
+ config?.enabled,
138830
+ config?.engineId,
138831
+ config?.engines.local.model,
138832
+ config?.engines.local.selectedGroupId,
138833
+ config?.engines.localCt2.model,
138834
+ config?.engines.localCt2.selectedGroupId,
138835
+ config?.engines.localLlama.model,
138836
+ config?.engines.localLlama.selectedGroupId,
138837
+ config?.engines.openai.model,
138838
+ config?.targetLanguage
138839
+ ]);
138067
138840
  (0, import_react.useEffect)(() => {
138068
138841
  let disposed = false;
138069
138842
  const controller = new AbortController();
@@ -138118,6 +138891,7 @@ function useDocumentTranslation(markdown, config) {
138118
138891
  segmentPatchMapRef.current.clear();
138119
138892
  setError(null);
138120
138893
  setStatus("initializing");
138894
+ let restoreServiceStatus = null;
138121
138895
  try {
138122
138896
  if (serviceStatus.state !== "ready") {
138123
138897
  setError(serviceStatus.message);
@@ -138138,6 +138912,30 @@ function useDocumentTranslation(markdown, config) {
138138
138912
  setStatus("unavailable");
138139
138913
  return;
138140
138914
  }
138915
+ restoreServiceStatus = nextState.status;
138916
+ const preparedState = await ensureBrowserTranslationReady({
138917
+ targetLanguage: config.targetLanguage,
138918
+ browserSupportTable: nextState.browserSupportTable,
138919
+ signal: controller.signal,
138920
+ onUpdate: (browserState) => {
138921
+ if (controller.signal.aborted || abortRef.current !== controller || generationRef.current !== generationId) return;
138922
+ setCapability(browserState.capability);
138923
+ setBrowserSupportTable(browserState.browserSupportTable);
138924
+ setServiceStatus(browserState.capability.availability === "available" ? nextState.status : {
138925
+ state: "checking",
138926
+ engineId: "browser",
138927
+ message: browserState.capability.message ?? "Preparing browser translation support."
138928
+ });
138929
+ }
138930
+ });
138931
+ if (controller.signal.aborted || abortRef.current !== controller || generationRef.current !== generationId) return;
138932
+ setCapability(preparedState.capability);
138933
+ setBrowserSupportTable(preparedState.browserSupportTable);
138934
+ setServiceStatus({
138935
+ state: "ready",
138936
+ engineId: "browser",
138937
+ message: preparedState.capability.message ?? "Browser translator is ready."
138938
+ });
138141
138939
  }
138142
138940
  setStatus("translating");
138143
138941
  setResult({
@@ -138150,6 +138948,7 @@ function useDocumentTranslation(markdown, config) {
138150
138948
  targetLanguage: config.targetLanguage,
138151
138949
  displayMode: config.displayMode,
138152
138950
  signal: controller.signal,
138951
+ timeoutMs: DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS,
138153
138952
  engine: createTranslationEngineExecution(config),
138154
138953
  cache: config.cacheEnabled && !isStaticMode() ? {
138155
138954
  read: (keyHash) => trpcClient.translationCache.read.query({ keyHash }),
@@ -138173,6 +138972,10 @@ function useDocumentTranslation(markdown, config) {
138173
138972
  setStatus("translated");
138174
138973
  } catch (translationError) {
138175
138974
  if (controller.signal.aborted || abortRef.current !== controller || generationRef.current !== generationId) return;
138975
+ if (restoreServiceStatus?.engineId === "browser") {
138976
+ const fallbackServiceStatus = restoreServiceStatus;
138977
+ setServiceStatus((current) => current.state === "checking" ? fallbackServiceStatus : current);
138978
+ }
138176
138979
  setError(translationError instanceof Error ? translationError.message : "Translation failed.");
138177
138980
  setStatus("error");
138178
138981
  } finally {
@@ -138195,6 +138998,100 @@ function useDocumentTranslation(markdown, config) {
138195
138998
  markdown,
138196
138999
  serviceStatus
138197
139000
  ]);
139001
+ const retrySegment = (0, import_react.useCallback)(async (segmentId) => {
139002
+ if (!config?.enabled || !result) return;
139003
+ const segmentIndex = result.segments.findIndex((segment) => segment.id === segmentId);
139004
+ const segment = result.segments[segmentIndex];
139005
+ if (segmentIndex < 0 || !segment || segment.status !== "error") return;
139006
+ const controller = new AbortController();
139007
+ let restoreServiceStatus = null;
139008
+ const retryingSegment = {
139009
+ ...segment,
139010
+ error: void 0,
139011
+ status: "pending"
139012
+ };
139013
+ setResult((current) => current ? {
139014
+ ...current,
139015
+ segments: current.segments.map((entry, index) => index === segmentIndex ? retryingSegment : entry)
139016
+ } : current);
139017
+ try {
139018
+ if (config.engineId === "browser") {
139019
+ restoreServiceStatus = {
139020
+ state: "ready",
139021
+ engineId: "browser",
139022
+ message: capability?.message ?? "Browser translator is ready."
139023
+ };
139024
+ const preparedState = await ensureBrowserTranslationReady({
139025
+ targetLanguage: config.targetLanguage,
139026
+ sourceLanguage: segment.sourceLanguage,
139027
+ browserSupportTable,
139028
+ signal: controller.signal,
139029
+ onUpdate: (browserState) => {
139030
+ setCapability(browserState.capability);
139031
+ setBrowserSupportTable(browserState.browserSupportTable);
139032
+ setServiceStatus(browserState.capability.availability === "available" ? {
139033
+ state: "ready",
139034
+ engineId: "browser",
139035
+ message: browserState.capability.message ?? "Browser translator is ready."
139036
+ } : {
139037
+ state: "checking",
139038
+ engineId: "browser",
139039
+ message: browserState.capability.message ?? "Preparing browser translation support."
139040
+ });
139041
+ }
139042
+ });
139043
+ setCapability(preparedState.capability);
139044
+ setBrowserSupportTable(preparedState.browserSupportTable);
139045
+ setServiceStatus({
139046
+ state: "ready",
139047
+ engineId: "browser",
139048
+ message: preparedState.capability.message ?? "Browser translator is ready."
139049
+ });
139050
+ }
139051
+ const nextSegment = await retryTranslationSegment({
139052
+ segment,
139053
+ sourceLanguage: segment.sourceLanguage,
139054
+ targetLanguage: config.targetLanguage,
139055
+ signal: controller.signal,
139056
+ timeoutMs: DEFAULT_BATCH_TRANSLATION_TIMEOUT_MS,
139057
+ engine: createTranslationEngineExecution(config),
139058
+ cache: config.cacheEnabled && !isStaticMode() ? {
139059
+ read: (keyHash) => trpcClient.translationCache.read.query({ keyHash }),
139060
+ write: (input) => trpcClient.translationCache.write.mutate(input)
139061
+ } : void 0
139062
+ });
139063
+ setResult((current) => current ? {
139064
+ ...current,
139065
+ segments: current.segments.map((entry, index) => index === segmentIndex ? nextSegment : entry)
139066
+ } : current);
139067
+ setError((current) => {
139068
+ if (nextSegment.status === "error") return nextSegment.error ?? current;
139069
+ return getDocumentTranslationFailureMessage(buildRetriedDocumentResult(result, segmentIndex, nextSegment));
139070
+ });
139071
+ setStatus((current) => {
139072
+ if (nextSegment.status === "error") return current === "translated" ? "translated" : "error";
139073
+ return getDocumentTranslationFailureMessage(buildRetriedDocumentResult(result, segmentIndex, nextSegment)) ? "error" : "translated";
139074
+ });
139075
+ } catch (retryError) {
139076
+ if (restoreServiceStatus?.engineId === "browser") {
139077
+ const fallbackServiceStatus = restoreServiceStatus;
139078
+ setServiceStatus((current) => current.state === "checking" ? fallbackServiceStatus : current);
139079
+ }
139080
+ setResult((current) => current ? {
139081
+ ...current,
139082
+ segments: current.segments.map((entry, index) => index === segmentIndex ? {
139083
+ ...segment,
139084
+ status: "error",
139085
+ error: retryError instanceof Error ? retryError.message : "Translation retry failed."
139086
+ } : entry)
139087
+ } : current);
139088
+ }
139089
+ }, [
139090
+ browserSupportTable,
139091
+ capability?.message,
139092
+ config,
139093
+ result
139094
+ ]);
138198
139095
  (0, import_react.useEffect)(() => {
138199
139096
  latestStartRef.current = start;
138200
139097
  }, [start]);
@@ -138202,10 +139099,14 @@ function useDocumentTranslation(markdown, config) {
138202
139099
  if (activation !== "translated" || !config?.enabled || markdown.length === 0) return;
138203
139100
  if (status !== "source") return;
138204
139101
  if (serviceStatus.state !== "ready") return;
139102
+ if (config.engineId === "browser" && !canAutoStartBrowserTranslation(capability, browserSupportTable)) return;
138205
139103
  latestStartRef.current?.();
138206
139104
  }, [
138207
139105
  activation,
139106
+ browserSupportTable,
139107
+ capability,
138208
139108
  config?.enabled,
139109
+ config?.engineId,
138209
139110
  markdown.length,
138210
139111
  serviceStatus.state,
138211
139112
  status
@@ -138217,12 +139118,13 @@ function useDocumentTranslation(markdown, config) {
138217
139118
  error,
138218
139119
  result,
138219
139120
  start,
139121
+ retrySegment,
138220
139122
  cancel,
138221
139123
  reset
138222
139124
  };
138223
139125
  }
138224
139126
  function isDocumentTranslationSegment(segment) {
138225
- return typeof segment === "object" && segment !== null && !Array.isArray(segment);
139127
+ return isRenderableTranslationSegment(segment);
138226
139128
  }
138227
139129
  function getDocumentTranslationFailureMessage(result) {
138228
139130
  const segments = (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment);
@@ -138250,6 +139152,16 @@ function normalizeDocumentTranslationResult(result) {
138250
139152
  segments: (Array.isArray(result.segments) ? result.segments : []).filter(isDocumentTranslationSegment)
138251
139153
  };
138252
139154
  }
139155
+ function buildRetriedDocumentResult(current, segmentIndex, nextSegment) {
139156
+ return normalizeDocumentTranslationResult({
139157
+ ...current,
139158
+ segments: current.segments.map((entry, index) => index === segmentIndex ? nextSegment : entry)
139159
+ });
139160
+ }
139161
+ function canAutoStartBrowserTranslation(capability, browserSupportTable) {
139162
+ if (capability?.availability === "available") return true;
139163
+ return (browserSupportTable?.table?.rows ?? []).some((row) => row.availability === "available");
139164
+ }
138253
139165
  //#endregion
138254
139166
  //#region ../../node_modules/.pnpm/comma-separated-tokens@2.0.3/node_modules/comma-separated-tokens/index.js
138255
139167
  /**
@@ -148659,15 +149571,21 @@ function createAnnotationComponents(inlineAnnotations, blockAnnotationByOffset)
148659
149571
  });
148660
149572
  },
148661
149573
  th: ({ children, node, ...props }) => {
149574
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "tableCell");
148662
149575
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
148663
149576
  ...props,
148664
- children: render(children)
149577
+ ...annotation?.dataAttributes,
149578
+ className: mergeClassName$2(props.className, annotation?.className),
149579
+ children: renderBlockChildren(children, annotation)
148665
149580
  });
148666
149581
  },
148667
149582
  td: ({ children, node, ...props }) => {
149583
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "tableCell");
148668
149584
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
148669
149585
  ...props,
148670
- children: render(children)
149586
+ ...annotation?.dataAttributes,
149587
+ className: mergeClassName$2(props.className, annotation?.className),
149588
+ children: renderBlockChildren(children, annotation)
148671
149589
  });
148672
149590
  }
148673
149591
  };
@@ -148739,7 +149657,7 @@ function CodeBlock({ children, className, title }) {
148739
149657
  isInline
148740
149658
  ]);
148741
149659
  if (isInline) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
148742
- className: "bg-muted text-foreground rounded px-1.5 py-0.5 font-mono text-sm",
149660
+ className: "text-foreground rounded px-1 font-mono text-sm [box-shadow:0_1px_0px_var(--primary)]",
148743
149661
  title,
148744
149662
  children: code
148745
149663
  });
@@ -148855,8 +149773,9 @@ function transformSafeMarkdownUrl(value) {
148855
149773
  }
148856
149774
  //#endregion
148857
149775
  //#region src/components/document-translation-segment-render.tsx
148858
- function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className }) {
149776
+ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode, targetChildren, className, onRetry }) {
148859
149777
  const target = segment.target ?? "";
149778
+ const showRetry = segment.status === "error" && typeof onRetry === "function";
148860
149779
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
148861
149780
  className: mergeClassName$1("document-translation-segment", className),
148862
149781
  children: [displayMode === "bilingual" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -148869,7 +149788,19 @@ function renderTranslationSegmentChildren({ sourceChildren, segment, displayMode
148869
149788
  title: displayMode === "direct" ? segment.source : void 0,
148870
149789
  lang: segment.targetLanguage,
148871
149790
  "data-translation-target": "",
148872
- children: targetChildren ?? (segment.targetNodes ? renderTranslatedHastNodes(segment.targetNodes) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownInlineContent, { markdown: target }))
149791
+ children: showRetry ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
149792
+ className: "inline-flex items-center gap-2",
149793
+ children: [displayMode === "direct" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
149794
+ className: "text-muted-foreground",
149795
+ children: segment.source
149796
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
149797
+ size: "sm",
149798
+ variant: "secondary",
149799
+ "aria-label": "Retry translation",
149800
+ onClick: () => onRetry(segment.id),
149801
+ children: "Retry"
149802
+ })]
149803
+ }) : targetChildren ?? (segment.targetNodes ? renderTranslatedHastNodes(segment.targetNodes) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownInlineContent, { markdown: target }))
148873
149804
  })]
148874
149805
  });
148875
149806
  }
@@ -148894,13 +149825,13 @@ var OPENSPEC_SECTION_TARGET_LABELS = {
148894
149825
  overview: "目的",
148895
149826
  requirements: "需求"
148896
149827
  };
148897
- function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode) {
149828
+ function createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetry) {
148898
149829
  const kind = getCurrentDataAttribute(input, "data-openspec-kind");
148899
- if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode);
149830
+ if (kind === "section") return createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry);
148900
149831
  if (kind !== "requirement" && kind !== "scenario") return void 0;
148901
- return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind);
149832
+ return createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry);
148902
149833
  }
148903
- function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode) {
149834
+ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displayMode, onRetry) {
148904
149835
  const sectionKind = getCurrentDataAttribute(input, "data-openspec-section-kind");
148905
149836
  if (!sectionKind) return void 0;
148906
149837
  const targetTitle = OPENSPEC_SECTION_TARGET_LABELS[sectionKind];
@@ -148918,11 +149849,12 @@ function createTranslatedOpenSpecSectionHeadingProjection(input, segment, displa
148918
149849
  },
148919
149850
  displayMode,
148920
149851
  targetChildren: targetTitle,
148921
- className: "document-translation-heading-segment"
149852
+ className: "document-translation-heading-segment",
149853
+ onRetry
148922
149854
  })
148923
149855
  };
148924
149856
  }
148925
- function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind) {
149857
+ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, displayMode, kind, onRetry) {
148926
149858
  const sourceParts = splitOpenSpecHeadingText(segment.source, kind);
148927
149859
  const targetParts = splitOpenSpecHeadingText(segment.target ?? "", kind);
148928
149860
  const visualLabel = getCurrentDataAttribute(input, "data-openspec-visual-label") ?? getCurrentDataAttribute(input, "data-openspec-label");
@@ -148967,7 +149899,8 @@ function createTranslatedOpenSpecStructureHeadingProjection(input, segment, disp
148967
149899
  segment: titleSegment,
148968
149900
  displayMode,
148969
149901
  targetChildren: targetNodes ? renderTranslatedHastNodes(targetNodes) : void 0,
148970
- className: "document-translation-heading-segment"
149902
+ className: "document-translation-heading-segment",
149903
+ onRetry
148971
149904
  })
148972
149905
  })] }),
148973
149906
  target: targetTitle
@@ -149028,7 +149961,7 @@ function useDocumentTranslationRenderPlugin({ markdown, translationConfig }) {
149028
149961
  const resolvedTranslationConfig = (0, import_react.useMemo)(() => translationConfig === void 0 ? void 0 : DocumentTranslationConfigSchema.parse(resolveDocumentTranslationConfig(translationConfig, globalSettings)), [globalSettings, translationConfig]);
149029
149962
  const session = useDocumentTranslation(markdown ?? "", resolvedTranslationConfig);
149030
149963
  const canTranslate = resolvedTranslationConfig !== void 0 && typeof markdown === "string" && markdown.length > 0;
149031
- const translationProjection = (0, import_react.useMemo)(() => createTranslationProjection(session.result), [session.result]);
149964
+ const translationProjection = (0, import_react.useMemo)(() => createTranslationProjection(session.result, session.retrySegment), [session.result, session.retrySegment]);
149032
149965
  const translationAction = (0, import_react.useMemo)(() => canTranslate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DocumentTranslationAction, {
149033
149966
  enabled: resolvedTranslationConfig?.enabled ?? false,
149034
149967
  session
@@ -149099,7 +150032,7 @@ function hashString(value) {
149099
150032
  for (let index = 0; index < value.length; index++) hash = hash * 31 + value.charCodeAt(index) | 0;
149100
150033
  return hash.toString(36);
149101
150034
  }
149102
- function createTranslationProjection(result) {
150035
+ function createTranslationProjection(result, onRetrySegment) {
149103
150036
  if (!result) return { blockAnnotations: [] };
149104
150037
  const segments = getRenderableTranslationSegments(result);
149105
150038
  const segmentByOffset = new Map(segments.filter((segment) => segment.target).map((segment) => [segment.sourceStartOffset, segment]));
@@ -149121,7 +150054,7 @@ function createTranslationProjection(result) {
149121
150054
  transformHeading(input) {
149122
150055
  const segment = input.sourceStartOffset === void 0 ? void 0 : segmentByOffset.get(input.sourceStartOffset);
149123
150056
  if (!segment?.target || segment.kind !== "heading") return void 0;
149124
- return createTranslatedHeadingTransform(input, segment, result.displayMode);
150057
+ return createTranslatedHeadingTransform(input, segment, result.displayMode, onRetrySegment);
149125
150058
  }
149126
150059
  },
149127
150060
  blockAnnotations: segments.filter((segment) => segment.target && segment.kind !== "heading").map((segment) => ({
@@ -149140,7 +150073,8 @@ function createTranslationProjection(result) {
149140
150073
  renderChildren: (children) => segment.targetNodes && result.displayMode === "direct" ? children : renderTranslationSegmentChildren({
149141
150074
  sourceChildren: children,
149142
150075
  segment,
149143
- displayMode: result.displayMode
150076
+ displayMode: result.displayMode,
150077
+ onRetry: onRetrySegment
149144
150078
  })
149145
150079
  }))
149146
150080
  };
@@ -149148,9 +150082,9 @@ function createTranslationProjection(result) {
149148
150082
  function getRenderableTranslationSegments(result) {
149149
150083
  return (Array.isArray(result.segments) ? result.segments : []).filter(isRenderableTranslationSegment);
149150
150084
  }
149151
- function createTranslatedHeadingTransform(input, segment, displayMode) {
150085
+ function createTranslatedHeadingTransform(input, segment, displayMode, onRetrySegment) {
149152
150086
  const projectedTarget = segment.target ?? "";
149153
- const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode);
150087
+ const openSpecHeading = createTranslatedOpenSpecHeadingProjection(input, segment, displayMode, onRetrySegment);
149154
150088
  if (openSpecHeading) return {
149155
150089
  tocDataLabel: displayMode === "direct" ? openSpecHeading.tocDataLabel : input.current?.tocLabel ?? input.text,
149156
150090
  children: openSpecHeading.children,
@@ -149165,7 +150099,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149165
150099
  segment,
149166
150100
  displayMode,
149167
150101
  targetChildren,
149168
- className: "document-translation-heading-segment"
150102
+ className: "document-translation-heading-segment",
150103
+ onRetry: onRetrySegment
149169
150104
  }),
149170
150105
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149171
150106
  };
@@ -149176,7 +150111,8 @@ function createTranslatedHeadingTransform(input, segment, displayMode) {
149176
150111
  segment,
149177
150112
  displayMode,
149178
150113
  targetChildren,
149179
- className: "document-translation-heading-segment"
150114
+ className: "document-translation-heading-segment",
150115
+ onRetry: onRetrySegment
149180
150116
  }),
149181
150117
  dataAttributes: createTranslationDataAttributes(segment, projectedTarget, displayMode)
149182
150118
  };