codex-overleaf-link 2.3.4 → 2.3.6

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 (41) hide show
  1. package/README.md +325 -211
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/src/background.js +9 -0
  4. package/extension/src/backgroundUpdateCoordinator.js +88 -4
  5. package/extension/src/content/composerPanel.js +1 -1
  6. package/extension/src/content/contentRuntime.js +51 -35
  7. package/extension/src/content/diagnosticsController.js +13 -3
  8. package/extension/src/content/diagnosticsPanel.js +1 -1
  9. package/extension/src/content/generated/content.bundle.js +1233 -298
  10. package/extension/src/content/generated/content.bundle.meta.json +8 -3
  11. package/extension/src/content/globalPreferencesController.js +125 -0
  12. package/extension/src/content/legacyGlobalRegistry.js +4 -0
  13. package/extension/src/content/otWarmMirror.js +95 -65
  14. package/extension/src/content/otWarmMirrorController.js +102 -1
  15. package/extension/src/content/panelMaintenance.js +26 -15
  16. package/extension/src/content/panelRenderer.js +6 -1
  17. package/extension/src/content/projectSettingsCoordinator.js +1 -0
  18. package/extension/src/content/providerSettingsCoordinator.js +16 -0
  19. package/extension/src/content/providerSettingsDialog.js +24 -15
  20. package/extension/src/content/runFailureNotice.js +104 -0
  21. package/extension/src/content/runScrollLayout.js +124 -0
  22. package/extension/src/content/runTimelineView.js +36 -10
  23. package/extension/src/content/settingsPanel.js +32 -13
  24. package/extension/src/content/themeController.js +3 -0
  25. package/extension/src/content/updateNotice.js +24 -4
  26. package/extension/src/page/overleafRealtimeObserver.js +116 -24
  27. package/extension/src/shared/compatibility.js +1 -1
  28. package/extension/src/shared/globalPreferences.js +131 -0
  29. package/extension/src/shared/i18n.js +79 -39
  30. package/extension/src/shared/managedUpdateProjection.js +1 -1
  31. package/extension/src/shared/otText.js +0 -1
  32. package/extension/src/shared/sessionState.js +17 -21
  33. package/extension/src/shared/sessionTitle.js +120 -0
  34. package/extension/src/shared/updateRuntimeIdentity.js +32 -0
  35. package/extension/styles/panel.css +90 -16
  36. package/native-host/src/mirrorWorkspace.js +20 -1
  37. package/native-host/src/releaseProxy.js +177 -0
  38. package/native-host/src/releaseTransport.js +154 -0
  39. package/native-host/src/updateManager.js +40 -39
  40. package/package.json +1 -1
  41. package/scripts/install-managed.mjs +1 -1
@@ -3895,7 +3895,7 @@ ${block}`;
3895
3895
  return sha256Hex(utf8Bytes(value));
3896
3896
  }
3897
3897
  function normalizePath(value) {
3898
- return String(value || "").replace(/\s+/g, " ").replace(/\\/g, "/").trim().replace(/^\/+/, "");
3898
+ return String(value || "").replace(/\\/g, "/").trim().replace(/^\/+/, "");
3899
3899
  }
3900
3900
  function normalizeText(value) {
3901
3901
  return String(value ?? "");
@@ -4114,20 +4114,31 @@ ${block}`;
4114
4114
  diagnosticsPageSubtitle: "Current file, write access, track changes",
4115
4115
  diagnosticsSnapshotTitle: "Check Project Read",
4116
4116
  diagnosticsSnapshotSubtitle: "Full project, assets, read source",
4117
- diagnosticsOtTitle: "Check Experimental OT Mirror",
4117
+ diagnosticsOtTitle: "Check OT Mirror",
4118
4118
  diagnosticsOtSubtitle: "Status, fresh files, fallback",
4119
4119
  diagnosticsExportTitle: "Export Diagnostics",
4120
4120
  diagnosticsExportSubtitle: "Redacted audit and environment bundle",
4121
4121
  diagnosticsExportDone: "Diagnostics bundle exported.",
4122
- diagnosticsOtSummaryEnabled: "Experimental OT is enabled. Codex will use the OT-warmed mirror only when matching files are fresh; otherwise it falls back to the normal project read path.",
4123
- diagnosticsOtSummaryDisabled: "Experimental OT is disabled, so Codex is using the normal project read path.",
4124
- diagnosticsOtNextStep: "If you expected OT warm reads, turn on OT and wait for the status to show Live before relying on it.",
4125
- experimentalOtMenuTitle: "Experimental OT Mirror",
4126
- experimentalOtMenuSubtitle: "Experimental \u2014 speeds up reading the file you are editing; Codex falls back to normal reading whenever unsure.",
4127
- experimentalOtConfirmTitle: "Experimental OT Mirror",
4128
- experimentalOtConfirmMessage: "Experimental: tracks the current Overleaf editor and warms the local mirror so Codex can read recent focused-file edits faster. Codex uses it only when file scope is clear and the mirror is fresh; otherwise it falls back to normal project reading.",
4122
+ diagnosticsOtSummaryEnabled: "OT Mirror is enabled. Codex will use the OT-warmed mirror only when matching files are fresh; otherwise it falls back to the normal project read path.",
4123
+ diagnosticsOtSummaryDisabled: "OT Mirror is disabled, so Codex is using the normal project read path.",
4124
+ diagnosticsOtNextStep: "Observer status describes warming availability. File scope and freshness are checked again before each run.",
4125
+ experimentalOtMenuTitle: "OT Mirror",
4126
+ otSettingsTitle: "Project cache",
4127
+ contextLoadingTitle: "Context loading",
4128
+ runFeedbackFailedTitle: "Needs attention",
4129
+ runFeedbackPartialTitle: "Partially completed",
4130
+ runFeedbackUnconfirmedTitle: "Write status needs review",
4131
+ runFeedbackGeneric: "Open the run details for the reported cause and the next step.",
4132
+ runFeedbackUnconfirmed: "Some changes may already be in Overleaf. Check the document before retrying.",
4133
+ runFeedbackUnconfirmedResult: "Not fully confirmed. Review Overleaf before retrying.",
4134
+ runFeedbackReviewFile: "Review {file} in Overleaf",
4135
+ runFeedbackReviewDetails: "Review run details",
4136
+ runFeedbackDetailsUnavailable: "Detailed steps are unavailable for this older run.",
4137
+ experimentalOtMenuSubtitle: "Warms recent edits to the active text file locally. File scope and freshness are checked before reuse.",
4138
+ experimentalOtConfirmTitle: "Enable OT Mirror?",
4139
+ experimentalOtConfirmMessage: "Observes the active text editor and warms its local mirror. This is read-only with respect to Overleaf and does not upload assets. Each run checks file scope and freshness before reuse; normal project reading remains the fallback.",
4129
4140
  experimentalOtConfirmEnable: "Turn on",
4130
- experimentalOtEnabledToast: "Experimental OT Mirror is on. Use @file for the cleanest warm-mirror test.",
4141
+ experimentalOtEnabledToast: "OT Mirror requested for this project. Its status shows whether warming is available.",
4131
4142
  diagnosticsSnapshotLoading: "Checking whether the extension can read the full Overleaf project.",
4132
4143
  diagnosticsSnapshotErrorSummary: "Overleaf may still be loading, or the page API did not return project files yet.",
4133
4144
  diagnosticsNativeLoading: "Checking whether local Codex, Native Host, and LaTeX tools are available.",
@@ -4400,18 +4411,27 @@ ${block}`;
4400
4411
  requireReviewingTitle: "When enabled, Codex checks or switches Overleaf Reviewing/Track Changes before writing. Deletes still require confirmation.",
4401
4412
  autoCompile: "Compile",
4402
4413
  autoCompileTitle: "After Codex writes, click Overleaf Recompile and record the compile result for this task. Ask mode will not trigger it.",
4403
- experimentalOtWarmMirror: "Experimental OT warm mirror",
4414
+ experimentalOtWarmMirror: "OT warm mirror",
4404
4415
  experimentalOtWarmMirrorShort: "OT",
4405
- experimentalOtWarmMirrorTitle: "Experimental read-only OT mirror warming. If unavailable, stale, or inconsistent, Codex falls back to the normal project read path.",
4416
+ experimentalOtWarmMirrorTitle: "Read-only local text warming. File scope and freshness are checked before reuse; normal project reading remains the fallback.",
4406
4417
  otStatus: "OT status",
4407
- otFreshFiles: "OT fresh files",
4408
- otFallback: "Fallback",
4418
+ otFreshFiles: "Files with recent OT evidence",
4419
+ otFallback: "Read fallback",
4409
4420
  otStatusOff: "Off",
4410
- otStatusStarting: "Start",
4411
- otStatusObserving: "Live",
4412
- otStatusUnavailable: "N/A",
4413
- otStatusStale: "Stale",
4414
- otStatusInconsistent: "Check",
4421
+ otStatusStarting: "Connecting",
4422
+ otStatusObserving: "Observing current editor",
4423
+ otStatusUnavailable: "Unavailable",
4424
+ otStatusStale: "Warming paused; awaiting verification",
4425
+ otStatusInconsistent: "Needs verification",
4426
+ otFailureBridge: "The page connection needs to be restored. Reload the extension and Overleaf before retrying.",
4427
+ otFailureObserver: "The editor observer is unavailable. The next run will try normal project reading.",
4428
+ otFailureEditor: "The active editor could not be read. Warming is paused; the next run will try normal project reading.",
4429
+ otFailureFileLimit: "This file exceeds the 1 MiB warming limit. The next run will try normal project reading.",
4430
+ otFailureQueueLimit: "The mirror queue reached its limit. Warming is paused; the next run will try normal project reading.",
4431
+ otFailureExpired: "The observation is too old to reuse. The next run will try normal project reading.",
4432
+ otFailureMirror: "The mirror update was not fully confirmed. The next run will try normal project reading.",
4433
+ otFailureGeneric: "Mirror warming could not continue. The next run will try normal project reading.",
4434
+ otFailureNextStep: "Resolve the reported cause, then enable OT again. If the page connection is unavailable, reload the extension and Overleaf first.",
4415
4435
  modelSourceFallback: "fallback",
4416
4436
  modelSourceDiscovered: "discovered",
4417
4437
  modelSourceFailed: "failed",
@@ -4454,7 +4474,7 @@ ${block}`;
4454
4474
  contextReadFailed: "Could not read files: {message}",
4455
4475
  contextNoFiles: "No usable text files were read from this Overleaf project.",
4456
4476
  contextSelectedFiles: "Added {count} @file items. Future tasks will focus on them first.",
4457
- contextDefaultWholeProject: "Default is the whole project. Add @file; @compile-log and @current-section are available as context options.",
4477
+ contextDefaultWholeProject: "Default is the whole project. Choose files or add @compile-log. For a section, name it in your request.",
4458
4478
  preloadProjectContextTitle: "Preload project context",
4459
4479
  preloadProjectContextDescription: "Fetch the exact project file list in the background after an Overleaf project becomes idle.",
4460
4480
  contextResourceTitle: "{path} (non-text resource, shown only as project structure for now)",
@@ -4634,20 +4654,31 @@ ${block}`;
4634
4654
  diagnosticsPageSubtitle: "\u5F53\u524D\u6587\u4EF6\u3001\u5199\u5165\u80FD\u529B\u3001\u7559\u75D5\u72B6\u6001",
4635
4655
  diagnosticsSnapshotTitle: "\u68C0\u67E5\u9879\u76EE\u8BFB\u53D6",
4636
4656
  diagnosticsSnapshotSubtitle: "\u5B8C\u6574\u9879\u76EE\u3001\u8D44\u6E90\u6587\u4EF6\u3001\u8BFB\u53D6\u6765\u6E90",
4637
- diagnosticsOtTitle: "\u68C0\u67E5\u5B9E\u9A8C\u6027 OT Mirror",
4657
+ diagnosticsOtTitle: "\u68C0\u67E5 OT Mirror",
4638
4658
  diagnosticsOtSubtitle: "\u72B6\u6001\u3001\u65B0\u9C9C\u6587\u4EF6\u3001\u56DE\u9000\u8DEF\u5F84",
4639
4659
  diagnosticsExportTitle: "\u5BFC\u51FA\u8BCA\u65AD\u4FE1\u606F",
4640
4660
  diagnosticsExportSubtitle: "\u5DF2\u8131\u654F\u7684\u5BA1\u8BA1\u548C\u73AF\u5883\u4FE1\u606F\u5305",
4641
4661
  diagnosticsExportDone: "\u8BCA\u65AD\u4FE1\u606F\u5DF2\u5BFC\u51FA\u3002",
4642
- diagnosticsOtSummaryEnabled: "\u5B9E\u9A8C\u6027 OT \u5DF2\u5F00\u542F\u3002\u53EA\u6709\u5339\u914D\u6587\u4EF6\u662F\u65B0\u9C9C\u72B6\u6001\u65F6\uFF0CCodex \u624D\u4F1A\u4F7F\u7528 OT \u9884\u70ED mirror\uFF1B\u5426\u5219\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4643
- diagnosticsOtSummaryDisabled: "\u5B9E\u9A8C\u6027 OT \u5DF2\u5173\u95ED\uFF0CCodex \u4F1A\u4F7F\u7528\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4644
- diagnosticsOtNextStep: "\u5982\u679C\u4F60\u5E0C\u671B\u4F7F\u7528 OT \u9884\u70ED\u8BFB\u53D6\uFF0C\u8BF7\u5F00\u542F OT\uFF0C\u5E76\u7B49\u72B6\u6001\u663E\u793A\u4E3A\u201C\u5B9E\u65F6\u201D\u540E\u518D\u4F9D\u8D56\u5B83\u3002",
4645
- experimentalOtMenuTitle: "\u5B9E\u9A8C\u6027 OT Mirror",
4646
- experimentalOtMenuSubtitle: "\u5B9E\u9A8C\u6027\u2014\u2014\u52A0\u5FEB\u8BFB\u53D6\u4F60\u6B63\u5728\u7F16\u8F91\u7684\u6587\u4EF6\uFF1B\u62FF\u4E0D\u51C6\u65F6\u81EA\u52A8\u56DE\u9000\u5230\u666E\u901A\u8BFB\u53D6\u3002",
4647
- experimentalOtConfirmTitle: "\u5B9E\u9A8C\u6027 OT Mirror",
4648
- experimentalOtConfirmMessage: "\u5B9E\u9A8C\u6027\uFF1A\u5B9E\u65F6\u8DDF\u8E2A\u5F53\u524D Overleaf \u7F16\u8F91\u5668\uFF0C\u628A\u6539\u52A8\u9884\u70ED\u5230\u672C\u5730 mirror\uFF0C\u8BA9 Codex \u66F4\u5FEB\u8BFB\u5230\u6700\u8FD1\u7684\u5F53\u524D\u6587\u4EF6\u6539\u52A8\u3002\u53EA\u6709\u6587\u4EF6\u8303\u56F4\u660E\u786E\u4E14 mirror \u65B0\u9C9C\u65F6\u624D\u4F1A\u4F7F\u7528\uFF1B\u5426\u5219\u81EA\u52A8\u56DE\u9000\u5230\u666E\u901A\u9879\u76EE\u8BFB\u53D6\u3002",
4662
+ diagnosticsOtSummaryEnabled: "OT Mirror \u5DF2\u5F00\u542F\u3002\u53EA\u6709\u5339\u914D\u6587\u4EF6\u662F\u65B0\u9C9C\u72B6\u6001\u65F6\uFF0CCodex \u624D\u4F1A\u4F7F\u7528 OT \u9884\u70ED\u955C\u50CF\uFF1B\u5426\u5219\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4663
+ diagnosticsOtSummaryDisabled: "OT Mirror \u5DF2\u5173\u95ED\uFF0CCodex \u4F1A\u4F7F\u7528\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u8DEF\u5F84\u3002",
4664
+ diagnosticsOtNextStep: "\u89C2\u6D4B\u72B6\u6001\u8BF4\u660E\u9884\u70ED\u662F\u5426\u53EF\u7528\uFF1B\u6BCF\u6B21\u4EFB\u52A1\u5F00\u59CB\u524D\u4ECD\u4F1A\u91CD\u65B0\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\u3002",
4665
+ experimentalOtMenuTitle: "OT Mirror",
4666
+ otSettingsTitle: "\u9879\u76EE\u7F13\u5B58",
4667
+ contextLoadingTitle: "\u4E0A\u4E0B\u6587\u52A0\u8F7D",
4668
+ runFeedbackFailedTitle: "\u9700\u8981\u5904\u7406",
4669
+ runFeedbackPartialTitle: "\u90E8\u5206\u5B8C\u6210",
4670
+ runFeedbackUnconfirmedTitle: "\u5199\u5165\u72B6\u6001\u5F85\u786E\u8BA4",
4671
+ runFeedbackGeneric: "\u53EF\u5728\u672C\u8F6E\u8BE6\u60C5\u4E2D\u67E5\u770B\u5177\u4F53\u539F\u56E0\u548C\u540E\u7EED\u5904\u7406\u65B9\u5F0F\u3002",
4672
+ runFeedbackUnconfirmed: "\u90E8\u5206\u6539\u52A8\u53EF\u80FD\u5DF2\u7ECF\u5199\u5165 Overleaf\uFF0C\u91CD\u8BD5\u524D\u8BF7\u5148\u68C0\u67E5\u6587\u6863\u3002",
4673
+ runFeedbackUnconfirmedResult: "\u7ED3\u679C\u5C1A\u672A\u5B8C\u5168\u786E\u8BA4\uFF0C\u8BF7\u68C0\u67E5 Overleaf \u540E\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002",
4674
+ runFeedbackReviewFile: "\u5728 Overleaf \u68C0\u67E5 {file}",
4675
+ runFeedbackReviewDetails: "\u67E5\u770B\u8FD0\u884C\u6B65\u9AA4",
4676
+ runFeedbackDetailsUnavailable: "\u8FD9\u6761\u5386\u53F2\u8BB0\u5F55\u6CA1\u6709\u53EF\u5C55\u5F00\u7684\u8BE6\u7EC6\u6B65\u9AA4\u3002",
4677
+ experimentalOtMenuSubtitle: "\u5728\u672C\u5730\u9884\u70ED\u5F53\u524D\u6587\u672C\u6587\u4EF6\u7684\u6700\u8FD1\u6539\u52A8\uFF0C\u590D\u7528\u524D\u91CD\u65B0\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\u3002",
4678
+ experimentalOtConfirmTitle: "\u5F00\u542F OT Mirror\uFF1F",
4679
+ experimentalOtConfirmMessage: "\u89C2\u6D4B\u5F53\u524D\u6587\u672C\u7F16\u8F91\u5668\u5E76\u9884\u70ED\u672C\u5730\u955C\u50CF\u3002\u6B64\u529F\u80FD\u4E0D\u4F1A\u5199\u56DE Overleaf\uFF0C\u4E5F\u4E0D\u8D1F\u8D23\u4E0A\u4F20\u8D44\u4EA7\u3002\u6BCF\u6B21\u4EFB\u52A1\u590D\u7528\u524D\u90FD\u4F1A\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\uFF0C\u4E0D\u6EE1\u8DB3\u6761\u4EF6\u65F6\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4649
4680
  experimentalOtConfirmEnable: "\u5F00\u542F",
4650
- experimentalOtEnabledToast: "\u5B9E\u9A8C\u6027 OT Mirror \u5DF2\u5F00\u542F\u3002\u4F7F\u7528 @file \u53EF\u4EE5\u6700\u6E05\u695A\u5730\u6D4B\u8BD5 warm mirror\u3002",
4681
+ experimentalOtEnabledToast: "\u5DF2\u4E3A\u5F53\u524D\u9879\u76EE\u8BF7\u6C42\u5F00\u542F OT Mirror\uFF0C\u53EF\u7528\u60C5\u51B5\u89C1\u72B6\u6001\u63D0\u793A\u3002",
4651
4682
  diagnosticsSnapshotLoading: "\u786E\u8BA4\u63D2\u4EF6\u80FD\u5426\u8BFB\u53D6\u5B8C\u6574 Overleaf \u9879\u76EE\u3002",
4652
4683
  diagnosticsSnapshotErrorSummary: "\u8FD9\u901A\u5E38\u662F Overleaf \u9875\u9762\u8FD8\u6CA1\u52A0\u8F7D\u5B8C\u6210\uFF0C\u6216\u8005\u5F53\u524D\u9875\u9762\u63A5\u53E3\u6682\u65F6\u6CA1\u6709\u8FD4\u56DE\u9879\u76EE\u6587\u4EF6\u3002",
4653
4684
  diagnosticsNativeLoading: "\u786E\u8BA4\u672C\u673A Codex\u3001Native Host \u548C LaTeX \u5DE5\u5177\u662F\u5426\u53EF\u7528\u3002",
@@ -4920,18 +4951,27 @@ ${block}`;
4920
4951
  requireReviewingTitle: "\u5F00\u542F\u540E\uFF0C\u5199\u5165\u524D\u4F1A\u786E\u8BA4\u5E76\u5C1D\u8BD5\u5207\u5230 Overleaf Reviewing/Track Changes\uFF1B\u5220\u9664\u4ECD\u9700\u786E\u8BA4\u3002",
4921
4952
  autoCompile: "\u7F16\u8BD1",
4922
4953
  autoCompileTitle: "Codex \u5199\u5165\u540E\u81EA\u52A8\u70B9\u51FB Overleaf Recompile\uFF0C\u5E76\u628A\u7F16\u8BD1\u7ED3\u679C\u8BB0\u5F55\u5230\u672C\u8F6E\u4EFB\u52A1\u3002\u53EA\u95EE\u4E0D\u6539\u65F6\u4E0D\u4F1A\u89E6\u53D1\u3002",
4923
- experimentalOtWarmMirror: "\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF",
4954
+ experimentalOtWarmMirror: "OT \u9884\u70ED\u955C\u50CF",
4924
4955
  experimentalOtWarmMirrorShort: "OT",
4925
- experimentalOtWarmMirrorTitle: "\u5B9E\u9A8C\u6027\u53EA\u8BFB OT \u955C\u50CF\u9884\u70ED\uFF1B\u4E0D\u53EF\u7528\u3001\u8FC7\u671F\u6216\u4E0D\u4E00\u81F4\u65F6\u4F1A\u56DE\u9000\u5230\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4956
+ experimentalOtWarmMirrorTitle: "\u53EA\u8BFB\u672C\u5730\u6587\u672C\u9884\u70ED\uFF1B\u590D\u7528\u524D\u6821\u9A8C\u6587\u4EF6\u8303\u56F4\u548C\u65B0\u9C9C\u5EA6\uFF0C\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u4F5C\u4E3A\u56DE\u9000\u8DEF\u5F84\u3002",
4926
4957
  otStatus: "OT \u72B6\u6001",
4927
- otFreshFiles: "OT \u65B0\u9C9C\u6587\u4EF6",
4928
- otFallback: "\u56DE\u9000",
4929
- otStatusOff: "\u5173",
4930
- otStatusStarting: "\u542F\u52A8",
4931
- otStatusObserving: "\u5B9E\u65F6",
4932
- otStatusUnavailable: "\u4E0D\u53EF\u7528",
4933
- otStatusStale: "\u8FC7\u671F",
4934
- otStatusInconsistent: "\u9700\u68C0\u67E5",
4958
+ otFreshFiles: "\u8FD1\u671F\u6709 OT \u6821\u9A8C\u8BC1\u636E\u7684\u6587\u4EF6",
4959
+ otFallback: "\u8BFB\u53D6\u56DE\u9000",
4960
+ otStatusOff: "\u672A\u5F00\u542F",
4961
+ otStatusStarting: "\u6B63\u5728\u8FDE\u63A5",
4962
+ otStatusObserving: "\u6B63\u5728\u89C2\u6D4B\u5F53\u524D\u7F16\u8F91\u5668",
4963
+ otStatusUnavailable: "\u6682\u4E0D\u53EF\u7528",
4964
+ otStatusStale: "\u9884\u70ED\u6682\u7F13\uFF0C\u7B49\u5F85\u91CD\u65B0\u6821\u9A8C",
4965
+ otStatusInconsistent: "\u9700\u91CD\u65B0\u6821\u9A8C",
4966
+ otFailureBridge: "\u9875\u9762\u8FDE\u63A5\u9700\u8981\u6062\u590D\uFF0C\u8BF7\u5148\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u5E76\u5237\u65B0 Overleaf\uFF0C\u518D\u91CD\u8BD5\u3002",
4967
+ otFailureObserver: "\u7F16\u8F91\u5668\u89C2\u5BDF\u5668\u6682\u4E0D\u53EF\u7528\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4968
+ otFailureEditor: "\u5F53\u524D\u7F16\u8F91\u5668\u5185\u5BB9\u5C1A\u4E0D\u53EF\u8BFB\uFF0C\u9884\u70ED\u5DF2\u6682\u505C\uFF1B\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4969
+ otFailureFileLimit: "\u6587\u4EF6\u8D85\u8FC7 1 MiB \u9884\u70ED\u4E0A\u9650\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4970
+ otFailureQueueLimit: "\u955C\u50CF\u961F\u5217\u8FBE\u5230\u5BB9\u91CF\u4E0A\u9650\uFF0C\u9884\u70ED\u5DF2\u6682\u505C\uFF1B\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4971
+ otFailureExpired: "\u89C2\u6D4B\u8BB0\u5F55\u5DF2\u8FC7\u671F\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4972
+ otFailureMirror: "\u955C\u50CF\u66F4\u65B0\u5C1A\u672A\u5B8C\u6574\u786E\u8BA4\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4973
+ otFailureGeneric: "\u955C\u50CF\u9884\u70ED\u672A\u80FD\u7EE7\u7EED\uFF0C\u4E0B\u6B21\u4EFB\u52A1\u5C06\u5C1D\u8BD5\u5E38\u89C4\u9879\u76EE\u8BFB\u53D6\u3002",
4974
+ otFailureNextStep: "\u5904\u7406\u6240\u663E\u793A\u7684\u539F\u56E0\u540E\u91CD\u65B0\u5F00\u542F OT\uFF1B\u82E5\u9875\u9762\u8FDE\u63A5\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u5E76\u5237\u65B0 Overleaf\u3002",
4935
4975
  modelSourceFallback: "\u5907\u7528\u5217\u8868",
