codex-overleaf-link 2.5.0 → 2.6.0

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 (189) hide show
  1. package/README.md +155 -392
  2. package/README.zh-CN.md +154 -393
  3. package/extension/bootstrap/background.js +52 -9
  4. package/extension/bootstrap/manifest.template.json +1 -1
  5. package/extension/bootstrap/popup.html +1 -0
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/updateStatus.js +1 -0
  8. package/extension/src/background.js +283 -55
  9. package/extension/src/backgroundBootstrapMigration.js +181 -0
  10. package/extension/src/backgroundSharedSessions.js +129 -16
  11. package/extension/src/backgroundUpdateCoordinator.js +242 -29
  12. package/extension/src/backgroundUpdateSafety.js +173 -0
  13. package/extension/src/content/activeTurnControl.js +171 -8
  14. package/extension/src/content/applyResultFormatters.js +46 -73
  15. package/extension/src/content/assetTransferBroker.js +72 -14
  16. package/extension/src/content/composerAttachments.js +104 -47
  17. package/extension/src/content/composerPanel.js +110 -12
  18. package/extension/src/content/contentRuntime.js +1555 -926
  19. package/extension/src/content/contextTray.js +8 -2
  20. package/extension/src/content/contextUsageView.js +267 -0
  21. package/extension/src/content/diagnosticsController.js +122 -96
  22. package/extension/src/content/diagnosticsExport.js +19 -5
  23. package/extension/src/content/diagnosticsPanel.js +48 -0
  24. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  25. package/extension/src/content/generated/content.bundle.js +11077 -3412
  26. package/extension/src/content/generated/content.bundle.meta.json +14 -3
  27. package/extension/src/content/globalPreferencesController.js +1 -1
  28. package/extension/src/content/legacyGlobalRegistry.js +10 -0
  29. package/extension/src/content/localSkillsPanel.js +104 -14
  30. package/extension/src/content/markdownDomRenderer.js +5 -3
  31. package/extension/src/content/markdownText.js +47 -11
  32. package/extension/src/content/modelPicker.js +8 -4
  33. package/extension/src/content/nativeCompatibilityController.js +74 -30
  34. package/extension/src/content/otWarmMirror.js +5 -6
  35. package/extension/src/content/otWarmMirrorController.js +17 -20
  36. package/extension/src/content/pageBridgeClient.js +17 -4
  37. package/extension/src/content/panelMaintenance.js +46 -14
  38. package/extension/src/content/panelRenderer.js +57 -27
  39. package/extension/src/content/pendingInputView.js +1 -1
  40. package/extension/src/content/projectProviderSelection.js +13 -4
  41. package/extension/src/content/projectSessionCleanup.js +109 -62
  42. package/extension/src/content/projectSettingsCoordinator.js +67 -6
  43. package/extension/src/content/providerSettingsCoordinator.js +46 -8
  44. package/extension/src/content/providerSettingsDialog.js +73 -14
  45. package/extension/src/content/recentProjects.js +15 -8
  46. package/extension/src/content/runActivitySummary.js +160 -18
  47. package/extension/src/content/runCompletionMeta.js +532 -0
  48. package/extension/src/content/runController.js +39 -15
  49. package/extension/src/content/runFailureNotice.js +29 -13
  50. package/extension/src/content/runGuidanceController.js +72 -27
  51. package/extension/src/content/runGuidanceView.js +59 -10
  52. package/extension/src/content/runPresence.js +18 -8
  53. package/extension/src/content/runQueueScheduler.js +157 -47
  54. package/extension/src/content/runRecoveryActions.js +147 -0
  55. package/extension/src/content/runResultActions.js +28 -143
  56. package/extension/src/content/runReviewController.js +633 -0
  57. package/extension/src/content/runScrollLayout.js +21 -2
  58. package/extension/src/content/runTimelineView.js +367 -175
  59. package/extension/src/content/scopedPersistencePanelState.js +134 -20
  60. package/extension/src/content/selectionContextView.js +1 -1
  61. package/extension/src/content/sensitivePreflight.js +153 -0
  62. package/extension/src/content/sessionForkController.js +80 -17
  63. package/extension/src/content/sessionManager.js +50 -4
  64. package/extension/src/content/sessionMenuView.js +54 -18
  65. package/extension/src/content/sessionPersistence.js +25 -5
  66. package/extension/src/content/settingsPanel.js +22 -5
  67. package/extension/src/content/settingsWorkbench.js +132 -89
  68. package/extension/src/content/sharedSessionViewSync.js +49 -11
  69. package/extension/src/content/subagentActivityView.js +23 -7
  70. package/extension/src/content/updateIdle.js +4 -1
  71. package/extension/src/content/updateNotice.js +118 -12
  72. package/extension/src/content/updateSafetyReceiver.js +58 -0
  73. package/extension/src/content/writebackController.js +53 -4
  74. package/extension/src/content/writebackOrchestrator.js +116 -94
  75. package/extension/src/content/writingStyleSettings.js +35 -11
  76. package/extension/src/page/binaryAssetUploader.js +136 -14
  77. package/extension/src/page/compileBridge.js +193 -180
  78. package/extension/src/page/fileMover.js +256 -0
  79. package/extension/src/page/overleafCapabilities.js +4 -1
  80. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  81. package/extension/src/page/referenceProjects.js +1 -0
  82. package/extension/src/page/reviewContext.js +113 -0
  83. package/extension/src/page/saveState.js +6 -4
  84. package/extension/src/page/snapshotRouter.js +84 -39
  85. package/extension/src/page/textCoordinates.js +17 -1
  86. package/extension/src/page/textFileCreator.js +114 -26
  87. package/extension/src/page/trackedChangeCapture.js +12 -2
  88. package/extension/src/page/trackedChangeOwnership.js +19 -1
  89. package/extension/src/page/trackedChangeReplay.js +41 -21
  90. package/extension/src/page/trackedChangesLifecycle.js +217 -199
  91. package/extension/src/page/treeOperations.js +7 -10
  92. package/extension/src/page/updateSafetyProbe.js +41 -0
  93. package/extension/src/page/writebackReceiptJournal.js +2 -0
  94. package/extension/src/page/writebackRouter.js +103 -155
  95. package/extension/src/pageBridge.js +126 -69
  96. package/extension/src/popup.js +62 -10
  97. package/extension/src/shared/agentTranscript.js +373 -17
  98. package/extension/src/shared/auditRecords.js +4 -0
  99. package/extension/src/shared/compatibility.js +22 -6
  100. package/extension/src/shared/compileAdapter.js +129 -66
  101. package/extension/src/shared/composerAttachmentDraft.js +156 -0
  102. package/extension/src/shared/contextUsage.js +125 -0
  103. package/extension/src/shared/directoryCleanupProof.js +43 -0
  104. package/extension/src/shared/failureReasons.js +349 -4
  105. package/extension/src/shared/globalPreferences.js +30 -13
  106. package/extension/src/shared/governanceRules.js +4 -37
  107. package/extension/src/shared/i18n.js +590 -24
  108. package/extension/src/shared/lineReferences.js +38 -31
  109. package/extension/src/shared/managedUpdateProjection.js +2 -2
  110. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  111. package/extension/src/shared/pageRpcContract.js +5 -4
  112. package/extension/src/shared/panelIcons.js +39 -0
  113. package/extension/src/shared/projectFiles.js +21 -44
  114. package/extension/src/shared/providerProfiles.js +5 -3
  115. package/extension/src/shared/reviewing.js +130 -1
  116. package/extension/src/shared/runActivityModel.js +70 -4
  117. package/extension/src/shared/runExecutionSnapshot.js +13 -0
  118. package/extension/src/shared/runInputQueue.js +48 -5
  119. package/extension/src/shared/selectionContext.js +12 -3
  120. package/extension/src/shared/sessionState.js +132 -28
  121. package/extension/src/shared/sessionTitle.js +14 -1
  122. package/extension/src/shared/settlementFacts.js +10 -2
  123. package/extension/src/shared/sharedSessionBridge.js +118 -27
  124. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  125. package/extension/src/shared/staleGuard.js +166 -52
  126. package/extension/src/shared/storageDb.js +90 -26
  127. package/extension/src/shared/storageMigration.js +78 -112
  128. package/extension/src/shared/storageRunActions.js +267 -30
  129. package/extension/src/shared/subagentActivityModel.js +1 -1
  130. package/extension/src/shared/undoOperations.js +253 -32
  131. package/extension/src/shared/updateConsent.js +6 -4
  132. package/extension/src/shared/updateRuntimeIdentity.js +17 -3
  133. package/extension/src/shared/writebackIntent.js +16 -0
  134. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  135. package/extension/src/shared/writebackSettlement.js +58 -40
  136. package/extension/styles/panel.css +1123 -493
  137. package/native-host/src/anthropicBridgeRequest.js +4 -2
  138. package/native-host/src/anthropicBridgeResponse.js +6 -2
  139. package/native-host/src/anthropicMessagesBridge.js +24 -16
  140. package/native-host/src/bootstrapMigration.js +296 -0
  141. package/native-host/src/bootstrapMigrationContract.js +101 -0
  142. package/native-host/src/bootstrapUpdate.js +269 -0
  143. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  144. package/native-host/src/chatBridgeRequest.js +60 -6
  145. package/native-host/src/chatBridgeResponse.js +20 -1
  146. package/native-host/src/chatCompletionsBridge.js +48 -15
  147. package/native-host/src/codexConfigPaths.js +106 -0
  148. package/native-host/src/codexHome.js +66 -73
  149. package/native-host/src/codexHomeScope.js +124 -0
  150. package/native-host/src/codexInheritedAuth.js +90 -0
  151. package/native-host/src/codexLocalSkills.js +301 -0
  152. package/native-host/src/codexPromptAssembly.js +114 -127
  153. package/native-host/src/codexProviderTest.js +24 -26
  154. package/native-host/src/codexSessionRunner.js +234 -198
  155. package/native-host/src/codexSessionTiming.js +18 -8
  156. package/native-host/src/codexSkillIsolation.js +170 -0
  157. package/native-host/src/codexThreadFork.js +19 -3
  158. package/native-host/src/index.js +19 -7
  159. package/native-host/src/managedInstall.js +110 -35
  160. package/native-host/src/managedInstallRecovery.js +140 -0
  161. package/native-host/src/managedRuntimeEvidence.js +57 -0
  162. package/native-host/src/mirrorWorkspace.js +92 -22
  163. package/native-host/src/nativeDoctor.js +15 -0
  164. package/native-host/src/nativeHelperLifecycle.js +21 -0
  165. package/native-host/src/nativeHostRegistration.js +33 -0
  166. package/native-host/src/nativeRunLifecycle.js +71 -0
  167. package/native-host/src/processStopBarrier.js +42 -7
  168. package/native-host/src/projectPathPlatform.js +30 -0
  169. package/native-host/src/providerContextError.js +20 -0
  170. package/native-host/src/providerHttp.js +80 -0
  171. package/native-host/src/providerProfile.js +4 -2
  172. package/native-host/src/providerReasoning.js +2 -7
  173. package/native-host/src/providerRuntime.js +1 -0
  174. package/native-host/src/providerStore.js +72 -14
  175. package/native-host/src/responsesPassthroughBridge.js +266 -52
  176. package/native-host/src/runtimeInstaller.js +21 -1
  177. package/native-host/src/selectionScope.js +36 -8
  178. package/native-host/src/subagentAdmission.js +26 -0
  179. package/native-host/src/subagentBroker.js +79 -66
  180. package/native-host/src/subagentTelemetry.js +19 -10
  181. package/native-host/src/subagentWorkspacePath.js +5 -2
  182. package/native-host/src/taskRunnerRuntime.js +92 -57
  183. package/native-host/src/textPatch.js +473 -377
  184. package/native-host/src/tomlStatements.js +140 -0
  185. package/native-host/src/updateManager.js +409 -46
  186. package/native-host/src/writingStyleRuntime.js +57 -29
  187. package/native-host/src/writingStyleStore.js +73 -0
  188. package/package.json +5 -8
  189. package/scripts/install-managed.mjs +1 -0
