codex-overleaf-link 2.5.1 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +149 -398
  2. package/README.zh-CN.md +153 -404
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/src/background.js +148 -3
  5. package/extension/src/backgroundSharedSessions.js +55 -2
  6. package/extension/src/backgroundUpdateCoordinator.js +128 -12
  7. package/extension/src/content/activeTurnControl.js +164 -7
  8. package/extension/src/content/composerAttachments.js +46 -10
  9. package/extension/src/content/composerPanel.js +92 -12
  10. package/extension/src/content/contentRuntime.js +1114 -321
  11. package/extension/src/content/contextTray.js +8 -2
  12. package/extension/src/content/contextUsageView.js +267 -0
  13. package/extension/src/content/diagnosticsController.js +77 -34
  14. package/extension/src/content/diagnosticsExport.js +1 -1
  15. package/extension/src/content/diagnosticsPanel.js +147 -42
  16. package/extension/src/content/generated/content.bundle.js +5761 -1237
  17. package/extension/src/content/generated/content.bundle.meta.json +6 -3
  18. package/extension/src/content/legacyGlobalRegistry.js +4 -0
  19. package/extension/src/content/localSkillsPanel.js +6 -4
  20. package/extension/src/content/markdownDomRenderer.js +5 -3
  21. package/extension/src/content/markdownText.js +53 -14
  22. package/extension/src/content/modelPicker.js +29 -19
  23. package/extension/src/content/nativeCompatibilityController.js +52 -19
  24. package/extension/src/content/otWarmMirror.js +1 -1
  25. package/extension/src/content/pageBridgeClient.js +7 -7
  26. package/extension/src/content/panelMaintenance.js +10 -2
  27. package/extension/src/content/panelRenderer.js +55 -28
  28. package/extension/src/content/pendingInputView.js +1 -1
  29. package/extension/src/content/projectSettingsCoordinator.js +30 -6
  30. package/extension/src/content/providerSettingsDialog.js +28 -19
  31. package/extension/src/content/recentProjects.js +30 -19
  32. package/extension/src/content/runActivitySummary.js +122 -14
  33. package/extension/src/content/runCompletionMeta.js +194 -6
  34. package/extension/src/content/runController.js +2 -1
  35. package/extension/src/content/runFailureNotice.js +8 -1
  36. package/extension/src/content/runPresence.js +18 -8
  37. package/extension/src/content/runQueueScheduler.js +27 -13
  38. package/extension/src/content/runRecoveryActions.js +2 -1
  39. package/extension/src/content/runResultActions.js +73 -24
  40. package/extension/src/content/runReviewController.js +102 -37
  41. package/extension/src/content/runTimelineView.js +224 -25
  42. package/extension/src/content/scopedPersistencePanelState.js +124 -46
  43. package/extension/src/content/selectionContextView.js +30 -3
  44. package/extension/src/content/sensitivePreflight.js +14 -1
  45. package/extension/src/content/sessionManager.js +49 -3
  46. package/extension/src/content/sessionMenuView.js +54 -18
  47. package/extension/src/content/sessionPersistence.js +25 -5
  48. package/extension/src/content/settingsPanel.js +15 -2
  49. package/extension/src/content/settingsWorkbench.js +127 -89
  50. package/extension/src/content/sharedSessionViewSync.js +13 -4
  51. package/extension/src/content/subagentActivityView.js +23 -7
  52. package/extension/src/content/updateNotice.js +60 -6
  53. package/extension/src/content/writebackOrchestrator.js +301 -72
  54. package/extension/src/content/writingStyleSettings.js +70 -17
  55. package/extension/src/page/compileBridge.js +193 -180
  56. package/extension/src/page/overleafCapabilities.js +4 -1
  57. package/extension/src/page/overleafEditor.js +12 -3
  58. package/extension/src/page/overleafProjectSnapshot.js +56 -3
  59. package/extension/src/page/snapshotRouter.js +75 -66
  60. package/extension/src/page/textCoordinates.js +17 -1
  61. package/extension/src/page/textFileCreator.js +9 -1
  62. package/extension/src/page/treeOperations.js +200 -19
  63. package/extension/src/page/writebackRouter.js +172 -113
  64. package/extension/src/pageBridge.js +124 -73
  65. package/extension/src/popup.js +1 -1
  66. package/extension/src/shared/agentTranscript.js +372 -18
  67. package/extension/src/shared/auditRecords.js +3 -0
  68. package/extension/src/shared/compatibility.js +1 -1
  69. package/extension/src/shared/compileAdapter.js +58 -17
  70. package/extension/src/shared/composerAttachmentDraft.js +27 -7
  71. package/extension/src/shared/contextUsage.js +125 -0
  72. package/extension/src/shared/failureReasons.js +57 -3
  73. package/extension/src/shared/i18n.js +315 -29
  74. package/extension/src/shared/lineReferences.js +38 -31
  75. package/extension/src/shared/pageRpcContract.js +1 -1
  76. package/extension/src/shared/panelIcons.js +39 -0
  77. package/extension/src/shared/projectFiles.js +2 -1
  78. package/extension/src/shared/reviewing.js +130 -1
  79. package/extension/src/shared/runActivityModel.js +70 -4
  80. package/extension/src/shared/runExecutionSnapshot.js +13 -0
  81. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +34 -0
  82. package/extension/src/shared/sessionState.js +63 -4
  83. package/extension/src/shared/sessionTitle.js +14 -1
  84. package/extension/src/shared/sharedSessionBridge.js +81 -13
  85. package/extension/src/shared/staleGuard.js +130 -63
  86. package/extension/src/shared/storageDb.js +35 -3
  87. package/extension/src/shared/storageMigration.js +111 -2
  88. package/extension/src/shared/storageRunActions.js +62 -8
  89. package/extension/src/shared/undoOperations.js +126 -28
  90. package/extension/src/shared/updateRuntimeIdentity.js +16 -2
  91. package/extension/src/shared/writebackSettlement.js +21 -3
  92. package/extension/styles/panel.css +1293 -549
  93. package/native-host/src/bootstrapUpdate.js +52 -16
  94. package/native-host/src/chatBridgeRequest.js +60 -6
  95. package/native-host/src/chatBridgeResponse.js +10 -1
  96. package/native-host/src/chatCompletionsBridge.js +29 -2
  97. package/native-host/src/codexPromptAssembly.js +86 -118
  98. package/native-host/src/codexSessionRunner.js +127 -17
  99. package/native-host/src/debugLog.js +27 -3
  100. package/native-host/src/index.js +42 -11
  101. package/native-host/src/localSkills.js +1 -1
  102. package/native-host/src/mirrorWorkspace.js +47 -3
  103. package/native-host/src/nativeEnvironment.js +27 -5
  104. package/native-host/src/providerContextError.js +20 -0
  105. package/native-host/src/providerRuntime.js +1 -0
  106. package/native-host/src/skills/reviewer-response/SKILL.md +65 -0
  107. package/native-host/src/taskRunnerRuntime.js +28 -8
  108. package/native-host/src/textPatch.js +473 -377
  109. package/native-host/src/updateManager.js +144 -21
  110. package/native-host/src/writingStyleRuntime.js +1 -1
  111. package/package.json +5 -8
