codex-overleaf-link 2.3.3 → 2.3.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Codex Overleaf Link",
4
- "version": "2.3.3",
4
+ "version": "2.3.5",
5
5
  "description": "Connect the active Overleaf project to a local Codex bridge.",
6
6
  "icons": {
7
7
  "16": "assets/icons/icon16.png",
@@ -15,6 +15,11 @@ if (!globalThis.CodexOverleafNativeRequestIdentity) {
15
15
  : 'shared/nativeRequestIdentity.js';
16
16
  importScripts(runtimeBase ? chrome.runtime.getURL(identityPath) : identityPath);
17
17
  }
18
+ if (!globalThis.CodexOverleafGlobalPreferences) {
19
+ const runtimeBase = String(globalThis.__CODEX_OVERLEAF_RUNTIME_BASE__ || '');
20
+ importScripts(runtimeBase ? chrome.runtime.getURL(`${runtimeBase}/src/shared/globalPreferences.js`) : 'shared/globalPreferences.js');
21
+ }
22
+ globalThis.CodexOverleafGlobalPreferences?.installBackground(chrome);
18
23
 
19
24
  (function initBackground() {
20
25
  'use strict';
@@ -110,7 +110,7 @@
110
110
  </span>
111
111
  </div>
112
112
  <div class="codex-context-selection" data-context-selection></div>
113
- <div class="codex-context-status" data-context-status>Type @ to add context: @file, @compile-log, @current-section.</div>
113
+ <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>
114
114
  <div class="codex-context-files" data-context-file-list></div>
115
115
  </div>
116
116
  </form>
@@ -217,6 +217,7 @@
217
217
  renderAuditHistoryPanel: () => renderAuditHistoryPanel(),
218
218
  renderRecentProjectsVariant: (...args) => renderRecentProjectsVariant(...args),
219
219
  saveStateSoon: () => saveStateSoon(),
220
+ updateGlobalPreferences: patch => getGlobalPreferences().update(patch),
220
221
  setState: next => { state = next; },
221
222
  tr,
222
223
  tx,
@@ -383,7 +384,8 @@
383
384
  getState: () => state,
384
385
  setState: next => { state = next; },
385
386
  getCurrentRunView: () => currentRunView,
386
- getSettingsPanelInstance: () => settingsPanelInstance
387
+ getSettingsPanelInstance: () => settingsPanelInstance,
388
+ prepareRetryReplacement: run => prepareRetryReplacement(run)
387
389
  });
388
390
  const {
389
391
  showUndoFileSelection,
@@ -663,6 +665,7 @@
663
665
  const providerSettingsCoordinator = Modules.ProviderSettingsCoordinator.create({
664
666
  ProviderProfiles: Modules.ProviderProfiles,
665
667
  ProviderSettingsDialog: Modules.ProviderSettingsDialog,
668
+ hasCurrentProject: () => Boolean(getCurrentProjectId()),
666
669
  tx,
667
670
  sendBackgroundNative,
668
671
  document,
@@ -839,7 +842,9 @@
839
842
  let composerPanelInstance = null;
840
843
  let state = null;
841
844
  let storageKey = LEGACY_STORAGE_KEY;
845
+ let globalPreferencesController = null;
842
846
  let currentRunView = null;
847
+ let pendingRetryReplacement = null;
843
848
  let runQueueScheduler = null;
844
849
  let runInputCoordinator = null;
845
850
  const activeTurnControl = ActiveTurnControl.create({ chrome });
@@ -936,6 +941,13 @@
936
941
  sendResponse?.(getPanelStateResponse());
937
942
  return;
938
943
  }
944
+ if (message?.type === 'codex-overleaf/toggle-launcher') {
945
+ const visible = !PanelRenderer.isLauncherVisible(panelRendererInstance);
946
+ PanelRenderer.setLauncherVisible(panelRendererInstance, visible, { persist: true });
947
+ if (!visible) closePanel();
948
+ sendResponse?.(getPanelStateResponse());
949
+ return;
950
+ }
939
951
  if (message?.type === 'codex-overleaf/toggle-panel') {
940
952
  if (isPanelOpen()) {
941
953
  closePanel();
@@ -966,9 +978,10 @@
966
978
  });
967
979
 
968
980
  async function init() {
981
+ await getGlobalPreferences().initialize();
969
982
  await refreshAccountScopeId();
970
983
  storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
971
- state = normalizePanelState(await loadStoredState(), { restoreRunningRuns: true });
984
+ state = normalizePanelState(getGlobalPreferences().overlay(await loadStoredState()), { restoreRunningRuns: true });
972
985
  initializeRunQueueScheduler();
973
986
  recoverInterruptedRunJournals()
974
987
  .then(() => applyStateToPanel())
@@ -990,6 +1003,7 @@
990
1003
  recentProjects.mountDashboardShell();
991
1004
  }
992
1005
  applyStateToPanel();
1006
+ getGlobalPreferences().refreshView();
993
1007
  // Welcome-panel + write-guard: seed the lifecycle
994
1008
  // module-locals from the current URL, install the SPA route hook so
995
1009
  // future Overleaf navigations swap variants, and warm the account-scope
@@ -1186,7 +1200,12 @@
1186
1200
  maxWidth: PANEL_MAX_WIDTH,
1187
1201
  pageMinWidth: PAGE_MIN_WIDTH,
1188
1202
  initialWidth: state?.panelWidth || PANEL_DEFAULT_WIDTH,
1203
+ chromeApi: chrome,
1204
+ launcherLabel: tx('Show or hide Codex panel', '显示或隐藏 Codex 边栏'),
1189
1205
  callbacks: {
1206
+ onLauncherToggle: () => ensurePanelOpen(),
1207
+ onClosePanel: () => closePanel(),
1208
+ onLauncherVisibilityChange: visible => { if (!visible) closePanel(); },
1190
1209
  onRefresh: () => refreshProbe({ userInitiated: true }),
1191
1210
  onNewSession: () => startNewSession(),
1192
1211
  onChangeHistory: () => openChangeHistory(),
@@ -1249,7 +1268,7 @@
1249
1268
  button: panel.querySelector('[data-custom-instructions-settings]'),
1250
1269
  callbacks: {
1251
1270
  onBack: () => closeCustomInstructionsSettings(),
1252
- onInputChange: () => persistPanelInputs(),
1271
+ onInputChange: event => persistPanelInputs(event),
1253
1272
  onSkillsOpen: () => openSkillsView(),
1254
1273
  onSkillsBack: () => closeSkillsView(),
1255
1274
  onProvidersOpen: () => providerSettingsCoordinator.open(),
@@ -1364,7 +1383,8 @@
1364
1383
  function getPanelStateResponse() {
1365
1384
  return {
1366
1385
  ok: true,
1367
- open: isPanelOpen()
1386
+ open: isPanelOpen(),
1387
+ launcherVisible: PanelRenderer.isLauncherVisible(panelRendererInstance)
1368
1388
  };
1369
1389
  }
1370
1390
 
@@ -1789,6 +1809,12 @@
1789
1809
  // Freeze submitted run identity before the panel can change during the run.
1790
1810
  const submittedMode = executionSnapshot.mode;
1791
1811
  const submittedRequireReviewing = executionSnapshot.requireReviewing === true;
1812
+ const replaceRunId = queuedInput ? '' : runController.resolveRetryReplacement({
1813
+ candidate: pendingRetryReplacement,
1814
+ activeSessionId: state.activeSessionId,
1815
+ runs: state.runs,
1816
+ projectRunSettlement: run => WritebackSettlement.projectRunSettlement(run)
1817
+ });
1792
1818
 
1793
1819
  runCancellationRequested = false;
1794
1820
  runCancellationController = new AbortController();
@@ -1802,8 +1828,10 @@
1802
1828
  executionSnapshot,
1803
1829
  attachments: submittedAttachments,
1804
1830
  skillInvocation: submittedSkillInvocation,
1805
- queueItemId: queuedInput?.id || ''
1831
+ queueItemId: queuedInput?.id || '',
1832
+ replaceRunId
1806
1833
  });
1834
+ if (!queuedInput) pendingRetryReplacement = null;
1807
1835
  const runSessionId = currentRunView.sessionId;
1808
1836
  let runAuditDraft = null;
1809
1837
  try {
@@ -4309,6 +4337,19 @@
4309
4337
  return scopedPersistenceCoordinator;
4310
4338
  }
4311
4339
 
4340
+ function getGlobalPreferences() {
4341
+ if (!globalPreferencesController) globalPreferencesController = Modules.GlobalPreferencesController.create({
4342
+ chromeApi: chrome, getState: () => state, setState: next => { state = next; }, getPanel: () => panel,
4343
+ applyTheme: applyPanelTheme, applyLocale: () => { applyLocaleToPanel(); refreshDashboardSoon(); },
4344
+ onPreloadChange: enabled => contextTrayController.scheduleContextPrefetch({ enabled }),
4345
+ onSkillsChange: () => { renderLocalSkillList(); updateSkillsEntrySummary(); },
4346
+ onError: error => showPluginToast(tx(
4347
+ `Global settings could not be saved or loaded: ${error.message}. Please retry.`,
4348
+ `全局设置未能保存或读取:${error.message}。请重试。`), { status: 'failed', sticky: true })
4349
+ });
4350
+ return globalPreferencesController;
4351
+ }
4352
+
4312
4353
  async function reloadProjectRunHistory(newProjectId) {
4313
4354
  // Rebind the storage key to the new project and reload the panel state
4314
4355
  // from chrome.storage.local so the per-project session list reflects
@@ -4316,7 +4357,7 @@
4316
4357
  try {
4317
4358
  storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
4318
4359
  const reloaded = await loadStoredState();
4319
- state = normalizePanelState(reloaded, { restoreRunningRuns: true });
4360
+ state = normalizePanelState(getGlobalPreferences().overlay(reloaded), { restoreRunningRuns: true });
4320
4361
  applyStateToPanel();
4321
4362
  } catch (_error) {
4322
4363
  // Reload failures fall back to the in-memory state; the next saveState
@@ -5054,8 +5095,8 @@
5054
5095
  return fileItems.length
5055
5096
  ? fileItems.join(', ')
5056
5097
  : tx(
5057
- '@whole-project (default); add @file, @compile-log, or @current-section',
5058
- '@whole-project(默认);可添加 @file、@compile-log、@current-section'
5098
+ '@whole-project (default); choose files or add @compile-log',
5099
+ '@whole-project(默认);可选择文件或添加 @compile-log'
5059
5100
  );
5060
5101
  }
5061
5102
 
@@ -5888,6 +5929,8 @@
5888
5929
  }
5889
5930
 
5890
5931
  async function persistPanelInputs(event) {
5932
+ const globalWrite = getGlobalPreferences().handleInput(event);
5933
+ if (globalWrite) return globalWrite;
5891
5934
  // Typing in the task box is the hottest write path: a full saveState per
5892
5935
  // keystroke serializes every session/run/event and rewrites IndexedDB, so
5893
5936
  // panels get slower as history grows. Drafts take the debounced saver;
@@ -5986,29 +6029,8 @@
5986
6029
  setCustomInstructionsForProject(projectId, customInstructionsInput.value);
5987
6030
  }
5988
6031
  setGovernanceRulesForCurrentProject(readGovernanceRulesFromSettings());
5989
- const newSettings = readSkillLoadingSettingsFromSettings();
5990
- const prevOverleafSkills = getSkillLoadingSettings().loadCodexOverleafSkills;
5991
- setSkillLoadingSettings(newSettings);
5992
- // Theme is a global preference; read it from the settings select (which
5993
- // lives inside the panel, like the other settings inputs), persist it on
5994
- // state, and apply immediately. applyPanelTheme normalizes the value.
5995
- const themePref = panel?.querySelector('[data-theme-select]')?.value || 'dark';
5996
- if (state) {
5997
- state.theme = themePref;
5998
- }
5999
- applyPanelTheme(themePref);
6000
- // Language is a global UI preference (moved here from the diagnostics
6001
- // menu); re-translate the panel when it changes.
6002
- const languagePref = panel?.querySelector('[data-language-select]')?.value;
6003
- if (languagePref && state && languagePref !== getLocale()) {
6004
- state.locale = i18n?.normalizeLocale?.(languagePref) || languagePref;
6005
- applyLocaleToPanel();
6006
- }
6007
- // Re-render skill list when master toggle changes so per-skill toggles update their disabled state.
6008
- if (prevOverleafSkills !== getSkillLoadingSettings().loadCodexOverleafSkills) {
6009
- renderLocalSkillList();
6010
- }
6011
- updateSkillsEntrySummary();
6032
+ // Global controls are handled as field patches by getGlobalPreferences;
6033
+ // unrelated session autosaves must not read stale settings controls.
6012
6034
  }
6013
6035
  }
6014
6036
 
@@ -6127,6 +6149,17 @@
6127
6149
  renderPendingInputs();
6128
6150
  }