@@ -2,9 +2,9 @@
2
2
  "schemaVersion": 1,
3
3
  "entry": "extension/entries/content-entry.mjs",
4
4
  "output": "extension/src/content/generated/content.bundle.js",
5
- "sourceDigest": "a96cd1d3e417ecc9e124f48d9ad6dd728256c084f09a804d809299de8e4a7da3",
6
- "outputDigest": "09a6e2a4d4c6be0f8f8ad284c601e6cdf1843be09161f70a225abd56277b9c19",
7
- "byteLength": 2838686,
5
+ "sourceDigest": "0cf851b033afb0885464fd86506f423f35fe82d6da8c1f1644012350cab0ddc4",
6
+ "outputDigest": "0abf629ca414d00be2ab6bbde553c66a7f66ad2d0a4551bb272f3a5948a7d461",
7
+ "byteLength": 3387559,
8
8
  "inputs": [
9
9
  "extension/entries/content-entry.mjs",
10
10
  "extension/src/content/activeTurnControl.js",
@@ -15,10 +15,12 @@
15
15
  "extension/src/content/composerPanel.js",
16
16
  "extension/src/content/contentRuntime.js",
17
17
  "extension/src/content/contextTray.js",
18
+ "extension/src/content/contextUsageView.js",
18
19
  "extension/src/content/diagnosticsController.js",
19
20
  "extension/src/content/diagnosticsExport.js",
20
21
  "extension/src/content/diagnosticsPanel.js",
21
22
  "extension/src/content/diffReviewPanel.js",
23
+ "extension/src/content/directoryCleanupRecovery.js",
22
24
  "extension/src/content/globalPreferencesController.js",
23
25
  "extension/src/content/legacyGlobalRegistry.js",
24
26
  "extension/src/content/localSkillsPanel.js",
@@ -46,13 +48,16 @@
46
48
  "extension/src/content/recentProjects.js",
47
49
  "extension/src/content/reviewHunks.js",
48
50
  "extension/src/content/runActivitySummary.js",
51
+ "extension/src/content/runCompletionMeta.js",
49
52
  "extension/src/content/runController.js",
50
53
  "extension/src/content/runFailureNotice.js",
51
54
  "extension/src/content/runGuidanceController.js",
52
55
  "extension/src/content/runGuidanceView.js",
53
56
  "extension/src/content/runPresence.js",
54
57
  "extension/src/content/runQueueScheduler.js",
58
+ "extension/src/content/runRecoveryActions.js",
55
59
  "extension/src/content/runResultActions.js",
60
+ "extension/src/content/runReviewController.js",
56
61
  "extension/src/content/runScrollLayout.js",
57
62
  "extension/src/content/runSettlementPersistence.js",
58
63
  "extension/src/content/runTimelineView.js",
@@ -60,6 +65,7 @@
60
65
  "extension/src/content/scopedPersistenceDetachedCommit.js",
61
66
  "extension/src/content/scopedPersistencePanelState.js",
62
67
  "extension/src/content/selectionContextView.js",
68
+ "extension/src/content/sensitivePreflight.js",
63
69
  "extension/src/content/sessionForkController.js",
64
70
  "extension/src/content/sessionManager.js",
65
71
  "extension/src/content/sessionMenuView.js",
@@ -83,6 +89,9 @@
83
89
  "extension/src/shared/changeHistoryModel.js",
84
90
  "extension/src/shared/compatibility.js",
85
91
  "extension/src/shared/compileAdapter.js",
92
+ "extension/src/shared/composerAttachmentDraft.js",
93
+ "extension/src/shared/contextUsage.js",
94
+ "extension/src/shared/directoryCleanupProof.js",
86
95
  "extension/src/shared/failureReasons.js",
87
96
  "extension/src/shared/globalPreferences.js",
88
97
  "extension/src/shared/governanceRules.js",
@@ -92,6 +101,7 @@
92
101
  "extension/src/shared/models.js",
93
102
  "extension/src/shared/otText.js",
94
103
  "extension/src/shared/pageRpcContract.js",
104
+ "extension/src/shared/panelIcons.js",
95
105
  "extension/src/shared/pathRedaction.js",
96
106
  "extension/src/shared/projectFiles.js",
97
107
  "extension/src/shared/providerProfiles.js",
@@ -110,6 +120,7 @@
110
120
  "extension/src/shared/sessionTitle.js",
111
121
  "extension/src/shared/settlementFacts.js",
112
122
  "extension/src/shared/sharedSessionBridge.js",
123
+ "extension/src/shared/sharedSessionTransfer.js",
113
124
  "extension/src/shared/staleGuard.js",
114
125
  "extension/src/shared/storageDb.js",
115
126
  "extension/src/shared/storageKeys.js",
@@ -40,7 +40,7 @@
40
40
  if (force || changed.includes('theme')) options.applyTheme?.(values.theme);
41
41
  if (changed.includes('locale')) options.applyLocale?.();
42
42
  if (force || changed.includes('preloadProjectContext')) options.onPreloadChange?.(values.preloadProjectContext);
43
- if (changed.some(key => key === 'codexOverleafSkillEnabled' || key.startsWith('loadCodex'))) options.onSkillsChange?.();
43
+ if (changed.some(key => key === 'codexOverleafSkillEnabled' || key === 'codexLocalSkillEnabled' || key.startsWith('loadCodex'))) options.onSkillsChange?.();
44
44
  }