4936
4976
  modelSourceDiscovered: "\u5DF2\u53D1\u73B0",
4937
4977
  modelSourceFailed: "\u83B7\u53D6\u5931\u8D25",
@@ -4974,7 +5014,7 @@ ${block}`;
4974
5014
  contextReadFailed: "\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A{message}",
4975
5015
  contextNoFiles: "\u6CA1\u6709\u4ECE\u5F53\u524D Overleaf \u9879\u76EE\u8BFB\u53D6\u5230\u53EF\u7528\u6587\u672C\u6587\u4EF6\u3002",
4976
5016
  contextSelectedFiles: "\u5DF2\u6DFB\u52A0 {count} \u4E2A @file\uFF1B\u540E\u7EED\u4EFB\u52A1\u4F1A\u4F18\u5148\u56F4\u7ED5\u5B83\u4EEC\u3002",
4977
- contextDefaultWholeProject: "\u9ED8\u8BA4\u4F7F\u7528\u6574\u4E2A\u9879\u76EE\u3002\u53EF\u6DFB\u52A0 @file\uFF1B@compile-log \u548C @current-section \u5C06\u4F5C\u4E3A\u540E\u7EED\u4E0A\u4E0B\u6587\u80FD\u529B\u3002",
5017
+ contextDefaultWholeProject: "\u9ED8\u8BA4\u4F7F\u7528\u6574\u4E2A\u9879\u76EE\u3002\u53EF\u9009\u62E9\u6587\u4EF6\u6216\u6DFB\u52A0 @compile-log\uFF1B\u5904\u7406\u7AE0\u8282\u65F6\u8BF7\u5728\u8BF7\u6C42\u4E2D\u6CE8\u660E\u7AE0\u8282\u540D\u79F0\u3002",
4978
5018
  preloadProjectContextTitle: "\u9884\u52A0\u8F7D\u9879\u76EE\u4E0A\u4E0B\u6587",
4979
5019
  preloadProjectContextDescription: "Overleaf \u9879\u76EE\u8FDB\u5165\u7A7A\u95F2\u72B6\u6001\u540E\uFF0C\u5728\u540E\u53F0\u8BFB\u53D6\u7CBE\u786E\u7684\u9879\u76EE\u6587\u4EF6\u5217\u8868\u3002",
4980
5020
  contextResourceTitle: "{path}\uFF08\u975E\u6587\u672C\u8D44\u6E90\uFF0C\u5F53\u524D\u53EA\u4F5C\u4E3A\u9879\u76EE\u7ED3\u6784\u663E\u793A\uFF09",
@@ -5519,6 +5559,275 @@ ${block}`;
5519
5559
  });
5520
5560
  }).call(globalThis, void 0, void 0, void 0);
5521
5561
  (function runClassicContentModule14(module, exports, require2) {
5562
+ (function(root, factory) {
5563
+ const api = factory();
5564
+ if (typeof module === "object" && module.exports) module.exports = api;
5565
+ if (root) root.CodexOverleafGlobalPreferences = api;
5566
+ })(typeof globalThis !== "undefined" ? globalThis : this, function() {
5567
+ "use strict";
5568
+ const STORAGE_KEY = "codexOverleafGlobalPrefsV1";
5569
+ const LEGACY_KEY = "codexOverleafPrefs";
5570
+ const READ_MESSAGE = "codex-overleaf/global-preferences/read";
5571
+ const PATCH_MESSAGE = "codex-overleaf/global-preferences/patch";
5572
+ const DEFAULTS = Object.freeze({
5573
+ theme: "dark",
5574
+ locale: "en",
5575
+ preloadProjectContext: true,
5576
+ loadCodexLocalSkills: true,
5577
+ loadCodexOverleafSkills: true
5578
+ });
5579
+ const FIELDS = Object.freeze([...Object.keys(DEFAULTS), "codexOverleafSkillEnabled"]);
5580
+ function validSkillId(id) {
5581
+ return /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(id) && !["__proto__", "prototype", "constructor"].includes(id) && !id.includes("..");
5582
+ }
5583
+ function pick(value = {}) {
5584
+ const skills = {};
5585
+ for (const [id, enabled] of Object.entries(value.codexOverleafSkillEnabled || {}).slice(0, 200)) {
5586
+ if (validSkillId(id) && typeof enabled === "boolean") skills[id] = enabled;
5587
+ }
5588
+ return {
5589
+ theme: ["dark", "light", "auto"].includes(value.theme) ? value.theme : "dark",
5590
+ locale: value.locale === "zh" ? "zh" : "en",
5591
+ preloadProjectContext: value.preloadProjectContext !== false,
5592
+ loadCodexLocalSkills: value.loadCodexLocalSkills !== false,
5593
+ loadCodexOverleafSkills: value.loadCodexOverleafSkills !== false,
5594
+ codexOverleafSkillEnabled: skills
5595
+ };
5596
+ }
5597
+ function validatePatch(value) {
5598
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid global preference patch.");
5599
+ const patch = {};
5600
+ for (const [key, item] of Object.entries(value)) {
5601
+ if (!FIELDS.includes(key)) throw new Error("Unknown global preference: " + key);
5602
+ if (key === "theme" && !["dark", "light", "auto"].includes(item) || key === "locale" && !["en", "zh"].includes(item) || typeof DEFAULTS[key] === "boolean" && typeof item !== "boolean") {
5603
+ throw new Error("Invalid global preference: " + key);
5604
+ }
5605
+ if (key === "codexOverleafSkillEnabled") {
5606
+ if (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).length > 200) {
5607
+ throw new Error("Invalid skill preference map.");
5608
+ }
5609
+ for (const [id, enabled] of Object.entries(item)) {
5610
+ if (!validSkillId(id) || typeof enabled !== "boolean") throw new Error("Invalid skill preference.");
5611
+ }
5612
+ patch[key] = { ...item };
5613
+ } else patch[key] = item;
5614
+ }
5615
+ return patch;
5616
+ }
5617
+ function applyPatch(values, patch) {
5618
+ return { ...pick(values), ...patch, codexOverleafSkillEnabled: {
5619
+ ...pick(values).codexOverleafSkillEnabled,
5620
+ ...patch.codexOverleafSkillEnabled || {}
5621
+ } };
5622
+ }
5623
+ function changedKeys(left, right) {
5624
+ return FIELDS.filter((key) => JSON.stringify(left[key]) !== JSON.stringify(right[key]));
5625
+ }
5626
+ function validateSnapshot(value) {
5627
+ if (!value || value.schemaVersion !== 1 || !Number.isSafeInteger(value.revision) || value.revision < 1) {
5628
+ throw new Error("Invalid global preference revision.");
5629
+ }
5630
+ validatePatch(value.values);
5631
+ if (!FIELDS.every((key) => Object.hasOwn(value.values, key))) throw new Error("Incomplete global preferences.");
5632
+ return { schemaVersion: 1, revision: value.revision, values: pick(value.values) };
5633
+ }
5634
+ function createStore(storage) {
5635
+ let tail = Promise.resolve();
5636
+ function serial(work) {
5637
+ const pending = tail.then(work);
5638
+ tail = pending.catch(() => {
5639
+ });
5640
+ return pending;
5641
+ }
5642
+ async function read() {
5643
+ const stored = await storage.get([STORAGE_KEY, LEGACY_KEY]);
5644
+ if (stored[STORAGE_KEY] !== void 0) return validateSnapshot(stored[STORAGE_KEY]);
5645
+ const snapshot = { schemaVersion: 1, revision: 1, values: pick(stored[LEGACY_KEY] || {}) };
5646
+ await storage.set({ [STORAGE_KEY]: snapshot });
5647
+ return snapshot;
5648
+ }
5649
+ return Object.freeze({
5650
+ read: () => serial(read),
5651
+ patch: (input) => serial(async () => {
5652
+ const patch = validatePatch(input);
5653
+ const previous = await read();
5654
+ const values = applyPatch(previous.values, patch);
5655
+ if (Object.keys(values.codexOverleafSkillEnabled).length > 200) throw new Error("Too many skill preferences.");
5656
+ if (!changedKeys(previous.values, values).length) return previous;
5657
+ const snapshot = { schemaVersion: 1, revision: previous.revision + 1, values };
5658
+ await storage.set({ [STORAGE_KEY]: snapshot });
5659
+ return snapshot;
5660
+ })
5661
+ });
5662
+ }
5663
+ function installBackground(chromeApi) {
5664
+ const store = createStore(chromeApi.storage?.local);
5665
+ chromeApi.runtime.onMessage.addListener((message, sender, respond) => {
5666
+ if (![READ_MESSAGE, PATCH_MESSAGE].includes(message?.type)) return void 0;
5667
+ let allowed = false;
5668
+ try {
5669
+ const url = new URL(sender?.tab?.url || "");
5670
+ allowed = sender.id === chromeApi.runtime.id && url.protocol === "https:" && ["overleaf.com", "www.overleaf.com"].includes(url.hostname) && /^\/project(?:\/|$)/.test(url.pathname);
5671
+ } catch (_error) {
5672
+ }
5673
+ if (!allowed) {
5674
+ respond({ ok: false, error: { message: "Global preferences require an Overleaf tab." } });
5675
+ return false;
5676
+ }
5677
+ const pending = message.type === READ_MESSAGE ? store.read() : store.patch(message.patch);
5678
+ pending.then((result) => respond({ ok: true, result }), (error) => respond({ ok: false, error: { message: error.message } }));
5679
+ return true;
5680
+ });
5681
+ return store;
5682
+ }
5683
+ return Object.freeze({
5684
+ STORAGE_KEY,
5685
+ LEGACY_KEY,
5686
+ READ_MESSAGE,
5687
+ PATCH_MESSAGE,
5688
+ FIELDS,
5689
+ pick,
5690
+ validatePatch,
5691
+ validateSnapshot,
5692
+ applyPatch,
5693
+ changedKeys,
5694
+ createStore,
5695
+ installBackground
5696
+ });
5697
+ });
5698
+ }).call(globalThis, void 0, void 0, void 0);
5699
+ (function runClassicContentModule15(module, exports, require2) {
5700
+ (function(root, factory) {
5701
+ if (typeof module === "object" && module.exports) module.exports = factory(require2("../shared/globalPreferences"));
5702
+ else root.CodexOverleafModuleRegistry.define("GlobalPreferencesController", ["GlobalPreferences"], factory);
5703
+ })(typeof globalThis !== "undefined" ? globalThis : this, function(Preferences) {
5704
+ "use strict";
5705
+ const CONTROLS = Object.freeze({
5706
+ theme: "[data-theme-select]",
5707
+ locale: "[data-language-select]",
5708
+ preloadProjectContext: "[data-preload-project-context]",
5709
+ loadCodexLocalSkills: "[data-load-codex-local-skills]",
5710
+ loadCodexOverleafSkills: "[data-load-codex-overleaf-skills]"
5711
+ });
5712
+ function create(options2) {
5713
+ const chromeApi = options2.chromeApi;
5714
+ const fallback = Preferences.pick(options2.getState?.() || {});
5715
+ let snapshot = null;
5716
+ let loading = null;
5717
+ let subscribed = false;
5718
+ let disposed = false;
5719
+ let lastError = null;
5720
+ let tail = Promise.resolve();
5721
+ const pending = [];
5722
+ function view() {
5723
+ return pending.reduce((values, operation) => Preferences.applyPatch(values, operation.patch), snapshot?.values || fallback);
5724
+ }
5725
+ function render(force = false) {
5726
+ if (disposed) return;
5727
+ const previous = options2.getState?.() || {};
5728
+ const values = view();
5729
+ const changed = Preferences.changedKeys(Preferences.pick(previous), values);
5730
+ options2.setState?.({ ...previous, ...values });
5731
+ const panel = options2.getPanel?.();
5732
+ if (!panel) return;
5733
+ for (const [field, selector] of Object.entries(CONTROLS)) {
5734
+ const control = panel.querySelector(selector);
5735
+ if (!control) continue;
5736
+ if (typeof values[field] === "boolean") control.checked = values[field];
5737
+ else if (control.value !== values[field]) control.value = values[field];
5738
+ }
5739
+ if (force || changed.includes("theme")) options2.applyTheme?.(values.theme);
5740
+ if (changed.includes("locale")) options2.applyLocale?.();
5741
+ if (force || changed.includes("preloadProjectContext")) options2.onPreloadChange?.(values.preloadProjectContext);
5742
+ if (changed.some((key) => key === "codexOverleafSkillEnabled" || key.startsWith("loadCodex"))) options2.onSkillsChange?.();
5743
+ }
5744
+ function accept(value) {
5745
+ const next = Preferences.validateSnapshot(value);
5746
+ lastError = null;
5747
+ if (!snapshot || next.revision > snapshot.revision) {
5748
+ snapshot = next;
5749
+ render();
5750
+ }
5751
+ return snapshot;
5752
+ }
5753
+ async function request(type, patch) {
5754
+ const response = await chromeApi.runtime.sendMessage({ type, ...patch ? { patch } : {} });
5755
+ if (!response?.ok) throw new Error(response?.error?.message || "Global preferences are unavailable.");
5756
+ return response.result;
5757
+ }
5758
+ function onStorageChange(changes, area) {
5759
+ if (disposed || area !== "local" || !changes[Preferences.STORAGE_KEY]?.newValue) return;
5760
+ try {
5761
+ accept(changes[Preferences.STORAGE_KEY].newValue);
5762
+ } catch (error) {
5763
+ reportError(error);
5764
+ }
5765
+ }
5766
+ function reportError(error) {
5767
+ lastError = error;
5768
+ options2.onError?.(error);
5769
+ }
5770
+ async function initialize() {
5771
+ if (!subscribed) {
5772
+ chromeApi.storage.onChanged.addListener(onStorageChange);
5773
+ subscribed = true;
5774
+ }
5775
+ if (snapshot) return snapshot;
5776
+ if (!loading) {
5777
+ loading = request(Preferences.READ_MESSAGE).then(accept).catch((error) => {
5778
+ reportError(error);
5779
+ return null;
5780
+ }).finally(() => {
5781
+ loading = null;
5782
+ });
5783
+ }
5784
+ return loading;
5785
+ }
5786
+ function update(input) {
5787
+ const patch = Preferences.validatePatch(input);
5788
+ if (!Preferences.changedKeys(view(), Preferences.applyPatch(view(), patch)).length) return pending.length ? tail : Promise.resolve(snapshot);
5789
+ const operation = { patch };
5790
+ pending.push(operation);
5791
+ render();
5792
+ const result = tail.catch(() => {
5793
+ }).then(() => request(Preferences.PATCH_MESSAGE, patch)).then(Preferences.validateSnapshot).then((value) => {
5794
+ accept(value);
5795
+ pending.splice(pending.indexOf(operation), 1);
5796
+ render();
5797
+ return snapshot;
5798
+ }, (error) => {
5799
+ pending.splice(pending.indexOf(operation), 1);
5800
+ render();
5801
+ reportError(error);
5802
+ throw error;
5803
+ });
5804
+ tail = result;
5805
+ return result;
5806
+ }
5807
+ function handleInput(event) {
5808
+ const field = Object.keys(CONTROLS).find((key) => event?.target?.matches?.(CONTROLS[key]));
5809
+ if (!field) return null;
5810
+ return update({ [field]: ["theme", "locale"].includes(field) ? event.target.value : Boolean(event.target.checked) });
5811
+ }
5812
+ return Object.freeze({
5813
+ initialize,
5814
+ update,
5815
+ handleInput,
5816
+ overlay: (state) => snapshot || pending.length ? { ...state, ...view() } : state,
5817
+ refreshView: () => {
5818
+ if (snapshot) render(true);
5819
+ if (lastError) options2.onError?.(lastError);
5820
+ },
5821
+ destroy() {
5822
+ disposed = true;
5823
+ if (subscribed) chromeApi.storage.onChanged.removeListener(onStorageChange);
5824
+ }
5825
+ });
5826
+ }
5827
+ return Object.freeze({ create });
5828
+ });
5829
+ }).call(globalThis, void 0, void 0, void 0);
5830
+ (function runClassicContentModule16(module, exports, require2) {
5522
5831
  (function initCodexOverleafRunExecutionSnapshotCodec(root, factory) {
5523
5832
  if (typeof module === "object" && module.exports) {
5524
5833
  module.exports = factory();
@@ -5678,7 +5987,7 @@ ${block}`;
5678
5987
  };
5679
5988
  });
5680
5989
  }).call(globalThis, void 0, void 0, void 0);
5681
- (function runClassicContentModule15(module, exports, require2) {
5990
+ (function runClassicContentModule17(module, exports, require2) {
5682
5991
  (function initCodexOverleafRunExecutionSnapshot(root, factory) {
5683
5992
  if (typeof module === "object" && module.exports) {
5684
5993
  module.exports = factory(require2("./providerProfiles"), require2("./runExecutionSnapshotCodec"));
@@ -5848,7 +6157,7 @@ ${block}`;
5848
6157
  };
5849
6158
  });
5850
6159
  }).call(globalThis, void 0, void 0, void 0);
5851
- (function runClassicContentModule16(module, exports, require2) {
6160
+ (function runClassicContentModule18(module, exports, require2) {
5852
6161
  (function(root, factory) {
5853
6162
  var api = factory();
5854
6163
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -6027,7 +6336,7 @@ ${block}`;
6027
6336
  });
6028
6337
  });
6029
6338
  }).call(globalThis, void 0, void 0, void 0);
6030
- (function runClassicContentModule17(module, exports, require2) {
6339
+ (function runClassicContentModule19(module, exports, require2) {
6031
6340
  (function(root, factory) {
6032
6341
  if (typeof module === "object" && module.exports) {
6033
6342
  module.exports = factory(require2("./scopedPersistenceQueuePolicy"));
@@ -6100,7 +6409,7 @@ ${block}`;
6100
6409
  });
6101
6410
  });
6102
6411
  }).call(globalThis, void 0, void 0, void 0);
