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.
- package/README.md +325 -211
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +9 -0
- package/extension/src/backgroundUpdateCoordinator.js +88 -4
- package/extension/src/content/composerPanel.js +1 -1
- package/extension/src/content/contentRuntime.js +51 -35
- package/extension/src/content/diagnosticsController.js +13 -3
- package/extension/src/content/diagnosticsPanel.js +1 -1
- package/extension/src/content/generated/content.bundle.js +1233 -298
- package/extension/src/content/generated/content.bundle.meta.json +8 -3
- package/extension/src/content/globalPreferencesController.js +125 -0
- package/extension/src/content/legacyGlobalRegistry.js +4 -0
- package/extension/src/content/otWarmMirror.js +95 -65
- package/extension/src/content/otWarmMirrorController.js +102 -1
- package/extension/src/content/panelMaintenance.js +26 -15
- package/extension/src/content/panelRenderer.js +6 -1
- package/extension/src/content/projectSettingsCoordinator.js +1 -0
- package/extension/src/content/providerSettingsCoordinator.js +16 -0
- package/extension/src/content/providerSettingsDialog.js +24 -15
- package/extension/src/content/runFailureNotice.js +104 -0
- package/extension/src/content/runScrollLayout.js +124 -0
- package/extension/src/content/runTimelineView.js +36 -10
- package/extension/src/content/settingsPanel.js +32 -13
- package/extension/src/content/themeController.js +3 -0
- package/extension/src/content/updateNotice.js +24 -4
- package/extension/src/page/overleafRealtimeObserver.js +116 -24
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/globalPreferences.js +131 -0
- package/extension/src/shared/i18n.js +79 -39
- package/extension/src/shared/managedUpdateProjection.js +1 -1
- package/extension/src/shared/otText.js +0 -1
- package/extension/src/shared/sessionState.js +17 -21
- package/extension/src/shared/sessionTitle.js +120 -0
- package/extension/src/shared/updateRuntimeIdentity.js +32 -0
- package/extension/styles/panel.css +90 -16
- package/native-host/src/mirrorWorkspace.js +20 -1
- package/native-host/src/releaseProxy.js +177 -0
- package/native-host/src/releaseTransport.js +154 -0
- package/native-host/src/updateManager.js +40 -39
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -1
|
@@ -15,6 +15,15 @@ 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);
|
|
23
|
+
if (!globalThis.CodexOverleafUpdateRuntimeIdentity) {
|
|
24
|
+
const runtimeBase = String(globalThis.__CODEX_OVERLEAF_RUNTIME_BASE__ || '');
|
|
25
|
+
importScripts(runtimeBase ? chrome.runtime.getURL(`${runtimeBase}/src/shared/updateRuntimeIdentity.js`) : 'shared/updateRuntimeIdentity.js');
|
|
26
|
+
}
|
|
18
27
|
|
|
19
28
|
(function initBackground() {
|
|
20
29
|
'use strict';
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const UPDATE_STATE_KEY = 'codex-overleaf-managed-update-state-v1';
|
|
5
5
|
const CONSENT_STATE_KEY = 'codex-overleaf-update-consent-v1';
|
|
6
6
|
const UPDATE_RELOAD_TABS_KEY = 'codex-overleaf-managed-update-tabs-v1';
|
|
7
|
+
const MANUAL_RELOAD_TABS_KEY = 'codex-overleaf-manual-runtime-reload-v1';
|
|
7
8
|
const CHECK_ALARM = 'codex-overleaf-consent-update-check';
|
|
8
9
|
const IDLE_ALARM = 'codex-overleaf-consent-update-idle';
|
|
9
10
|
const WATCHDOG_ALARM = 'codex-overleaf-consent-update-watchdog';
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
const CHECK_INTERVAL_MINUTES = 24 * 60;
|
|
13
14
|
const SNOOZE_MS = 24 * 60 * 60 * 1000;
|
|
14
15
|
const CANDIDATE_MAX_AGE_MS = 5 * 60 * 1000;
|
|
15
|
-
const CHECK_REQUEST_TIMEOUT_MS =
|
|
16
|
+
const CHECK_REQUEST_TIMEOUT_MS = 55 * 1000;
|
|
16
17
|
const ACTIVATION_TIMEOUT_MS = 20 * 1000;
|
|
17
18
|
const ACTIVATION_POLL_MS = 250;
|
|
18
19
|
const FAST_IDLE_RETRY_MS = 4000;
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
'codex-overleaf/consent-update-check',
|
|
32
33
|
'codex-overleaf/consent-update-install',
|
|
33
34
|
'codex-overleaf/consent-update-later',
|
|
34
|
-
'codex-overleaf/consent-update-dismiss'
|
|
35
|
+
'codex-overleaf/consent-update-dismiss',
|
|
36
|
+
'codex-overleaf/consent-update-reload'
|
|
35
37
|
]);
|
|
36
38
|
|
|
37
39
|
const policy = root.CodexOverleafUpdateConsent;
|
|
@@ -41,6 +43,10 @@
|
|
|
41
43
|
let policyTail = Promise.resolve();
|
|
42
44
|
let idleRetryTimer = null;
|
|
43
45
|
let activationPromise = null;
|
|
46
|
+
let runtimeStatus = null;
|
|
47
|
+
let runtimeStatusAt = 0;
|
|
48
|
+
let runtimeStatusRead = null;
|
|
49
|
+
let runtimeReloadMessage = '';
|
|
44
50
|
|
|
45
51
|
function init(options = {}) {
|
|
46
52
|
if (initialized || !policy || !revocation) return;
|
|
@@ -92,6 +98,7 @@
|
|
|
92
98
|
await reconcileRecoveryState();
|
|
93
99
|
await settleTerminalConsent();
|
|
94
100
|
await armLegacyGuard();
|
|
101
|
+
await finishManualRuntimeReload();
|
|
95
102
|
await publishView();
|
|
96
103
|
const updateState = await getUpdateState();
|
|
97
104
|
if (['staged', 'waiting_for_idle'].includes(updateState.state)) {
|
|
@@ -129,6 +136,8 @@
|
|
|
129
136
|
return postponeUpdate();
|
|
130
137
|
case 'codex-overleaf/consent-update-dismiss':
|
|
131
138
|
return dismissCompletedUpdate();
|
|
139
|
+
case 'codex-overleaf/consent-update-reload':
|
|
140
|
+
return reloadInstalledRuntime();
|
|
132
141
|
default:
|
|
133
142
|
throw codedError('unknown_update_action', 'Unknown update action.');
|
|
134
143
|
}
|
|
@@ -648,11 +657,86 @@
|
|
|
648
657
|
}
|
|
649
658
|
|
|
650
659
|
async function getView() {
|
|
651
|
-
|
|
652
|
-
|
|
660
|
+
let [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
661
|
+
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId;
|
|
662
|
+
const runtime = busy ? { state: 'transaction_active' } : await readRuntimeStatus();
|
|
663
|
+
if (runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
664
|
+
policy.compareStableVersions(state.latestVersion || state.currentVersion, runtime.installedVersion) <= 0) {
|
|
665
|
+
state = await setUpdateState({ ...state, state: 'idle', currentVersion: runtime.installedVersion,
|
|
666
|
+
latestVersion: runtime.installedVersion, code: '', message: '', transactionId: '', blocker: '', blockers: [] });
|
|
667
|
+
}
|
|
668
|
+
const view = policy.deriveViewModel(state, consent, {
|
|
653
669
|
currentVersion: currentVersion(),
|
|
654
670
|
now: Date.now()
|
|
655
671
|
});
|
|
672
|
+
view.runtime = runtime;
|
|
673
|
+
const pending = (await chrome.storage.local.get(MANUAL_RELOAD_TABS_KEY))?.[MANUAL_RELOAD_TABS_KEY];
|
|
674
|
+
const pagesPending = runtime.state === 'aligned' && pending?.targetVersion === runtime.installedVersion && pending.tabIds?.length;
|
|
675
|
+
if (runtime.state === 'reload_required' || pagesPending) {
|
|
676
|
+
view.showPanel = true;
|
|
677
|
+
view.state = { ...state, state: pagesPending ? 'reload_tabs_required' : 'reload_required',
|
|
678
|
+
currentVersion: runtime.extensionVersion, latestVersion: runtime.installedVersion,
|
|
679
|
+
code: 'update_reload_required', message: runtimeReloadMessage };
|
|
680
|
+
view.badge = { text: 'UP', color: '#3578bd' };
|
|
681
|
+
}
|
|
682
|
+
return view;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async function readRuntimeStatus(force = false) {
|
|
686
|
+
if (!root.CodexOverleafUpdateRuntimeIdentity) return { state: 'unknown' };
|
|
687
|
+
if (!force && runtimeStatus && Date.now() - runtimeStatusAt < 5000) return runtimeStatus;
|
|
688
|
+
if (runtimeStatusRead) return runtimeStatusRead;
|
|
689
|
+
runtimeStatusRead = withTimeout(requestNative('update.status'), 4000,
|
|
690
|
+
codedError('update_status_timeout', 'Installed version inspection timed out.')).then(status => {
|
|
691
|
+
runtimeStatusAt = Date.now();
|
|
692
|
+
runtimeStatus = root.CodexOverleafUpdateRuntimeIdentity.inspectInstalledRuntime(status, {
|
|
693
|
+
extensionVersion: chrome.runtime.getManifest().version, runtimeVersion: currentVersion()
|
|
694
|
+
});
|
|
695
|
+
return runtimeStatus;
|
|
696
|
+
}).catch(() => ({ state: 'unknown' })).finally(() => { runtimeStatusRead = null; });
|
|
697
|
+
return runtimeStatusRead;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function requireReloadSafePoint() {
|
|
701
|
+
const tabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES });
|
|
702
|
+
const probes = await Promise.all(tabs.filter(isUsableEditorTab).map(tab => probeTabIdle(tab.id)));
|
|
703
|
+
const nativeGate = await requestNative('update.canApply').then(result => ({ ok: true, result }))
|
|
704
|
+
.catch(error => ({ ok: false, error: safeError(error) }));
|
|
705
|
+
const blockers = root.CodexOverleafUpdateStatus?.collectBlockers(probes, nativeGate) || ['busy'];
|
|
706
|
+
if (nativeBridge?.getPendingState?.().executionRequests > 0) blockers.push('background_execution_pending');
|
|
707
|
+
if (blockers.length) {
|
|
708
|
+
runtimeReloadMessage = 'Reload is waiting for Overleaf to be saved and idle (' + blockers.join(', ') + ').';
|
|
709
|
+
throw codedError('update_reload_busy', runtimeReloadMessage);
|
|
710
|
+
}
|
|
711
|
+
runtimeReloadMessage = '';
|
|
712
|
+
return tabs.filter(tab => Number.isInteger(tab.id) && !tab.discarded && tab.status !== 'unloaded');
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async function reloadInstalledRuntime() {
|
|
716
|
+
const runtime = await readRuntimeStatus(true);
|
|
717
|
+
if (runtime.state === 'aligned') { await finishManualRuntimeReload(); return getView(); }
|
|
718
|
+
if (runtime.state !== 'reload_required') throw codedError('update_reload_unavailable', 'No verified installed update is awaiting reload.');
|
|
719
|
+
const tabs = await requireReloadSafePoint();
|
|
720
|
+
await chrome.storage.local.set({ [MANUAL_RELOAD_TABS_KEY]: {
|
|
721
|
+
targetVersion: runtime.installedVersion, tabIds: tabs.map(tab => tab.id)
|
|
722
|
+
} });
|
|
723
|
+
setTimeout(() => chrome.runtime.reload(), 0);
|
|
724
|
+
return getView();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
async function finishManualRuntimeReload() {
|
|
728
|
+
const pending = (await chrome.storage.local.get(MANUAL_RELOAD_TABS_KEY))?.[MANUAL_RELOAD_TABS_KEY];
|
|
729
|
+
if (!pending || !Array.isArray(pending.tabIds)) return;
|
|
730
|
+
const runtime = await readRuntimeStatus(true);
|
|
731
|
+
if (runtime.state !== 'aligned' || runtime.installedVersion !== pending.targetVersion) return;
|
|
732
|
+
let tabs;
|
|
733
|
+
try { tabs = await requireReloadSafePoint(); } catch (_error) { return; }
|
|
734
|
+
const remaining = [];
|
|
735
|
+
for (const tab of tabs.filter(tab => pending.tabIds.includes(tab.id))) {
|
|
736
|
+
try { await chrome.tabs.reload(tab.id); } catch (_error) { remaining.push(tab.id); }
|
|
737
|
+
}
|
|
738
|
+
if (remaining.length) await chrome.storage.local.set({ [MANUAL_RELOAD_TABS_KEY]: { ...pending, tabIds: remaining } });
|
|
739
|
+
else await chrome.storage.local.remove(MANUAL_RELOAD_TABS_KEY);
|
|
656
740
|
}
|
|
657
741
|
|
|
658
742
|
async function publishView() {
|
|
@@ -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
|
|
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,
|
|
@@ -417,6 +418,8 @@
|
|
|
417
418
|
const runTimelineView = Modules.RunTimelineView.create({
|
|
418
419
|
RunGuidanceView: Modules.RunGuidanceView,
|
|
419
420
|
RunResultActions: runResultActions,
|
|
421
|
+
RunScrollLayout: Modules.RunScrollLayout,
|
|
422
|
+
RunFailureNotice: Modules.RunFailureNotice,
|
|
420
423
|
tr,
|
|
421
424
|
tx,
|
|
422
425
|
getLocale,
|
|
@@ -664,6 +667,7 @@
|
|
|
664
667
|
const providerSettingsCoordinator = Modules.ProviderSettingsCoordinator.create({
|
|
665
668
|
ProviderProfiles: Modules.ProviderProfiles,
|
|
666
669
|
ProviderSettingsDialog: Modules.ProviderSettingsDialog,
|
|
670
|
+
hasCurrentProject: () => Boolean(getCurrentProjectId()),
|
|
667
671
|
tx,
|
|
668
672
|
sendBackgroundNative,
|
|
669
673
|
document,
|
|
@@ -833,6 +837,7 @@
|
|
|
833
837
|
});
|
|
834
838
|
|
|
835
839
|
let panel = null;
|
|
840
|
+
let probeStatusSnapshot = null;
|
|
836
841
|
let panelRendererInstance = null;
|
|
837
842
|
let sessionPanelInstance = null;
|
|
838
843
|
let settingsPanelInstance = null;
|
|
@@ -840,6 +845,7 @@
|
|
|
840
845
|
let composerPanelInstance = null;
|
|
841
846
|
let state = null;
|
|
842
847
|
let storageKey = LEGACY_STORAGE_KEY;
|
|
848
|
+
let globalPreferencesController = null;
|
|
843
849
|
let currentRunView = null;
|
|
844
850
|
let pendingRetryReplacement = null;
|
|
845
851
|
let runQueueScheduler = null;
|
|
@@ -975,9 +981,10 @@
|
|
|
975
981
|
});
|
|
976
982
|
|
|
977
983
|
async function init() {
|
|
984
|
+
await getGlobalPreferences().initialize();
|
|
978
985
|
await refreshAccountScopeId();
|
|
979
986
|
storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
|
|
980
|
-
state = normalizePanelState(await loadStoredState(), { restoreRunningRuns: true });
|
|
987
|
+
state = normalizePanelState(getGlobalPreferences().overlay(await loadStoredState()), { restoreRunningRuns: true });
|
|
981
988
|
initializeRunQueueScheduler();
|
|
982
989
|
recoverInterruptedRunJournals()
|
|
983
990
|
.then(() => applyStateToPanel())
|
|
@@ -999,6 +1006,7 @@
|
|
|
999
1006
|
recentProjects.mountDashboardShell();
|
|
1000
1007
|
}
|
|
1001
1008
|
applyStateToPanel();
|
|
1009
|
+
getGlobalPreferences().refreshView();
|
|
1002
1010
|
// Welcome-panel + write-guard: seed the lifecycle
|
|
1003
1011
|
// module-locals from the current URL, install the SPA route hook so
|
|
1004
1012
|
// future Overleaf navigations swap variants, and warm the account-scope
|
|
@@ -1026,7 +1034,7 @@
|
|
|
1026
1034
|
providerSettingsCoordinator.syncSessionProvider().catch(() => {});
|
|
1027
1035
|
syncOtWarmMirrorController().catch(error => {
|
|
1028
1036
|
updateOtStatusDisplay('unavailable');
|
|
1029
|
-
appendPlainLog(tx(`Failed to sync
|
|
1037
|
+
appendPlainLog(tx(`Failed to sync OT warm mirror: ${error.message}`, `同步 OT 预热镜像失败:${error.message}`));
|
|
1030
1038
|
});
|
|
1031
1039
|
await refreshProbe({ quiet: true });
|
|
1032
1040
|
}
|
|
@@ -1263,12 +1271,13 @@
|
|
|
1263
1271
|
button: panel.querySelector('[data-custom-instructions-settings]'),
|
|
1264
1272
|
callbacks: {
|
|
1265
1273
|
onBack: () => closeCustomInstructionsSettings(),
|
|
1266
|
-
onInputChange:
|
|
1274
|
+
onInputChange: event => persistPanelInputs(event),
|
|
1267
1275
|
onSkillsOpen: () => openSkillsView(),
|
|
1268
1276
|
onSkillsBack: () => closeSkillsView(),
|
|
1269
1277
|
onProvidersOpen: () => providerSettingsCoordinator.open(),
|
|
1270
1278
|
onClearAllHistory: () => clearAllHistoryWithConfirm(),
|
|
1271
1279
|
onHistoryOpen: () => renderAuditHistoryPanel(),
|
|
1280
|
+
onStorageOpen: () => refreshStorageUsageSummary(),
|
|
1272
1281
|
onHistoryFilter: () => applyAuditHistoryFilter(),
|
|
1273
1282
|
// Experimental OT mirror: a single visible switch whose click is
|
|
1274
1283
|
// intercepted so the confirm-before-enable flow still runs.
|
|
@@ -1439,9 +1448,6 @@
|
|
|
1439
1448
|
function renderAttachmentPreviewList(attachments = [], container, options = {}) {
|
|
1440
1449
|
return composerAttachmentController.renderAttachmentPreviewList(attachments, container, options);
|
|
1441
1450
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
1451
|
function applyLocaleToPanel() {
|
|
1446
1452
|
if (!panel) {
|
|
1447
1453
|
return;
|
|
@@ -1530,6 +1536,17 @@
|
|
|
1530
1536
|
updateModelDisplay();
|
|
1531
1537
|
renderSessionList();
|
|
1532
1538
|
renderContextSelection();
|
|
1539
|
+
const probeStatus = panel.querySelector('[data-probe-status]');
|
|
1540
|
+
if (probeStatus?.dataset.refreshing === 'true') probeStatus.textContent = tr('refreshProbeLoading');
|
|
1541
|
+
else if (probeStatus && probeStatusSnapshot?.projectId === getCurrentProjectId()) {
|
|
1542
|
+
const { failed, probe, userInitiated } = probeStatusSnapshot;
|
|
1543
|
+
const text = failed ? tr('refreshProbeFailed') : formatProbeStatusBar(probe);
|
|
1544
|
+
probeStatus.textContent = userInitiated && !failed ? tr('refreshProbeDone', { status: text }) : text;
|
|
1545
|
+
if (!failed) updateExistingProbeNotice(probe);
|
|
1546
|
+
}
|
|
1547
|
+
updateSkillsEntrySummary();
|
|
1548
|
+
providerSettingsCoordinator.renderSummary();
|
|
1549
|
+
if (settingsPanelInstance?.container?.querySelector('[data-storage-card]')?.open) refreshStorageUsageSummary();
|
|
1533
1550
|
}
|
|
1534
1551
|
|
|
1535
1552
|
function setElementTitleAndAria(selector, title, ariaLabel) {
|
|
@@ -2388,7 +2405,7 @@
|
|
|
2388
2405
|
}
|
|
2389
2406
|
|
|
2390
2407
|
await saveState();
|
|
2391
|
-
applyStateToPanel();
|
|
2408
|
+
applyStateToPanel({ preserveScroll: true });
|
|
2392
2409
|
} catch (persistenceError) {
|
|
2393
2410
|
appendRunEvent({
|
|
2394
2411
|
title: tx('Codex result was generated, but saving local session history failed.', 'Codex 结果已生成,但保存本地会话记录失败。'),
|
|
@@ -4332,6 +4349,19 @@
|
|
|
4332
4349
|
return scopedPersistenceCoordinator;
|
|
4333
4350
|
}
|
|
4334
4351
|
|
|
4352
|
+
function getGlobalPreferences() {
|
|
4353
|
+
if (!globalPreferencesController) globalPreferencesController = Modules.GlobalPreferencesController.create({
|
|
4354
|
+
chromeApi: chrome, getState: () => state, setState: next => { state = next; }, getPanel: () => panel,
|
|
4355
|
+
applyTheme: applyPanelTheme, applyLocale: () => { applyLocaleToPanel(); refreshDashboardSoon(); },
|
|
4356
|
+
onPreloadChange: enabled => contextTrayController.scheduleContextPrefetch({ enabled }),
|
|
4357
|
+
onSkillsChange: () => { renderLocalSkillList(); updateSkillsEntrySummary(); },
|
|
4358
|
+
onError: error => showPluginToast(tx(
|
|
4359
|
+
`Global settings could not be saved or loaded: ${error.message}. Please retry.`,
|
|
4360
|
+
`全局设置未能保存或读取:${error.message}。请重试。`), { status: 'failed', sticky: true })
|
|
4361
|
+
});
|
|
4362
|
+
return globalPreferencesController;
|
|
4363
|
+
}
|
|
4364
|
+
|
|
4335
4365
|
async function reloadProjectRunHistory(newProjectId) {
|
|
4336
4366
|
// Rebind the storage key to the new project and reload the panel state
|
|
4337
4367
|
// from chrome.storage.local so the per-project session list reflects
|
|
@@ -4339,7 +4369,7 @@
|
|
|
4339
4369
|
try {
|
|
4340
4370
|
storageKey = getProjectStorageKey(LEGACY_STORAGE_KEY, window.location.href);
|
|
4341
4371
|
const reloaded = await loadStoredState();
|
|
4342
|
-
state = normalizePanelState(reloaded, { restoreRunningRuns: true });
|
|
4372
|
+
state = normalizePanelState(getGlobalPreferences().overlay(reloaded), { restoreRunningRuns: true });
|
|
4343
4373
|
applyStateToPanel();
|
|
4344
4374
|
} catch (_error) {
|
|
4345
4375
|
// Reload failures fall back to the in-memory state; the next saveState
|
|
@@ -4925,6 +4955,7 @@
|
|
|
4925
4955
|
}
|
|
4926
4956
|
|
|
4927
4957
|
async function refreshProbe(options = {}) {
|
|
4958
|
+
const projectId = getCurrentProjectId();
|
|
4928
4959
|
const userInitiated = options.userInitiated === true;
|
|
4929
4960
|
if (userInitiated) {
|
|
4930
4961
|
setRefreshProbeLoading(true);
|
|
@@ -4940,6 +4971,8 @@
|
|
|
4940
4971
|
const probe = await callPageBridge('probe', {
|
|
4941
4972
|
manualOverride: state?.requireReviewing === false
|
|
4942
4973
|
});
|
|
4974
|
+
if (projectId !== getCurrentProjectId()) return null;
|
|
4975
|
+
probeStatusSnapshot = { projectId, probe, userInitiated };
|
|
4943
4976
|
const status = panel?.querySelector('[data-probe-status]');
|
|
4944
4977
|
if (status) {
|
|
4945
4978
|
status.textContent = userInitiated
|
|
@@ -4958,6 +4991,8 @@
|
|
|
4958
4991
|
if (!userInitiated) {
|
|
4959
4992
|
throw error;
|
|
4960
4993
|
}
|
|
4994
|
+
if (projectId !== getCurrentProjectId()) return null;
|
|
4995
|
+
probeStatusSnapshot = { projectId, failed: true };
|
|
4961
4996
|
const status = panel?.querySelector('[data-probe-status]');
|
|
4962
4997
|
if (status) {
|
|
4963
4998
|
status.textContent = tr('refreshProbeFailed');
|
|
@@ -5077,8 +5112,8 @@
|
|
|
5077
5112
|
return fileItems.length
|
|
5078
5113
|
? fileItems.join(', ')
|
|
5079
5114
|
: tx(
|
|
5080
|
-
'@whole-project (default); add @
|
|
5081
|
-
'@whole-project
|
|
5115
|
+
'@whole-project (default); choose files or add @compile-log',
|
|
5116
|
+
'@whole-project(默认);可选择文件或添加 @compile-log'
|
|
5082
5117
|
);
|
|
5083
5118
|
}
|
|
5084
5119
|
|
|
@@ -5911,6 +5946,8 @@
|
|
|
5911
5946
|
}
|
|
5912
5947
|
|
|
5913
5948
|
async function persistPanelInputs(event) {
|
|
5949
|
+
const globalWrite = getGlobalPreferences().handleInput(event);
|
|
5950
|
+
if (globalWrite) return globalWrite;
|
|
5914
5951
|
// Typing in the task box is the hottest write path: a full saveState per
|
|
5915
5952
|
// keystroke serializes every session/run/event and rewrites IndexedDB, so
|
|
5916
5953
|
// panels get slower as history grows. Drafts take the debounced saver;
|
|
@@ -6009,29 +6046,8 @@
|
|
|
6009
6046
|
setCustomInstructionsForProject(projectId, customInstructionsInput.value);
|
|
6010
6047
|
}
|
|
6011
6048
|
setGovernanceRulesForCurrentProject(readGovernanceRulesFromSettings());
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
setSkillLoadingSettings(newSettings);
|
|
6015
|
-
// Theme is a global preference; read it from the settings select (which
|
|
6016
|
-
// lives inside the panel, like the other settings inputs), persist it on
|
|
6017
|
-
// state, and apply immediately. applyPanelTheme normalizes the value.
|
|
6018
|
-
const themePref = panel?.querySelector('[data-theme-select]')?.value || 'dark';
|
|
6019
|
-
if (state) {
|
|
6020
|
-
state.theme = themePref;
|
|
6021
|
-
}
|
|
6022
|
-
applyPanelTheme(themePref);
|
|
6023
|
-
// Language is a global UI preference (moved here from the diagnostics
|
|
6024
|
-
// menu); re-translate the panel when it changes.
|
|
6025
|
-
const languagePref = panel?.querySelector('[data-language-select]')?.value;
|
|
6026
|
-
if (languagePref && state && languagePref !== getLocale()) {
|
|
6027
|
-
state.locale = i18n?.normalizeLocale?.(languagePref) || languagePref;
|
|
6028
|
-
applyLocaleToPanel();
|
|
6029
|
-
}
|
|
6030
|
-
// Re-render skill list when master toggle changes so per-skill toggles update their disabled state.
|
|
6031
|
-
if (prevOverleafSkills !== getSkillLoadingSettings().loadCodexOverleafSkills) {
|
|
6032
|
-
renderLocalSkillList();
|
|
6033
|
-
}
|
|
6034
|
-
updateSkillsEntrySummary();
|
|
6049
|
+
// Global controls are handled as field patches by getGlobalPreferences;
|
|
6050
|
+
// unrelated session autosaves must not read stale settings controls.
|
|
6035
6051
|
}
|
|
6036
6052
|
}
|
|
6037
6053
|
|
|
@@ -6070,7 +6086,7 @@
|
|
|
6070
6086
|
saveStateSoon();
|
|
6071
6087
|
}
|
|
6072
6088
|
|
|
6073
|
-
function applyStateToPanel() {
|
|
6089
|
+
function applyStateToPanel(options = {}) {
|
|
6074
6090
|
applyPanelTheme(getThemePreference());
|
|
6075
6091
|
if ((state.providerId || 'builtin') === 'builtin' && !modelSelectHasOption(state.model)) {
|
|
6076
6092
|
renderModelOptions(getModelCatalog().FALLBACK_MODELS, state.model);
|
|
@@ -6106,7 +6122,7 @@
|
|
|
6106
6122
|
syncModeControls();
|
|
6107
6123
|
applySessionLabel();
|
|
6108
6124
|
renderSessionList();
|
|
6109
|
-
renderRunHistory();
|
|
6125
|
+
renderRunHistory(options);
|
|
6110
6126
|
renderContextSelection();
|
|
6111
6127
|
renderContextSummary();
|
|
6112
6128
|
// v1.8.0 C4: refresh recovery — small attachments persisted in state are
|
|
@@ -359,7 +359,7 @@
|
|
|
359
359
|
if (!options.collectOnly) {
|
|
360
360
|
showDiagnosticsLoading(tr('diagnosticsOtTitle'), tr('diagnosticsLoading'));
|
|
361
361
|
}
|
|
362
|
-
// OT warm mirror is
|
|
362
|
+
// OT warm mirror is opt-in and off by default. When it is disabled
|
|
363
363
|
// there is nothing live to probe, so report a clean, healthy "disabled"
|
|
364
364
|
// state instead of querying (then warning about) absent OT metadata — or
|
|
365
365
|
// degrading to the generic "could not run" error if a probe throws. A
|
|
@@ -374,6 +374,14 @@
|
|
|
374
374
|
nextStep: '',
|
|
375
375
|
technical: ''
|
|
376
376
|
};
|
|
377
|
+
const mirrorState = getOtWarmMirrorState?.() || {};
|
|
378
|
+
if (mirrorState.failureActive && mirrorState.lastFailure) {
|
|
379
|
+
const failure = mirrorState.lastFailure;
|
|
380
|
+
disabledResult.status = 'warning';
|
|
381
|
+
disabledResult.summary = tr(otWarmMirrorController.getFailureMessageKey(failure.code));
|
|
382
|
+
disabledResult.nextStep = tr('otFailureNextStep');
|
|
383
|
+
disabledResult.technical = `lastErrorCode: ${failure.code}\nphase: ${failure.phase}\nfailedAt: ${new Date(failure.at).toISOString()}`;
|
|
384
|
+
}
|
|
377
385
|
if (options.collectOnly) {
|
|
378
386
|
return disabledResult;
|
|
379
387
|
}
|
|
@@ -434,6 +442,7 @@
|
|
|
434
442
|
const queuedEventCount = normalizeOtDiagnosticsCount(otStatus?.queuedEventCount);
|
|
435
443
|
const lastEventAt = formatOtDiagnosticValue(otStatus?.lastEventAt, tr('noneValue'));
|
|
436
444
|
const warmMirrorState = getOtWarmMirrorState() || {};
|
|
445
|
+
const failureActive = warmMirrorState.failureActive === true;
|
|
437
446
|
const lastOtPatchAt = formatOtDiagnosticValue(mirrorStatus?.lastOtPatchAt || warmMirrorState.lastPatchAt, tr('noneValue'));
|
|
438
447
|
const lastOtErrorCode = formatOtDiagnosticValue(mirrorStatus?.lastOtErrorCode || warmMirrorState.lastErrorCode, tr('noneValue'));
|
|
439
448
|
const lastErrorCode = formatOtDiagnosticValue(otStatus?.lastErrorCode || otStatus?.reason || otStatus?.error, tr('noneValue'));
|
|
@@ -445,13 +454,14 @@
|
|
|
445
454
|
const bridgeFailed = otStatus?.ok === false;
|
|
446
455
|
const focusFiles = getActiveFocusFiles();
|
|
447
456
|
const otWarmStart = otWarmMirrorController?.canUseOtWarmStart?.({ enabled, focusFiles, mirrorStatus }) || { ok: false };
|
|
448
|
-
const fallback = !enabled || statusValue !== 'observing' || bridgeFailed || otWarmStart?.ok !== true;
|
|
457
|
+
const fallback = !enabled || failureActive || statusValue !== 'observing' || bridgeFailed || otWarmStart?.ok !== true;
|
|
449
458
|
|
|
450
459
|
return {
|
|
451
460
|
title: tr('diagnosticsOtTitle'),
|
|
452
461
|
subtitle: tr('diagnosticsOtSubtitle'),
|
|
453
462
|
status: enabled && !fallback && !bridgeFailed ? 'completed' : (enabled || bridgeFailed ? 'warning' : 'completed'),
|
|
454
|
-
summary: tr(
|
|
463
|
+
summary: tr(failureActive ? otWarmMirrorController.getFailureMessageKey(warmMirrorState.lastErrorCode)
|
|
464
|
+
: enabled ? 'diagnosticsOtSummaryEnabled' : 'diagnosticsOtSummaryDisabled'),
|
|
455
465
|
bullets: [
|
|
456
466
|
`${tr('otStatus')}: ${formatOtStatusLabel(statusValue)}`,
|
|
457
467
|
`${tr('otFreshFiles')}: ${otFreshFileCount}`,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<small data-i18n="diagnosticsSnapshotSubtitle">Full project, assets, read source</small>
|
|
39
39
|
</button>
|
|
40
40
|
<button type="button" data-diagnostics-ot>
|
|
41
|
-
<span data-i18n="diagnosticsOtTitle">Check
|
|
41
|
+
<span data-i18n="diagnosticsOtTitle">Check OT Mirror</span>
|
|
42
42
|
<small data-i18n="diagnosticsOtSubtitle">Status, fresh files, fallback</small>
|
|
43
43
|
</button>
|
|
44
44
|
<button type="button" data-diagnostics-export>
|