45
45
 
46
46
  function accept(value) {
@@ -20,13 +20,17 @@
20
20
  'SelectionContext',
21
21
  'SelectionContextView',
22
22
  'WritebackIntent',
23
+ 'DirectoryCleanupProof',
23
24
  'ChangeHistoryModel',
24
25
  'ChangeHistoryView',
25
26
  'Compatibility',
26
27
  'ComposerAttachments',
28
+ 'ComposerAttachmentDraft',
27
29
  'ComposerPanel',
28
30
  'ContentRuntime',
29
31
  'ContextTray',
32
+ 'ContextUsage',
33
+ 'ContextUsageView',
30
34
  'DiagnosticsController',
31
35
  'DiagnosticsExport',
32
36
  'DiagnosticsPanel',
@@ -53,14 +57,17 @@
53
57
  'PostNavigationSettlementPersistence',
54
58
  'ProjectFiles',
55
59
  'ProjectProviderSelection',
60
+ 'PanelIcons',
56
61
  'ProjectSettingsCoordinator',
57
62
  'ProviderSettingsCoordinator',
58
63
  'RecentProjects',
59
64
  'ReviewHunks',
65
+ 'Reviewing',
60
66
  'RunActivitySummary',
61
67
  'RunPresence',
62
68
  'RunActivityModel',
63
69
  'RunController',
70
+ 'RunReviewController',
64
71
  'RunExecutionSnapshot',
65
72
  'RunGuidanceController',
66
73
  'RunInputQueue',
@@ -73,6 +80,7 @@
73
80
  'ScopedPersistenceCoordinator',
74
81
  'ScopedPersistenceTransaction',
75
82
  'SensitiveScan',
83
+ 'SensitivePreflight',
76
84
  'SessionManager',
77
85
  'SessionForkController',
78
86
  'SessionPanel',
@@ -93,6 +101,7 @@
93
101
  'UpdateIdle',
94
102
  'UpdateNotice',
95
103
  'WritebackController',
104
+ 'DirectoryCleanupRecovery',
96
105
  'WritebackIntent',
97
106
  'WritebackOrchestrator',
98
107
  'WritebackPlan',
@@ -111,6 +120,7 @@
111
120
  'ProviderProfiles',
112
121
  'ProviderSettingsDialog',
113
122
  'RunGuidanceView',
123
+ 'RunRecoveryActions',
114
124
  'SessionMenuView'
115
125
  ]);