6103
- (function runClassicContentModule18(module, exports, require2) {
6412
+ (function runClassicContentModule20(module, exports, require2) {
6104
6413
  (function(root, factory) {
6105
6414
  if (typeof module === "object" && module.exports) {
6106
6415
  module.exports = factory();
@@ -6157,7 +6466,7 @@ ${block}`;
6157
6466
  });
6158
6467
  });
6159
6468
  }).call(globalThis, void 0, void 0, void 0);
6160
- (function runClassicContentModule19(module, exports, require2) {
6469
+ (function runClassicContentModule21(module, exports, require2) {
6161
6470
  (function(root, factory) {
6162
6471
  if (typeof module === "object" && module.exports) {
6163
6472
  module.exports = factory(
@@ -6381,7 +6690,7 @@ ${block}`;
6381
6690
  });
6382
6691
  });
6383
6692
  }).call(globalThis, void 0, void 0, void 0);
6384
- (function runClassicContentModule20(module, exports, require2) {
6693
+ (function runClassicContentModule22(module, exports, require2) {
6385
6694
  (function initCodexOverleafRunInputQueue(root, factory) {
6386
6695
  if (typeof module === "object" && module.exports) {
6387
6696
  module.exports = factory(require2("./runExecutionSnapshot"));
@@ -6651,7 +6960,7 @@ ${block}`;
6651
6960
  };
6652
6961
  });
6653
6962
  }).call(globalThis, void 0, void 0, void 0);
6654
- (function runClassicContentModule21(module, exports, require2) {
6963
+ (function runClassicContentModule23(module, exports, require2) {
6655
6964
  (function initCodexOverleafSettlementFacts(root, factory) {
6656
6965
  if (typeof module === "object" && module.exports) {
6657
6966
  module.exports = factory(require2("./failureReasons"));
@@ -6943,7 +7252,7 @@ ${block}`;
6943
7252
  };
6944
7253
  });
6945
7254
  }).call(globalThis, void 0, void 0, void 0);
6946
- (function runClassicContentModule22(module, exports, require2) {
7255
+ (function runClassicContentModule24(module, exports, require2) {
6947
7256
  (function initCodexOverleafWritebackEvidenceProjection(root, factory) {
6948
7257
  const api = factory();
6949
7258
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -7076,7 +7385,7 @@ ${block}`;
7076
7385
  });
7077
7386
  });
7078
7387
  }).call(globalThis, void 0, void 0, void 0);
7079
- (function runClassicContentModule23(module, exports, require2) {
7388
+ (function runClassicContentModule25(module, exports, require2) {
7080
7389
  (function initCodexOverleafWritebackOperationEvidence(root, factory) {
7081
7390
  if (typeof module === "object" && module.exports) {
7082
7391
  module.exports = factory();
@@ -7215,7 +7524,7 @@ ${block}`;
7215
7524
  };
7216
7525
  });
7217
7526
  }).call(globalThis, void 0, void 0, void 0);
7218
- (function runClassicContentModule24(module, exports, require2) {
7527
+ (function runClassicContentModule26(module, exports, require2) {
7219
7528
  (function initCodexOverleafWritebackSettlement(root, factory) {
7220
7529
  if (typeof module === "object" && module.exports) {
7221
7530
  module.exports = factory(
@@ -7838,7 +8147,107 @@ ${block}`;
7838
8147
  };
7839
8148
  });
7840
8149
  }).call(globalThis, void 0, void 0, void 0);
7841
- (function runClassicContentModule25(module, exports, require2) {
8150
+ (function runClassicContentModule27(module, exports, require2) {
8151
+ (function initSessionTitle(root, factory) {
8152
+ if (typeof module === "object" && module.exports) {
8153
+ module.exports = factory();
8154
+ } else {
8155
+ root.CodexOverleafModuleRegistry.define("SessionTitle", [], factory);
8156
+ }
8157
+ })(typeof globalThis !== "undefined" ? globalThis : window, function sessionTitleFactory() {
8158
+ "use strict";
8159
+ const INPUT_LIMIT = 12e3;
8160
+ const STORAGE_LIMIT = 80;
8161
+ const segmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
8162
+ const FILE_TOKEN = /@file:(?:"([^"]+)"|'([^']+)'|([^\s]+))/g;
8163
+ function derive(runs, task, options2 = {}) {
8164
+ let fallback = "";
8165
+ for (const run of Array.isArray(runs) ? runs : []) {
8166
+ const candidate = fromPrompt(run?.task, options2);
8167
+ fallback || (fallback = candidate);
8168
+ if (candidate && !isGeneric(candidate)) return candidate;
8169
+ }
8170
+ const draft = fromPrompt(task, options2);
8171
+ return draft && !isGeneric(draft) ? draft : fallback || draft;
8172
+ }
8173
+ function fromPrompt(value, { sanitize = (text) => text, maxChars = 24 } = {}) {
8174
+ if (typeof value !== "string" || !value.trim()) return "";
8175
+ const source = String(sanitize(value.slice(0, INPUT_LIMIT)) || "");
8176
+ let fence = null;
8177
+ let fallback = "";
8178
+ for (const rawLine of source.split(/\r?\n/)) {
8179
+ const line = rawLine.trim();
8180
+ const marker = /^(`{3,}|~{3,})(.*)$/.exec(line);
8181
+ if (marker) {
8182
+ if (!fence) {
8183
+ fence = marker[1];
8184
+ const language = marker[2].trim().match(/^[A-Za-z0-9_+-]{1,24}\b/)?.[0] || "code";
8185
+ fallback || (fallback = `${language} snippet`);
8186
+ } else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && !marker[2].trim()) {
8187
+ fence = null;
8188
+ }
8189
+ continue;
8190
+ }
8191
+ if (fence || !line || /^>/.test(line)) continue;
8192
+ const withoutContext = line.replace(FILE_TOKEN, " ").replace(/@(context|compile-log)\b/g, " ").trim();
8193
+ const cleaned = cleanLine(line);
8194
+ if (!withoutContext || /^https?:\/\/\S+$/i.test(line)) {
8195
+ fallback || (fallback = cleaned);
8196
+ continue;
8197
+ }
8198
+ for (const sentence of cleaned.split(/[\u3002\uff01\uff1f]+/u)) {
8199
+ const candidate = sentence.trim().replace(/[,;:\uff0c\uff1b\uff1a]+$/u, "").trim();
8200
+ if (!/[\p{L}\p{N}]/u.test(candidate) || isScaffolding(candidate)) continue;
8201
+ fallback || (fallback = candidate);
8202
+ if (!isGeneric(candidate)) return shorten(candidate, maxChars);
8203
+ }
8204
+ }
8205
+ return shorten(fallback, maxChars);
8206
+ }
8207
+ function cleanLine(value) {
8208
+ return value.replace(/^#{1,6}\s+/, "").replace(/^(?:[-*+]\s+|\d+\.\s+|\d+[)\u3001]\s*)/, "").replace(FILE_TOKEN, (_match, quoted, singleQuoted, bare) => String(quoted || singleQuoted || bare || "").replace(/\\/g, "/").split("/").pop()).replace(/@(context|compile-log)\b/g, " ").replace(/!?\[([^\]]*)\]\([^\n)]*\)/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/https?:\/\/\S+/gi, (address) => {
8209
+ try {
8210
+ return new URL(address).hostname;
8211
+ } catch (_error) {
8212
+ return "";
8213
+ }
8214
+ }).replace(/\[(?:REDACTED_SECRET|local path(?::\d+)?)\]/gi, " ").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\s+/g, " ").trim().replace(/^(?:\u8bf7(?:\u4f60)?\u5e2e\u6211|\u8bf7\u4f60|\u5e2e\u6211|\u9ebb\u70e6\u4f60(?:\u5e2e\u6211)?|\u9ebb\u70e6\u5e2e\u6211|please\s+)\s*/i, "");
8215
+ }
8216
+ function isScaffolding(value) {
8217
+ return /^(?:my request(?: for [^:]+)?|request|task|context|files(?: mentioned by [^:]+)?|attachments|\u6211\u7684\u8bf7\u6c42|\u9700\u6c42|\u4efb\u52a1|\u4e0a\u4e0b\u6587|\u9644\u4ef6|\u6587\u4ef6)$/i.test(value);
8218
+ }
8219
+ function isGeneric(value) {
8220
+ const text = String(value || "").trim().replace(/[\s.!?\u3002\uff1f\uff01\u2026]+$/u, "");
8221
+ if (!text || Array.from(text).length < 2) return true;
8222
+ return /^(?:ok(?:ay)?|yes|no|go|done|continue|proceed|thanks?|thank you|hello|hi|test|new task|untitled task|\u597d(?:\u7684)?|\u53ef\u4ee5|\u7ee7\u7eed|\u786e\u8ba4|\u786e\u5b9a|\u6536\u5230|\u8c22\u8c22|\u6d4b\u8bd5|\u6267\u884c|\u662f\u7684|\u4f60\u597d)$/i.test(text) || /^[A-Za-z0-9_+-]{1,24} snippet$/i.test(text) || /^[^\s]+\.(?:tex|bib|md|txt|pdf|svg|png|jpe?g|webp)(?::\d+)?$/i.test(text) || /^(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}$/i.test(text);
8223
+ }
8224
+ function shorten(value, maxChars) {
8225
+ const title = String(value || "").trim();
8226
+ if (!title) return "";
8227
+ const chars = Number.isFinite(maxChars) && maxChars > 0 ? Math.min(40, Math.floor(maxChars)) : 24;
8228
+ const maxUnits = chars * 2;
8229
+ const glyphs = segmenter ? Array.from(segmenter.segment(title), (item) => item.segment) : Array.from(title);
8230
+ const width = (glyph) => /[^\u0000-\u00ff]/u.test(glyph) ? 2 : 1;
8231
+ if (title.length <= STORAGE_LIMIT && glyphs.reduce((total, glyph) => total + width(glyph), 0) <= maxUnits) return title;
8232
+ let prefix = "";
8233
+ let units = 0;
8234
+ let index = 0;
8235
+ for (; index < glyphs.length; index++) {
8236
+ const glyph = glyphs[index];
8237
+ if (units + width(glyph) > maxUnits - 2 || prefix.length + glyph.length > STORAGE_LIMIT - 1) break;
8238
+ prefix += glyph;
8239
+ units += width(glyph);
8240
+ }
8241
+ const boundary = prefix.lastIndexOf(" ");
8242
+ if (/[A-Za-z0-9]$/.test(prefix) && /^[A-Za-z0-9]/.test(glyphs[index] || "") && boundary >= prefix.length * 0.6) {
8243
+ prefix = prefix.slice(0, boundary);
8244
+ }
8245
+ return prefix.trimEnd().replace(/[,;:\uff0c\uff1b\uff1a]+$/u, "") + "\u2026";
8246
+ }
8247
+ return Object.freeze({ derive, fromPrompt, isGeneric });
8248
+ });
8249
+ }).call(globalThis, void 0, void 0, void 0);
8250
+ (function runClassicContentModule28(module, exports, require2) {
7842
8251
  (function initSessionState(root, factory) {
7843
8252
  if (typeof module === "object" && module.exports) {
7844
8253
  module.exports = factory(
@@ -7846,16 +8255,17 @@ ${block}`;
7846
8255
  require2("./settlementFacts"),
7847
8256
  require2("./runInputQueue"),
7848
8257
  require2("./lineReferences"),
7849
- require2("./pathRedaction")
8258
+ require2("./pathRedaction"),
8259
+ require2("./sessionTitle")
7850
8260
  );
7851
8261
  } else {
7852
8262
  root.CodexOverleafModuleRegistry.define(
7853
8263
  "SessionState",
7854
- ["I18n", "SettlementFacts", "RunInputQueue", "LineReferences", "PathRedaction"],
8264
+ ["I18n", "SettlementFacts", "RunInputQueue", "LineReferences", "PathRedaction", "SessionTitle"],
7855
8265
  factory
7856
8266
  );
7857
8267
  }
7858
- })(typeof globalThis !== "undefined" ? globalThis : window, function sessionStateFactory(i18n, SettlementFacts, RunInputQueue, LineReferences, PathRedaction) {
8268
+ })(typeof globalThis !== "undefined" ? globalThis : window, function sessionStateFactory(i18n, SettlementFacts, RunInputQueue, LineReferences, PathRedaction, SessionTitle) {
7859
8269
  "use strict";
7860
8270
  const DEFAULT_PANEL_STATE = {
7861
8271
  mode: "ask",
@@ -8177,6 +8587,9 @@ ${block}`;
8177
8587
  if (session.titleSource === "manual" && patch.titleSource !== "manual" && Object.prototype.hasOwnProperty.call(patch, "title")) {
8178
8588
  next.title = session.title;
8179
8589
  next.titleSource = "manual";
8590
+ } else if (session.titleSource === "auto" && patch.titleSource !== "manual" && typeof patch.title === "string" && patch.title.trim() && session.runs?.length && SessionTitle.isGeneric(sanitizeAutoTitle(patch.title)) && !SessionTitle.isGeneric(sanitizeAutoTitle(session.title))) {
8591
+ next.title = session.title;
8592
+ next.titleSource = "auto";
8180
8593
  }
8181
8594
  return normalizeSession({
8182
8595
  ...next
@@ -8238,12 +8651,7 @@ ${block}`;
8238
8651
  }, sessions[0]);
8239
8652
  }
8240
8653
  function deriveSessionTitle(runs, task) {
8241
- const taskTitle = sanitizeAutoTitle(task);
8242
- if (taskTitle) {
8243
- return taskTitle;
8244
- }
8245
- const firstRunTask = Array.isArray(runs) && runs.length ? runs[0]?.task : "";
8246
- return sanitizeAutoTitle(firstRunTask);
8654
+ return SessionTitle.derive(runs, task, { sanitize: sanitizeAssistantVisibleText, maxChars: SESSION_AUTO_TITLE_CHARS });
8247
8655
  }
8248
8656
  function renameSession(state, sessionId, rawTitle, options2 = {}) {
8249
8657
  const sessions = Array.isArray(state?.sessions) ? state.sessions : [];
@@ -8273,14 +8681,13 @@ ${block}`;
8273
8681
  if (titleSource === "manual") {
8274
8682
  return normalizeTextField(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle, STORAGE_DEFAULT_LIMITS.sessionTitleChars);
8275
8683
  }
8276
- return sanitizeAutoTitle(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle) || derivedTitle || "";
8684
+ const current = sanitizeAutoTitle(rawTitle === LEGACY_DEFAULT_SESSION_TITLE ? "" : rawTitle);
8685
+ if (!current || SessionTitle.isGeneric(current)) return derivedTitle || current || "";
8686
+ if (current.endsWith("\u2026") && derivedTitle.startsWith(current.slice(0, -1))) return derivedTitle;
8687
+ return current;
8277
8688
  }
8278
8689
  function sanitizeAutoTitle(value) {
8279
- const title = sanitizeAssistantVisibleText(value).replace(/@file:[^\s]+/g, " ").replace(/@(context|compile-log)\b/g, " ").replace(/\s+/g, " ").trim();
8280
- if (!title) {
8281
- return "";
8282
- }
8283
- return title.length > SESSION_AUTO_TITLE_CHARS ? `${title.slice(0, SESSION_AUTO_TITLE_CHARS - 1)}\u2026` : title;
8690
+ return SessionTitle.fromPrompt(value, { sanitize: sanitizeAssistantVisibleText, maxChars: SESSION_AUTO_TITLE_CHARS });
8284
8691
  }
8285
8692
  function isDisplayableSession(session) {
8286
8693
  if (!session?.id) {
@@ -9157,7 +9564,7 @@ ${block}`;
9157
9564
  };
9158
9565
  });
9159
9566
  }).call(globalThis, void 0, void 0, void 0);
9160
- (function runClassicContentModule26(module, exports, require2) {
9567
+ (function runClassicContentModule29(module, exports, require2) {
9161
9568
  (function initStorageKeys(root, factory) {
9162
9569
  if (typeof module === "object" && module.exports) {
9163
9570
  module.exports = factory();
@@ -9185,7 +9592,7 @@ ${block}`;
9185
9592
  };
9186
9593
  });
9187
9594
  }).call(globalThis, void 0, void 0, void 0);
9188
- (function runClassicContentModule27(module, exports, require2) {
9595
+ (function runClassicContentModule30(module, exports, require2) {
9189
9596
  (function initStaleGuard(root, factory) {
9190
9597
  if (typeof module === "object" && module.exports) {
9191
9598
  module.exports = factory();
@@ -9336,7 +9743,7 @@ ${block}`;
9336
9743
  };
9337
9744
  });
9338
9745
  }).call(globalThis, void 0, void 0, void 0);
9339
- (function runClassicContentModule28(module, exports, require2) {
9746
+ (function runClassicContentModule31(module, exports, require2) {
9340
9747
  (function initStorageRunActions(root, factory) {
9341
9748
  if (typeof module === "object" && module.exports) {
9342
9749
  module.exports = factory();
@@ -9465,7 +9872,7 @@ ${block}`;
9465
9872
  };
9466
9873
  });
9467
9874
  }).call(globalThis, void 0, void 0, void 0);
9468
- (function runClassicContentModule29(module, exports, require2) {
9875
+ (function runClassicContentModule32(module, exports, require2) {
9469
9876
  (function initStorageSessionClaims(root, factory) {
9470
9877
  var api = factory();
9471
9878
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -9531,7 +9938,7 @@ ${block}`;
9531
9938
  return Object.freeze({ createSessionClaimer });
9532
9939
  });
9533
9940
  }).call(globalThis, void 0, void 0, void 0);
9534
- (function runClassicContentModule30(module, exports, require2) {
9941
+ (function runClassicContentModule33(module, exports, require2) {
9535
9942
  (function initStorageDb(root, factory) {
9536
9943
  if (typeof module === "object" && module.exports) {
9537
9944
  module.exports = factory(
@@ -10756,7 +11163,7 @@ ${block}`;
10756
11163
  };
10757
11164
  });
10758
11165
  }).call(globalThis, void 0, void 0, void 0);
10759
- (function runClassicContentModule31(module, exports, require2) {
11166
+ (function runClassicContentModule34(module, exports, require2) {
10760
11167
  (function initStorageMigration(root, factory) {
10761
11168
  if (typeof module === "object" && module.exports) {
10762
11169
  module.exports = factory(require2("./storageDb"));
@@ -11211,7 +11618,7 @@ ${block}`;
11211
11618
  };
11212
11619
  });
11213
11620
  }).call(globalThis, void 0, void 0, void 0);
11214
- (function runClassicContentModule32(module, exports, require2) {
11621
+ (function runClassicContentModule35(module, exports, require2) {
11215
11622
  (function initCompileAdapter(root, factory) {
11216
11623
  if (typeof module === "object" && module.exports) {
11217
11624
  module.exports = factory();
@@ -11354,7 +11761,7 @@ ${block}`;
11354
11761
  };
11355
11762
  });
11356
11763
  }).call(globalThis, void 0, void 0, void 0);
11357
- (function runClassicContentModule33(module, exports, require2) {
11764
+ (function runClassicContentModule36(module, exports, require2) {
11358
11765
  (function initCodexOverleafCompatibility(root, factory) {
11359
11766
  if (typeof module === "object" && module.exports) {
11360
11767
  module.exports = factory();
@@ -11368,7 +11775,7 @@ ${block}`;
11368
11775
  const MIN_NATIVE_VERSION = "1.0.0";
11369
11776
  const MIN_COMPATIBLE_NATIVE_VERSION = "1.0.0";
11370
11777
  const MIN_COMPATIBLE_EXTENSION_VERSION = "1.0.0";
11371
- const BUILD_TARGET_VERSION = "2.3.4";
11778
+ const BUILD_TARGET_VERSION = "2.3.6";
11372
11779
  const DEFAULT_CHROME_EXTENSION_ID = "illdpneeeopfffmiepaejglgmhpmdhdc";
11373
11780
  const REQUIRED_CAPABILITIES = Object.freeze([
11374
11781
  "bridgePing",
@@ -11664,7 +12071,7 @@ ${block}`;
11664
12071
  };
11665
12072
  });
11666
12073
  }).call(globalThis, void 0, void 0, void 0);
11667
- (function runClassicContentModule34(module, exports, require2) {
12074
+ (function runClassicContentModule37(module, exports, require2) {
11668
12075
  (function(root, factory) {
11669
12076
  var api = factory();
11670
12077
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -11795,7 +12202,7 @@ ${block}`;
11795
12202
  });
11796
12203
  });
11797
12204
  }).call(globalThis, void 0, void 0, void 0);
11798
- (function runClassicContentModule35(module, exports, require2) {
12205
+ (function runClassicContentModule38(module, exports, require2) {
11799
12206
  (function initCodexOverleafManagedUpdateProjection(root) {
11800
12207
  "use strict";
11801
12208
  const PHASE_ALIASES = Object.freeze({
@@ -11821,7 +12228,7 @@ ${block}`;
11821
12228
  "deferred"
11822
12229
  ]);
11823
12230
  const PHASE_TIMEOUTS = Object.freeze({
11824
- checking: 30 * 1e3,
12231
+ checking: 60 * 1e3,
11825
12232
  downloading: 2 * 60 * 1e3,
11826
12233
  applying: 90 * 1e3,
11827
12234
  awaiting_health: 2 * 60 * 1e3,
@@ -12101,7 +12508,7 @@ ${block}`;
12101
12508
  });
12102
12509
  })(typeof window !== "undefined" ? window : globalThis);
12103
12510
  }).call(globalThis, void 0, void 0, void 0);
12104
- (function runClassicContentModule36(module, exports, require2) {
12511
+ (function runClassicContentModule39(module, exports, require2) {
12105
12512
  (function initGovernanceRules(root, factory) {
12106
12513
  if (typeof module === "object" && module.exports) {
12107
12514
  let projectFiles = null;
@@ -12318,7 +12725,7 @@ ${block}`;
12318
12725
  };
12319
12726
  });
12320
12727
  }).call(globalThis, void 0, void 0, void 0);
12321
- (function runClassicContentModule37(module, exports, require2) {
12728
+ (function runClassicContentModule40(module, exports, require2) {
12322
12729
  (function initSensitiveScan(root, factory) {
12323
12730
  if (typeof module === "object" && module.exports) {
12324
12731
  module.exports = factory();
@@ -12428,7 +12835,7 @@ ${block}`;
12428
12835
  };
12429
12836
  });
12430
12837
  }).call(globalThis, void 0, void 0, void 0);
12431
- (function runClassicContentModule38(module, exports, require2) {
12838
+ (function runClassicContentModule41(module, exports, require2) {
12432
12839
  (function initAuditRecords(root, factory) {
12433
12840
  if (typeof module === "object" && module.exports) {
12434
12841
  module.exports = factory();
@@ -12954,7 +13361,7 @@ ${block}`;
12954
13361
  };
12955
13362
  });
12956
13363
  }).call(globalThis, void 0, void 0, void 0);
12957
- (function runClassicContentModule39(module, exports, require2) {
13364
+ (function runClassicContentModule42(module, exports, require2) {
12958
13365
  (function initCodexOverleafNativeChannel(root, factory) {
12959
13366
  if (typeof module === "object" && module.exports) {
12960
13367
  module.exports = factory();
@@ -13033,7 +13440,7 @@ ${block}`;
13033
13440
  };
13034
13441
  });
13035
13442
  }).call(globalThis, void 0, void 0, void 0);
13036
- (function runClassicContentModule40(module, exports, require2) {
13443
+ (function runClassicContentModule43(module, exports, require2) {
13037
13444
  (function initCodexOverleafPageBridgeClient(root, factory) {
13038
13445
  const api = factory();
13039
13446
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -13236,7 +13643,7 @@ ${block}`;
13236
13643
  });
13237
13644
  });
13238
13645
  }).call(globalThis, void 0, void 0, void 0);
13239
- (function runClassicContentModule41(module, exports, require2) {
13646
+ (function runClassicContentModule44(module, exports, require2) {
13240
13647
  (function initCodexOverleafNativeCompatibilityController(root, factory) {
13241
13648
  const api = factory();
13242
13649
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -13515,7 +13922,7 @@ ${block}`;
13515
13922
  return Object.freeze({ create });
13516
13923
  });
13517
13924
  }).call(globalThis, void 0, void 0, void 0);
13518
- (function runClassicContentModule42(module, exports, require2) {
13925
+ (function runClassicContentModule45(module, exports, require2) {
13519
13926
  (function initCodexOverleafWritebackController(root, factory) {
13520
13927
  if (typeof module === "object" && module.exports) {
13521
13928
  module.exports = factory();
@@ -13780,7 +14187,7 @@ ${block}`;
13780
14187
  };
13781
14188
  });
13782
14189
  }).call(globalThis, void 0, void 0, void 0);
13783
- (function runClassicContentModule43(module, exports, require2) {
14190
+ (function runClassicContentModule46(module, exports, require2) {
13784
14191
  (function initCodexOverleafAssetTransferBroker(root, factory) {
13785
14192
  const api = factory();
13786
14193
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -13942,7 +14349,7 @@ ${block}`;
13942
14349
  return { create, mergeApplyResults };
13943
14350
  });
13944
14351
  }).call(globalThis, void 0, void 0, void 0);
13945
- (function runClassicContentModule44(module, exports, require2) {
14352
+ (function runClassicContentModule47(module, exports, require2) {
13946
14353
  (function initCodexOverleafReviewHunks(root, factory) {
13947
14354
  if (typeof module === "object" && module.exports) {
13948
14355
  module.exports = factory();
@@ -14161,7 +14568,7 @@ ${block}`;
14161
14568
  };
14162
14569
  });
14163
14570
  }).call(globalThis, void 0, void 0, void 0);
14164
- (function runClassicContentModule45(module, exports, require2) {
14571
+ (function runClassicContentModule48(module, exports, require2) {
14165
14572
  (function initCodexOverleafDiffReviewPanel(root, factory) {
14166
14573
  if (typeof module === "object" && module.exports) {
14167
14574
  module.exports = factory(require2("./reviewHunks"));
@@ -14888,7 +15295,7 @@ ${block}`;
14888
15295
  };
14889
15296
  });
14890
15297
  }).call(globalThis, void 0, void 0, void 0);
14891
- (function runClassicContentModule46(module, exports, require2) {
15298
+ (function runClassicContentModule49(module, exports, require2) {
14892
15299
  (function initCodexOverleafContextTray(root, factory) {
14893
15300
  if (typeof module === "object" && module.exports) {
14894
15301
  module.exports = factory(require2("../shared/projectFiles"));
@@ -15636,7 +16043,7 @@ ${block}`;
15636
16043
  };
15637
16044
  });
15638
16045
  }).call(globalThis, void 0, void 0, void 0);
15639
- (function runClassicContentModule47(module, exports, require2) {
16046
+ (function runClassicContentModule50(module, exports, require2) {
15640
16047
  (function initCodexOverleafLocalSkillsPanel(root, factory) {
15641
16048
  if (typeof module === "object" && module.exports) {
15642
16049
  module.exports = factory();
@@ -15867,7 +16274,7 @@ ${block}`;
15867
16274
  };
15868
16275
  });
15869
16276
  }).call(globalThis, void 0, void 0, void 0);
15870
- (function runClassicContentModule48(module, exports, require2) {
16277
+ (function runClassicContentModule51(module, exports, require2) {
15871
16278
  (function initCodexOverleafRunController(root, factory) {
15872
16279
  if (typeof module === "object" && module.exports) {
15873
16280
  module.exports = factory();
@@ -16097,7 +16504,7 @@ ${block}`;
16097
16504
  };
16098
16505
  });
16099
16506
  }).call(globalThis, void 0, void 0, void 0);
16100
- (function runClassicContentModule49(module, exports, require2) {
16507
+ (function runClassicContentModule52(module, exports, require2) {
16101
16508
  (function initCodexOverleafMirrorHealth(root, factory) {
16102
16509
  if (typeof module === "object" && module.exports) {
16103
16510
  module.exports = factory();
@@ -16181,7 +16588,7 @@ ${block}`;
16181
16588
  };
16182
16589
  });
16183
16590
  }).call(globalThis, void 0, void 0, void 0);
16184
- (function runClassicContentModule50(module, exports, require2) {
16591
+ (function runClassicContentModule53(module, exports, require2) {
16185
16592
  (function initCodexOverleafOtWarmMirrorController(root, factory) {
16186
16593
  if (typeof module === "object" && module.exports) {
16187
16594
  module.exports = factory();
@@ -16193,6 +16600,12 @@ ${block}`;
16193
16600
  const OT_POLL_INTERVAL_MS = 1e3;
16194
16601
  const OT_PATCH_DEBOUNCE_MS = 500;
16195
16602
  const OT_MAX_PATCH_BATCH = 25;
16603
+ const OT_FRESHNESS_MAX_AGE_MS = 3e4;
16604
+ const OT_MAX_FILE_BYTES = 1024 * 1024;
16605
+ const OT_MAX_PENDING_EVENTS = 64;
16606
+ const OT_MAX_PENDING_BYTES = 4 * 1024 * 1024;
16607
+ const OT_MAX_BATCH_BYTES = 2 * 1024 * 1024;
16608
+ const encoder = typeof TextEncoder === "function" ? new TextEncoder() : null;
16196
16609
  const PAUSE_STATES = /* @__PURE__ */ new Set(["running", "writing", "undoing", "compiling"]);
16197
16610
  function buildPatchFilesRequest({ projectId, events, nativeCompatibility } = {}) {
16198
16611
  const files = [];
@@ -16244,13 +16657,14 @@ ${block}`;
16244
16657
  }
16245
16658
  return { pause: false };
16246
16659
  }
16247
- function canUseOtWarmStart({ enabled, focusFiles, mirrorStatus } = {}) {
16660
+ function canUseOtWarmStart({ enabled, focusFiles, mirrorStatus, now = Date.now() } = {}) {
16248
16661
  if (!enabled) {
16249
16662
  return { ok: false, reason: "disabled" };
16250
16663
  }
16251
16664
  if (!mirrorStatus || mirrorStatus.exists !== true) {
16252
16665
  return { ok: false, reason: "mirror_missing" };
16253
16666
  }
16667
+ if (mirrorStatus.dirty === true) return { ok: false, reason: "mirror_dirty" };
16254
16668
  const normalizedFocusFiles = normalizePaths(focusFiles);
16255
16669
  if (!normalizedFocusFiles.length) {
16256
16670
  return { ok: false, reason: "no_focus_files" };
@@ -16260,6 +16674,8 @@ ${block}`;
16260
16674
  if (file?.state !== "fresh") {
16261
16675
  continue;
16262
16676
  }
16677
+ const age = now - Date.parse(file.observedAt ?? file.lastPatchAt ?? "");
16678
+ if (!Number.isFinite(age) || age < 0 || age > OT_FRESHNESS_MAX_AGE_MS) continue;
16263
16679
  const filePath = normalizePath(file.path);
16264
16680
  if (filePath) {
16265
16681
  freshFiles.add(filePath);
@@ -16275,6 +16691,88 @@ ${block}`;
16275
16691
  }
16276
16692
  return { ok: false, reason: "missing_ot_fresh_focus" };
16277
16693
  }
16694
+ function textBytes(value) {
16695
+ const text = String(value || "");
16696
+ return encoder ? encoder.encode(text).byteLength : text.length * 3;
16697
+ }
16698
+ function patchBytes(event) {
16699
+ return textBytes(event.nextContent) + textBytes(event.path) + 512;
16700
+ }
16701
+ function queuePatchEvents(queue, events, now = Date.now()) {
16702
+ if (!Array.isArray(events) || events.length > OT_MAX_PENDING_EVENTS) return { ok: false, reason: "ot_queue_limit" };
16703
+ const next = Array.isArray(queue) ? queue.slice() : [];
16704
+ let bytes = next.reduce((sum, event) => sum + patchBytes(event), 0);
16705
+ if (next.length > OT_MAX_PENDING_EVENTS || bytes > OT_MAX_PENDING_BYTES) return { ok: false, reason: "ot_queue_limit" };
16706
+ for (const input of events) {
16707
+ const path = normalizePath(input?.path);
16708
+ const time = typeof input?.observedAt === "number" ? input.observedAt : Date.parse(input?.observedAt || "");
16709
+ if (!isSafeRelativePath(path) || path.length > 1024 || typeof input?.nextContent !== "string" || typeof input.baseHash !== "string" || !input.baseHash || input.baseHash.length > 128) {
16710
+ return { ok: false, reason: "ot_event_invalid" };
16711
+ }
16712
+ if (!Number.isFinite(time) || now - time < 0 || now - time > OT_FRESHNESS_MAX_AGE_MS) return { ok: false, reason: "ot_event_expired" };
16713
+ if (textBytes(input.nextContent) > OT_MAX_FILE_BYTES) return { ok: false, reason: "ot_file_limit" };
16714
+ const event = {
16715
+ path,
16716
+ baseHash: input.baseHash,
16717
+ nextContent: input.nextContent,
16718
+ nextHash: typeof input.nextHash === "string" && input.nextHash.length <= 128 ? input.nextHash : "",
16719
+ observedAt: new Date(time).toISOString()
16720
+ };
16721
+ if (isPrimitiveMetadata(input.observedVersion) && String(input.observedVersion).length <= 128) event.observedVersion = input.observedVersion;
16722
+ const tail = next[next.length - 1];
16723
+ if (tail?.path === path && tail.nextHash && tail.nextHash === event.baseHash) {
16724
+ event.baseHash = tail.baseHash;
16725
+ bytes -= patchBytes(next.pop());
16726
+ }
16727
+ if (event.nextHash !== event.baseHash) {
16728
+ next.push(event);
16729
+ bytes += patchBytes(event);
16730
+ }
16731
+ if (next.length > OT_MAX_PENDING_EVENTS || bytes > OT_MAX_PENDING_BYTES) return { ok: false, reason: "ot_queue_limit" };
16732
+ }
16733
+ return { ok: true, queue: next, bytes };
16734
+ }
16735
+ function takePatchBatch(queue) {
16736
+ let bytes = 0;
16737
+ let count = 0;
16738
+ while (count < Math.min(queue.length, OT_MAX_PATCH_BATCH)) {
16739
+ const size = patchBytes(queue[count]);
16740
+ if (bytes + size > OT_MAX_BATCH_BYTES) break;
16741
+ bytes += size;
16742
+ count += 1;
16743
+ }
16744
+ return queue.splice(0, count);
16745
+ }
16746
+ function validatePatchReceipt(events, result = {}, now = Date.now()) {
16747
+ if (Array.isArray(result.skippedFiles) && result.skippedFiles.length || Number(result.skippedCount || 0) > 0) {
16748
+ return { ok: false, reason: "mirror_patch_skipped" };
16749
+ }
16750
+ if (!events.length || Number(result.appliedCount) !== events.length || !Array.isArray(result.appliedFiles) || result.appliedFiles.length !== events.length || !result.appliedFiles.every((file, index) => normalizePath(file?.path) === events[index].path && (!events[index].nextHash || file.hash === events[index].nextHash))) {
16751
+ return { ok: false, reason: "mirror_patch_invalid_result" };
16752
+ }
16753
+ if (events.some((event) => {
16754
+ const time = Date.parse(event.observedAt);
16755
+ return !Number.isFinite(time) || time > now || now - time > OT_FRESHNESS_MAX_AGE_MS;
16756
+ })) return { ok: false, reason: "ot_event_expired" };
16757
+ return { ok: true };
16758
+ }
16759
+ function normalizeFailureCode(value, fallback = "ot_error") {
16760
+ const code = typeof value === "string" ? value : value?.code;
16761
+ if (/^[a-z][a-z0-9_]{0,79}$/.test(code || "")) return code;
16762
+ if (/page bridge|capability|bridge timed out/i.test(String(code || ""))) return "ot_bridge_unavailable";
16763
+ return fallback;
16764
+ }
16765
+ function getFailureMessageKey(value) {
16766
+ const code = normalizeFailureCode(value);
16767
+ if (/bridge|ot_status|ot_events/.test(code)) return "otFailureBridge";
16768
+ if (/missing_ot_text|observer_create|observer_unavailable/.test(code)) return "otFailureObserver";
16769
+ if (code === "ot_file_limit") return "otFailureFileLimit";
16770
+ if (code === "ot_queue_limit") return "otFailureQueueLimit";
16771
+ if (code === "ot_event_expired") return "otFailureExpired";
16772
+ if (/editor|active_path|document/.test(code)) return "otFailureEditor";
16773
+ if (/mirror|hash|baseline/.test(code)) return "otFailureMirror";
16774
+ return "otFailureGeneric";
16775
+ }
16278
16776
  function normalizePath(value) {
16279
16777
  return String(value ?? "").trim().replace(/^@file:/i, "").trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/+/, "");
16280
16778
  }
@@ -16326,6 +16824,16 @@ ${block}`;
16326
16824
  OT_POLL_INTERVAL_MS,
16327
16825
  OT_PATCH_DEBOUNCE_MS,
16328
16826
  OT_MAX_PATCH_BATCH,
16827
+ OT_FRESHNESS_MAX_AGE_MS,
16828
+ OT_MAX_FILE_BYTES,
16829
+ OT_MAX_PENDING_EVENTS,
16830
+ OT_MAX_PENDING_BYTES,
16831
+ OT_MAX_BATCH_BYTES,
16832
+ queuePatchEvents,
16833
+ takePatchBatch,
16834
+ validatePatchReceipt,
16835
+ normalizeFailureCode,
16836
+ getFailureMessageKey,
16329
16837
  buildPatchFilesRequest,
16330
16838
  canUseOtWarmStart,
16331
16839
  normalizePath,
@@ -16334,7 +16842,7 @@ ${block}`;
16334
16842
  };
16335
16843
  });
16336
16844
  }).call(globalThis, void 0, void 0, void 0);
16337
- (function runClassicContentModule51(module, exports, require2) {
16845
+ (function runClassicContentModule54(module, exports, require2) {
16338
16846
  (function initComposerAttachments(global) {
16339
16847
  "use strict";
16340
16848
  function createComposerAttachmentController(options2 = {}) {
@@ -16794,7 +17302,7 @@ ${block}`;
16794
17302
  };
16795
17303
  })(window);
16796
17304
  }).call(globalThis, void 0, void 0, void 0);
16797
- (function runClassicContentModule52(module, exports, require2) {
17305
+ (function runClassicContentModule55(module, exports, require2) {
16798
17306
  (function initCodexOverleafPanelRenderer() {
16799
17307
  "use strict";
16800
17308
  const ROOT_CLASS = "codex-overleaf-panel-mounted";
@@ -17089,7 +17597,8 @@ ${block}`;
17089
17597
  const doc = panel.ownerDocument || document;
17090
17598
  return new Promise((resolve) => {
17091
17599
  let settled = false;
17092
- const overlay = doc.createElement("div");
17600
+ const returnFocus = doc.activeElement;
17601
+ const overlay = doc.createElement("dialog");
17093
17602
  overlay.className = "codex-plugin-confirm";
17094
17603
  overlay.setAttribute("data-plugin-confirm", "true");
17095
17604
  overlay.setAttribute("role", "dialog");
@@ -17131,12 +17640,15 @@ ${block}`;
17131
17640
  card.append(head, body, actions);
17132
17641
  overlay.append(card);
17133
17642
  panel.append(overlay);
17643
+ overlay.showModal?.();
17134
17644
  const cleanup = (value) => {
17135
17645
  if (settled) return;
17136
17646
  settled = true;
17137
17647
  activeResolve = null;
17138
17648
  doc.removeEventListener("keydown", onKeydown, true);
17649
+ overlay.close?.();
17139
17650
  overlay.remove();
17651
+ returnFocus?.focus?.();
17140
17652
  resolve(value);
17141
17653
  };
17142
17654
  activeResolve = cleanup;
@@ -17157,6 +17669,10 @@ ${block}`;
17157
17669
  overlay.addEventListener("click", (event) => {
17158
17670
  if (event.target === overlay) cleanup(false);
17159
17671
  });
17672
+ overlay.addEventListener("cancel", (event) => {
17673
+ event.preventDefault();
17674
+ cleanup(false);
17675
+ });
17160
17676
  cancel.addEventListener("click", () => cleanup(false));
17161
17677
  confirm.addEventListener("click", () => cleanup(true));
17162
17678
  doc.addEventListener("keydown", onKeydown, true);
@@ -17187,7 +17703,7 @@ ${block}`;
17187
17703
  };
17188
17704
  })();
17189
17705
  }).call(globalThis, void 0, void 0, void 0);
17190
- (function runClassicContentModule53(module, exports, require2) {
17706
+ (function runClassicContentModule56(module, exports, require2) {
17191
17707
  (function initCodexOverleafSessionPanel() {
17192
17708
  "use strict";
17193
17709
  function create(options2 = {}) {
@@ -17423,7 +17939,7 @@ ${block}`;
17423
17939
  };
17424
17940
  })();
17425
17941
  }).call(globalThis, void 0, void 0, void 0);
17426
- (function runClassicContentModule54(module, exports, require2) {
17942
+ (function runClassicContentModule57(module, exports, require2) {
17427
17943
  (function initCodexOverleafSettingsPanel() {
17428
17944
  "use strict";
17429
17945
  function codexIcon(name) {
@@ -17517,18 +18033,18 @@ ${block}`;
17517
18033
  </details>
17518
18034
  <details class="codex-set-group" data-set-group="experimental">
17519
18035
  <summary class="codex-set-group-head">
17520
- <span class="codex-set-group-title">${codexSetIcon("flask")}<span data-i18n="experimentalTitle">Experimental</span></span>
18036
+ <span class="codex-set-group-title">${codexSetIcon("database")}<span data-i18n="otSettingsTitle">Project cache</span></span>
17521
18037
  <span class="codex-set-saved" data-set-saved hidden>\u2713 <span data-i18n="settingsSaved">Saved</span></span>
17522
18038
  </summary>
17523
18039
  <div class="codex-set-card">
17524
18040
  <label class="codex-project-settings-row codex-project-settings-row--switch">
17525
18041
  <span class="codex-project-settings-row-label">
17526
- <span data-i18n="experimentalOtMenuTitle">Experimental OT Mirror</span>
17527
- <small class="codex-set-row-sub" data-i18n="experimentalOtMenuSubtitle">Experimental \u2014 speeds up reading the file you are editing; Codex falls back to normal reading whenever unsure.</small>
18042
+ <span data-i18n="experimentalOtMenuTitle">OT Mirror</span>
18043
+ <small class="codex-set-row-sub" data-i18n="experimentalOtMenuSubtitle">Warms recent edits to the active text file locally. File scope and freshness are checked before reuse.</small>
17528
18044
  </span>
17529
18045
  <input type="checkbox" class="codex-switch" data-experimental-ot>
17530
18046
  </label>
17531
- <p class="codex-set-row-help" data-experimental-ot-menu-status></p>
18047
+ <p class="codex-set-row-help" data-experimental-ot-menu-status aria-live="polite"></p>
17532
18048
  </div>
17533
18049
  </details>
17534
18050
  </div>
@@ -17581,17 +18097,25 @@ ${block}`;
17581
18097
  <button type="button" class="codex-set-btn" data-provider-settings-open data-i18n="providerSettingsConfigure">Configure</button>
17582
18098
  </div>
17583
18099
  </details>
18100
+ <details class="codex-set-group" data-set-group="context-loading" open>
18101
+ <summary class="codex-set-group-head">
18102
+ <span class="codex-set-group-title">${codexSetIcon("database")}<span data-i18n="contextLoadingTitle">Context loading</span></span>
18103
+ <span class="codex-set-saved" data-set-saved hidden>\u2713 <span data-i18n="settingsSaved">Saved</span></span>
18104
+ </summary>
18105
+ <div class="codex-set-card">
18106
+ <label class="codex-project-settings-row codex-project-settings-row--switch">
18107
+ <span class="codex-project-settings-row-label" data-i18n="preloadProjectContextTitle">Preload project context</span>
18108
+ <input type="checkbox" class="codex-switch" data-preload-project-context>
18109
+ </label>
18110
+ <p class="codex-set-row-help" data-i18n="preloadProjectContextDescription">Fetch the exact project file list in the background after an Overleaf project becomes idle.</p>
18111
+ </div>
18112
+ </details>
17584
18113
  <details class="codex-set-group" data-set-group="updates" open>
17585
18114
  <summary class="codex-set-group-head">
17586
18115
  <span class="codex-set-group-title">${codexSetIcon("software")}<span data-i18n="softwareUpdatesTitle">Software updates</span></span>
17587
18116
  </summary>
17588
18117
  <div class="codex-set-card" data-update-settings>
17589
18118
  <div class="codex-set-row">
17590
- <label class="codex-project-settings-row codex-project-settings-row--switch">
17591
- <span class="codex-project-settings-row-label" data-i18n="preloadProjectContextTitle">Preload project context</span>
17592
- <input type="checkbox" class="codex-switch" data-preload-project-context>
17593
- </label>
17594
- <p class="codex-set-row-help" data-i18n="preloadProjectContextDescription">Fetch the exact project file list in the background after an Overleaf project becomes idle.</p>
17595
18119
  <p class="codex-set-row-help" data-update-settings-summary data-i18n="softwareUpdatesHelp">Automatically checks signed stable releases for the Extension and Native Host.</p>
17596
18120
  <p class="codex-set-note" data-update-settings-state aria-live="polite" hidden></p>
17597
18121
  <button type="button" class="codex-set-btn" data-check-updates data-i18n="checkForUpdates">Check for updates</button>
@@ -17656,6 +18180,9 @@ ${block}`;
17656
18180
  instance.callbacks.onHistoryOpen?.();
17657
18181
  }
17658
18182
  });
18183
+ container.querySelector("[data-storage-card]")?.addEventListener("toggle", (event) => {
18184
+ if (event.target.open) instance.callbacks.onStorageOpen?.();
18185
+ });
17659
18186
  container.querySelector("[data-history-filter]")?.addEventListener("input", () => instance.callbacks.onHistoryFilter?.());
17660
18187
  container.querySelector("[data-experimental-ot]")?.addEventListener("click", (event) => instance.callbacks.onOtToggleClick?.(event));
17661
18188
  const customInstructionsInput = container.querySelector("[data-custom-instructions-input]");
@@ -17666,10 +18193,18 @@ ${block}`;
17666
18193
  for (const selector of ["[data-governance-readonly-patterns]", "[data-governance-writable-patterns]", "[data-sensitive-check-enabled]", "[data-sensitive-confirm-allowed]", "[data-preload-project-context]", "[data-load-codex-local-skills]", "[data-load-codex-overleaf-skills]", "[data-theme-select]", "[data-language-select]"]) {
17667
18194
  const element = container.querySelector(selector);
17668
18195
  element?.addEventListener?.("change", (event) => {
17669
- instance.callbacks.onInputChange?.(event);
17670
- flashSaved(instance, event);
18196
+ Promise.resolve(instance.callbacks.onInputChange?.(event)).then(() => {
18197
+ flashSaved(instance, event);
18198
+ if (selector === "[data-language-select]" && container.querySelector("[data-storage-card]")?.open) {
18199
+ instance.callbacks.onStorageOpen?.();
18200
+ }
18201
+ }).catch(() => {
18202
+ });
18203
+ });
18204
+ element?.addEventListener?.("input", (event) => {
18205
+ Promise.resolve(instance.callbacks.onInputChange?.(event)).catch(() => {
18206
+ });
17671
18207
  });
17672
- element?.addEventListener?.("input", (event) => instance.callbacks.onInputChange?.(event));
17673
18208
  }
17674
18209
  for (const selector of ["[data-governance-readonly-patterns]", "[data-governance-writable-patterns]"]) {
17675
18210
  const element = container.querySelector(selector);
@@ -17678,6 +18213,9 @@ ${block}`;
17678
18213
  }
17679
18214
  setupGroupPersistence(instance);
17680
18215
  return {
18216
+ get container() {
18217
+ return instance.container;
18218
+ },
17681
18219
  show: () => show(instance),
17682
18220
  hide: () => hide(instance),
17683
18221
  loadState: (state) => loadState(instance, state),
@@ -17938,7 +18476,7 @@ ${block}`;
17938
18476
  };
17939
18477
  })();
17940
18478
  }).call(globalThis, void 0, void 0, void 0);
17941
- (function runClassicContentModule55(module, exports, require2) {
18479
+ (function runClassicContentModule58(module, exports, require2) {
17942
18480
  (function initCodexOverleafProviderSettingsDialog() {
17943
18481
  "use strict";
17944
18482
  const CODEX_BLOSSOM_PATH = "M13.798 23.976a5.7 5.7 0 0 1-2.26-.456 6.1 6.1 0 0 1-1.903-1.27 5.7 5.7 0 0 1-1.88.311 5.75 5.75 0 0 1-2.95-.79 6.2 6.2 0 0 1-2.188-2.159q-.81-1.366-.809-3.045 0-.695.19-1.51a6.4 6.4 0 0 1-1.475-2.038A5.95 5.95 0 0 1 0 10.573Q0 9.278.547 8.08q.547-1.2 1.523-2.062a5.5 5.5 0 0 1 2.307-1.223A5.7 5.7 0 0 1 5.472 2.35 6.1 6.1 0 0 1 7.565.623 5.8 5.8 0 0 1 10.206 0q1.19 0 2.26.456a6.1 6.1 0 0 1 1.903 1.27 5.7 5.7 0 0 1 1.88-.311q1.594 0 2.95.79a6 6 0 0 1 2.165 2.159q.832 1.366.832 3.045 0 .695-.19 1.51a6.3 6.3 0 0 1 1.475 2.062q.523 1.15.523 2.422a5.9 5.9 0 0 1-.547 2.493q-.547 1.2-1.546 2.086a5.4 5.4 0 0 1-2.284 1.199 5.56 5.56 0 0 1-1.118 2.445 5.9 5.9 0 0 1-2.07 1.727 5.8 5.8 0 0 1-2.64.623m-5.876-2.997q1.19 0 2.07-.504l4.472-2.589a.53.53 0 0 0 .238-.455v-2.062L8.945 18.7a.96.96 0 0 1-1.047 0l-4.496-2.613a.7.7 0 0 1-.024.168v.287q0 1.224.571 2.254a4.24 4.24 0 0 0 1.642 1.583q1.047.6 2.331.599m.238-3.908a.6.6 0 0 0 .262.072q.118 0 .238-.072l1.784-1.031-5.734-3.357q-.522-.312-.523-.935V6.545a4.3 4.3 0 0 0-1.903 1.63 4.25 4.25 0 0 0-.714 2.398q0 1.176.595 2.254.594 1.08 1.546 1.63zm5.638 5.323q1.26 0 2.284-.576a4.3 4.3 0 0 0 1.618-1.582q.595-1.008.595-2.254v-5.179a.47.47 0 0 0-.238-.431l-1.808-1.055v6.689q0 .624-.524.935l-4.496 2.613a4.3 4.3 0 0 0 2.57.84m.904-8.776v-3.26l-2.688-1.535-2.712 1.535v3.26l2.712 1.535zM7.756 5.97q0-.623.523-.935l4.496-2.613a4.3 4.3 0 0 0-2.569-.84q-1.26 0-2.284.576A4.3 4.3 0 0 0 6.304 3.74q-.57 1.008-.57 2.254v5.155q0 .287.237.455l1.785 1.055zM19.84 17.43a4.16 4.16 0 0 0 1.88-1.63 4.33 4.33 0 0 0 .713-2.397q0-1.176-.595-2.254-.594-1.08-1.546-1.63l-4.449-2.59q-.143-.096-.261-.072a.46.46 0 0 0-.238.072L13.56 7.936l5.758 3.38a.9.9 0 0 1 .38.384q.143.216.143.528zM15.059 5.25q.524-.335 1.047 0l4.52 2.662V7.48q0-1.15-.57-2.181A4.14 4.14 0 0 0 18.46 3.62q-1.023-.623-2.379-.623-1.19 0-2.07.503L9.54 6.09a.53.53 0 0 0-.238.455v2.062z";
@@ -17984,12 +18522,13 @@ ${block}`;
17984
18522
  if (instance.root) {
17985
18523
  return instance.root;
17986
18524
  }
17987
- const root = instance.document.createElement("div");
18525
+ const root = instance.document.createElement("dialog");
17988
18526
  root.className = "codex-provider-dialog-root";
18527
+ root.setAttribute("aria-labelledby", "codex-provider-dialog-title");
17989
18528
  root.hidden = true;
17990
18529
  root.innerHTML = `
17991
18530
  <div class="codex-provider-dialog-backdrop" data-provider-backdrop></div>
17992
- <section class="codex-provider-dialog" role="dialog" aria-modal="true" aria-labelledby="codex-provider-dialog-title">
18531
+ <section class="codex-provider-dialog">
17993
18532
  <header class="codex-provider-dialog-head">
17994
18533
  <div class="codex-provider-dialog-heading">
17995
18534
  <div class="codex-provider-dialog-titleline">
@@ -18016,6 +18555,10 @@ ${block}`;
18016
18555
  root.addEventListener("input", (event) => handleInput(instance, event));
18017
18556
  root.addEventListener("change", (event) => handleInput(instance, event));
18018
18557
  root.addEventListener("keydown", (event) => handleKeydown(instance, event));
18558
+ root.addEventListener("cancel", (event) => {
18559
+ event.preventDefault();
18560
+ requestClose(instance);
18561
+ });
18019
18562
  root.addEventListener("mousedown", (event) => event.stopPropagation());
18020
18563
  root.addEventListener("click", (event) => event.stopPropagation());
18021
18564
  return root;
@@ -18025,8 +18568,11 @@ ${block}`;
18025
18568
  instance.root.hidden = false;
18026
18569
  syncTheme(instance);
18027
18570
  setCatalog(instance, catalog || instance.catalog);
18571
+ if (!instance.root.open) instance.root.showModal?.();
18028
18572
  queueMicrotask(() => {
18029
- instance.root.querySelector('[data-provider-row][aria-current="true"]')?.focus?.() || instance.root.querySelector("input, button, select, textarea")?.focus?.();
18573
+ if (!instance.root || instance.root.hidden) return;
18574
+ const target = instance.root.querySelector('[data-provider-row][aria-current="true"]') || instance.root.querySelector("input, button, select, textarea");
18575
+ target?.focus?.();
18030
18576
  });
18031
18577
  }
18032
18578
  function requestClose(instance) {
@@ -18040,6 +18586,7 @@ ${block}`;
18040
18586
  instance.callbacks.onCancelTest?.();
18041
18587
  instance.busy = "";
18042
18588
  }
18589
+ instance.root.close?.();
18043
18590
  instance.root.hidden = true;
18044
18591
  instance.busy = "";
18045
18592
  instance.returnFocus?.focus?.();
@@ -18086,6 +18633,10 @@ ${block}`;
18086
18633
  "Configure experimental third-party integrations used by this project's future Codex runs. Compatibility varies by provider and gateway.",
18087
18634
  "\u914D\u7F6E\u5F53\u524D\u9879\u76EE\u540E\u7EED Codex \u4EFB\u52A1\u4F7F\u7528\u7684\u5B9E\u9A8C\u6027\u7B2C\u4E09\u65B9\u96C6\u6210\uFF1B\u517C\u5BB9\u6027\u53D6\u51B3\u4E8E\u5177\u4F53\u6A21\u578B\u670D\u52A1\u4E0E\u7F51\u5173\u3002"
18088
18635
  );
18636
+ if (instance.callbacks.hasCurrentProject?.() === false) instance.root.querySelector("[data-provider-dialog-subtitle]").textContent = tx(
18637
+ "Manage shared model API profiles. Open a project to choose which provider its future turns use.",
18638
+ "\u7BA1\u7406\u5171\u4EAB\u6A21\u578B API \u914D\u7F6E\u3002\u8FDB\u5165\u9879\u76EE\u540E\uFF0C\u518D\u9009\u62E9\u8BE5\u9879\u76EE\u540E\u7EED\u4EFB\u52A1\u4F7F\u7528\u7684\u670D\u52A1\u3002"
18639
+ );
18089
18640
  renderProviderList(instance);
18090
18641
  renderDetail(instance);
18091
18642
  renderFooter(instance);
@@ -18318,8 +18869,8 @@ ${block}`;
18318
18869
  <label class="codex-provider-disclosure">
18319
18870
  <input type="checkbox" data-provider-disclosure ${disclosureSatisfied ? "checked" : ""}>
18320
18871
  <span data-provider-disclosure-text>${escapeHtml(tx(
18321
- `Future Codex runs in every Overleaf project tab may send selected project content to ${draft.baseUrl || "this endpoint"}.`,
18322
- `\u6240\u6709 Overleaf \u9879\u76EE\u6807\u7B7E\u9875\u7684\u540E\u7EED Codex \u4EFB\u52A1\u90FD\u53EF\u80FD\u628A\u6240\u9009\u9879\u76EE\u5185\u5BB9\u53D1\u9001\u5230 ${draft.baseUrl || "\u6B64\u7AEF\u70B9"}\u3002`
18872
+ `Projects using this shared provider may send task context to ${draft.baseUrl || "this endpoint"}. Each project chooses its own provider.`,
18873
+ `\u4F7F\u7528\u6B64\u5171\u4EAB\u670D\u52A1\u914D\u7F6E\u7684\u9879\u76EE\u53EF\u80FD\u628A\u4EFB\u52A1\u4E0A\u4E0B\u6587\u53D1\u9001\u5230 ${draft.baseUrl || "\u6B64\u7AEF\u70B9"}\u3002\u5404\u9879\u76EE\u72EC\u7ACB\u9009\u62E9\u4F7F\u7528\u7684\u670D\u52A1\u3002`
18323
18874
  ))}</span>
18324
18875
  </label>
18325
18876
  <div class="codex-provider-test-row">
@@ -18365,11 +18916,12 @@ ${block}`;
18365
18916
  "Uses the authentication, model catalog, and provider configuration managed by the local Codex CLI.",
18366
18917
  "\u4F7F\u7528\u672C\u5730 Codex CLI \u7BA1\u7406\u7684\u8EAB\u4EFD\u9A8C\u8BC1\u3001\u6A21\u578B\u76EE\u5F55\u548C\u670D\u52A1\u914D\u7F6E\u3002"
18367
18918
  ))}</p>
18368
- ${active ? `<span class="codex-provider-active-badge">${escapeHtml(tx("Current project", "\u5F53\u524D\u9879\u76EE"))}</span>` : `<button type="button" class="codex-provider-primary-button" data-provider-action="activate-builtin">${escapeHtml(tx("Use for this project", "\u7528\u4E8E\u5F53\u524D\u9879\u76EE"))}</button>`}
18919
+ ${instance.callbacks.hasCurrentProject?.() === false ? "" : active ? `<span class="codex-provider-active-badge">${escapeHtml(tx("Current project", "\u5F53\u524D\u9879\u76EE"))}</span>` : `<button type="button" class="codex-provider-primary-button" data-provider-action="activate-builtin">${escapeHtml(tx("Use for this project", "\u7528\u4E8E\u5F53\u524D\u9879\u76EE"))}</button>`}
18369
18920
  </div>
18370
18921
  `;
18371
18922
  }
18372
18923
  function getCurrentProjectProviderId(instance) {
18924
+ if (instance.callbacks.hasCurrentProject?.() === false) return "";
18373
18925
  const providerId = instance.callbacks.getCurrentProjectProviderId?.();
18374
18926
  return typeof providerId === "string" && providerId.trim() ? providerId.trim() : "builtin";
18375
18927
  }
@@ -18390,6 +18942,7 @@ ${block}`;
18390
18942
  dirty: instance.dirty,
18391
18943
  destinationChanged,
18392
18944
  canSave: canSaveCurrentDraft(instance),
18945
+ hasProject: instance.callbacks.hasCurrentProject?.() !== false,
18393
18946
  canActivate: canActivateCurrentProvider(instance)
18394
18947
  });
18395
18948
  const saveDisabled = actionState.saveEnabled ? "" : ' disabled aria-disabled="true"';
@@ -18410,8 +18963,8 @@ ${block}`;
18410
18963
  const dirty = options2.dirty === true;
18411
18964
  return {
18412
18965
  showSave: isNew || dirty,
18413
- showSaveAndUse: (isNew || dirty) && (!active || options2.destinationChanged === true),
18414
- showUse: !isNew && !active && !dirty,
18966
+ showSaveAndUse: options2.hasProject !== false && (isNew || dirty) && (!active || options2.destinationChanged === true),
18967
+ showUse: options2.hasProject !== false && !isNew && !active && !dirty,
18415
18968
  saveEnabled: options2.canSave === true,
18416
18969
  useEnabled: options2.canActivate === true
18417
18970
  };
@@ -18558,7 +19111,7 @@ ${block}`;
18558
19111
  if (event.key !== "Tab") {
18559
19112
  return;
18560
19113
  }
18561
- const focusable = Array.from(instance.root.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])")).filter((element) => !element.closest("[hidden]"));
19114
+ const focusable = Array.from(instance.root.querySelectorAll("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])")).filter((element) => !element.closest("[hidden]") && (!element.getClientRects || element.getClientRects().length));
18562
19115
  if (!focusable.length) {
18563
19116
  return;
18564
19117
  }
@@ -18798,8 +19351,8 @@ ${block}`;
18798
19351
  if (!text) return;
18799
19352
  const baseUrl = getCurrentBaseUrl(instance) || instance.tx("this endpoint", "\u6B64\u7AEF\u70B9");
18800
19353
  text.textContent = instance.tx(
18801
- `Future Codex runs in every Overleaf project tab may send selected project content to ${baseUrl}.`,
18802
- `\u6240\u6709 Overleaf \u9879\u76EE\u6807\u7B7E\u9875\u7684\u540E\u7EED Codex \u4EFB\u52A1\u90FD\u53EF\u80FD\u628A\u6240\u9009\u9879\u76EE\u5185\u5BB9\u53D1\u9001\u5230 ${baseUrl}\u3002`
19354
+ `Projects using this shared provider may send task context to ${baseUrl}. Each project chooses its own provider.`,
19355
+ `\u4F7F\u7528\u6B64\u5171\u4EAB\u670D\u52A1\u914D\u7F6E\u7684\u9879\u76EE\u53EF\u80FD\u628A\u4EFB\u52A1\u4E0A\u4E0B\u6587\u53D1\u9001\u5230 ${baseUrl}\u3002\u5404\u9879\u76EE\u72EC\u7ACB\u9009\u62E9\u4F7F\u7528\u7684\u670D\u52A1\u3002`
18803
19356
  );
18804
19357
  }
18805
19358
  function syncTestAction(instance) {
@@ -18835,10 +19388,7 @@ ${block}`;
18835
19388
  if (!panel) {
18836
19389
  return;
18837
19390
  }
18838
- const style = instance.document.defaultView.getComputedStyle(panel);
18839
19391
  instance.root.dataset.theme = panel.dataset.theme === "light" ? "light" : "dark";
18840
- instance.root.style.setProperty("--provider-panel-bg", style.backgroundColor || "#151716");
18841
- instance.root.style.setProperty("--provider-panel-color", style.color || "#eceeea");
18842
19392
  }
18843
19393
  function escapeHtml(value) {
18844
19394
  return String(value ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
@@ -18853,13 +19403,14 @@ ${block}`;
18853
19403
  return escapeHtml(value).replaceAll("`", "&#96;");
18854
19404
  }
18855
19405
  function destroy(instance) {
19406
+ instance.root?.close?.();
18856
19407
  instance.root?.remove?.();
18857
19408
  instance.root = null;
18858
19409
  }
18859
19410
  window.CodexOverleafProviderSettingsDialog = { create, getFooterActionState };
18860
19411
  })();
18861
19412
  }).call(globalThis, void 0, void 0, void 0);
18862
- (function runClassicContentModule56(module, exports, require2) {
19413
+ (function runClassicContentModule59(module, exports, require2) {
18863
19414
  (function initCodexOverleafProjectProviderSelection() {
18864
19415
  "use strict";
18865
19416
  function create(deps = {}) {
@@ -18916,7 +19467,7 @@ ${block}`;
18916
19467
  window.CodexOverleafProjectProviderSelection = { create };
18917
19468
  })();
18918
19469
  }).call(globalThis, void 0, void 0, void 0);
18919
- (function runClassicContentModule57(module, exports, require2) {
19470
+ (function runClassicContentModule60(module, exports, require2) {
18920
19471
  (function initCodexOverleafProviderSettingsCoordinator() {
18921
19472
  "use strict";
18922
19473
  function create(options2 = {}) {
@@ -18932,6 +19483,7 @@ ${block}`;
18932
19483
  getSettingsPanelInstance: options2.getSettingsPanelInstance || (() => null),
18933
19484
  getSelectedModel: options2.getSelectedModel || (() => ""),
18934
19485
  getSelectedProviderId: options2.getSelectedProviderId || (() => "builtin"),
19486
+ hasCurrentProject: options2.hasCurrentProject || (() => true),
18935
19487
  setSelectedProviderId: options2.setSelectedProviderId || (() => {
18936
19488
  }),
18937
19489
  confirmProviderSwitch: options2.confirmProviderSwitch || (() => true),
@@ -18964,6 +19516,7 @@ ${block}`;
18964
19516
  tx: instance.tx,
18965
19517
  ProviderProfiles: Profiles,
18966
19518
  callbacks: {
19519
+ hasCurrentProject: instance.hasCurrentProject,
18967
19520
  getCurrentProjectProviderId: () => instance.getSelectedProviderId() || "builtin",
18968
19521
  onTest: (context) => testProvider(instance, context),
18969
19522
  onCancelTest: () => cancelProviderTest(instance),
@@ -18978,6 +19531,7 @@ ${block}`;
18978
19531
  return {
18979
19532
  open: () => open(instance),
18980
19533
  refreshSummary: () => refreshSummary(instance),
19534
+ renderSummary: () => updateSettingsSummary(instance),
18981
19535
  ensureLoaded: () => ensureLoaded(instance),
18982
19536
  getRunSelection: (providerId) => instance.loaded ? Profiles.buildRunSelection(instance.catalog, providerId || instance.getSelectedProviderId()) : null,
18983
19537
  getProviderSnapshot: (providerId) => getProviderSnapshot(instance, providerId),
@@ -19102,6 +19656,7 @@ ${block}`;
19102
19656
  return { providerId: requestedProviderId, selectedModel: loadResult?.selectedModel || "" };
19103
19657
  }
19104
19658
  async function prepareProviderActivation(instance, providerId) {
19659
+ requireCurrentProject(instance);
19105
19660
  const previousProviderId = instance.getSelectedProviderId() || "builtin";
19106
19661
  const provider = instance.Profiles.getProviderById(instance.catalog, providerId);
19107
19662
  if (providerId !== previousProviderId) {
@@ -19192,6 +19747,7 @@ ${block}`;
19192
19747
  async function saveProvider(instance, context, action = {}) {
19193
19748
  instance.dialog.setBusy("saving", instance.tx("Saving provider\u2026", "\u6B63\u5728\u4FDD\u5B58\u6A21\u578B\u670D\u52A1\u2026"));
19194
19749
  try {
19750
+ if (action.activate === true) requireCurrentProject(instance);
19195
19751
  const result = await request(instance, "codex.providers.upsert", {
19196
19752
  profileId: context.profileId,
19197
19753
  expectedRevision: context.expectedRevision,
@@ -19371,6 +19927,13 @@ ${block}`;
19371
19927
  return { applied: true, change: describeCatalogChange(instance, previousCatalog, nextCatalog) };
19372
19928
  }
19373
19929
  function updateSettingsSummary(instance, override = {}) {
19930
+ if (!instance.hasCurrentProject()) {
19931
+ instance.getSettingsPanelInstance()?.setProviderSummary?.({ summary: instance.tx(
19932
+ "Shared model API profiles \xB7 open a project to select a provider",
19933
+ "\u5171\u4EAB\u6A21\u578B API \u914D\u7F6E \xB7 \u8FDB\u5165\u9879\u76EE\u540E\u9009\u62E9\u4F7F\u7528\u7684\u670D\u52A1"
19934
+ ), tone: "info" });
19935
+ return;
19936
+ }
19374
19937
  const selectedProviderId = instance.getSelectedProviderId() || "builtin";
19375
19938
  const catalogProvider = instance.Profiles.getProviderById(
19376
19939
  instance.catalog,
@@ -19453,6 +20016,12 @@ ${block}`;
19453
20016
  }))
19454
20017
  });
19455
20018
  }
20019
+ function requireCurrentProject(instance) {
20020
+ if (!instance.hasCurrentProject()) throw createClientError(
20021
+ "provider_project_required",
20022
+ instance.tx("Open an Overleaf project before selecting its provider.", "\u8BF7\u5148\u8FDB\u5165 Overleaf \u9879\u76EE\uFF0C\u518D\u9009\u62E9\u8BE5\u9879\u76EE\u4F7F\u7528\u7684\u6A21\u578B\u670D\u52A1\u3002")
20023
+ );
20024
+ }
19456
20025
  function emptyCatalogChange() {
19457
20026
  return { activeProviderChanged: false, activeModelCatalogChanged: false, changedProviderIds: [] };
19458
20027
  }
@@ -19509,7 +20078,7 @@ ${block}`;
19509
20078
  window.CodexOverleafProviderSettingsCoordinator = { create };
19510
20079
  })();
19511
20080
  }).call(globalThis, void 0, void 0, void 0);
19512
- (function runClassicContentModule58(module, exports, require2) {
20081
+ (function runClassicContentModule61(module, exports, require2) {
19513
20082
  (function initCodexOverleafSessionPersistence(root, factory) {
19514
20083
  if (typeof module === "object" && module.exports) {
19515
20084
  module.exports = factory(require2("../shared/storageMigration"));
@@ -19603,7 +20172,7 @@ ${block}`;
19603
20172
  };
19604
20173
  });
19605
20174
  }).call(globalThis, void 0, void 0, void 0);
19606
- (function runClassicContentModule59(module, exports, require2) {
20175
+ (function runClassicContentModule62(module, exports, require2) {
19607
20176
  (function(root, factory) {
19608
20177
  var api = factory();
19609
20178
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -19707,7 +20276,7 @@ ${block}`;
19707
20276
  return Object.freeze({ persistPanelState });
19708
20277
  });
19709
20278
  }).call(globalThis, void 0, void 0, void 0);
19710
- (function runClassicContentModule60(module, exports, require2) {
20279
+ (function runClassicContentModule63(module, exports, require2) {
19711
20280
  (function(root, factory) {
19712
20281
  var api = factory();
19713
20282
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -19752,7 +20321,7 @@ ${block}`;
19752
20321
  return Object.freeze({ create });
19753
20322
  });
19754
20323
  }).call(globalThis, void 0, void 0, void 0);
19755
- (function runClassicContentModule61(module, exports, require2) {
20324
+ (function runClassicContentModule64(module, exports, require2) {
19756
20325
  (function(root, factory) {
19757
20326
  if (typeof module === "object" && module.exports) {
19758
20327
  module.exports = factory(
@@ -19847,7 +20416,7 @@ ${block}`;
19847
20416
  });
19848
20417
  });
19849
20418
  }).call(globalThis, void 0, void 0, void 0);
19850
- (function runClassicContentModule62(module, exports, require2) {
20419
+ (function runClassicContentModule65(module, exports, require2) {
19851
20420
  (function initCodexOverleafDiagnosticsPanel() {
19852
20421
  "use strict";
19853
20422
  function create(options2 = {}) {
@@ -19886,7 +20455,7 @@ ${block}`;
19886
20455
  <small data-i18n="diagnosticsSnapshotSubtitle">Full project, assets, read source</small>
19887
20456
  </button>
19888
20457
  <button type="button" data-diagnostics-ot>
19889
- <span data-i18n="diagnosticsOtTitle">Check Experimental OT Mirror</span>
20458
+ <span data-i18n="diagnosticsOtTitle">Check OT Mirror</span>
19890
20459
  <small data-i18n="diagnosticsOtSubtitle">Status, fresh files, fallback</small>
19891
20460
  </button>
19892
20461
  <button type="button" data-diagnostics-export>
@@ -20275,7 +20844,7 @@ ${block}`;
20275
20844
  };
20276
20845
  })();
20277
20846
  }).call(globalThis, void 0, void 0, void 0);
20278
- (function runClassicContentModule63(module, exports, require2) {
20847
+ (function runClassicContentModule66(module, exports, require2) {
20279
20848
  (function initCodexOverleafRunQueueScheduler(root, factory) {
20280
20849
  if (typeof module === "object" && module.exports) {
20281
20850
  module.exports = factory(require2("../shared/runInputQueue"));
@@ -20603,7 +21172,7 @@ ${block}`;
20603
21172
  return { create, createCoordinator };
20604
21173
  });
20605
21174
  }).call(globalThis, void 0, void 0, void 0);
20606
- (function runClassicContentModule64(module, exports, require2) {
21175
+ (function runClassicContentModule67(module, exports, require2) {
20607
21176
  (function initCodexOverleafActiveTurnControl(root, factory) {
20608
21177
  if (typeof module === "object" && module.exports) {
20609
21178
  module.exports = factory();
@@ -20772,7 +21341,7 @@ ${block}`;
20772
21341
  return { create };
20773
21342
  });
20774
21343
  }).call(globalThis, void 0, void 0, void 0);
20775
- (function runClassicContentModule65(module, exports, require2) {
21344
+ (function runClassicContentModule68(module, exports, require2) {
20776
21345
  (function initCodexOverleafPendingInputView(root, factory) {
20777
21346
  if (typeof module === "object" && module.exports) {
20778
21347
  module.exports = factory();
@@ -20874,7 +21443,7 @@ ${block}`;
20874
21443
  return { create };
20875
21444
  });
20876
21445
  }).call(globalThis, void 0, void 0, void 0);
20877
- (function runClassicContentModule66(module, exports, require2) {
21446
+ (function runClassicContentModule69(module, exports, require2) {
20878
21447
  (function initCodexOverleafComposerPanel() {
20879
21448
  "use strict";
20880
21449
  function create(options2 = {}) {
@@ -20985,7 +21554,7 @@ ${block}`;
20985
21554
  </span>
20986
21555
  </div>
20987
21556
  <div class="codex-context-selection" data-context-selection></div>
20988
- <div class="codex-context-status" data-context-status>Type @ to add context: @file, @compile-log, @current-section.</div>
21557
+ <div class="codex-context-status" data-context-status>Type @ to choose files or add @compile-log. Name the section you want in your request.</div>
20989
21558
  <div class="codex-context-files" data-context-file-list></div>
20990
21559
  </div>
20991
21560
  </form>
@@ -21133,7 +21702,7 @@ ${block}`;
21133
21702
  };
21134
21703
  })();
21135
21704
  }).call(globalThis, void 0, void 0, void 0);
21136
- (function runClassicContentModule67(module, exports, require2) {
21705
+ (function runClassicContentModule70(module, exports, require2) {
21137
21706
  (function initCodexOverleafTheme(root, factory) {
21138
21707
  if (typeof module === "object" && module.exports) {
21139
21708
  module.exports = factory();
@@ -21167,6 +21736,9 @@ ${block}`;
21167
21736
  const resolved = resolveTheme(preference, mediaQueryList);
21168
21737
  if (panelRoot && typeof panelRoot.setAttribute === "function") {
21169
21738
  panelRoot.setAttribute("data-theme", resolved);
21739
+ for (const target of panelRoot.ownerDocument?.querySelectorAll?.(".codex-provider-dialog-root, #codex-overleaf-launcher") || []) {
21740
+ target.setAttribute("data-theme", resolved);
21741
+ }
21170
21742
  }
21171
21743
  return resolved;
21172
21744
  }
@@ -21201,7 +21773,7 @@ ${block}`;
21201
21773
  };
21202
21774
  });
21203
21775
  }).call(globalThis, void 0, void 0, void 0);
21204
- (function runClassicContentModule68(module, exports, require2) {
21776
+ (function runClassicContentModule71(module, exports, require2) {
21205
21777
  !(function(e, t) {
21206
21778
  "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.katex = t() : e.katex = t();
21207
21779
  })("undefined" != typeof self ? self : this, function() {
@@ -24621,7 +25193,7 @@ ${block}`;
24621
25193
  })();
24622
25194
  });
24623
25195
  }).call(globalThis, void 0, void 0, void 0);
24624
- (function runClassicContentModule69(module, exports, require2) {
25196
+ (function runClassicContentModule72(module, exports, require2) {
24625
25197
  !(function(t, e) {
24626
25198
  "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define([], e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).markdownit = e();
24627
25199
  })(this, function() {
@@ -26693,7 +27265,7 @@ ${block}`;
26693
27265
  }, Ke;
26694
27266
  });
26695
27267
  }).call(globalThis, void 0, void 0, void 0);
26696
- (function runClassicContentModule70(module, exports, require2) {
27268
+ (function runClassicContentModule73(module, exports, require2) {
26697
27269
  (function initCodexOverleafMathText(root) {
26698
27270
  "use strict";
26699
27271
  const DELIMITERS = Object.freeze([
@@ -27078,7 +27650,7 @@ ${block}`;
27078
27650
  }
27079
27651
  })(typeof window !== "undefined" ? window : globalThis);
27080
27652
  }).call(globalThis, void 0, void 0, void 0);
27081
- (function runClassicContentModule71(module, exports, require2) {
27653
+ (function runClassicContentModule74(module, exports, require2) {
27082
27654
  (function initCodexOverleafMarkdownDomRenderer(root, factory) {
27083
27655
  const api = factory();
27084
27656
  if (typeof module === "object" && module.exports) {
@@ -27566,7 +28138,7 @@ ${block}`;
27566
28138
  };
27567
28139
  });
27568
28140
  }).call(globalThis, void 0, void 0, void 0);
27569
- (function runClassicContentModule72(module, exports, require2) {
28141
+ (function runClassicContentModule75(module, exports, require2) {
27570
28142
  (function initCodexOverleafMarkdownText() {
27571
28143
  "use strict";
27572
28144
  function create(deps = {}) {
@@ -28193,7 +28765,7 @@ ${block}`;
28193
28765
  window.CodexOverleafMarkdownText = { create };
28194
28766
  })();
28195
28767
  }).call(globalThis, void 0, void 0, void 0);
28196
- (function runClassicContentModule73(module, exports, require2) {
28768
+ (function runClassicContentModule76(module, exports, require2) {
28197
28769
  (function initCodexOverleafDiagnosticsController() {
28198
28770
  "use strict";
28199
28771
  function create(deps = {}) {
@@ -28510,6 +29082,16 @@ ${block}`;
28510
29082
  nextStep: "",
28511
29083
  technical: ""
28512
29084
  };
29085
+ const mirrorState = getOtWarmMirrorState?.() || {};
29086
+ if (mirrorState.failureActive && mirrorState.lastFailure) {
29087
+ const failure = mirrorState.lastFailure;
29088
+ disabledResult.status = "warning";
29089
+ disabledResult.summary = tr(otWarmMirrorController.getFailureMessageKey(failure.code));
29090
+ disabledResult.nextStep = tr("otFailureNextStep");
29091
+ disabledResult.technical = `lastErrorCode: ${failure.code}
29092
+ phase: ${failure.phase}
29093
+ failedAt: ${new Date(failure.at).toISOString()}`;
29094
+ }
28513
29095
  if (options2.collectOnly) {
28514
29096
  return disabledResult;
28515
29097
  }
@@ -28564,6 +29146,7 @@ ${block}`;
28564
29146
  const queuedEventCount = normalizeOtDiagnosticsCount(otStatus?.queuedEventCount);
28565
29147
  const lastEventAt = formatOtDiagnosticValue(otStatus?.lastEventAt, tr("noneValue"));
28566
29148
  const warmMirrorState = getOtWarmMirrorState() || {};
29149
+ const failureActive = warmMirrorState.failureActive === true;
28567
29150
  const lastOtPatchAt = formatOtDiagnosticValue(mirrorStatus?.lastOtPatchAt || warmMirrorState.lastPatchAt, tr("noneValue"));
28568
29151
  const lastOtErrorCode = formatOtDiagnosticValue(mirrorStatus?.lastOtErrorCode || warmMirrorState.lastErrorCode, tr("noneValue"));
28569
29152
  const lastErrorCode = formatOtDiagnosticValue(otStatus?.lastErrorCode || otStatus?.reason || otStatus?.error, tr("noneValue"));
@@ -28575,12 +29158,12 @@ ${block}`;
28575
29158
  const bridgeFailed = otStatus?.ok === false;
28576
29159
  const focusFiles = getActiveFocusFiles();
28577
29160
  const otWarmStart = otWarmMirrorController?.canUseOtWarmStart?.({ enabled, focusFiles, mirrorStatus }) || { ok: false };
28578
- const fallback = !enabled || statusValue !== "observing" || bridgeFailed || otWarmStart?.ok !== true;
29161
+ const fallback = !enabled || failureActive || statusValue !== "observing" || bridgeFailed || otWarmStart?.ok !== true;
28579
29162
  return {
28580
29163
  title: tr("diagnosticsOtTitle"),
28581
29164
  subtitle: tr("diagnosticsOtSubtitle"),
28582
29165
  status: enabled && !fallback && !bridgeFailed ? "completed" : enabled || bridgeFailed ? "warning" : "completed",
28583
- summary: tr(enabled ? "diagnosticsOtSummaryEnabled" : "diagnosticsOtSummaryDisabled"),
29166
+ summary: tr(failureActive ? otWarmMirrorController.getFailureMessageKey(warmMirrorState.lastErrorCode) : enabled ? "diagnosticsOtSummaryEnabled" : "diagnosticsOtSummaryDisabled"),
28584
29167
  bullets: [
28585
29168
  `${tr("otStatus")}: ${formatOtStatusLabel(statusValue)}`,
28586
29169
  `${tr("otFreshFiles")}: ${otFreshFileCount}`,
@@ -28762,7 +29345,7 @@ ${block}`;
28762
29345
  window.CodexOverleafDiagnosticsController = { create };
28763
29346
  })();
28764
29347
  }).call(globalThis, void 0, void 0, void 0);
28765
- (function runClassicContentModule74(module, exports, require2) {
29348
+ (function runClassicContentModule77(module, exports, require2) {
28766
29349
  (function initCodexOverleafRunGuidanceView() {
28767
29350
  "use strict";
28768
29351
  const LEGACY_GUIDANCE_TITLES = /* @__PURE__ */ new Set([
@@ -28802,7 +29385,7 @@ ${block}`;
28802
29385
  };
28803
29386
  })();
28804
29387
  }).call(globalThis, void 0, void 0, void 0);
28805
- (function runClassicContentModule75(module, exports, require2) {
29388
+ (function runClassicContentModule78(module, exports, require2) {
28806
29389
  (function initCodexOverleafRunGuidanceController() {
28807
29390
  "use strict";
28808
29391
  function create(deps = {}) {
@@ -28865,7 +29448,7 @@ ${block}`;
28865
29448
  window.CodexOverleafRunGuidanceController = { create };
28866
29449
  })();
28867
29450
  }).call(globalThis, void 0, void 0, void 0);
28868
- (function runClassicContentModule76(module, exports, require2) {
29451
+ (function runClassicContentModule79(module, exports, require2) {
28869
29452
  (function initCodexOverleafRunResultActions(root, factory) {
28870
29453
  const api = factory();
28871
29454
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -28970,13 +29553,224 @@ ${block}`;
28970
29553
  return Object.freeze({ create });
28971
29554
  });
28972
29555
  }).call(globalThis, void 0, void 0, void 0);
28973
- (function runClassicContentModule77(module, exports, require2) {
29556
+ (function runClassicContentModule80(module, exports, require2) {
29557
+ (function initRunScrollLayout(root, factory) {
29558
+ if (typeof module === "object" && module.exports) {
29559
+ module.exports = factory();
29560
+ } else {
29561
+ root.CodexOverleafModuleRegistry.define("RunScrollLayout", [], factory);
29562
+ }
29563
+ })(typeof window !== "undefined" ? window : globalThis, function runScrollLayoutFactory() {
29564
+ "use strict";
29565
+ const READING_ANCHORS = ".run-final-answer p, .run-final-answer li, .run-final-answer pre, .run-final-answer h1, .run-final-answer h2, .run-final-answer h3, .run-stream p, .run-stream li, .run-stream pre, [data-run-task], [data-run-status]";
29566
+ function create({ getScroller, onLayoutChange, isFollowing = () => true, window: host = globalThis } = {}) {
29567
+ let current = null;
29568
+ let resizeObserver = null;
29569
+ let mutationObserver = null;
29570
+ let anchor = null;
29571
+ let extent = "";
29572
+ function measure() {
29573
+ return current ? `${current.scrollHeight}:${current.clientHeight}` : "";
29574
+ }
29575
+ function captureAnchor(force = false) {
29576
+ if (!current || isFollowing()) {
29577
+ anchor = null;
29578
+ return;
29579
+ }
29580
+ if (!force && extent !== measure()) return;
29581
+ const bounds = current.getBoundingClientRect?.();
29582
+ if (!bounds) return;
29583
+ const nodes = current.querySelectorAll?.(READING_ANCHORS) || [];
29584
+ anchor = null;
29585
+ for (const node of nodes) {
29586
+ const rect = node.getBoundingClientRect();
29587
+ if (rect.height > 0 && rect.bottom > bounds.top && rect.top < bounds.bottom) {
29588
+ anchor = { node, offset: rect.top - bounds.top };
29589
+ break;
29590
+ }
29591
+ }
29592
+ }
29593
+ function restoreAnchor() {
29594
+ if (isFollowing() || !anchor || !current.contains?.(anchor.node)) return;
29595
+ const rect = anchor.node.getBoundingClientRect();
29596
+ if (!rect.height) return;
29597
+ const delta = rect.top - current.getBoundingClientRect().top - anchor.offset;
29598
+ if (Math.abs(delta) > 0.5) current.scrollTop += delta;
29599
+ }
29600
+ function snapshot() {
29601
+ captureAnchor();
29602
+ const run = anchor?.node.closest?.("[data-run-id]");
29603
+ return {
29604
+ scrollTop: current?.scrollTop || 0,
29605
+ runId: run?.dataset?.runId || "",
29606
+ index: run ? Array.from(run.querySelectorAll(READING_ANCHORS)).indexOf(anchor.node) : -1,
29607
+ offset: anchor?.offset || 0
29608
+ };
29609
+ }
29610
+ function restore(reading) {
29611
+ if (!current || !reading) return;
29612
+ current.scrollTop = reading.scrollTop;
29613
+ const run = Array.from(current.querySelectorAll?.("[data-run-id]") || []).find((node2) => node2.dataset.runId === reading.runId);
29614
+ const node = run?.querySelectorAll(READING_ANCHORS)[reading.index];
29615
+ anchor = node ? { node, offset: reading.offset } : null;
29616
+ restoreAnchor();
29617
+ extent = measure();
29618
+ captureAnchor(true);
29619
+ }
29620
+ function dispose() {
29621
+ resizeObserver?.disconnect();
29622
+ mutationObserver?.disconnect();
29623
+ resizeObserver = null;
29624
+ mutationObserver = null;
29625
+ current = null;
29626
+ anchor = null;
29627
+ extent = "";
29628
+ }
29629
+ function notify() {
29630
+ if (!current) return;
29631
+ const active = getScroller();
29632
+ if (active !== current) {
29633
+ bind(active);
29634
+ return;
29635
+ }
29636
+ restoreAnchor();
29637
+ extent = measure();
29638
+ onLayoutChange(current);
29639
+ captureAnchor(true);
29640
+ }
29641
+ function bind(scroller) {
29642
+ if (scroller === current) return;
29643
+ dispose();
29644
+ if (!scroller || typeof host.ResizeObserver !== "function") return;
29645
+ current = scroller;
29646
+ extent = measure();
29647
+ captureAnchor(true);
29648
+ resizeObserver = new host.ResizeObserver(notify);
29649
+ resizeObserver.observe(scroller);
29650
+ for (const child of scroller.children || []) resizeObserver.observe(child);
29651
+ if (typeof host.MutationObserver === "function") {
29652
+ mutationObserver = new host.MutationObserver((records) => {
29653
+ if (current !== scroller) return;
29654
+ for (const record of records) {
29655
+ for (const node of record.removedNodes) {
29656
+ if (node.nodeType === 1) resizeObserver.unobserve(node);
29657
+ }
29658
+ for (const node of record.addedNodes) {
29659
+ if (node.nodeType === 1) resizeObserver.observe(node);
29660
+ }
29661
+ }
29662
+ notify();
29663
+ });
29664
+ mutationObserver.observe(scroller, { childList: true });
29665
+ }
29666
+ }
29667
+ return Object.freeze({ bind, dispose, captureAnchor, snapshot, restore });
29668
+ }
29669
+ return Object.freeze({ create });
29670
+ });
29671
+ }).call(globalThis, void 0, void 0, void 0);
29672
+ (function runClassicContentModule81(module, exports, require2) {
29673
+ (function initRunFailureNotice(root, factory) {
29674
+ if (typeof module === "object" && module.exports) {
29675
+ module.exports = factory();
29676
+ } else {
29677
+ root.CodexOverleafModuleRegistry.define("RunFailureNotice", [], factory);
29678
+ }
29679
+ })(typeof window !== "undefined" ? window : globalThis, function runFailureNoticeFactory() {
29680
+ "use strict";
29681
+ const REVIEW_CODES = /* @__PURE__ */ new Set(["write_timeout", "partial_write_needs_review"]);
29682
+ const CANCEL_CODES = /* @__PURE__ */ new Set(["cancelled", "user_cancelled", "codex_cancelled"]);
29683
+ function create({
29684
+ tr,
29685
+ projectRunSettlement,
29686
+ sanitizeText = (value) => value,
29687
+ document: documentRef = globalThis.document
29688
+ } = {}) {
29689
+ const notices = /* @__PURE__ */ new WeakMap();
29690
+ function append(report, event = {}, run = {}) {
29691
+ const projection = typeof projectRunSettlement === "function" && run ? projectRunSettlement(run) : null;
29692
+ const failure = projection?.primaryFailure || event.failure;
29693
+ if (report.dataset.status === "cancelled" || CANCEL_CODES.has(failure?.code)) return;
29694
+ const facts = run?.settlement || run?.settlementFacts || {};
29695
+ const unconfirmed = REVIEW_CODES.has(failure?.code) || facts.documentEffect === "possibly-changed" || facts.evidence?.applied === "unknown" && facts.evidence?.settled === "needs-review";
29696
+ const partial = facts.evidence?.applied === "partial" || run?.partialWriteback === true;
29697
+ if (!failure && !unconfirmed && !partial && report.dataset.status !== "failed") return;
29698
+ const notice = documentRef.createElement("aside");
29699
+ notice.className = "run-feedback";
29700
+ notice.dataset.tone = unconfirmed || partial || failure?.terminalState === "degraded" || failure?.terminalState === "needs_review" ? "warning" : "error";
29701
+ notice.setAttribute("role", "note");
29702
+ const heading = documentRef.createElement("strong");
29703
+ heading.className = "run-feedback__title";
29704
+ heading.textContent = tr(unconfirmed ? "runFeedbackUnconfirmedTitle" : partial ? "runFeedbackPartialTitle" : "runFeedbackFailedTitle");
29705
+ const message = documentRef.createElement("p");
29706
+ const detail = typeof failure?.userMessage === "string" ? failure.userMessage : "";
29707
+ const file = safeProjectPath(failure?.file);
29708
+ message.textContent = sanitizeText([file, detail].filter(Boolean).join(": ").slice(0, 1e3)) || tr("runFeedbackGeneric");
29709
+ notice.append(heading, message);
29710
+ if (unconfirmed) {
29711
+ const warning = documentRef.createElement("p");
29712
+ warning.textContent = tr("runFeedbackUnconfirmed");
29713
+ notice.append(warning);
29714
+ }
29715
+ notices.set(report, { notice, unconfirmed, failure, file });
29716
+ report.append(notice);
29717
+ }
29718
+ function formatMetaValue(report, row) {
29719
+ if (!notices.get(report)?.unconfirmed) return row.value;
29720
+ const key = String(row.key || "").toLowerCase().replace(/[^a-z]/g, "");
29721
+ const label = String(row.label || "").trim().toLowerCase();
29722
+ if (key === "writeresult" || key === "next" || ["write result", "next", "\u5199\u5165\u7ED3\u679C", "\u4E0B\u4E00\u6B65"].includes(label)) {
29723
+ return tr("runFeedbackUnconfirmedResult");
29724
+ }
29725
+ return row.value;
29726
+ }
29727
+ function prepareRecovery(report, { openFile } = {}) {
29728
+ const info = notices.get(report);
29729
+ const target = info?.notice || report;
29730
+ if (!info?.unconfirmed) return { target, handled: false };
29731
+ const canOpen = Boolean(info.file && typeof openFile === "function");
29732
+ const button = documentRef.createElement("button");
29733
+ button.type = "button";
29734
+ button.className = "run-final-answer__recovery-action";
29735
+ button.dataset.recoveryFor = info.failure?.code || "write_unconfirmed";
29736
+ button.textContent = tr(canOpen ? "runFeedbackReviewFile" : "runFeedbackReviewDetails", { file: info.file });
29737
+ button.addEventListener("click", (event) => {
29738
+ event.stopPropagation();
29739
+ if (canOpen) {
29740
+ openFile(info.file);
29741
+ return;
29742
+ }
29743
+ const process = report.closest?.("[data-run-id]")?.querySelector?.("[data-run-process]");
29744
+ if (process) {
29745
+ process.open = true;
29746
+ process.scrollIntoView?.({ block: "nearest" });
29747
+ } else {
29748
+ button.disabled = true;
29749
+ button.title = tr("runFeedbackDetailsUnavailable");
29750
+ }
29751
+ });
29752
+ target.append(button);
29753
+ return { target, handled: true };
29754
+ }
29755
+ return Object.freeze({ append, formatMetaValue, prepareRecovery });
29756
+ }
29757
+ function safeProjectPath(value) {
29758
+ if (typeof value !== "string") return "";
29759
+ const path = value.replace(/\\/g, "/");
29760
+ return path && !path.startsWith("/") && !/^[A-Za-z]:/.test(path) && !path.split("/").includes("..") ? path.slice(0, 300) : "";
29761
+ }
29762
+ return Object.freeze({ create });
29763
+ });
29764
+ }).call(globalThis, void 0, void 0, void 0);
29765
+ (function runClassicContentModule82(module, exports, require2) {
28974
29766
  (function initCodexOverleafRunTimelineView() {
28975
29767
  "use strict";
28976
29768
  function create(deps = {}) {
28977
29769
  const {
28978
29770
  RunGuidanceView,
28979
29771
  RunResultActions,
29772
+ RunScrollLayout,
29773
+ RunFailureNotice,
28980
29774
  tr,
28981
29775
  tx,
28982
29776
  getLocale,
@@ -29010,17 +29804,25 @@ ${block}`;
29010
29804
  } = deps;
29011
29805
  let logAutoFollow = true;
29012
29806
  let userScrollIntentUntil = 0;
29807
+ let userScrollExtent = "";
29013
29808
  let scrollLogRafId = 0;
29014
29809
  let scrollLogPendingForce = false;
29015
29810
  let jumpToLatestButton = null;
29016
29811
  let unreadSinceDetach = 0;
29017
29812
  let runElapsedTimer = null;
29813
+ const scrollLayout = RunScrollLayout?.create({
29814
+ getScroller: getLogScrollContainer,
29815
+ isFollowing: () => logAutoFollow,
29816
+ onLayoutChange: (scroller) => logAutoFollow ? scrollLogToBottom() : updateJumpToLatestButton(scroller)
29817
+ });
29818
+ const failureNotice = RunFailureNotice?.create({ tr, projectRunSettlement, sanitizeText: sanitizeAssistantVisibleText });
29018
29819
  function resetAutoFollow() {
29019
29820
  logAutoFollow = true;
29020
29821
  userScrollIntentUntil = 0;
29021
29822
  }
29022
29823
  function bindLogAutoFollow() {
29023
29824
  const scroller = getLogScrollContainer();
29825
+ scrollLayout?.bind(scroller);
29024
29826
  if (!scroller || scroller.dataset.autoFollowBound === "true") {
29025
29827
  return;
29026
29828
  }
@@ -29030,15 +29832,14 @@ ${block}`;
29030
29832
  scroller.addEventListener("pointerdown", markUserScrollIntent, { passive: true });
29031
29833
  scroller.addEventListener("keydown", (event) => {
29032
29834
  if (["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(event.key)) {
29033
- markUserScrollIntent();
29835
+ markUserScrollIntent(event);
29034
29836
  }
29035
29837
  });
29036
29838
  scroller.addEventListener("scroll", () => {
29037
- if (Date.now() <= userScrollIntentUntil) {
29839
+ if (Date.now() <= userScrollIntentUntil && userScrollExtent === `${scroller.scrollHeight}:${scroller.clientHeight}`) {
29038
29840
  logAutoFollow = isLogNearBottom(scroller);
29039
- } else if (isLogNearBottom(scroller)) {
29040
- logAutoFollow = true;
29041
29841
  }
29842
+ scrollLayout?.captureAnchor();
29042
29843
  updateJumpToLatestButton(scroller);
29043
29844
  }, { passive: true });
29044
29845
  }
@@ -29069,6 +29870,7 @@ ${block}`;
29069
29870
  if (!el) {
29070
29871
  return;
29071
29872
  }
29873
+ if (el.dataset) el.dataset.logAutoFollow = logAutoFollow ? "true" : "false";
29072
29874
  const button = ensureJumpToLatestButton();
29073
29875
  if (!button) {
29074
29876
  return;
@@ -29091,8 +29893,16 @@ ${block}`;
29091
29893
  function getLogScrollContainer() {
29092
29894
  return getPanel()?.querySelector("[data-log]") || getPanel()?.querySelector("[data-main]");
29093
29895
  }
29094
- function markUserScrollIntent() {
29896
+ function markUserScrollIntent(event) {
29897
+ const scroller = getLogScrollContainer();
29898
+ if (!scroller || event?.type === "pointerdown" && event.target !== scroller) return;
29899
+ if (event?.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;
29095
29900
  userScrollIntentUntil = Date.now() + 1200;
29901
+ userScrollExtent = `${scroller.scrollHeight}:${scroller.clientHeight}`;
29902
+ if (event?.type === "wheel" && event.deltaY < 0 || ["ArrowUp", "PageUp", "Home"].includes(event?.key)) {
29903
+ logAutoFollow = false;
29904
+ scrollLogPendingForce = false;
29905
+ }
29096
29906
  }
29097
29907
  function isLogNearBottom(log) {
29098
29908
  if (!log) {
@@ -29111,7 +29921,7 @@ ${block}`;
29111
29921
  userScrollIntentUntil = 0;
29112
29922
  unreadSinceDetach = 0;
29113
29923
  scrollLogPendingForce = true;
29114
- } else if (!(logAutoFollow || isLogNearBottom(scroller))) {
29924
+ } else if (!logAutoFollow) {
29115
29925
  updateJumpToLatestButton(scroller);
29116
29926
  return;
29117
29927
  }
@@ -29120,7 +29930,7 @@ ${block}`;
29120
29930
  if (!el) {
29121
29931
  return;
29122
29932
  }
29123
- if (scrollLogPendingForce || logAutoFollow || isLogNearBottom(el)) {
29933
+ if (scrollLogPendingForce || logAutoFollow) {
29124
29934
  setLogScrollPosition(el);
29125
29935
  }
29126
29936
  scrollLogPendingForce = false;
@@ -29196,11 +30006,12 @@ ${block}`;
29196
30006
  }
29197
30007
  return tr("processed", { elapsed });
29198
30008
  }
29199
- function renderRunHistory() {
30009
+ function renderRunHistory(options2 = {}) {
29200
30010
  const log = getPanel()?.querySelector("[data-log]");
29201
30011
  if (!log) {
29202
30012
  return;
29203
30013
  }
30014
+ const reading = options2.preserveScroll === true && !logAutoFollow ? scrollLayout?.snapshot() || { scrollTop: log.scrollTop } : null;
29204
30015
  log.replaceChildren();
29205
30016
  if (!getState().runs?.length) {
29206
30017
  const empty = document.createElement("div");
@@ -29229,7 +30040,11 @@ ${block}`;
29229
30040
  for (const run of runs) {
29230
30041
  log.append(renderRunCard(run));
29231
30042
  }
29232
- scrollLogToBottom({ force: true });
30043
+ if (reading) {
30044
+ log.scrollTop = reading.scrollTop;
30045
+ scrollLayout?.restore(reading);
30046
+ updateJumpToLatestButton(log);
30047
+ } else scrollLogToBottom({ force: true });
29233
30048
  }
29234
30049
  function renderRunCard(run) {
29235
30050
  const root = document.createElement("section");
@@ -29504,7 +30319,7 @@ ${block}`;
29504
30319
  if (row.key) dt.dataset.metaKey = row.key;
29505
30320
  const dd = document.createElement("dd");
29506
30321
  dd.className = "run-final-answer__meta-value";
29507
- dd.textContent = row.value;
30322
+ dd.textContent = failureNotice?.formatMetaValue(report, row) ?? row.value;
29508
30323
  metaBlock.append(dt, dd);
29509
30324
  }
29510
30325
  if (metaBlock.children.length) {
@@ -29577,6 +30392,7 @@ ${lines}`
29577
30392
  decorateCompletionReport(main, { tx });
29578
30393
  report.append(main);
29579
30394
  }
30395
+ failureNotice?.append(report, event, run);
29580
30396
  appendCompletionMetaBlock(report, structured.meta);
29581
30397
  appendCompileFix(report);
29582
30398
  appendRejectedRedo(report);
@@ -29592,6 +30408,7 @@ ${lines}`
29592
30408
  report.append(body);
29593
30409
  appendCompileFix(report);
29594
30410
  appendRejectedRedo(report);
30411
+ failureNotice?.append(report, event, run);
29595
30412
  appendCompletionMetaBlock(report, split.meta);
29596
30413
  appendRecoveryActionForFailure(report, event, run);
29597
30414
  return report;
@@ -29645,6 +30462,9 @@ ${conclusion}` : `${label} ${conclusion}`;
29645
30462
  return button;
29646
30463
  }
29647
30464
  function appendRecoveryActionForFailure(report, event, run) {
30465
+ const recovery = failureNotice?.prepareRecovery(report, { openFile: openProjectFileForFailure });
30466
+ if (recovery?.handled) return;
30467
+ report = recovery?.target || report;
29648
30468
  const failureCode = event?.failure?.code || "";
29649
30469
  if (RETRYABLE_FAILURE_CODES.has(failureCode) && typeof refillComposerForRetry === "function") {
29650
30470
  const retry = buildRecoveryButton(
@@ -29942,7 +30762,7 @@ ${conclusion}` : `${label} ${conclusion}`;
29942
30762
  window.CodexOverleafRunTimelineView = { create };
29943
30763
  })();
29944
30764
  }).call(globalThis, void 0, void 0, void 0);
29945
- (function runClassicContentModule78(module, exports, require2) {
30765
+ (function runClassicContentModule83(module, exports, require2) {
29946
30766
  (function initCodexOverleafProjectSettingsCoordinator() {
29947
30767
  "use strict";
29948
30768
  function create(deps = {}) {
@@ -30205,6 +31025,8 @@ ${conclusion}` : `${label} ${conclusion}`;
30205
31025
  });
30206
31026
  saveStateSoon();
30207
31027
  renderLocalSkillList();
31028
+ deps.updateGlobalPreferences?.({ codexOverleafSkillEnabled: { [skillId]: Boolean(enabled) } })?.catch(() => {
31029
+ });
30208
31030
  }
30209
31031
  function readSkillLoadingSettingsFromSettings() {
30210
31032
  return SettingsPanel.readState(getSettingsPanelInstance()).skillToggles;
@@ -30269,7 +31091,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30269
31091
  window.CodexOverleafProjectSettingsCoordinator = { create };
30270
31092
  })();
30271
31093
  }).call(globalThis, void 0, void 0, void 0);
30272
- (function runClassicContentModule79(module, exports, require2) {
31094
+ (function runClassicContentModule84(module, exports, require2) {
30273
31095
  (function initCodexOverleafSessionMenuView() {
30274
31096
  "use strict";
30275
31097
  function create(deps = {}) {
@@ -30474,7 +31296,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30474
31296
  window.CodexOverleafSessionMenuView = { create };
30475
31297
  })();
30476
31298
  }).call(globalThis, void 0, void 0, void 0);
30477
- (function runClassicContentModule80(module, exports, require2) {
31299
+ (function runClassicContentModule85(module, exports, require2) {
30478
31300
  (function initCodexOverleafSessionManager() {
30479
31301
  "use strict";
30480
31302
  function create(deps = {}) {
@@ -30733,7 +31555,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30733
31555
  window.CodexOverleafSessionManager = { create };
30734
31556
  })();
30735
31557
  }).call(globalThis, void 0, void 0, void 0);
30736
- (function runClassicContentModule81(module, exports, require2) {
31558
+ (function runClassicContentModule86(module, exports, require2) {
30737
31559
  (function initCodexOverleafSessionForkController(root, factory) {
30738
31560
  const api = factory();
30739
31561
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -30862,7 +31684,7 @@ ${conclusion}` : `${label} ${conclusion}`;
30862
31684
  return { copyRunSnapshot, create };
30863
31685
  });
30864
31686
  }).call(globalThis, void 0, void 0, void 0);
30865
- (function runClassicContentModule82(module, exports, require2) {
31687
+ (function runClassicContentModule87(module, exports, require2) {
30866
31688
  (function initCodexOverleafApplyResultFormatters() {
30867
31689
  "use strict";
30868
31690
  function create(deps = {}) {
@@ -31182,7 +32004,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31182
32004
  window.CodexOverleafApplyResultFormatters = { create };
31183
32005
  })();
31184
32006
  }).call(globalThis, void 0, void 0, void 0);
31185
- (function runClassicContentModule83(module, exports, require2) {
32007
+ (function runClassicContentModule88(module, exports, require2) {
31186
32008
  (function initCodexOverleafModelPickerSupport(root, factory) {
31187
32009
  if (typeof module === "object" && module.exports) {
31188
32010
  module.exports = factory(require2("../shared/models"));
@@ -31283,7 +32105,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31283
32105
  return api;
31284
32106
  });
31285
32107
  }).call(globalThis, void 0, void 0, void 0);
31286
- (function runClassicContentModule84(module, exports, require2) {
32108
+ (function runClassicContentModule89(module, exports, require2) {
31287
32109
  (function initCodexOverleafModelPicker() {
31288
32110
  "use strict";
31289
32111
  function create(deps = {}) {
@@ -31761,7 +32583,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31761
32583
  window.CodexOverleafModelPicker = { create };
31762
32584
  })();
31763
32585
  }).call(globalThis, void 0, void 0, void 0);
31764
- (function runClassicContentModule85(module, exports, require2) {
32586
+ (function runClassicContentModule90(module, exports, require2) {
31765
32587
  (function initCodexOverleafProjectSessionCleanup() {
31766
32588
  "use strict";
31767
32589
  const FRESH_RUNNING_WINDOW_MS = 30 * 60 * 1e3;
@@ -31861,7 +32683,7 @@ ${conclusion}` : `${label} ${conclusion}`;
31861
32683
  window.CodexOverleafProjectSessionCleanup = { create };
31862
32684
  })();
31863
32685
  }).call(globalThis, void 0, void 0, void 0);
31864
- (function runClassicContentModule86(module, exports, require2) {
32686
+ (function runClassicContentModule91(module, exports, require2) {
31865
32687
  (function initCodexOverleafRecentProjects() {
31866
32688
  "use strict";
31867
32689
  function create(deps = {}) {
@@ -32787,7 +33609,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32787
33609
  window.CodexOverleafRecentProjects = { create };
32788
33610
  })();
32789
33611
  }).call(globalThis, void 0, void 0, void 0);
32790
- (function runClassicContentModule87(module, exports, require2) {
33612
+ (function runClassicContentModule92(module, exports, require2) {
32791
33613
  (function initCodexOverleafOtWarmMirror() {
32792
33614
  "use strict";
32793
33615
  function create(deps = {}) {
@@ -32829,6 +33651,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32829
33651
  RUN_SNAPSHOT_ZIP_TIMEOUT_MS
32830
33652
  } = deps;
32831
33653
  let currentOtStatus = "off";
33654
+ let otTogglePending = false;
32832
33655
  let otSyncRequestId = 0;
32833
33656
  let otWarmMirrorProjectId = "";
32834
33657
  let lastExperimentalOtProjectId = "";
@@ -32840,7 +33663,10 @@ ${conclusion}` : `${label} ${conclusion}`;
32840
33663
  patchQueue: [],
32841
33664
  lastStatus: "off",
32842
33665
  lastPatchAt: 0,
32843
- lastErrorCode: ""
33666
+ lastErrorCode: "",
33667
+ failureActive: false,
33668
+ lastFailure: null,
33669
+ suspended: false
32844
33670
  };
32845
33671
  let mirrorPrefetchState = {
32846
33672
  inFlight: null,
@@ -32922,7 +33748,10 @@ ${conclusion}` : `${label} ${conclusion}`;
32922
33748
  async function handleExperimentalOtToggleClick(event) {
32923
33749
  const clicked = event?.currentTarget || event?.target;
32924
33750
  const targetEnabled = clicked && typeof clicked.checked === "boolean" ? clicked.checked : void 0;
33751
+ const projectId = getCurrentProjectId();
32925
33752
  event.preventDefault();
33753
+ await new Promise((resolve) => window.setTimeout(resolve, 0));
33754
+ if (projectId !== getCurrentProjectId()) return;
32926
33755
  await toggleExperimentalOtCheckbox(targetEnabled);
32927
33756
  }
32928
33757
  async function handleExperimentalOtToggleKeydown(event) {
@@ -32934,28 +33763,35 @@ ${conclusion}` : `${label} ${conclusion}`;
32934
33763
  }
32935
33764
  async function toggleExperimentalOtCheckbox(targetEnabled) {
32936
33765
  const checkbox = getPanel()?.querySelector("[data-experimental-ot]");
32937
- if (!checkbox) {
33766
+ if (!checkbox || otTogglePending) {
32938
33767
  return;
32939
33768
  }
32940
- const nextEnabled = typeof targetEnabled === "boolean" ? targetEnabled : !checkbox.checked;
32941
- if (nextEnabled) {
32942
- closeDiagnosticsMenu();
32943
- const approved = await showPluginConfirm({
32944
- title: tr("experimentalOtConfirmTitle"),
32945
- message: tr("experimentalOtConfirmMessage"),
32946
- confirmLabel: tr("experimentalOtConfirmEnable"),
32947
- cancelLabel: tr("confirmDefaultCancel")
32948
- });
32949
- if (!approved) {
32950
- return;
33769
+ const projectId = getCurrentProjectId();
33770
+ otTogglePending = true;
33771
+ try {
33772
+ const nextEnabled = typeof targetEnabled === "boolean" ? targetEnabled : !checkbox.checked;
33773
+ if (nextEnabled) {
33774
+ closeDiagnosticsMenu();
33775
+ const approved = await showPluginConfirm({
33776
+ title: tr("experimentalOtConfirmTitle"),
33777
+ message: tr("experimentalOtConfirmMessage"),
33778
+ confirmLabel: tr("experimentalOtConfirmEnable"),
33779
+ cancelLabel: tr("confirmDefaultCancel")
33780
+ });
33781
+ if (!approved) {
33782
+ return;
33783
+ }
33784
+ } else {
33785
+ closeDiagnosticsMenu();
32951
33786
  }
32952
- } else {
32953
- closeDiagnosticsMenu();
32954
- }
32955
- checkbox.checked = nextEnabled;
32956
- handleExperimentalOtToggleChange({ currentTarget: checkbox });
32957
- if (nextEnabled) {
32958
- showPluginToast(tr("experimentalOtEnabledToast"), { status: "info" });
33787
+ if (projectId !== getCurrentProjectId() || checkbox !== getPanel()?.querySelector("[data-experimental-ot]")) return;
33788
+ checkbox.checked = nextEnabled;
33789
+ handleExperimentalOtToggleChange({ currentTarget: checkbox });
33790
+ if (nextEnabled) {
33791
+ showPluginToast(tr("experimentalOtEnabledToast"), { status: "info" });
33792
+ }
33793
+ } finally {
33794
+ otTogglePending = false;
32959
33795
  }
32960
33796
  }
32961
33797
  function handleExperimentalOtToggleChange(event) {
@@ -32964,6 +33800,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32964
33800
  return;
32965
33801
  }
32966
33802
  const projectId = getCurrentProjectId();
33803
+ otWarmMirrorState.failureActive = false;
32967
33804
  lastExperimentalOtProjectId = projectId;
32968
33805
  setExperimentalOtEnabledForProject(projectId, checkbox.checked);
32969
33806
  updateExperimentalOtToggleControl(checkbox.checked);
@@ -32980,7 +33817,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32980
33817
  updateOtStatusDisplay(checkbox.checked ? currentOtStatus === "off" ? "starting" : currentOtStatus : "off");
32981
33818
  syncOtWarmMirrorController().catch((error) => {
32982
33819
  updateOtStatusDisplay("unavailable");
32983
- appendPlainLog(tx(`Experimental OT warm mirror unavailable: ${error.message}`, `\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
33820
+ appendPlainLog(tx(`OT warm mirror unavailable: ${error.message}`, `OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
32984
33821
  });
32985
33822
  saveStateSoon();
32986
33823
  }
@@ -32998,6 +33835,7 @@ ${conclusion}` : `${label} ${conclusion}`;
32998
33835
  otWarmMirrorProjectId = projectId;
32999
33836
  ensureOtWarmMirrorStateProject(projectId);
33000
33837
  if (enabled) {
33838
+ otWarmMirrorState.suspended = false;
33001
33839
  const pause = otWarmMirrorController?.shouldPauseOtWarmMirror?.({ running: Boolean(getCurrentRunView()) }) || { pause: false };
33002
33840
  if (pause.pause) {
33003
33841
  clearOtEventPolling({ clearPatchQueue: true });
@@ -33009,7 +33847,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33009
33847
  } else {
33010
33848
  clearOtEventPolling({ clearPatchQueue: true });
33011
33849
  }
33012
- const response = enabled ? await callPageBridge("startOtObserver", { projectId }) : await callPageBridge("stopOtObserver", {});
33850
+ let response;
33851
+ try {
33852
+ response = enabled ? await callPageBridge("startOtObserver", { projectId }) : await callPageBridge("stopOtObserver", {});
33853
+ } catch (error) {
33854
+ response = { ok: false, error: otWarmMirrorController.normalizeFailureCode(error?.code || error?.message, "ot_bridge_unavailable") };
33855
+ }
33013
33856
  if (!isCurrentOtSync(requestId, projectId) || getCurrentProjectId() !== projectId) {
33014
33857
  if (enabled) {
33015
33858
  await handleStaleOtStartResponse(projectId, requestId);
@@ -33026,7 +33869,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33026
33869
  }
33027
33870
  const status = readOtBridgeStatus(response);
33028
33871
  if (!isSuccessfulOtBridgeResponse(response) || status === "unavailable") {
33029
- handleFailedOtStart(projectId, requestId);
33872
+ await handleFailedOtStart(projectId, requestId, response);
33030
33873
  return response;
33031
33874
  }
33032
33875
  const pauseAfterStart = otWarmMirrorController?.shouldPauseOtWarmMirror?.({ running: Boolean(getCurrentRunView()) }) || { pause: false };
@@ -33034,6 +33877,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33034
33877
  await pauseOtWarmMirror(pauseAfterStart.reason || "paused");
33035
33878
  return response;
33036
33879
  }
33880
+ otWarmMirrorState.failureActive = false;
33037
33881
  updateOtStatusDisplay(status);
33038
33882
  scheduleOtEventPolling(projectId, { immediate: true });
33039
33883
  return response;
@@ -33054,11 +33898,13 @@ ${conclusion}` : `${label} ${conclusion}`;
33054
33898
  await syncOtWarmMirrorController();
33055
33899
  }
33056
33900
  }
33057
- function handleFailedOtStart(projectId, requestId) {
33901
+ async function handleFailedOtStart(projectId, requestId, response) {
33058
33902
  if (!isCurrentOtSync(requestId, projectId) || getCurrentProjectId() !== projectId) {
33059
33903
  return;
33060
33904
  }
33061
33905
  clearOtEventPolling({ clearPatchQueue: true });
33906
+ recordOtFailure(readOtBridgeErrorCode(response, "ot_start_failed"), "start");
33907
+ otWarmMirrorState.suspended = true;
33062
33908
  setExperimentalOtEnabledForProject(projectId, false);
33063
33909
  const experimentalOtCheckbox = getPanel()?.querySelector("[data-experimental-ot]");
33064
33910
  if (experimentalOtCheckbox) {
@@ -33066,12 +33912,16 @@ ${conclusion}` : `${label} ${conclusion}`;
33066
33912
  }
33067
33913
  updateOtStatusDisplay("unavailable");
33068
33914
  saveStateSoon();
33915
+ try {
33916
+ await callPageBridge("stopOtObserver", { projectId });
33917
+ } catch (_error) {
33918
+ }
33069
33919
  }
33070
33920
  function readOtBridgeStatus(response) {
33071
33921
  if (!response || response.ok === false) {
33072
33922
  return "unavailable";
33073
33923
  }
33074
- return response.getState() || response.status || response.result?.getState() || response.result?.status || "unavailable";
33924
+ return response.state || response.status || response.result?.state || response.result?.status || "unavailable";
33075
33925
  }
33076
33926
  function ensureOtWarmMirrorStateProject(projectId) {
33077
33927
  if (!projectId || otWarmMirrorState.projectId === projectId) {
@@ -33081,6 +33931,9 @@ ${conclusion}` : `${label} ${conclusion}`;
33081
33931
  otWarmMirrorState.projectId = projectId;
33082
33932
  otWarmMirrorState.lastPatchAt = 0;
33083
33933
  otWarmMirrorState.lastErrorCode = "";
33934
+ otWarmMirrorState.failureActive = false;
33935
+ otWarmMirrorState.lastFailure = null;
33936
+ otWarmMirrorState.suspended = false;
33084
33937
  otWarmMirrorState.lastStatus = currentOtStatus;
33085
33938
  }
33086
33939
  function clearOtEventPolling(options2 = {}) {
@@ -33114,7 +33967,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33114
33967
  }, delayMs);
33115
33968
  }
33116
33969
  function canPollOtWarmMirror(projectId) {
33117
- return Boolean(projectId && otWarmMirrorController?.shouldPauseOtWarmMirror && otWarmMirrorController?.buildPatchFilesRequest && isExperimentalOtEnabledForProject(projectId) && otWarmMirrorProjectId === projectId && getCurrentProjectId() === projectId);
33970
+ return Boolean(projectId && otWarmMirrorController?.shouldPauseOtWarmMirror && otWarmMirrorController?.buildPatchFilesRequest && isExperimentalOtEnabledForProject(projectId) && otWarmMirrorProjectId === projectId && getCurrentProjectId() === projectId && !otWarmMirrorState.suspended);
33118
33971
  }
33119
33972
  async function pollOtEvents(projectId = otWarmMirrorState.projectId || getCurrentProjectId()) {
33120
33973
  if (!canPollOtWarmMirror(projectId)) {
@@ -33134,11 +33987,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33134
33987
  return { ok: false, skipped: true, reason: "project_changed" };
33135
33988
  }
33136
33989
  if (!isSuccessfulOtBridgeResponse(statusResponse)) {
33137
- otWarmMirrorState.lastErrorCode = readOtBridgeErrorCode(statusResponse, "get_ot_status_failed");
33990
+ recordOtFailure(readOtBridgeErrorCode(statusResponse, "get_ot_status_failed"), "observe");
33138
33991
  updateOtStatusDisplay("unavailable");
33139
33992
  return statusResponse;
33140
33993
  }
33141
33994
  const status = readOtBridgeStatus(statusResponse);
33995
+ if (["unavailable", "inconsistent"].includes(status)) recordOtFailure(readOtBridgeErrorCode(statusResponse, "ot_observer_unavailable"), "observe");
33142
33996
  if (status) {
33143
33997
  updateOtStatusDisplay(status);
33144
33998
  }
@@ -33147,7 +34001,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33147
34001
  return { ok: false, skipped: true, reason: "project_changed" };
33148
34002
  }
33149
34003
  if (!isSuccessfulOtBridgeResponse(drainResponse)) {
33150
- otWarmMirrorState.lastErrorCode = readOtBridgeErrorCode(drainResponse, "drain_ot_events_failed");
34004
+ recordOtFailure(readOtBridgeErrorCode(drainResponse, "drain_ot_events_failed"), "observe");
33151
34005
  updateOtStatusDisplay("unavailable");
33152
34006
  return drainResponse;
33153
34007
  }
@@ -33157,7 +34011,8 @@ ${conclusion}` : `${label} ${conclusion}`;
33157
34011
  }
33158
34012
  return drainResponse;
33159
34013
  } catch (error) {
33160
- otWarmMirrorState.lastErrorCode = error?.code || error?.message || "ot_poll_failed";
34014
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
34015
+ recordOtFailure(error?.code || error?.message || "ot_poll_failed", "observe");
33161
34016
  updateOtStatusDisplay("unavailable");
33162
34017
  return { ok: false, error };
33163
34018
  } finally {
@@ -33182,12 +34037,28 @@ ${conclusion}` : `${label} ${conclusion}`;
33182
34037
  const value = response?.lastErrorCode || response?.reason || response?.error?.code || response?.error || fallback;
33183
34038
  return typeof value === "string" ? value : fallback;
33184
34039
  }
34040
+ function recordOtFailure(code, phase) {
34041
+ code = otWarmMirrorController.normalizeFailureCode(code);
34042
+ otWarmMirrorState.lastErrorCode = code;
34043
+ otWarmMirrorState.failureActive = true;
34044
+ otWarmMirrorState.lastFailure = { code, phase, at: Date.now() };
34045
+ }
33185
34046
  function queueOtPatchEvents(events, projectId = getCurrentProjectId()) {
33186
34047
  if (!Array.isArray(events) || !events.length || !canPollOtWarmMirror(projectId)) {
33187
34048
  return;
33188
34049
  }
33189
34050
  ensureOtWarmMirrorStateProject(projectId);
33190
- otWarmMirrorState.patchQueue.push(...events);
34051
+ const queued = otWarmMirrorController.queuePatchEvents(otWarmMirrorState.patchQueue, events);
34052
+ if (!queued.ok) {
34053
+ recordOtFailure(queued.reason, "queue");
34054
+ clearOtEventPolling({ clearPatchQueue: true });
34055
+ otWarmMirrorState.suspended = true;
34056
+ updateOtStatusDisplay("inconsistent");
34057
+ void callPageBridge("stopOtObserver", { projectId }).catch(() => {
34058
+ });
34059
+ return;
34060
+ }
34061
+ otWarmMirrorState.patchQueue = queued.queue;
33191
34062
  scheduleOtPatchFlush(projectId);
33192
34063
  }
33193
34064
  function scheduleOtPatchFlush(projectId = otWarmMirrorState.projectId || getCurrentProjectId(), options2 = {}) {
@@ -33221,11 +34092,18 @@ ${conclusion}` : `${label} ${conclusion}`;
33221
34092
  clearOtEventPolling({ clearPatchQueue: false });
33222
34093
  return { ok: false, skipped: true, reason: pause.reason || "paused" };
33223
34094
  }
33224
- const maxBatch = Math.max(1, Number(otWarmMirrorController?.OT_MAX_PATCH_BATCH) || 25);
33225
- const batch = otWarmMirrorState.patchQueue.splice(0, maxBatch);
34095
+ let batch = otWarmMirrorController.takePatchBatch(otWarmMirrorState.patchQueue);
33226
34096
  if (!batch.length) {
33227
34097
  return { ok: true, skipped: true, reason: "empty_batch" };
33228
34098
  }
34099
+ const fresh = otWarmMirrorController.queuePatchEvents([], batch);
34100
+ if (!fresh.ok) {
34101
+ recordOtFailure(fresh.reason, "queue");
34102
+ clearOtEventPolling({ clearPatchQueue: true });
34103
+ updateOtStatusDisplay("inconsistent");
34104
+ return { ok: false, skipped: true, reason: fresh.reason };
34105
+ }
34106
+ batch = fresh.queue;
33229
34107
  otWarmMirrorState.flushing = true;
33230
34108
  try {
33231
34109
  const request = {
@@ -33239,40 +34117,35 @@ ${conclusion}` : `${label} ${conclusion}`;
33239
34117
  return { ok: false, skipped: true, reason: "project_changed" };
33240
34118
  }
33241
34119
  const response = await sendBackgroundNative(request);
34120
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
33242
34121
  if (!response?.ok) {
33243
- otWarmMirrorState.lastErrorCode = response?.error?.code || response?.error?.message || "mirror_patch_failed";
34122
+ recordOtFailure(response?.error?.code || "mirror_patch_failed", "flush");
33244
34123
  updateOtStatusDisplay("inconsistent");
33245
34124
  return response;
33246
34125
  }
33247
- const result = response.result || {};
33248
- const skippedFiles = Array.isArray(result?.skippedFiles) ? result.skippedFiles : [];
33249
- const skippedCount = Number.isFinite(Number(result?.skippedCount)) ? Number(result.skippedCount) : skippedFiles.length;
33250
- if (skippedFiles.length || skippedCount > 0) {
33251
- otWarmMirrorState.lastErrorCode = "mirror_patch_skipped";
33252
- updateOtStatusDisplay("inconsistent");
33253
- return response;
33254
- }
33255
- const appliedFiles = Array.isArray(result?.appliedFiles) ? result.appliedFiles : null;
33256
- const appliedCount = Number.isFinite(Number(result?.appliedCount)) ? Number(result.appliedCount) : NaN;
33257
- if (!appliedFiles || !Number.isFinite(appliedCount) || appliedCount <= 0 || appliedFiles.length !== appliedCount) {
33258
- otWarmMirrorState.lastErrorCode = "mirror_patch_invalid_result";
34126
+ const receipt = otWarmMirrorController.validatePatchReceipt(batch, response.result);
34127
+ if (!receipt.ok) {
34128
+ recordOtFailure(receipt.reason, "flush");
33259
34129
  updateOtStatusDisplay("inconsistent");
33260
34130
  return response;
33261
34131
  }
33262
34132
  otWarmMirrorState.lastPatchAt = Date.now();
33263
34133
  otWarmMirrorState.lastErrorCode = "";
33264
- if (canPollOtWarmMirror(projectId)) {
34134
+ otWarmMirrorState.failureActive = false;
34135
+ if (canPollOtWarmMirror(projectId) && !getCurrentRunView()) {
33265
34136
  updateOtStatusDisplay("observing");
33266
34137
  }
33267
34138
  return response;
33268
34139
  } catch (error) {
33269
- otWarmMirrorState.lastErrorCode = error?.code || error?.message || "mirror_patch_failed";
34140
+ if (!canPollOtWarmMirror(projectId)) return { ok: false, skipped: true, reason: "project_changed" };
34141
+ recordOtFailure(error?.code || "mirror_patch_failed", "flush");
33270
34142
  updateOtStatusDisplay("inconsistent");
33271
34143
  return { ok: false, error };
33272
34144
  } finally {
33273
34145
  otWarmMirrorState.flushing = false;
33274
- if (otWarmMirrorState.patchQueue.length && canPollOtWarmMirror(projectId) && !getCurrentRunView()) {
33275
- scheduleOtPatchFlush(projectId, { immediate: true });
34146
+ const nextProjectId = otWarmMirrorState.projectId;
34147
+ if (otWarmMirrorState.patchQueue.length && canPollOtWarmMirror(nextProjectId) && !getCurrentRunView()) {
34148
+ scheduleOtPatchFlush(nextProjectId, { immediate: true });
33276
34149
  }
33277
34150
  }
33278
34151
  }
@@ -33312,12 +34185,12 @@ ${conclusion}` : `${label} ${conclusion}`;
33312
34185
  }
33313
34186
  }
33314
34187
  function updateOtStatusDisplay(status = currentOtStatus) {
33315
- const normalized = normalizeOtStatus(status);
34188
+ const normalized = otWarmMirrorState.failureActive && status === "observing" ? "inconsistent" : normalizeOtStatus(status);
33316
34189
  currentOtStatus = normalized;
33317
34190
  otWarmMirrorState.lastStatus = normalized;
33318
34191
  const statusElement = getPanel()?.querySelector("[data-ot-status]");
33319
34192
  if (statusElement) {
33320
- const label = formatOtStatusLabel(normalized);
34193
+ const label = formatOtStatusLabel(normalized) + (otWarmMirrorState.failureActive ? ": " + tr(otWarmMirrorController.getFailureMessageKey(otWarmMirrorState.lastErrorCode)) : "");
33321
34194
  statusElement.textContent = formatOtToggleStatusText(label);
33322
34195
  statusElement.dataset.otStatus = normalized;
33323
34196
  statusElement.title = formatOtToggleStatusText(label);
@@ -33465,37 +34338,23 @@ ${conclusion}` : `${label} ${conclusion}`;
33465
34338
  return health.reusable === true;
33466
34339
  }
33467
34340
  async function resolveWarmRunStart(taskContext = {}) {
34341
+ const projectId = getCurrentProjectId();
33468
34342
  const focusFiles = taskContext.focusFiles || [];
33469
34343
  const mode = taskContext.mode || getState()?.mode;
33470
34344
  const mirrorStatus = await getMirrorFreshness();
34345
+ if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: "project_changed" };
33471
34346
  const otWarmStart = otWarmMirrorController.canUseOtWarmStart({
33472
- enabled: isExperimentalOtEnabled(),
34347
+ enabled: isExperimentalOtEnabled() && !otWarmMirrorState.failureActive,
33473
34348
  focusFiles,
33474
34349
  mirrorStatus
33475
34350
  });
33476
- if (otWarmStart.ok) {
33477
- return {
33478
- useExistingMirror: true,
33479
- warmStart: true,
33480
- otWarmStart: true,
33481
- reason: "ot_focus_fresh",
33482
- mirrorStatus,
33483
- focusFiles: otWarmStart.focusFiles,
33484
- project: {
33485
- capabilities: {
33486
- fullProjectSnapshot: false,
33487
- method: "ot-warm-mirror"
33488
- },
33489
- files: []
33490
- }
33491
- };
33492
- }
33493
- if (!mirrorStatus?.exists || !isMirrorReusable(mirrorStatus)) {
34351
+ if (!mirrorStatus?.exists || !otWarmStart.ok && !isMirrorReusable(mirrorStatus)) {
33494
34352
  return { useExistingMirror: false, reason: "mirror_not_fresh", mirrorStatus };
33495
34353
  }
33496
34354
  let overlayProject = null;
33497
34355
  try {
33498
34356
  overlayProject = await callPageBridge("getProjectSnapshot", {
34357
+ projectId,
33499
34358
  force: true,
33500
34359
  maxAgeMs: 0,
33501
34360
  preferLightweight: true,
@@ -33509,6 +34368,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33509
34368
  } catch (error) {
33510
34369
  return { useExistingMirror: false, reason: "overlay_probe_failed", mirrorStatus };
33511
34370
  }
34371
+ if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: "project_changed" };
33512
34372
  const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles);
33513
34373
  if (!fileOverlays.length) {
33514
34374
  return { useExistingMirror: false, reason: "missing_current_overlay", mirrorStatus };
@@ -33522,7 +34382,9 @@ ${conclusion}` : `${label} ${conclusion}`;
33522
34382
  return {
33523
34383
  useExistingMirror: true,
33524
34384
  warmStart: true,
33525
- reason: mode === "ask" ? "warm_whole_project_ask" : "warm_whole_project",
34385
+ otWarmStart: otWarmStart.ok,
34386
+ focusFiles,
34387
+ reason: otWarmStart.ok ? "ot_focus_fresh" : mode === "ask" ? "warm_whole_project_ask" : "warm_whole_project",
33526
34388
  mirrorStatus,
33527
34389
  fileOverlays,
33528
34390
  project: overlayProject
@@ -33607,7 +34469,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33607
34469
  return syncOtWarmMirrorController();
33608
34470
  }).catch((error) => {
33609
34471
  updateOtStatusDisplay("unavailable");
33610
- appendPlainLog(tx(`Experimental OT warm mirror unavailable: ${error.message}`, `\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
34472
+ appendPlainLog(tx(`OT warm mirror unavailable: ${error.message}`, `OT \u9884\u70ED\u955C\u50CF\u4E0D\u53EF\u7528\uFF1A${error.message}`));
33611
34473
  });
33612
34474
  }
33613
34475
  return {
@@ -33642,7 +34504,7 @@ ${conclusion}` : `${label} ${conclusion}`;
33642
34504
  window.CodexOverleafOtWarmMirror = { create };
33643
34505
  })();
33644
34506
  }).call(globalThis, void 0, void 0, void 0);
33645
- (function runClassicContentModule88(module, exports, require2) {
34507
+ (function runClassicContentModule93(module, exports, require2) {
33646
34508
  (function initCodexOverleafWritebackOrchestrator() {
33647
34509
  "use strict";
33648
34510
  let tr;
@@ -34398,7 +35260,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34398
35260
  window.CodexOverleafWritebackOrchestrator = { create };
34399
35261
  })();
34400
35262
  }).call(globalThis, void 0, void 0, void 0);
34401
- (function runClassicContentModule89(module, exports, require2) {
35263
+ (function runClassicContentModule94(module, exports, require2) {
34402
35264
  (function initCodexOverleafRunSettlementPersistence(root, factory) {
34403
35265
  const api = factory();
34404
35266
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -34441,7 +35303,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34441
35303
  return Object.freeze({ create });
34442
35304
  });
34443
35305
  }).call(globalThis, void 0, void 0, void 0);
34444
- (function runClassicContentModule90(module, exports, require2) {
35306
+ (function runClassicContentModule95(module, exports, require2) {
34445
35307
  (function initPostNavigationSettlementPersistence(root, factory) {
34446
35308
  const api = factory();
34447
35309
  if (typeof module === "object" && module.exports) module.exports = api;
@@ -34533,7 +35395,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34533
35395
  return Object.freeze({ persist, persistRequired });
34534
35396
  });
34535
35397
  }).call(globalThis, void 0, void 0, void 0);
34536
- (function runClassicContentModule91(module, exports, require2) {
35398
+ (function runClassicContentModule96(module, exports, require2) {
34537
35399
  (function initCodexOverleafPanelMaintenance() {
34538
35400
  "use strict";
34539
35401
  function create(deps = {}) {
@@ -34636,30 +35498,43 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34636
35498
  list.append(row);
34637
35499
  }
34638
35500
  }
35501
+ let storageUsageRequestId = 0;
34639
35502
  function refreshStorageUsageSummary() {
34640
35503
  const node = getSettingsPanelInstance()?.container?.querySelector("[data-storage-usage]");
34641
35504
  if (!node) {
34642
35505
  return;
34643
35506
  }
34644
- const sessionCount = Array.isArray(getState()?.sessions) ? getState().sessions.length : 0;
34645
- const runCount = Array.isArray(getState()?.runs) ? getState().runs.length : 0;
35507
+ const requestId = ++storageUsageRequestId;
35508
+ const projectId = getCurrentProjectId?.();
35509
+ const isCurrent = () => requestId === storageUsageRequestId && projectId === getCurrentProjectId?.() && getSettingsPanelInstance()?.container?.querySelector("[data-storage-usage]") === node;
35510
+ const current = getState();
35511
+ const sessionCount = Array.isArray(current?.sessions) ? current.sessions.length : 0;
35512
+ const runCount = Array.isArray(current?.runs) ? current.runs.length : 0;
34646
35513
  const counts = tx(
34647
- `${sessionCount} session(s) \xB7 ${runCount} recent run(s) kept`,
34648
- `${sessionCount} \u4E2A\u4F1A\u8BDD \xB7 \u4FDD\u7559\u6700\u8FD1 ${runCount} \u8F6E\u8FD0\u884C`
35514
+ `${sessionCount} loaded session(s) in this project \xB7 ${runCount} run(s) in the active session`,
35515
+ `\u5F53\u524D\u9879\u76EE\u5DF2\u52A0\u8F7D ${sessionCount} \u4E2A\u4F1A\u8BDD \xB7 \u5F53\u524D\u4F1A\u8BDD ${runCount} \u8F6E\u8FD0\u884C`
34649
35516
  );
35517
+ node.removeAttribute?.("data-i18n");
34650
35518
  node.textContent = counts;
34651
- const estimate = navigator.storage?.estimate?.();
34652
- if (estimate?.then) {
34653
- estimate.then((info) => {
34654
- const usage = Number(info?.usage);
34655
- if (Number.isFinite(usage) && usage >= 0) {
34656
- const mb = usage / (1024 * 1024);
34657
- const size = mb >= 1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(usage / 1024))} KB`;
34658
- node.textContent = tx(`${counts} \xB7 ~${size} used on this site`, `${counts} \xB7 \u672C\u7AD9\u70B9\u7EA6\u5360\u7528 ${size}`);
34659
- }
34660
- }).catch(() => {
34661
- });
34662
- }
35519
+ const unavailable = () => {
35520
+ if (isCurrent()) node.textContent = tx(`${counts} \xB7 Site storage estimate unavailable`, `${counts} \xB7 \u672C\u7AD9\u70B9\u5BB9\u91CF\u6682\u65E0\u6CD5\u4F30\u7B97`);
35521
+ };
35522
+ let timer;
35523
+ const estimate = Promise.resolve().then(() => navigator.storage?.estimate?.());
35524
+ const deadline = new Promise((resolve) => {
35525
+ timer = setTimeout(() => resolve(null), 2e3);
35526
+ });
35527
+ return Promise.race([estimate, deadline]).then((info) => {
35528
+ if (!isCurrent()) return;
35529
+ const usage = info?.usage;
35530
+ if (!Number.isFinite(usage) || usage < 0) {
35531
+ unavailable();
35532
+ return;
35533
+ }
35534
+ const mb = usage / (1024 * 1024);
35535
+ const size = usage === 0 ? "0 KB" : mb >= 1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(usage / 1024))} KB`;
35536
+ node.textContent = tx(`${counts} \xB7 Site total ~${size}`, `${counts} \xB7 \u672C\u7AD9\u70B9\u603B\u5360\u7528\u7EA6 ${size}`);
35537
+ }).catch(unavailable).finally(() => clearTimeout(timer));
34663
35538
  }
34664
35539
  async function clearAllHistoryWithConfirm() {
34665
35540
  if (getCurrentRunView()) {
@@ -34851,7 +35726,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34851
35726
  window.CodexOverleafPanelMaintenance = { create };
34852
35727
  })();
34853
35728
  }).call(globalThis, void 0, void 0, void 0);
34854
- (function runClassicContentModule92(module, exports, require2) {
35729
+ (function runClassicContentModule97(module, exports, require2) {
34855
35730
  (function initCodexOverleafUpdateIdle(root) {
34856
35731
  "use strict";
34857
35732
  function create(options2 = {}) {
@@ -34919,7 +35794,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
34919
35794
  root.CodexOverleafUpdateIdle = Object.freeze({ create });
34920
35795
  })(typeof window !== "undefined" ? window : globalThis);
34921
35796
  }).call(globalThis, void 0, void 0, void 0);
34922
- (function runClassicContentModule93(module, exports, require2) {
35797
+ (function runClassicContentModule98(module, exports, require2) {
34923
35798
  (function initCodexOverleafUpdateNotice(root, factory) {
34924
35799
  if (typeof module === "object" && module.exports) {
34925
35800
  module.exports = factory(require2("../shared/managedUpdateProjection"));
@@ -35007,7 +35882,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35007
35882
  install: "codex-overleaf/consent-update-install",
35008
35883
  retry: "codex-overleaf/consent-update-check",
35009
35884
  later: "codex-overleaf/consent-update-later",
35010
- dismiss: "codex-overleaf/consent-update-dismiss"
35885
+ dismiss: "codex-overleaf/consent-update-dismiss",
35886
+ reload: "codex-overleaf/consent-update-reload"
35011
35887
  }[action]);
35012
35888
  } catch (error) {
35013
35889
  currentView = await reconcileAfterActionError(error, {
@@ -35120,7 +35996,10 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35120
35996
  const stateName = currentView?.state?.state || "idle";
35121
35997
  let requestedAction = "check";
35122
35998
  try {
35123
- if (stateName === "update_available") {
35999
+ if (["reload_required", "reload_tabs_required"].includes(stateName)) {
36000
+ requestedAction = "reload";
36001
+ currentView = await request("codex-overleaf/consent-update-reload");
36002
+ } else if (stateName === "update_available") {
35124
36003
  requestedAction = "install";
35125
36004
  currentView = await request("codex-overleaf/consent-update-install");
35126
36005
  } else if (["failed", "rolled_back"].includes(stateName)) {
@@ -35179,7 +36058,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35179
36058
  command.textContent = manualUpdateCommand(state);
35180
36059
  body.append(recovery, command);
35181
36060
  }
35182
- if (!["update_available", "failed", "rolled_back"].includes(state.state)) {
36061
+ if (!["update_available", "failed", "rolled_back", "reload_required", "reload_tabs_required"].includes(state.state)) {
35183
36062
  const bar = document.createElement("div");
35184
36063
  bar.className = "codex-update-notice-progress" + (progress.determinate ? "" : " is-indeterminate");
35185
36064
  bar.setAttribute("role", "progressbar");
@@ -35225,7 +36104,11 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35225
36104
  );
35226
36105
  let statusText = "";
35227
36106
  let buttonText = tx("Check for updates", "\u68C0\u67E5\u66F4\u65B0");
35228
- if (stateName === "checking") {
36107
+ if (["reload_required", "reload_tabs_required"].includes(stateName)) {
36108
+ summaryText = tx(`Installed v${latestVersion}; running extension v${currentVersion}.`, `\u5DF2\u5B89\u88C5 v${latestVersion}\uFF1B\u6269\u5C55\u5F53\u524D\u8FD0\u884C v${currentVersion}\u3002`);
36109
+ statusText = getCopy(state).detail;
36110
+ buttonText = stateName === "reload_required" ? tx("Reload extension", "\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55") : tx("Refresh Overleaf tabs", "\u5237\u65B0 Overleaf \u6807\u7B7E\u9875");
36111
+ } else if (stateName === "checking") {
35229
36112
  statusText = tx("Checking the latest stable release\u2026", "\u6B63\u5728\u68C0\u67E5\u6700\u65B0\u7A33\u5B9A\u7248\u672C\u2026");
35230
36113
  buttonText = tx("Checking\u2026", "\u68C0\u67E5\u4E2D\u2026");
35231
36114
  } else if (stateName === "update_available") {
@@ -35250,6 +36133,9 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35250
36133
  }
35251
36134
  function visibleActions(state) {
35252
36135
  if (actionInFlight) return [];
36136
+ if (["reload_required", "reload_tabs_required"].includes(state)) {
36137
+ return [{ id: "reload", label: state === "reload_required" ? tx("Reload extension", "\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55") : tx("Refresh Overleaf tabs", "\u5237\u65B0 Overleaf \u6807\u7B7E\u9875"), primary: true }];
36138
+ }
35253
36139
  if (state === "update_available") {
35254
36140
  return [
35255
36141
  { id: "later", label: tx("Later", "\u7A0D\u540E"), primary: false },
@@ -35274,6 +36160,16 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35274
36160
  const target = state.latestVersion ? "v" + state.latestVersion : "";
35275
36161
  const blocker = blockersCopy(state.blockers?.length ? state.blockers : [state.blocker]);
35276
36162
  return {
36163
+ reload_required: {
36164
+ eyebrow: tx("Installed, awaiting reload", "\u5DF2\u5B89\u88C5\uFF0C\u7B49\u5F85\u52A0\u8F7D"),
36165
+ title: target,
36166
+ detail: state.message || tx("The new files are installed. Reload the extension when Overleaf is saved and idle.", "\u65B0\u7248\u6587\u4EF6\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728 Overleaf \u4FDD\u5B58\u5E76\u7A7A\u95F2\u540E\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u3002")
36167
+ },
36168
+ reload_tabs_required: {
36169
+ eyebrow: tx("Updated runtime is ready", "\u65B0\u7248\u8FD0\u884C\u7EC4\u4EF6\u5DF2\u5C31\u7EEA"),
36170
+ title: target,
36171
+ detail: state.message || tx("Refresh the saved, idle Overleaf tabs to load the updated panel.", "\u5237\u65B0\u5DF2\u4FDD\u5B58\u4E14\u7A7A\u95F2\u7684 Overleaf \u6807\u7B7E\u9875\u4EE5\u52A0\u8F7D\u65B0\u7248\u9762\u677F\u3002")
36172
+ },
35277
36173
  update_available: {
35278
36174
  eyebrow: tx("Update available", "\u53D1\u73B0\u65B0\u7248\u672C"),
35279
36175
  title: target,
@@ -35363,7 +36259,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35363
36259
  return Object.freeze({ mount });
35364
36260
  });
35365
36261
  }).call(globalThis, void 0, void 0, void 0);
35366
- (function runClassicContentModule94(module, exports, require2) {
36262
+ (function runClassicContentModule99(module, exports, require2) {
35367
36263
  (function initCodexOverleafContentRuntime(root) {
35368
36264
  "use strict";
35369
36265
  function init(dependencies2) {
@@ -35556,6 +36452,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35556
36452
  renderAuditHistoryPanel: () => renderAuditHistoryPanel(),
35557
36453
  renderRecentProjectsVariant: (...args) => renderRecentProjectsVariant(...args),
35558
36454
  saveStateSoon: () => saveStateSoon(),
36455
+ updateGlobalPreferences: (patch) => getGlobalPreferences().update(patch),
35559
36456
  setState: (next) => {
35560
36457
  state = next;
35561
36458
  },
@@ -35756,6 +36653,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35756
36653
  const runTimelineView = Modules.RunTimelineView.create({
35757
36654
  RunGuidanceView: Modules.RunGuidanceView,
35758
36655
  RunResultActions: runResultActions,
36656
+ RunScrollLayout: Modules.RunScrollLayout,
36657
+ RunFailureNotice: Modules.RunFailureNotice,
35759
36658
  tr,
35760
36659
  tx,
35761
36660
  getLocale,
@@ -35997,6 +36896,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
35997
36896
  const providerSettingsCoordinator = Modules.ProviderSettingsCoordinator.create({
35998
36897
  ProviderProfiles: Modules.ProviderProfiles,
35999
36898
  ProviderSettingsDialog: Modules.ProviderSettingsDialog,
36899
+ hasCurrentProject: () => Boolean(getCurrentProjectId()),
36000
36900
  tx,
36001
36901
  sendBackgroundNative,
36002
36902
  document,
@@ -36156,6 +37056,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36156
37056
  }
36157
37057
  });
36158
37058
  let panel = null;
37059
+ let probeStatusSnapshot = null;
36159
37060
  let panelRendererInstance = null;
36160
37061
  let sessionPanelInstance = null;
36161
37062
  let settingsPanelInstance = null;
@@ -36163,6 +37064,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36163
37064
  let composerPanelInstance = null;
36164
37065
  let state = null;
36165
37066
  let storageKey = LEGACY_STORAGE_KEY;
37067
+ let globalPreferencesController = null;
36166
37068
  let currentRunView = null;
36167
37069
  let pendingRetryReplacement = null;
36168
37070
  let runQueueScheduler = null;
@@ -36290,9 +37192,10 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36290
37192
  throw error;
36291
37193
  });
36292
37194
  async function init2() {
37195
+ await getGlobalPreferences().initialize();
36293
37196
  await refreshAccountScopeId();
36294
37197
  storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
36295
- state = normalizePanelState(await loadStoredState(), { restoreRunningRuns: true });
37198
+ state = normalizePanelState(getGlobalPreferences().overlay(await loadStoredState()), { restoreRunningRuns: true });
36296
37199
  initializeRunQueueScheduler();
36297
37200
  recoverInterruptedRunJournals().then(() => applyStateToPanel()).catch(() => {
36298
37201
  });
@@ -36308,6 +37211,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36308
37211
  recentProjects.mountDashboardShell();
36309
37212
  }
36310
37213
  applyStateToPanel();
37214
+ getGlobalPreferences().refreshView();
36311
37215
  if (isProjectEditorRoute(window.location)) {
36312
37216
  activeProjectId = window.location.pathname.match(/^\/project\/([^/?#]+)/)[1];
36313
37217
  }
@@ -36326,7 +37230,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36326
37230
  });
36327
37231
  syncOtWarmMirrorController().catch((error) => {
36328
37232
  updateOtStatusDisplay("unavailable");
36329
- appendPlainLog(tx(`Failed to sync experimental OT warm mirror: ${error.message}`, `\u540C\u6B65\u5B9E\u9A8C\u6027 OT \u9884\u70ED\u955C\u50CF\u5931\u8D25\uFF1A${error.message}`));
37233
+ appendPlainLog(tx(`Failed to sync OT warm mirror: ${error.message}`, `\u540C\u6B65 OT \u9884\u70ED\u955C\u50CF\u5931\u8D25\uFF1A${error.message}`));
36330
37234
  });
36331
37235
  await refreshProbe({ quiet: true });
36332
37236
  }
@@ -36543,12 +37447,13 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36543
37447
  button: panel.querySelector("[data-custom-instructions-settings]"),
36544
37448
  callbacks: {
36545
37449
  onBack: () => closeCustomInstructionsSettings(),
36546
- onInputChange: () => persistPanelInputs(),
37450
+ onInputChange: (event) => persistPanelInputs(event),
36547
37451
  onSkillsOpen: () => openSkillsView(),
36548
37452
  onSkillsBack: () => closeSkillsView(),
36549
37453
  onProvidersOpen: () => providerSettingsCoordinator.open(),
36550
37454
  onClearAllHistory: () => clearAllHistoryWithConfirm(),
36551
37455
  onHistoryOpen: () => renderAuditHistoryPanel(),
37456
+ onStorageOpen: () => refreshStorageUsageSummary(),
36552
37457
  onHistoryFilter: () => applyAuditHistoryFilter(),
36553
37458
  // Experimental OT mirror: a single visible switch whose click is
36554
37459
  // intercepted so the confirm-before-enable flow still runs.
@@ -36775,6 +37680,17 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
36775
37680
  updateModelDisplay();
36776
37681
  renderSessionList();
36777
37682
  renderContextSelection();
37683
+ const probeStatus = panel.querySelector("[data-probe-status]");
37684
+ if (probeStatus?.dataset.refreshing === "true") probeStatus.textContent = tr("refreshProbeLoading");
37685
+ else if (probeStatus && probeStatusSnapshot?.projectId === getCurrentProjectId()) {
37686
+ const { failed, probe, userInitiated } = probeStatusSnapshot;
37687
+ const text = failed ? tr("refreshProbeFailed") : formatProbeStatusBar(probe);
37688
+ probeStatus.textContent = userInitiated && !failed ? tr("refreshProbeDone", { status: text }) : text;
37689
+ if (!failed) updateExistingProbeNotice(probe);
37690
+ }
37691
+ updateSkillsEntrySummary();
37692
+ providerSettingsCoordinator.renderSummary();
37693
+ if (settingsPanelInstance?.container?.querySelector("[data-storage-card]")?.open) refreshStorageUsageSummary();
36778
37694
  }
36779
37695
  function setElementTitleAndAria(selector, title, ariaLabel) {
36780
37696
  const element = panel?.querySelector(selector);
@@ -37497,7 +38413,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
37497
38413
  }
37498
38414
  }
37499
38415
  await saveState();
37500
- applyStateToPanel();
38416
+ applyStateToPanel({ preserveScroll: true });
37501
38417
  } catch (persistenceError) {
37502
38418
  appendRunEvent({
37503
38419
  title: tx("Codex result was generated, but saving local session history failed.", "Codex \u7ED3\u679C\u5DF2\u751F\u6210\uFF0C\u4F46\u4FDD\u5B58\u672C\u5730\u4F1A\u8BDD\u8BB0\u5F55\u5931\u8D25\u3002"),
@@ -39128,11 +40044,36 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39128
40044
  });
39129
40045
  return scopedPersistenceCoordinator;
39130
40046
  }
40047
+ function getGlobalPreferences() {
40048
+ if (!globalPreferencesController) globalPreferencesController = Modules.GlobalPreferencesController.create({
40049
+ chromeApi: chrome,
40050
+ getState: () => state,
40051
+ setState: (next) => {
40052
+ state = next;
40053
+ },
40054
+ getPanel: () => panel,
40055
+ applyTheme: applyPanelTheme,
40056
+ applyLocale: () => {
40057
+ applyLocaleToPanel();
40058
+ refreshDashboardSoon();
40059
+ },
40060
+ onPreloadChange: (enabled) => contextTrayController.scheduleContextPrefetch({ enabled }),
40061
+ onSkillsChange: () => {
40062
+ renderLocalSkillList();
40063
+ updateSkillsEntrySummary();
40064
+ },
40065
+ onError: (error) => showPluginToast(tx(
40066
+ `Global settings could not be saved or loaded: ${error.message}. Please retry.`,
40067
+ `\u5168\u5C40\u8BBE\u7F6E\u672A\u80FD\u4FDD\u5B58\u6216\u8BFB\u53D6\uFF1A${error.message}\u3002\u8BF7\u91CD\u8BD5\u3002`
40068
+ ), { status: "failed", sticky: true })
40069
+ });
40070
+ return globalPreferencesController;
40071
+ }
39131
40072
  async function reloadProjectRunHistory(newProjectId) {
39132
40073
  try {
39133
40074
  storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
39134
40075
  const reloaded = await loadStoredState();
39135
- state = normalizePanelState(reloaded, { restoreRunningRuns: true });
40076
+ state = normalizePanelState(getGlobalPreferences().overlay(reloaded), { restoreRunningRuns: true });
39136
40077
  applyStateToPanel();
39137
40078
  } catch (_error) {
39138
40079
  }
@@ -39603,6 +40544,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39603
40544
  return { safe, skipped };
39604
40545
  }
39605
40546
  async function refreshProbe(options2 = {}) {
40547
+ const projectId = getCurrentProjectId();
39606
40548
  const userInitiated = options2.userInitiated === true;
39607
40549
  if (userInitiated) {
39608
40550
  setRefreshProbeLoading(true);
@@ -39617,6 +40559,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39617
40559
  const probe = await callPageBridge("probe", {
39618
40560
  manualOverride: state?.requireReviewing === false
39619
40561
  });
40562
+ if (projectId !== getCurrentProjectId()) return null;
40563
+ probeStatusSnapshot = { projectId, probe, userInitiated };
39620
40564
  const status = panel?.querySelector("[data-probe-status]");
39621
40565
  if (status) {
39622
40566
  status.textContent = userInitiated ? tr("refreshProbeDone", { status: formatProbeStatusBar(probe) }) : formatProbeStatusBar(probe);
@@ -39633,6 +40577,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39633
40577
  if (!userInitiated) {
39634
40578
  throw error;
39635
40579
  }
40580
+ if (projectId !== getCurrentProjectId()) return null;
40581
+ probeStatusSnapshot = { projectId, failed: true };
39636
40582
  const status = panel?.querySelector("[data-probe-status]");
39637
40583
  if (status) {
39638
40584
  status.textContent = tr("refreshProbeFailed");
@@ -39732,8 +40678,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
39732
40678
  function formatContextItems(focusFiles = []) {
39733
40679
  const fileItems = (focusFiles || []).map((path) => `@file:${path}`);
39734
40680
  return fileItems.length ? fileItems.join(", ") : tx(
39735
- "@whole-project (default); add @file, @compile-log, or @current-section",
39736
- "@whole-project\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u53EF\u6DFB\u52A0 @file\u3001@compile-log\u3001@current-section"
40681
+ "@whole-project (default); choose files or add @compile-log",
40682
+ "@whole-project\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u53EF\u9009\u62E9\u6587\u4EF6\u6216\u6DFB\u52A0 @compile-log"
39737
40683
  );
39738
40684
  }
39739
40685
  function appendProbeUserStatus(probe) {
@@ -40404,6 +41350,8 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40404
41350
  return error?.message || String(error);
40405
41351
  }
40406
41352
  async function persistPanelInputs(event) {
41353
+ const globalWrite = getGlobalPreferences().handleInput(event);
41354
+ if (globalWrite) return globalWrite;
40407
41355
  if (event?.type === "input" && event?.target?.matches?.("[data-task]")) {
40408
41356
  readPanelInputs();
40409
41357
  saveStateSoon();
@@ -40486,23 +41434,6 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40486
41434
  setCustomInstructionsForProject(projectId, customInstructionsInput.value);
40487
41435
  }
40488
41436
  setGovernanceRulesForCurrentProject(readGovernanceRulesFromSettings());
40489
- const newSettings = readSkillLoadingSettingsFromSettings();
40490
- const prevOverleafSkills = getSkillLoadingSettings().loadCodexOverleafSkills;
40491
- setSkillLoadingSettings(newSettings);
40492
- const themePref = panel?.querySelector("[data-theme-select]")?.value || "dark";
40493
- if (state) {
40494
- state.theme = themePref;
40495
- }
40496
- applyPanelTheme(themePref);
40497
- const languagePref = panel?.querySelector("[data-language-select]")?.value;
40498
- if (languagePref && state && languagePref !== getLocale()) {
40499
- state.locale = i18n?.normalizeLocale?.(languagePref) || languagePref;
40500
- applyLocaleToPanel();
40501
- }
40502
- if (prevOverleafSkills !== getSkillLoadingSettings().loadCodexOverleafSkills) {
40503
- renderLocalSkillList();
40504
- }
40505
- updateSkillsEntrySummary();
40506
41437
  }
40507
41438
  }
40508
41439
  function readSelectedModelInput() {
@@ -40534,7 +41465,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40534
41465
  state = updateActiveSession(state, { task: "" });
40535
41466
  saveStateSoon();
40536
41467
  }
40537
- function applyStateToPanel() {
41468
+ function applyStateToPanel(options2 = {}) {
40538
41469
  applyPanelTheme(getThemePreference());
40539
41470
  if ((state.providerId || "builtin") === "builtin" && !modelSelectHasOption(state.model)) {
40540
41471
  renderModelOptions(getModelCatalog().FALLBACK_MODELS, state.model);
@@ -40568,7 +41499,7 @@ ${tx("Writeback note: Codex could not confirm any Overleaf write result entries,
40568
41499
  syncModeControls();
40569
41500
  applySessionLabel();
40570
41501
  renderSessionList();
40571
- renderRunHistory();
41502
+ renderRunHistory(options2);
40572
41503
  renderContextSelection();
40573
41504
  renderContextSummary();
40574
41505
  if (!composerAttachmentsRestoredOnce && Array.isArray(state.composerAttachments) && state.composerAttachments.length && !composerAttachmentController.getAttachmentsForRun?.().length && typeof composerAttachmentController.restorePersistedAttachments === "function") {
@@ -42172,7 +43103,7 @@ Reason: ${item.reason || "No reason provided"}`),
42172
43103
  root.CodexOverleafContentRuntime = { init };
42173
43104
  })(typeof window !== "undefined" ? window : globalThis);
42174
43105
  }).call(globalThis, void 0, void 0, void 0);
42175
- (function runClassicContentModule95(module, exports, require2) {
43106
+ (function runClassicContentModule100(module, exports, require2) {
42176
43107
  (function initCodexOverleafLegacyGlobalRegistry(root, factory) {
42177
43108
  "use strict";
42178
43109
  const api = factory();
@@ -42200,6 +43131,8 @@ Reason: ${item.reason || "No reason provided"}`),
42200
43131
  "DiffReviewPanel",
42201
43132
  "FailureReasons",
42202
43133
  "GovernanceRules",
43134
+ "GlobalPreferences",
43135
+ "GlobalPreferencesController",
42203
43136
  "I18n",
42204
43137
  "LineReferences",
42205
43138
  "LocalSkillsPanel",
@@ -42228,6 +43161,8 @@ Reason: ${item.reason || "No reason provided"}`),
42228
43161
  "RunInputQueue",
42229
43162
  "RunQueueScheduler",
42230
43163
  "RunResultActions",
43164
+ "RunScrollLayout",
43165
+ "RunFailureNotice",
42231
43166
  "RunSettlementPersistence",
42232
43167
  "RunTimelineView",
42233
43168
  "ScopedPersistenceCoordinator",