6129
6151
 
6152
+ function prepareRetryReplacement(run) {
6153
+ const sessionId = state?.activeSessionId || '';
6154
+ const source = findRunRecord(run?.id, sessionId);
6155
+ const eligible = source && runController.isRetryReplacementEligible(
6156
+ source,
6157
+ WritebackSettlement.projectRunSettlement(source)
6158
+ );
6159
+ pendingRetryReplacement = eligible ? { sessionId, runId: source.id } : null;
6160
+ return Boolean(eligible);
6161
+ }
6162
+
6130
6163
  function startRunView({
6131
6164
  task,
6132
6165
  mode,
@@ -6134,6 +6167,7 @@
6134
6167
  reasoningEffort,
6135
6168
  speedTier,
6136
6169
  queueItemId = '',
6170
+ replaceRunId = '',
6137
6171
  executionSnapshot = null
6138
6172
  }) {
6139
6173
  let attachments = [];
@@ -6185,13 +6219,14 @@
6185
6219
  }
6186
6220
  : {};
6187
6221
  state = updateActiveSession(state, {
6188
- runs: [...(state.runs || []), record].slice(-20),
6222
+ runs: runController.replaceRunForRetry(state.runs, replaceRunId, record),
6189
6223
  ...titlePatch
6190
6224
  });
6191
- saveStateSoon();
6225
+ saveStateSoon(replaceRunId ? 0 : 120);
6192
6226
 
6193
6227
  const log = panel.querySelector('[data-log]');
6194
6228
  removeEmptyRunsMessage(log);
6229
+ if (replaceRunId) log.querySelector(`[data-run-id="${cssEscape(replaceRunId)}"]`)?.remove();
6195
6230
  const root = renderRunCard(record);
6196
6231
  log.append(root);
6197
6232
  scrollLogToBottom({ force: true });