116
126
 
@@ -17,11 +17,17 @@
17
17
  document: documentDep = null,
18
18
  getPanel = () => null,
19
19
  getState = () => null,
20
+ getScope = () => '',
20
21
  setState = () => {},
21
22
  saveState = null,
22
23
  sendBackgroundNative = () => Promise.resolve({ ok: false }),
23
24
  getSkillLoadingSettings = () => ({ loadCodexOverleafSkills: true }),
24
25
  isCodexOverleafSkillEnabled = () => true,
26
+ isCodexLocalSkillEnabled = (id, skill) => skill?.enabled !== false,
27
+ setCodexLocalSkillEnabled = () => {},
28
+ getCodexLocalSkills = () => [],
29
+ getCodexLocalSkillWarnings = () => [],
30
+ refreshCodexLocalSkills = async () => [],
25
31
  setCodexOverleafSkillEnabled = () => {},
26
32
  setProjectSettingsStatus = () => {},
27
33
  tr = (key) => key,
@@ -36,27 +42,51 @@
36
42
  || root.document
37
43
  || (typeof globalThis !== 'undefined' ? globalThis.document : null);
38
44
 
45
+ let localSkillsListError = false;
46
+ let refreshGeneration = 0;
39
47
  async function refreshLocalSkills() {
48
+ const generation = ++refreshGeneration;
49
+ const scope = getScope();
50
+ const isCurrent = () => generation === refreshGeneration && scope === getScope();
51
+ localSkillsListError = false;
40
52
  showSkillListLoading();
53
+ const localList = getPanel()?.querySelector('[data-codex-local-skill-list]');
54
+ if (localList) {
55
+ localList.textContent = '';
56
+ appendLocalSkillEmpty(localList, 'codexOverleafSkillsLoading');
57
+ }
58
+ const localRefresh = localList ? refreshCodexLocalSkills({ force: true }) : Promise.resolve([]);
59
+ const localOutcome = localRefresh.then(() => {
60
+ if (isCurrent()) renderCodexLocalSkillList();
61
+ return null;
62
+ }, error => {
63
+ if (isCurrent()) { localSkillsListError = true; renderCodexLocalSkillList(); }
64
+ return error;
65
+ });
41
66
  try {
42
67
  const codexOverleafResponse = await sendBackgroundNative({
43
68
  method: 'skills.list',
44
69
  params: { scope: 'codex-overleaf' }
45
70
  });
46
71
  if (!codexOverleafResponse?.ok) {
47
- throw new Error(codexOverleafResponse?.error?.message || 'native host did not return Codex Overleaf skills');
72
+ throw new Error(tx('Could not load Codex Overleaf skills. Try again.', '无法加载 Codex Overleaf 技能,请重试。'));
48
73
  }
49
74
  const codexOverleafSkills = Array.isArray(codexOverleafResponse.result?.skills)
50
75
  ? codexOverleafResponse.result.skills
51
76
  : [];
77
+ if (!isCurrent()) return;
52
78
  const state = getState() || {};
53
79
  setState({
54
80
  ...state,
55
81
  codexOverleafSkills
56
82
  });
57
83
  setSlashCodexOverleafSkills(codexOverleafSkills);
84
+ const localError = await localOutcome;
85
+ if (!isCurrent()) return;
58
86
  renderLocalSkillList();
87
+ if (localError) throw localError;
59
88
  } catch (err) {
89
+ if (!isCurrent()) return;
60
90
  renderLocalSkillList();
61
91
  throw err;
62
92
  }
@@ -71,6 +101,7 @@
71
101
  const placeholder = document.createElement('div');
72
102
  placeholder.className = 'codex-project-settings-empty';
73
103
  placeholder.textContent = tr('codexOverleafSkillsLoading');
104
+ placeholder.dataset.i18n = 'codexOverleafSkillsLoading';
74
105
  list.append(placeholder);
75
106
  }