@@ -58,6 +58,8 @@
58
58
  document,
59
59
  getActiveFilePathFromEditorStore,
60
60
  getActiveEditorIdentity,
61
+ getCodeMirrorEditorView,
62
+ getCodeMirrorEditorViews: () => codeMirrorEditorViewCandidates().map(([, view]) => view).filter(isCodeMirrorEditorView),
61
63
  normalizePath: normalizeSafeProjectPath,
62
64
  readActiveEditorText,
63
65
  window
@@ -73,6 +75,7 @@
73
75
  const textFileCreator = requirePageModule('CodexOverleafTextFileCreator').create({
74
76
  window, document, treeOperations, snapshotRouter,
75
77
  collectElements, ensureEditing, ensureReviewing, readActiveEditorText, readWriteCancellationSequence,
78
+ findUpgradeModal: () => window.CodexOverleafReviewing.findUpgradeModal(document),
76
79
  uploadHelpers: requirePageModule('CodexOverleafBinaryAssetUploader')
77
80
  });
78
81
  binaryAssetUploader = requirePageModule('CodexOverleafBinaryAssetUploader').create({
@@ -96,6 +99,11 @@
96
99
  projectSnapshotBridge = window.CodexOverleafProjectSnapshot.create({
97
100
  buildProjectFileList: snapshotRouter.buildProjectFileList,
98
101
  buildProjectSnapshot: snapshotRouter.buildProjectSnapshot,
102
+ getSnapshotEditorCaptures: snapshotRouter.getSnapshotEditorCaptures,
103
+ captureEditorDocument: treeOperations.captureEditorDocument,
104
+ isCaptureCurrent: treeOperations.isCaptureCurrent,
105
+ getActiveFilePath,
106
+ getActiveEditorIdentity,
99
107
  getProjectId,
100
108
  normalizePath: normalizeSafeProjectPath,
101
109
  window
@@ -399,6 +407,11 @@
399
407
  fallbackUserMessage: 'Target file is active but the editor was not ready before timeout.',
400
408
  fallbackNextAction: 'Wait for Overleaf to finish loading, then retry.'
401
409
  },
410
+ reviewing_unavailable: {
411
+ stage: 'reviewing', severity: 'blocked', defaultRetryable: false,
412
+ fallbackUserMessage: "Track Changes is unavailable for this project. It depends on the project owner's Overleaf plan.",
413
+ fallbackNextAction: 'Turn off Track in the composer to write without tracked changes. Codex still records each change so you can undo it.'
414
+ },
402
415
  reviewing_state_unknown: {
403
416
  stage: 'reviewing', severity: 'blocked', defaultRetryable: true,
404
417
  fallbackUserMessage: 'Codex could not determine whether Reviewing/Track Changes is enabled.',
@@ -488,6 +501,8 @@
488
501
  findFileTreeManager,
489
502
  findHistoryObject,
490
503
  findReviewingActivationControl,
504
+ findUpgradeModal: () => window.CodexOverleafReviewing.findUpgradeModal(document),
505
+ reviewingConfirmed: isReviewingConfirmedForWrite(getReviewingState()),
491
506
  getCodeMirrorEditorView,
492
507
  getDeepActiveElement,
493
508
  isInsideCodexPanel,
@@ -497,7 +512,7 @@
497
512
 
498
513
  async function ensureReviewing(params = {}) {
499
514
  const initial = getReviewingState(params);
500
- if (isReviewingConfirmedForWrite(initial)) {
515
+ if (isReviewingConfirmedForWrite(initial) && !window.CodexOverleafReviewing.findUpgradeModal(document)) {
501
516
  return {
502
517
  ok: true,
503
518
  activated: false,
@@ -506,6 +521,7 @@
506
521
  }
507
522
 
508
523
  const switched = await toggleReviewingMode(true, params);
524
+ if (switched.code === 'reviewing_unavailable') return { ...switched, reason: switched.failure.userMessage };
509
525
  if (switched.ok) {
510
526
  return {
511
527
  ok: true,
@@ -553,6 +569,7 @@
553
569
  }
554
570
 
555
571
  const switched = await toggleReviewingMode(false, params);
572
+ if (switched.code === 'reviewing_unavailable') return { ...switched, reason: switched.failure.userMessage };
556
573
  if (switched.ok) {
557
574
  return {
558
575
  ok: true,
@@ -743,9 +760,16 @@
743
760
  const activeFileMatches = getActiveFilePath() === filePath;
744
761
  const text = readActiveEditorText();
745
762
  lastText = text;
763
+ const currentEditorIdentity = getActiveEditorIdentity();
764
+ // A file-tree click can reconfigure or blur the old editor before the
765
+ // target document loads. A new view/state alone does not prove a switch.
766
+ const documentChanged = currentEditorIdentity?.type === 'codemirror-view'
767
+ && previousEditorIdentity?.type === 'codemirror-view'
768
+ ? currentEditorIdentity.doc !== previousEditorIdentity.doc
769
+ : Boolean(currentEditorIdentity) && activeEditorIdentityChanged(previousEditorIdentity);
746
770
  if (activeFileMatches
747
771
  && window.CodexOverleafProjectFiles.isUsableProjectFileContent(text)
748
- && activeEditorIdentityChanged(previousEditorIdentity)) {
772
+ && documentChanged) {
749
773
  return {
750
774
  ok: true,
751
775
  text
@@ -871,6 +895,7 @@
871
895
  }
872
896
 
873
897
  const switched = await toggleReviewingMode(enabled, params);
898
+ if (switched.code === 'reviewing_unavailable') return { ...switched, reason: switched.failure.userMessage };
874
899
  if (switched.ok) {
875
900
  return {
876
901
  ok: true,
@@ -917,67 +942,23 @@
917
942
  }
918
943
 
919
944
  async function toggleReviewingMode(enabled, params = {}) {
920
- const attemptedNodes = uniqueNodes([
921
- findReviewingActivationControl(),
922
- findReviewingModeMenuControl()
923
- ].filter(Boolean));
924
-
925
- for (const control of attemptedNodes) {
926
- clickNode(control);
927
- let after = await waitForReviewingEnabled(enabled, {
928
- ...params,
929
- waitMs: Math.min(resolveWaitMs(params), 180)
930
- });
931
- if (isReviewingTargetStateConfirmed(enabled, after)) {
932
- return {
933
- ok: true,
934
- attempted: true,
935
- reviewing: after.reviewing
936
- };
945
+ const sequence = readWriteCancellationSequence();
946
+ const assertCurrent = () => {
947
+ if (sequence !== readWriteCancellationSequence()) {
948
+ throw Object.assign(new Error('Writeback was cancelled.'), { code: 'codex_cancelled' });
937
949
  }
938
-
939
- const modeOption = findReviewingModeOption(enabled, { requireMenuOption: true });
940
- if (modeOption) {
941
- clickNode(modeOption);
942
- after = await waitForReviewingEnabled(enabled, params);
943
- if (isReviewingTargetStateConfirmed(enabled, after)) {
944
- return {
945
- ok: true,
946
- attempted: true,
947
- reviewing: after.reviewing
948
- };
949
- }
950
+ if (params.runProjectId && writeGuard.getEditorProjectIdPageSide(params.runProjectId) !== params.runProjectId) {
951
+ throw Object.assign(new Error('The active project changed.'), { code: 'aborted_project_changed' });
950
952
  }
951
- }
952
-
953
- const visibleModeOption = findReviewingModeOption(enabled, { requireMenuOption: true });
954
- if (visibleModeOption) {
955
- clickNode(visibleModeOption);
956
- const after = await waitForReviewingEnabled(enabled, params);
957
- return {
958
- ok: isReviewingTargetStateConfirmed(enabled, after),
959
- attempted: true,
960
- reviewing: after.reviewing
961
- };
962
- }
963
-
964
- const after = getReviewingState(params);
965
- return {
966
- ok: false,
967
- attempted: attemptedNodes.length > 0,
968
- reviewing: after.reviewing
969
953
  };
970
- }
971
-
972
- async function waitForReviewingEnabled(enabled, params = {}) {
973
- const waitMs = resolveWaitMs(params);
974
- const deadline = Date.now() + Math.max(0, waitMs);
975
- let current = getReviewingState(params);
976
- while (!isReviewingTargetStateConfirmed(enabled, current) && Date.now() < deadline) {
977
- await delay(120);
978
- current = getReviewingState(params);
979
- }
980
- return current;
954
+ return window.CodexOverleafReviewing.toggleMode(enabled, params, {
955
+ document, assertCurrent, delay,
956
+ getState: () => getReviewingState(params), isConfirmed: isReviewingTargetStateConfirmed,
957
+ click: clickNode,
958
+ findControls: () => findReviewingModeControls(enabled),
959
+ findOption: value => findReviewingModeOption(value, { requireMenuOption: true }),
960
+ buildFailure: buildPageBridgeFailure
961
+ });
981
962
  }
982
963
 
983
964
  function resolveWaitMs(params = {}) {
@@ -1109,7 +1090,72 @@
1109
1090
  return String(value || '').replace(/\s+/g, ' ').trim();
1110
1091
  }
1111
1092
 
1093
+ function isReviewingModeNodeAvailable(node) {
1094
+ if (!node || node.ownerDocument !== document || !node.isConnected || node.disabled
1095
+ || node.getAttribute?.('aria-disabled') === 'true' || !node.getClientRects?.().length) return false;
1096
+ for (let ancestor = node; ancestor; ancestor = ancestor.parentElement || ancestor.getRootNode?.().host) {
1097
+ if (isInsideCodexPanel(ancestor) || ancestor.closest?.(
1098
+ '.review-panel, .review-panel-container, .review-panel-header-slot, .review-sidebar, #review-panel, [data-testid="review-panel"]'
1099
+ ) || ancestor.hidden || ancestor.getAttribute?.('aria-hidden') === 'true' || ancestor.hasAttribute?.('inert')) return false;
1100
+ const style = window.getComputedStyle?.(ancestor);
1101
+ if (style?.display === 'none' || style?.visibility === 'hidden' || style?.visibility === 'collapse'
1102
+ || style?.opacity === '0') return false;
1103
+ }
1104
+ return true;
1105
+ }
1106
+
1107
+ function findReviewingToolbarControl() {
1108
+ return Array.from(document.querySelectorAll(
1109
+ '.toolbar-editor .review-mode-switcher > .review-mode-switcher-toggle-button'
1110
+ )).find(isReviewingModeNodeAvailable) || null;
1111
+ }
1112
+
1113
+ function findReviewingModeControls(enabled) {
1114
+ const toolbarControl = findReviewingToolbarControl();
1115
+ return toolbarControl ? [toolbarControl]
1116
+ : [findReviewingActivationControl(enabled), findReviewingModeMenuControl()];
1117
+ }
1118
+
1119
+ function findReviewingToolbarMenu(control) {
1120
+ const controlledIds = (control.getAttribute('aria-controls') || '').split(/\s+/).filter(Boolean);
1121
+ for (const id of controlledIds) {
1122
+ const menu = document.getElementById(id);
1123
+ if (isReviewingModeNodeAvailable(menu)) return menu;
1124
+ }
1125
+ if (control.getAttribute('aria-expanded') !== 'true') return null;
1126
+ const selector = '[role="menu"], [role="listbox"], .dropdown-menu';
1127
+ // Current Overleaf keeps the menu beside the toggle, inside this switcher.
1128
+ const localMenu = Array.from(control.parentElement?.querySelectorAll(selector) || [])
1129
+ .find(node => node.closest('.review-mode-switcher') === control.parentElement && isReviewingModeNodeAvailable(node));
1130
+ if (localMenu) return localMenu;
1131
+ // A portalled menu must still name this switch, rather than an unrelated dropdown.
1132
+ return control.id ? Array.from(document.querySelectorAll(selector)).find(node =>
1133
+ (node.getAttribute('aria-labelledby') || '').split(/\s+/).includes(control.id)
1134
+ && isReviewingModeNodeAvailable(node)) || null : null;
1135
+ }
1136
+
1137
+ function isReviewingToolbarModeOption(node, enabled) {
1138
+ const mode = enabled ? 'reviewing' : 'editing';
1139
+ const values = [node.value, node.getAttribute('value'), node.getAttribute('data-value'),
1140
+ node.getAttribute('label'), node.getAttribute('aria-label'), ...String(node.className || '').split(/\s+/)];
1141
+ if (values.some(value => normalizeReviewingSignalText(value).toLowerCase() === mode)) return true;
1142
+ // Read a label separately from icon text and the option's explanatory subtitle.
1143
+ return [node, ...node.querySelectorAll('*')].some(label => {
1144
+ if (label.closest('[aria-hidden="true"]')) return false;
1145
+ const directText = Array.from(label.childNodes).filter(child => child.nodeType === 3)
1146
+ .map(child => child.textContent).join(' ');
1147
+ return [label.textContent, directText].some(value => normalizeReviewingSignalText(value).toLowerCase() === mode);
1148
+ });
1149
+ }
1150
+
1112
1151
  function findReviewingModeOption(enabled, options = {}) {
1152
+ const toolbarControl = findReviewingToolbarControl();
1153
+ if (toolbarControl) {
1154
+ const menu = findReviewingToolbarMenu(toolbarControl);
1155
+ return Array.from(menu?.querySelectorAll(
1156
+ '[role="menuitem"], [role="menuitemradio"], [role="option"], .dropdown-item, button, a, option'
1157
+ ) || []).find(node => isReviewingModeNodeAvailable(node) && isReviewingToolbarModeOption(node, enabled)) || null;
1158
+ }
1113
1159
  const candidates = collectReviewingModeOptionNodes()
1114
1160
  .filter(node => isReviewingModeOptionCandidate(node, enabled, options))
1115
1161
  .sort((left, right) => scoreReviewingModeOption(right, enabled) - scoreReviewingModeOption(left, enabled));
@@ -1145,7 +1191,7 @@
1145
1191
  }
1146
1192
 
1147
1193
  function isReviewingModeMenuControlCandidate(node) {
1148
- if (!node || node.disabled || node.getAttribute?.('aria-disabled') === 'true') {
1194
+ if (!isReviewingModeNodeAvailable(node) || isMenuLikeModeOption(node)) {
1149
1195
  return false;
1150
1196
  }
1151
1197
  const text = readNodeSignalText(node);
@@ -1198,7 +1244,7 @@
1198
1244
  }
1199
1245
 
1200
1246
  function isReviewingModeOptionCandidate(node, enabled, options = {}) {
1201
- if (!node || node.disabled || node.getAttribute?.('aria-disabled') === 'true') {
1247
+ if (!isReviewingModeNodeAvailable(node)) {
1202
1248
  return false;
1203
1249
  }
1204
1250
  if (options.requireMenuOption && !isMenuLikeModeOption(node)) {
@@ -1244,10 +1290,10 @@
1244
1290
  && !/\bsyntax highlighting\b/i.test(text);
1245
1291
  }
1246
1292
 
1247
- function findReviewingActivationControl() {
1293
+ function findReviewingActivationControl(enabled = true) {
1248
1294
  const nodes = collectReviewingControlNodes();
1249
1295
  const candidates = nodes
1250
- .filter(node => isReviewingActivationCandidate(node))
1296
+ .filter(node => isReviewingActivationCandidate(node, enabled))
1251
1297
  .sort((left, right) => scoreReviewingControl(right) - scoreReviewingControl(left));
1252
1298
  return candidates[0] || null;
1253
1299
  }
@@ -1271,10 +1317,12 @@
1271
1317
  ]).slice(0, 1500);
1272
1318
  }
1273
1319
 
1274
- function isReviewingActivationCandidate(node) {
1275
- if (!node || node.disabled || node.getAttribute?.('aria-disabled') === 'true') {
1320
+ function isReviewingActivationCandidate(node, enabled) {
1321
+ if (!isReviewingModeNodeAvailable(node) || isMenuLikeModeOption(node)) {
1276
1322
  return false;
1277
1323
  }
1324
+ // A standalone Reviewing command enables tracking; it cannot open Editing.
1325
+ if (!enabled && !node.getAttribute?.('aria-haspopup') && !node.hasAttribute?.('aria-expanded')) return false;
1278
1326
  const text = readNodeSignalText(node);
1279
1327
  return /\breviewing\b|\btrack(?:ed)? changes?\b|\bsuggest(?:ing|ions?)\b/i.test(text)
1280
1328
  && !/\breview panel\b/i.test(text);
@@ -1634,21 +1682,21 @@
1634
1682
  return editorAdapter.readActiveEditorText();
1635
1683
  }
1636
1684
 
1637
- function replaceActiveEditorText(text) {
1638
- return editorAdapter.replaceActiveEditorText(text);
1685
+ function replaceActiveEditorText(text, options) {
1686
+ return editorAdapter.replaceActiveEditorText(text, options);
1639
1687
  }
1640
1688
 
1641
- function replaceActiveEditorPatches(patches, nextContent) {
1642
- return editorAdapter.replaceActiveEditorPatches(patches, nextContent);
1689
+ function replaceActiveEditorPatches(patches, nextContent, options) {
1690
+ return editorAdapter.replaceActiveEditorPatches(patches, nextContent, options);
1643
1691
  }
1644
1692
 
1645
1693
  function getCodeMirrorEditorView() {
1646
1694
  return findCodeMirrorEditorView()?.view || null;
1647
1695
  }
1648
1696
 
1649
- function findCodeMirrorEditorView() {
1697
+ function codeMirrorEditorViewCandidates() {
1650
1698
  const store = getOverleafUnstableStore();
1651
- const candidates = [
1699
+ return [
1652
1700
  ['store:editor.view', readStoreValue(store, 'editor.view')],
1653
1701
  ['store:editor.view.camel', readStoreValue(store, 'editor.view.camel')],
1654
1702
  ['store:editor', readStoreValue(store, 'editor').view],
@@ -1657,7 +1705,10 @@
1657
1705
  ['window._ide.editorView', window._ide?.editorView]
1658
1706
  ];
1659
1707
 
1660
- for (const [source, candidate] of candidates) {
1708
+ }
1709
+
1710
+ function findCodeMirrorEditorView() {
1711
+ for (const [source, candidate] of codeMirrorEditorViewCandidates()) {
1661
1712
  if (isCodeMirrorEditorView(candidate)) {
1662
1713
  return {
1663
1714
  view: candidate,
@@ -708,7 +708,7 @@ async function sendConsentAction(action) {
708
708
  const code = String(state.code || '');
709
709
  if (code === 'update_recovery_pending') return utx('The update result is being checked. Recovery has not been confirmed.', '正在确认更新结果,恢复状态尚未确认。');
710
710
  if (['update_bootstrap_health_timeout', 'update_bootstrap_reload_failed'].includes(code)) return utx('The updated startup components did not finish restarting and confirming. Check recovery before retrying.', '更新后的启动组件未能完成重启和确认,请先检查恢复状态。');
711
- if (['update_bootstrap_source_mismatch', 'update_bootstrap_source_version_mismatch'].includes(code)) return utx('The installed startup files do not match a supported update source. Check recovery before retrying.', '已安装的启动文件与支持的升级来源不一致,请先检查恢复状态。');
711
+ if (['update_bootstrap_source_mismatch', 'update_bootstrap_source_version_mismatch'].includes(code)) return utx('The installed startup files do not match a supported update source. Restore the previous version in Settings, or reinstall once.', '已安装的启动文件与支持的升级来源不一致。请在设置中恢复到上一版本,或重新安装一次。');
712
712
  if (['update_bootstrap_record_missing', 'update_bootstrap_record_invalid', 'update_bootstrap_backup_mismatch', 'update_bootstrap_already_restoring'].includes(code)) return utx('The startup recovery material could not be confirmed. The update result remains unconfirmed.', '尚未确认启动组件的恢复材料有效,更新结果仍待确认。');
713
713
  if (['update_bootstrap_confirmation_pending', 'update_bootstrap_native_restart_required'].includes(code)) return utx('Waiting for the updated startup components and Native Host to finish restarting and confirming.', '正在等待更新后的启动组件和本机服务完成重启及确认。');
714
714
  if (code === 'update_bootstrap_monitor_unavailable') return utx('The update recovery monitor could not start. Check recovery before retrying.', '更新恢复监护程序未能启动,请先检查恢复状态。');