76
107
 
@@ -84,6 +115,7 @@
84
115
  }
85
116
 
86
117
  function renderLocalSkillList() {
118
+ renderCodexLocalSkillList();
87
119
  const list = getPanel()?.querySelector('[data-local-skill-list]');
88
120
  if (!list) {
89
121
  return;
@@ -93,10 +125,10 @@
93
125
  const codexOverleafEnabled = getSkillLoadingSettings().loadCodexOverleafSkills !== false;
94
126
  list.textContent = '';
95
127
  if (!codexOverleafEnabled) {
96
- appendLocalSkillEmpty(list, tr('codexOverleafSkillsDisabled'));
128
+ appendLocalSkillEmpty(list, 'codexOverleafSkillsDisabled');
97
129
  }
98
130
  if (!codexOverleafSkills.length) {
99
- appendLocalSkillEmpty(list, tr('codexOverleafSkillsEmpty'));
131
+ appendLocalSkillEmpty(list, 'codexOverleafSkillsEmpty');
100
132
  return;
101
133
  }
102
134
  for (const skill of codexOverleafSkills) {
@@ -104,6 +136,31 @@
104
136
  }
105
137
  }
106
138
 
139
+ function renderCodexLocalSkillList() {
140
+ const list = getPanel()?.querySelector('[data-codex-local-skill-list]');
141
+ if (!list) return;
142
+ list.textContent = '';
143
+ const enabled = getSkillLoadingSettings().loadCodexLocalSkills !== false;
144
+ if (!enabled) appendLocalSkillEmpty(list, 'codexLocalSkillsDisabled');
145
+ const skills = getCodexLocalSkills();
146
+ if (localSkillsListError) { appendLocalSkillEmpty(list, 'codexLocalSkillsLoadFailed'); return; }
147
+ if (!Array.isArray(skills)) { appendLocalSkillEmpty(list, 'codexOverleafSkillsLoading'); return; }
148
+ if (!skills.length) appendLocalSkillEmpty(list, 'codexLocalSkillsEmpty');
149
+ const skippedCount = getCodexLocalSkillWarnings().reduce((sum, warning) => sum + (warning?.code === 'codex_local_skills_skipped' ? warning.count : 0), 0);
150
+ if (skippedCount > 0) {
151
+ const warning = document.createElement('div');
152
+ warning.className = 'codex-project-settings-empty';
153
+ warning._refreshLocale = () => { warning.textContent = tr('codexLocalSkillsSkipped', { count: skippedCount }); };
154
+ warning._refreshLocale();
155
+ list.append(warning);
156
+ }
157
+ for (const skill of skills) {
158
+ const title = String(skill.title || skill.name || skill.id).trim();
159
+ const duplicateTitle = skills.filter(other => String(other.title || other.name || other.id).trim() === title).length > 1;
160
+ renderCodexOverleafSkillRow(list, { ...skill, title, duplicateTitle, scope: 'codex-local', removable: false }, enabled);
161
+ }
162
+ }
163
+
107
164
  function createSkillMark(id) {
108
165
  const mark = document.createElement('span');
109
166
  mark.className = 'codex-local-skill-icon';
@@ -123,10 +180,11 @@
123
180
  if (!id) {
124
181
  return;
125
182
  }
183
+ const local = skill.scope === 'codex-local';
126
184
  const compact = Boolean(list.closest?.('.codex-settings-workbench'));
127
185
  const row = document.createElement('div');
128
186
  row.className = 'codex-local-skill-row';
129
- row.dataset.scope = 'codex-overleaf';
187
+ row.dataset.scope = local ? 'codex-local' : 'codex-overleaf';
130
188
  if (!masterEnabled) {
131
189
  row.dataset.disabled = 'true';
132
190
  }
@@ -148,7 +206,7 @@
148
206
  'annotated-rewrite': tx('Guided editing for document revisions.', '用于文稿修订的编辑流程。'),
149
207
  'parallel-subagents': tx('Split independent subtasks across multiple agents.', '将相互独立的子任务分配给多个 agent。')
150
208
  };
151
- const description = descriptions[id]
209
+ const description = (!local && descriptions[id])
152
210
  || (typeof skill.description === 'string' ? skill.description.replace(/\s+/g, ' ').trim() : '');
153
211
  if (description) {
154
212
  const help = document.createElement('span');
@@ -157,7 +215,17 @@
157
215
  help.title = description;
158
216
  copy.append(help);
159
217
  }
160
- if (compact) row.append(createSkillMark(id));
218
+ const sourceText = () => (skill.sourceKind === 'system' ? tx('Codex built-in', 'Codex 内置')
219
+ : skill.sourceKind === 'plugin' && skill.sourceLabel ? String(skill.sourceLabel) : tx('Local Codex', 'Codex 本地'))
220
+ + (skill.duplicateTitle ? ' · ' + (skill.sourceHint || id.slice(-8)) : '');
221
+ let sourceLabel = null;
222
+ if (local) {
223
+ sourceLabel = document.createElement('span');
224
+ sourceLabel.className = 'codex-local-skill-description';
225
+ sourceLabel.textContent = sourceText();
226
+ copy.append(sourceLabel);
227
+ }
228
+ if (compact) row.append(createSkillMark(local ? '' : id));
161
229
  row.append(copy);
162
230
  const controls = compact ? document.createElement('div') : row;
163
231
  if (compact) controls.className = 'codex-local-skill-controls';
@@ -166,14 +234,25 @@
166
234
  const toggle = document.createElement('input');
167
235
  toggle.type = 'checkbox';
168
236
  toggle.className = 'codex-switch';
169
- toggle.checked = isCodexOverleafSkillEnabled(id);
170
- toggle.setAttribute('aria-label', (trimmedTitle || id) + ': ' + tr('codexOverleafSkillEnableToggle'));
237
+ toggle.checked = local ? isCodexLocalSkillEnabled(id, skill) : isCodexOverleafSkillEnabled(id);
238
+ const accessibleSkillName = (trimmedTitle || id)
239
+ + (local && skill.duplicateTitle ? ': ' + (skill.sourceHint || id.slice(-8)) : '');
240
+ toggle.setAttribute('aria-label', accessibleSkillName + ': ' + tr('codexOverleafSkillEnableToggle'));
171
241
  if (!masterEnabled) {
172
242
  toggle.disabled = true;
173
243
  }
174
244
  toggle.addEventListener('change', event => {
175
- setCodexOverleafSkillEnabled(id, event.target.checked);
245
+ if (local) setCodexLocalSkillEnabled(id, event.target.checked);
246
+ else setCodexOverleafSkillEnabled(id, event.target.checked);
176
247
  });
248
+ row._refreshLocale = () => {
249
+ if (sourceLabel) sourceLabel.textContent = sourceText();
250
+ toggle.setAttribute('aria-label', accessibleSkillName + ': ' + tr('codexOverleafSkillEnableToggle'));
251
+ const label = !local && id === 'annotated-rewrite' ? tx('Guided editing for document revisions.', '用于文稿修订的编辑流程。')
252
+ : !local && id === 'parallel-subagents' ? tx('Split independent subtasks across multiple agents.', '将相互独立的子任务分配给多个 agent。') : '';
253
+ const help = copy.querySelector('.codex-local-skill-description');
254
+ if (label && help) { help.textContent = label; help.title = label; }
255
+ };
177
256
 
178
257
  if (skill.removable !== false) {
179
258
  // The Remove / Confirm / Cancel controls share an actions container so
@@ -198,6 +277,11 @@
198
277
  cancelBtn.type = 'button';
199
278
  cancelBtn.className = 'codex-set-btn';
200
279
  cancelBtn.textContent = tr('localSkillRemoveCancel');
280
+ const refreshRow = row._refreshLocale;
281
+ row._refreshLocale = () => {
282
+ refreshRow(); removeBtn.textContent = tr('localSkillRemove'); cancelBtn.textContent = tr('localSkillRemoveCancel');
283
+ confirmBtn.textContent = tr(confirmBtn.disabled ? 'localSkillRemoving' : 'localSkillRemoveConfirm');
284
+ };
201
285
 
202
286
  function enterConfirming() {
203
287
  // Reset any previously confirming row back to its Remove button state.
@@ -236,7 +320,7 @@
236
320
  toggle.disabled = true;
237
321
  confirmBtn.textContent = tr('localSkillRemoving');
238
322
  removeCodexOverleafSkill(id).catch(error => {
239
- setProjectSettingsStatus(tx(`Failed to remove Codex Overleaf skill: ${error.message}`, `删除 Codex Overleaf 技能失败:${error.message}`), 'failed');
323
+ setProjectSettingsStatus(tx(`Failed to remove Codex Overleaf skill: ${error.message}`, `删除 Codex Overleaf 技能失败:${error.message}`), 'failed', { page: 'context' });
240
324
  // Restore row on failure so the user can retry.
241
325
  confirmBtn.disabled = false;
242
326
  cancelBtn.disabled = false;
@@ -274,10 +358,11 @@
274
358
  }
275
359
  }
276
360
 
277
- function appendLocalSkillEmpty(list, text) {
361
+ function appendLocalSkillEmpty(list, key) {
278
362
  const empty = document.createElement('div');
279
363
  empty.className = 'codex-project-settings-empty';
280
- empty.textContent = text;
364
+ empty.textContent = tr(key);
365
+ empty.dataset.i18n = key;
281
366
  list.append(empty);
282
367
  }
283
368
 
@@ -293,10 +378,10 @@
293
378
  throw new Error(response?.error?.message || 'native host did not remove Codex Overleaf skill');
294
379
  }
295
380
  const activeSkill = normalizeComposerSkillInvocation(getComposerSkillInvocation());
296
- if (activeSkill?.scope === 'codex-overleaf' && activeSkill.skillId === id) {
381
+ if (activeSkill?.scope === 'codex-overleaf' && activeSkill.id === id) {
297
382
  clearComposerSkillInvocation();
298
383
  }
299
- setProjectSettingsStatus(tr('codexOverleafSkillRemoveDone'), 'completed');
384
+ setProjectSettingsStatus(tr('codexOverleafSkillRemoveDone'), 'completed', { page: 'context' });
300
385
  clearSlashCodexOverleafSkills();
301
386
  await refreshLocalSkills();
302
387
  await saveState?.();
@@ -304,6 +389,11 @@
304
389
 
305
390
  return {
306
391
  refreshLocalSkills,
392
+ refreshLocale: () => {
393
+ for (const selector of ['[data-local-skill-list]', '[data-codex-local-skill-list]']) {
394
+ for (const row of getPanel()?.querySelector(selector)?.children || []) row._refreshLocale?.();
395
+ }
396
+ },
307
397
  renderLocalSkillList,
308
398
  removeCodexOverleafSkill
309
399
  };
@@ -468,10 +468,10 @@
468
468
  textNode.textContent = `${match[1]}${text.slice(match[0].length)}`;
469
469
  textNode.parentNode.insertBefore(label, textNode);
470
470
  }
471
- decorateChangeReceipts(container, document, tx);
471
+ decorateChangeReceipts(container, document, tx, options.formatOperationType);
472
472
  }
473
473
 
474
- function decorateChangeReceipts(container, document, tx) {
474
+ function decorateChangeReceipts(container, document, tx, formatOperationType = type => type) {
475
475
  const label = container.querySelector?.('.run-system-label[data-system-label="changes"]');
476
476
  const list = label?.closest?.('p')?.nextElementSibling;
477
477
  if (!list || !['UL', 'OL'].includes(list.tagName)) return;
@@ -508,7 +508,9 @@
508
508
  file.textContent = receipt.file;
509
509
  const operation = document.createElement('span');
510
510
  operation.className = 'run-change-receipt__operation';
511
- operation.textContent = receipt.operation;
511
+ const type = ({ 编辑: 'edit', 新建: 'create', 重命名: 'rename', 移动: 'move', 删除: 'delete' })[receipt.operation]
512
+ || receipt.operation.toLowerCase();
513
+ operation.textContent = formatOperationType(type);
512
514
  receipt.item.classList.add('run-change-receipt');
513
515
  receipt.item.setAttribute('aria-label', receipt.original);
514
516
  receipt.item.title = receipt.original;
@@ -1,11 +1,8 @@
1
1
  (function initCodexOverleafMarkdownText() {
2
2
  'use strict';
3
3
 
4
- // Markdown / assistant-text rendering — block + inline markdown, the
5
- // assistant-visible sanitizers, and line-reference resolution/buttons carved
6
- // out of contentRuntime.js (v1.4.5 structural-debt phase 1). The code below
7
- // moved verbatim (original indentation kept); the four runtime collaborators
8
- // are factory-injected so behavior is unchanged.
4
+ // Assistant-text rendering, sanitizers, and line-reference controls.
5
+ // Runtime collaborators are factory-injected.
9
6
  function create(deps = {}) {
10
7
  const {
11
8
  LineReferences,
@@ -102,7 +99,7 @@
102
99
  index = next.end;
103
100
  }
104
101
 
105
- return nodes;
102
+ return groupLineReferenceNodes(nodes);
106
103
  }
107
104
 
108
105
  function buildInlineCodeNodes(value) {
@@ -155,7 +152,7 @@
155
152
  cursor = start + rawText.length;
156
153
  }
157
154
  appendSanitizedTextNode(nodes, source.slice(cursor));
158
- return nodes;
155
+ return groupLineReferenceNodes(nodes);
159
156
  }
160
157
 
161
158
  function buildMarkdownLinkNodes(text, href) {
@@ -190,6 +187,24 @@
190
187
  }
191
188
  }
192
189
 
190
+ function groupLineReferenceNodes(nodes) {
191
+ const grouped = [];
192
+ for (const node of nodes) {
193
+ const previous = grouped[grouped.length - 1];
194
+ if (node.classList?.contains('codex-line-reference') && previous?.nodeType === 3 &&
195
+ previous.textContent.endsWith('(')) {
196
+ previous.textContent = previous.textContent.slice(0, -1);
197
+ const group = document.createElement('span');
198
+ group.className = 'codex-line-reference-group';
199
+ group.append(document.createTextNode('('), node);
200
+ grouped.push(group);
201
+ } else {
202
+ grouped.push(node);
203
+ }
204
+ }
205
+ return grouped;
206
+ }
207
+
193
208
  function resolveMarkdownLineReference(text, href) {
194
209
  const candidates = [
195
210
  { value: href, mode: 'markdown-link-target' },
@@ -233,12 +248,16 @@
233
248
  return null;
234
249
  }
235
250
  const line = Number(ref?.line);
251
+ const endLine = ref?.endLine === undefined ? null : Number(ref.endLine);
236
252
  const column = ref?.column === null || ref?.column === undefined || ref?.column === ''
237
253
  ? null
238
254
  : Number(ref.column);
239
255
  if (!Number.isSafeInteger(line) || line < 1 || (column !== null && (!Number.isSafeInteger(column) || column < 1))) {
240
256
  return null;
241
257
  }
258
+ if (endLine !== null && (!Number.isSafeInteger(endLine) || endLine < line)) {
259
+ return null;
260
+ }
242
261
  let resolved;
243
262
  try {
244
263
  resolved = LineReferences.resolveProjectReference({
@@ -254,6 +273,7 @@
254
273
  return {
255
274
  path: resolved.path,
256
275
  line,
276
+ ...(endLine > line ? { endLine } : {}),
257
277
  column
258
278
  };
259
279
  }
@@ -265,10 +285,15 @@
265
285
  button.textContent = formatLineReferenceText(reference);
266
286
  button.dataset.path = reference.path;
267
287
  button.dataset.line = String(reference.line);
288
+ if (reference.endLine) {
289
+ button.dataset.endLine = String(reference.endLine);
290
+ }
268
291
  if (reference.column) {
269
292
  button.dataset.column = String(reference.column);
270
293
  }
271
- const ariaLabel = reference.column
294
+ const ariaLabel = reference.endLine
295
+ ? `Open ${reference.path} lines ${reference.line} to ${reference.endLine}`
296
+ : reference.column
272
297
  ? `Open ${reference.path} line ${reference.line} column ${reference.column}`
273
298
  : `Open ${reference.path} line ${reference.line}`;
274
299
  button.setAttribute('aria-label', ariaLabel);
@@ -281,7 +306,10 @@
281
306
  line: reference.line,
282
307
  selectLine: !reference.column
283
308
  };
284
- if (reference.column) {
309
+ if (reference.endLine) {
310
+ params.endLine = reference.endLine;
311
+ params.selectLine = true;
312
+ } else if (reference.column) {
285
313
  params.column = reference.column;
286
314
  params.selectLine = false;
287
315
  }
@@ -313,7 +341,7 @@
313
341
  }
314
342
 
315
343
  function formatLineReferenceText(reference) {
316
- return `${reference.path}:${reference.line}${reference.column ? `:${reference.column}` : ''}`;
344
+ return `${reference.path}:${reference.line}${reference.endLine ? `–${reference.endLine}` : reference.column ? `:${reference.column}` : ''}`;
317
345
  }
318
346
 
319
347
  function normalizeReferencePathForRuntime(value) {
@@ -502,6 +530,12 @@
502
530
  renderText: text => buildMarkdownInlineNodes(text) }),
503
531
  recordDiagnostic: recordRenderingDiagnostic
504
532
  });
533
+ // The DOM renderer emits text and links from separate Markdown tokens.
534
+ const parents = new Set(Array.from(target.querySelectorAll('.codex-line-reference'), button => button.parentNode)
535
+ .filter(parent => !parent.classList.contains('codex-line-reference-group')));
536
+ for (const parent of parents) {
537
+ parent.replaceChildren(...groupLineReferenceNodes(Array.from(parent.childNodes)));
538
+ }
505
539
  return;
506
540
  } catch (error) {
507
541
  recordRenderingDiagnostic({
@@ -588,7 +622,9 @@
588
622
  return;
589
623
  }
590
624
  renderingDiagnosticKeys.add(key);
591
- if (typeof options.recordRenderingDiagnostic === 'function') options.recordRenderingDiagnostic({ code, detail });
625
+ try {
626
+ if (typeof deps.recordRenderingDiagnostic === 'function') deps.recordRenderingDiagnostic({ code, detail });
627
+ } catch (_error) { /* A diagnostic sink must not interrupt rendering. */ }
592
628
  }
593
629
 
594
630
  function appendMarkdownParagraphFlow(target, value) {
@@ -117,11 +117,12 @@
117
117
  if (generation !== modelLoadGeneration) {
118
118
  return { stale: true };
119
119
  }
120
+ options.assertCurrent?.();
120
121
  const responseProviderId = response?.result?.providerId || '';
121
122
  if (selection?.providerId && responseProviderId && responseProviderId !== selection.providerId) {
122
123
  return { stale: true };
123
124
  }
124
- if (selection?.providerId && selection.providerId !== 'builtin'
125
+ if (response?.ok && selection?.providerId && selection.providerId !== 'builtin'
125
126
  && Number(response?.result?.providerRevision) !== Number(selection.providerRevision)) {
126
127
  return { stale: true };
127
128
  }
@@ -170,6 +171,7 @@
170
171
  if (generation !== modelLoadGeneration) {
171
172
  return { stale: true };
172
173
  }
174
+ options.assertCurrent?.();
173
175
  applyUnavailableModelOptions(error);
174
176
  return { stale: false, error };
175
177
  }
@@ -251,7 +253,7 @@
251
253
  }
252
254
  const option = document.createElement('option');
253
255
  option.value = id;
254
- option.textContent = model.label;
256
+ option.textContent = model.unverified && model.label === `${id} (custom)` ? tx(`${id} (custom)`, `${id}(自定义)`) : model.label;
255
257
  option.dataset.reasoningEfforts = normalizeReasoningEffortsForSelect(model.reasoningEfforts).join(',');
256
258
  option.dataset.defaultReasoningEffort = model.defaultReasoningEffort || '';
257
259
  option.dataset.reasoningPresentation = model.reasoningPresentation || '';
@@ -269,7 +271,7 @@
269
271
  if (selectedId && !renderedSelected && options.allowUnknownSelected !== false) {
270
272
  const option = document.createElement('option');
271
273
  option.value = selectedId;
272
- option.textContent = `${selectedId} (custom)`;
274
+ option.textContent = tx(`${selectedId} (custom)`, `${selectedId}(自定义)`);
273
275
  option.dataset.reasoningEfforts = '';
274
276
  option.dataset.defaultReasoningEffort = '';
275
277
  option.dataset.reasoningPresentation = 'none';
@@ -370,7 +372,7 @@
370
372
  for (const option of Array.from(reasoningSelect.options || [])) {
371
373
  list.append(createModelConfigChoice({
372
374
  value: option.value,
373
- label: option.textContent || formatReasoningEffortLabel(option.value, reasoningSelect.dataset.presentation),
375
+ label: formatReasoningEffortLabel(option.value, reasoningSelect.dataset.presentation),
374
376
  datasetName: 'reasoningChoice'
375
377
  }));
376
378
  }
@@ -384,6 +386,7 @@
384
386
  }
385
387
  list.textContent = '';
386
388
  for (const option of Array.from(modelSelect.options || [])) {
389
+ if (option.dataset.unverified === 'true' && [`${option.value} (custom)`, `${option.value}(自定义)`].includes(option.textContent)) option.textContent = tx(`${option.value} (custom)`, `${option.value}(自定义)`);
387
390
  const choice = createModelConfigChoice({
388
391
  value: option.value,
389
392
  label: option.textContent || option.value,
@@ -522,6 +525,7 @@
522
525
  readSelectedSpeedInput() === 'fast' ? tx('Fast mode', '快速模式') : tx('Standard speed', '标准速度')
523
526
  ].filter(Boolean).join(' · ');
524
527
  }
528
+ deps.onSelectionRendered?.();
525
529
  }
526
530
 
527
531
  return {