codex-overleaf-link 2.2.1 → 2.3.1
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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +8 -6
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +17 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
(function initCodexOverleafRunTimelineView() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const RunGuidanceView = window.CodexOverleafRunGuidanceView;
|
|
5
4
|
// Run-timeline view — the timeline render pipeline carved out of
|
|
6
5
|
// contentRuntime.js (v1.4.6 structural-debt phase 2): the scroll engine +
|
|
7
6
|
// jump-to-latest button, the live-elapsed tick and collapsed-header summary,
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
// that owns it.
|
|
14
13
|
function create(deps = {}) {
|
|
15
14
|
const {
|
|
15
|
+
RunGuidanceView,
|
|
16
16
|
tr,
|
|
17
17
|
tx,
|
|
18
18
|
getLocale,
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
showNativeSetupGuidance,
|
|
41
41
|
openProjectFileForFailure,
|
|
42
42
|
openStorageSettings,
|
|
43
|
-
trackedChangeInFlight
|
|
43
|
+
trackedChangeInFlight,
|
|
44
|
+
projectRunSettlement
|
|
44
45
|
} = deps;
|
|
45
46
|
|
|
46
47
|
let logAutoFollow = true;
|
|
@@ -273,6 +274,9 @@
|
|
|
273
274
|
if (status === 'failed') {
|
|
274
275
|
return tr('processedFailed', { elapsed });
|
|
275
276
|
}
|
|
277
|
+
if (status === 'rejected' || status === 'cancelled') {
|
|
278
|
+
return tr('processedCancelled', { elapsed });
|
|
279
|
+
}
|
|
276
280
|
if (status === 'running') {
|
|
277
281
|
return tr('processing', { elapsed });
|
|
278
282
|
}
|
|
@@ -410,6 +414,9 @@
|
|
|
410
414
|
if (run.startedAt && run.finishedAt) {
|
|
411
415
|
return formatProcessedSummary(run.status || 'completed', new Date(run.finishedAt).getTime() - new Date(run.startedAt).getTime());
|
|
412
416
|
}
|
|
417
|
+
if (run.status === 'rejected' || run.status === 'cancelled') {
|
|
418
|
+
return tx('Cancelled', '已取消');
|
|
419
|
+
}
|
|
413
420
|
return run.status === 'failed' ? tx('Failed', '处理失败') : tx('Done', '已处理');
|
|
414
421
|
}
|
|
415
422
|
|
|
@@ -884,8 +891,8 @@
|
|
|
884
891
|
return;
|
|
885
892
|
}
|
|
886
893
|
|
|
887
|
-
const
|
|
888
|
-
if (!
|
|
894
|
+
const projection = projectRunSettlement(run);
|
|
895
|
+
if (!projection.canUndo && run.undoStatus !== 'applied') {
|
|
889
896
|
button.hidden = true;
|
|
890
897
|
return;
|
|
891
898
|
}
|
|
@@ -912,13 +919,14 @@
|
|
|
912
919
|
// same executable state as `pending`.
|
|
913
920
|
function configureLifecycleUndoButton(button, run) {
|
|
914
921
|
const status = run.trackedChangeStatus || '';
|
|
922
|
+
const projection = projectRunSettlement(run);
|
|
915
923
|
// §7 settlement matrix: needs_review keeps BOTH controls visible AND
|
|
916
924
|
// actionable. Branch placed before the terminal branches so a
|
|
917
925
|
// needs_review run is never treated as terminal.
|
|
918
926
|
if (status === 'needs_review') {
|
|
919
927
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
920
928
|
button.hidden = false;
|
|
921
|
-
button.disabled = inFlight === 'reject' || inFlight === 'accept';
|
|
929
|
+
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
922
930
|
button.textContent = tr('undoRun');
|
|
923
931
|
button.title = tr('undoRunTitle');
|
|
924
932
|
button.addEventListener('click', event => {
|
|
@@ -949,7 +957,7 @@
|
|
|
949
957
|
}
|
|
950
958
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
951
959
|
button.hidden = false;
|
|
952
|
-
button.disabled = inFlight === 'reject' || inFlight === 'accept';
|
|
960
|
+
button.disabled = !projection.canUndo || inFlight === 'reject' || inFlight === 'accept';
|
|
953
961
|
button.textContent = tr('undoRun');
|
|
954
962
|
button.title = tr('undoRunTitle');
|
|
955
963
|
button.addEventListener('click', event => {
|
|
@@ -980,13 +988,14 @@
|
|
|
980
988
|
}
|
|
981
989
|
|
|
982
990
|
const status = run.trackedChangeStatus || '';
|
|
991
|
+
const projection = projectRunSettlement(run);
|
|
983
992
|
// §7 settlement matrix: needs_review keeps BOTH controls visible AND
|
|
984
993
|
// actionable. It remains an internal retryable proof state, while the
|
|
985
994
|
// primary button label stays in the same executable state as `pending`.
|
|
986
995
|
if (status === 'needs_review') {
|
|
987
996
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
988
997
|
button.hidden = false;
|
|
989
|
-
button.disabled = inFlight === 'accept' || inFlight === 'reject';
|
|
998
|
+
button.disabled = !projection.canAccept || inFlight === 'accept' || inFlight === 'reject';
|
|
990
999
|
if (inFlight === 'accept') {
|
|
991
1000
|
button.textContent = tr('runAcceptTrackedConfirming');
|
|
992
1001
|
button.title = tr('runAcceptTrackedConfirming');
|
|
@@ -1020,7 +1029,7 @@
|
|
|
1020
1029
|
|
|
1021
1030
|
const inFlight = trackedChangeInFlight.get(run.id);
|
|
1022
1031
|
button.hidden = false;
|
|
1023
|
-
button.disabled = inFlight === 'accept' || inFlight === 'reject';
|
|
1032
|
+
button.disabled = !projection.canAccept || inFlight === 'accept' || inFlight === 'reject';
|
|
1024
1033
|
if (inFlight === 'accept') {
|
|
1025
1034
|
button.textContent = tr('runAcceptTrackedConfirming');
|
|
1026
1035
|
button.title = tr('runAcceptTrackedConfirming');
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(
|
|
4
|
+
require('./scopedPersistencePanelState'),
|
|
5
|
+
require('./scopedPersistenceDetachedCommit')
|
|
6
|
+
);
|
|
7
|
+
} else {
|
|
8
|
+
root.CodexOverleafModuleRegistry.define(
|
|
9
|
+
'ScopedPersistenceCoordinator',
|
|
10
|
+
['ScopedPersistencePanelState', 'ScopedPersistenceDetachedCommit'],
|
|
11
|
+
factory
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (PanelState, DetachedCommit) {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
function createController(options) {
|
|
18
|
+
options = options || {};
|
|
19
|
+
var Transaction = options.Transaction;
|
|
20
|
+
if (!Transaction || typeof Transaction.createCoordinator !== 'function') return null;
|
|
21
|
+
var writerId = getStableWriterId(options.sessionStorage);
|
|
22
|
+
var coordinator = Transaction.createCoordinator({
|
|
23
|
+
adapter: Transaction.createBrowserAdapter({ chromeApi: options.chromeApi }),
|
|
24
|
+
writerId: writerId
|
|
25
|
+
});
|
|
26
|
+
var commitDetached = (options.DetachedCommit || DetachedCommit)?.create?.({
|
|
27
|
+
Transaction: Transaction, chromeApi: options.chromeApi, writerId: writerId
|
|
28
|
+
});
|
|
29
|
+
var activeScope = null;
|
|
30
|
+
|
|
31
|
+
function scope(projectId) {
|
|
32
|
+
var accountScopeId = '';
|
|
33
|
+
try {
|
|
34
|
+
accountScopeId = typeof options.getAccountScopeId === 'function'
|
|
35
|
+
? options.getAccountScopeId() || ''
|
|
36
|
+
: '';
|
|
37
|
+
} catch (_error) {
|
|
38
|
+
accountScopeId = '';
|
|
39
|
+
}
|
|
40
|
+
return { accountScopeId: accountScopeId, projectId: projectId };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
async beginHydration(projectId) {
|
|
45
|
+
var nextScope = scope(projectId);
|
|
46
|
+
if (!nextScope.accountScopeId) {
|
|
47
|
+
activeScope = null;
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
var token = await coordinator.beginHydration(nextScope);
|
|
51
|
+
if (coordinator.isCurrent(token)) activeScope = nextScope;
|
|
52
|
+
return token;
|
|
53
|
+
},
|
|
54
|
+
isCurrent: coordinator.isCurrent,
|
|
55
|
+
async commit(projectId, commitOptions, action) {
|
|
56
|
+
var nextScope = scope(projectId);
|
|
57
|
+
if (!nextScope.accountScopeId) {
|
|
58
|
+
return { ok: false, reason: 'account_scope_unavailable' };
|
|
59
|
+
}
|
|
60
|
+
if (!activeScope
|
|
61
|
+
|| activeScope.accountScopeId !== nextScope.accountScopeId
|
|
62
|
+
|| activeScope.projectId !== nextScope.projectId) {
|
|
63
|
+
var hydration = await coordinator.beginHydration(nextScope);
|
|
64
|
+
if (!coordinator.isCurrent(hydration)) {
|
|
65
|
+
return { ok: false, reason: 'stale_view' };
|
|
66
|
+
}
|
|
67
|
+
activeScope = nextScope;
|
|
68
|
+
}
|
|
69
|
+
var view = await coordinator.captureCommitView(nextScope, {
|
|
70
|
+
detached: Boolean(commitOptions && commitOptions.detached)
|
|
71
|
+
});
|
|
72
|
+
return coordinator.commit(view, action, {
|
|
73
|
+
rebase: !commitOptions || commitOptions.rebase !== false,
|
|
74
|
+
queueMutation: commitOptions && commitOptions.queueMutation,
|
|
75
|
+
queueMutations: commitOptions && commitOptions.queueMutations,
|
|
76
|
+
sessionTombstones: commitOptions && commitOptions.sessionTombstones
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
commitDetached: commitDetached
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function getStableWriterId(storage) {
|
|
83
|
+
var key = 'codex-overleaf-scoped-writer-id';
|
|
84
|
+
try {
|
|
85
|
+
var existing = storage?.getItem?.(key);
|
|
86
|
+
if (existing) return existing;
|
|
87
|
+
var created = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
88
|
+
? crypto.randomUUID()
|
|
89
|
+
: 'writer-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
|
|
90
|
+
storage?.setItem?.(key, created);
|
|
91
|
+
return created;
|
|
92
|
+
} catch (_error) {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
createController: createController,
|
|
98
|
+
persistPanelState: PanelState?.persistPanelState || null
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
var api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafScopedPersistenceDetachedCommit = api;
|
|
5
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(options) {
|
|
9
|
+
options = options || {};
|
|
10
|
+
var Transaction = options.Transaction;
|
|
11
|
+
if (!Transaction
|
|
12
|
+
|| typeof Transaction.createBrowserAdapter !== 'function'
|
|
13
|
+
|| typeof Transaction.createCoordinator !== 'function') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
var adapter = Transaction.createBrowserAdapter({ chromeApi: options.chromeApi });
|
|
17
|
+
return async function commitDetached(projectId, accountScopeId, commitOptions, action) {
|
|
18
|
+
var nextScope = {
|
|
19
|
+
accountScopeId: String(accountScopeId || '').trim(),
|
|
20
|
+
projectId: String(projectId || '').trim()
|
|
21
|
+
};
|
|
22
|
+
if (!nextScope.accountScopeId) {
|
|
23
|
+
return { ok: false, reason: 'account_scope_unavailable' };
|
|
24
|
+
}
|
|
25
|
+
if (!nextScope.projectId) {
|
|
26
|
+
return { ok: false, reason: 'project_scope_unavailable' };
|
|
27
|
+
}
|
|
28
|
+
var coordinator = Transaction.createCoordinator({
|
|
29
|
+
adapter: adapter,
|
|
30
|
+
writerId: options.writerId
|
|
31
|
+
});
|
|
32
|
+
var hydration = await coordinator.beginHydration(nextScope);
|
|
33
|
+
if (!coordinator.isCurrent(hydration)) {
|
|
34
|
+
return { ok: false, reason: 'stale_view' };
|
|
35
|
+
}
|
|
36
|
+
var view = await coordinator.captureCommitView(nextScope, { detached: true });
|
|
37
|
+
return coordinator.commit(view, action, {
|
|
38
|
+
rebase: !commitOptions || commitOptions.rebase !== false,
|
|
39
|
+
queueMutation: commitOptions && commitOptions.queueMutation,
|
|
40
|
+
queueMutations: commitOptions && commitOptions.queueMutations,
|
|
41
|
+
sessionTombstones: commitOptions && commitOptions.sessionTombstones
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Object.freeze({ create: create });
|
|
47
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
var api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafScopedPersistencePanelState = api;
|
|
5
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function cloneSnapshot(value) {
|
|
9
|
+
if (!value || typeof value !== 'object') {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (typeof structuredClone === 'function') {
|
|
13
|
+
return structuredClone(value);
|
|
14
|
+
}
|
|
15
|
+
return JSON.parse(JSON.stringify(value));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function persistPanelState(input) {
|
|
19
|
+
var state = cloneSnapshot(input.state || {});
|
|
20
|
+
var compactState = cloneSnapshot(input.compactState || {});
|
|
21
|
+
var projectId = input.projectId;
|
|
22
|
+
var Migration = input.Migration;
|
|
23
|
+
var StorageDb = input.StorageDb;
|
|
24
|
+
var scope = input.persistenceContext?.scope || {};
|
|
25
|
+
var accountScopeId = typeof scope.accountScopeId === 'string'
|
|
26
|
+
? scope.accountScopeId.trim()
|
|
27
|
+
: '';
|
|
28
|
+
if (!accountScopeId) {
|
|
29
|
+
throw Object.assign(new Error('Scoped persistence requires an account identity.'), {
|
|
30
|
+
code: 'account_scope_unavailable'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (scope.projectId !== projectId) {
|
|
34
|
+
throw Object.assign(new Error('Scoped persistence project changed before commit.'), {
|
|
35
|
+
code: 'stale_view'
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
var latestPrefs = typeof Migration.loadPrefs === 'function'
|
|
39
|
+
? await Migration.loadPrefs(accountScopeId, projectId)
|
|
40
|
+
: {};
|
|
41
|
+
var prefs = Object.assign(
|
|
42
|
+
{},
|
|
43
|
+
latestPrefs && typeof latestPrefs === 'object' ? latestPrefs : {},
|
|
44
|
+
StorageDb.extractLightweightPrefs(compactState, projectId)
|
|
45
|
+
);
|
|
46
|
+
prefs.activeSessionByProject = StorageDb.buildActiveSessionByProject(
|
|
47
|
+
latestPrefs && latestPrefs.activeSessionByProject || {},
|
|
48
|
+
projectId,
|
|
49
|
+
compactState.activeSessionId || state.activeSessionId || ''
|
|
50
|
+
);
|
|
51
|
+
prefs.experimentalOtByProject = Object.assign(
|
|
52
|
+
{},
|
|
53
|
+
latestPrefs && latestPrefs.experimentalOtByProject || {},
|
|
54
|
+
{ [projectId]: input.normalizeExperimentalOtByProject(state.experimentalOtByProject)[projectId] === true }
|
|
55
|
+
);
|
|
56
|
+
prefs.governanceRulesByProject = Object.assign(
|
|
57
|
+
{},
|
|
58
|
+
latestPrefs && latestPrefs.governanceRulesByProject || {},
|
|
59
|
+
input.normalizeGovernanceRulesByProject(state.governanceRulesByProject)
|
|
60
|
+
);
|
|
61
|
+
var normalizedCustomProject = input.normalizeCustomInstructionsByProject({ [projectId]: '' });
|
|
62
|
+
var customProjectId = Object.keys(normalizedCustomProject)[0] || '';
|
|
63
|
+
var currentCustom = input.normalizeCustomInstructionsByProject(state.customInstructionsByProject);
|
|
64
|
+
prefs.customInstructionsByProject = Object.assign(
|
|
65
|
+
{},
|
|
66
|
+
latestPrefs && latestPrefs.customInstructionsByProject || {}
|
|
67
|
+
);
|
|
68
|
+
if (customProjectId) {
|
|
69
|
+
prefs.customInstructionsByProject[customProjectId] =
|
|
70
|
+
Object.prototype.hasOwnProperty.call(currentCustom, customProjectId)
|
|
71
|
+
? currentCustom[customProjectId]
|
|
72
|
+
: '';
|
|
73
|
+
}
|
|
74
|
+
await Migration.savePrefs(prefs, accountScopeId, projectId);
|
|
75
|
+
var persistenceMeta = input.persistenceContext?.nextMeta || {};
|
|
76
|
+
var queueTombstones = persistenceMeta.queueTombstones || {};
|
|
77
|
+
var sessionTombstones = persistenceMeta.sessionTombstones || {};
|
|
78
|
+
var deletedSessionIds = Array.from(new Set([
|
|
79
|
+
...(Array.isArray(input.deletedSessionIds) ? input.deletedSessionIds : []),
|
|
80
|
+
...Object.keys(sessionTombstones)
|
|
81
|
+
]));
|
|
82
|
+
var sessionRecords = (state.sessions || []).map(function (session) {
|
|
83
|
+
return StorageDb.buildSessionRecord(Object.assign({}, session, {
|
|
84
|
+
projectId: projectId,
|
|
85
|
+
accountScopeId: accountScopeId,
|
|
86
|
+
title: session.title || '',
|
|
87
|
+
titleSource: session.titleSource || 'auto',
|
|
88
|
+
codexThreadId: session.codexThreadId || '',
|
|
89
|
+
status: 'active',
|
|
90
|
+
focusFiles: Array.isArray(session.focusFiles) ? session.focusFiles : [],
|
|
91
|
+
createdAt: session.createdAt,
|
|
92
|
+
updatedAt: session.updatedAt,
|
|
93
|
+
pendingInputs: (Array.isArray(session.pendingInputs) ? session.pendingInputs : [])
|
|
94
|
+
.filter(function (item) { return !queueTombstones[item?.id]; })
|
|
95
|
+
}), { preserveRunActionPayload: true });
|
|
96
|
+
});
|
|
97
|
+
await input.SessionPersistence.writeSessions({
|
|
98
|
+
Migration: Migration,
|
|
99
|
+
StorageDb: StorageDb,
|
|
100
|
+
projectId: projectId,
|
|
101
|
+
accountScopeId: accountScopeId,
|
|
102
|
+
deletedSessionIds: deletedSessionIds,
|
|
103
|
+
sessionRecords: sessionRecords,
|
|
104
|
+
queueClaims: persistenceMeta.queueClaims || {},
|
|
105
|
+
queueTombstones: queueTombstones,
|
|
106
|
+
writerId: input.persistenceContext?.writerId || ''
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Object.freeze({ persistPanelState: persistPanelState });
|
|
111
|
+
});
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
(function initCodexOverleafSessionManager() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const SessionPanel = window.CodexOverleafSessionPanel;
|
|
5
|
-
const {
|
|
6
|
-
createSession,
|
|
7
|
-
deleteSession,
|
|
8
|
-
setActiveSession,
|
|
9
|
-
getActiveSession,
|
|
10
|
-
isDisplayableSession,
|
|
11
|
-
normalizePanelState,
|
|
12
|
-
deriveSessionTitle
|
|
13
|
-
} = window.CodexOverleafSessionState;
|
|
14
|
-
|
|
15
4
|
// Session manager — the session lifecycle + list/delete surface
|
|
16
5
|
// carved out of contentRuntime.js (v1.4.7 structural-debt phase 3). Code
|
|
17
6
|
// moved verbatim except that mutable runtime state is read through
|
|
@@ -19,6 +8,20 @@
|
|
|
19
8
|
// setState() (sessions are immutable-update: every mutation rebuilds state
|
|
20
9
|
// via the shared sessionState helpers and hands it back to the runtime).
|
|
21
10
|
function create(deps = {}) {
|
|
11
|
+
const {
|
|
12
|
+
SessionPanel,
|
|
13
|
+
SessionState,
|
|
14
|
+
SessionMenuView
|
|
15
|
+
} = deps;
|
|
16
|
+
const {
|
|
17
|
+
createSession,
|
|
18
|
+
deleteSession,
|
|
19
|
+
setActiveSession,
|
|
20
|
+
getActiveSession,
|
|
21
|
+
isDisplayableSession,
|
|
22
|
+
normalizePanelState,
|
|
23
|
+
deriveSessionTitle
|
|
24
|
+
} = SessionState;
|
|
22
25
|
const {
|
|
23
26
|
tr,
|
|
24
27
|
showPluginConfirm,
|
|
@@ -36,7 +39,7 @@
|
|
|
36
39
|
formatSessionTime
|
|
37
40
|
} = deps;
|
|
38
41
|
|
|
39
|
-
const sessionMenuView =
|
|
42
|
+
const sessionMenuView = SessionMenuView.create({
|
|
40
43
|
commitSessionRename,
|
|
41
44
|
deleteSessionWithConfirm,
|
|
42
45
|
document,
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
(function initCodexOverleafSessionPersistence() {
|
|
1
|
+
(function initCodexOverleafSessionPersistence(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(require('../shared/storageMigration'));
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('SessionPersistence', ['StorageMigration'], factory);
|
|
6
|
+
}
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function sessionPersistenceFactory(Migration) {
|
|
2
8
|
'use strict';
|
|
3
9
|
|
|
4
10
|
function migration() {
|
|
5
|
-
return
|
|
11
|
+
return Migration;
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
async function loadTombstones() {
|
|
@@ -21,11 +27,20 @@
|
|
|
21
27
|
function isVisibleRecord(record, deletedIds, accountScopeId) {
|
|
22
28
|
return Boolean(record)
|
|
23
29
|
&& !(deletedIds || []).includes(record.id)
|
|
24
|
-
&& (
|
|
30
|
+
&& Boolean(accountScopeId)
|
|
31
|
+
&& record.accountScopeId === accountScopeId;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
async function writeSessions(options = {}) {
|
|
28
35
|
const { StorageDb, projectId } = options;
|
|
36
|
+
const accountScopeId = typeof options.accountScopeId === 'string'
|
|
37
|
+
? options.accountScopeId.trim()
|
|
38
|
+
: '';
|
|
39
|
+
if (!accountScopeId) {
|
|
40
|
+
throw Object.assign(new Error('Session persistence requires an account identity.'), {
|
|
41
|
+
code: 'account_scope_unavailable'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
29
44
|
const Migration = options.Migration || migration();
|
|
30
45
|
const requested = (Array.isArray(options.deletedSessionIds) ? options.deletedSessionIds : [])
|
|
31
46
|
.filter(id => typeof id === 'string' && id);
|
|
@@ -34,8 +49,23 @@
|
|
|
34
49
|
: await Migration?.loadSessionTombstones?.() || {};
|
|
35
50
|
const deleted = new Set(requested);
|
|
36
51
|
for (const id of Migration?.getDeletedSessionIds?.(tombstones, projectId) || []) deleted.add(id);
|
|
37
|
-
const
|
|
38
|
-
|
|
52
|
+
const queueTombstones = options.queueTombstones && typeof options.queueTombstones === 'object'
|
|
53
|
+
? options.queueTombstones
|
|
54
|
+
: {};
|
|
55
|
+
const rawSessionRecords = (options.sessionRecords || [])
|
|
56
|
+
.filter(record => !deleted.has(record.id) && record.accountScopeId === accountScopeId);
|
|
57
|
+
const rawExistingSessions = (await StorageDb.getAllByIndex('sessions', 'projectId', projectId))
|
|
58
|
+
.filter(record => record?.accountScopeId === accountScopeId);
|
|
59
|
+
const rawExistingById = new Map(rawExistingSessions.map(record => [record.id, record]));
|
|
60
|
+
const sessionRecords = rawSessionRecords
|
|
61
|
+
.map(record => applyQueuePolicy(
|
|
62
|
+
record,
|
|
63
|
+
rawExistingById.get(record.id),
|
|
64
|
+
options,
|
|
65
|
+
queueTombstones
|
|
66
|
+
));
|
|
67
|
+
const existingSessions = rawExistingSessions
|
|
68
|
+
.map(record => applyQueuePolicy(record, record, options, queueTombstones));
|
|
39
69
|
const deletedExisting = existingSessions.filter(record => deleted.has(record.id));
|
|
40
70
|
if (deletedExisting.length) {
|
|
41
71
|
await Promise.all(deletedExisting.map(record => StorageDb.deleteRecord('sessions', record.id)));
|
|
@@ -48,15 +78,44 @@
|
|
|
48
78
|
if (!existing) return true;
|
|
49
79
|
return (Date.parse(record.updatedAt || '') || 0) >= (Date.parse(existing.updatedAt || '') || 0);
|
|
50
80
|
});
|
|
51
|
-
|
|
81
|
+
const cleanedExisting = existingSessions.filter((record, index) =>
|
|
82
|
+
record !== rawExistingSessions[index] && !writable.some(item => item.id === record.id)
|
|
83
|
+
);
|
|
84
|
+
if (cleanedExisting.length || writable.length) {
|
|
85
|
+
await StorageDb.putRecords('sessions', [...cleanedExisting, ...writable]);
|
|
86
|
+
}
|
|
52
87
|
return writable;
|
|
53
88
|
}
|
|
54
89
|
|
|
55
|
-
|
|
90
|
+
function applyQueuePolicy(record, existingRecord, options, queueTombstones) {
|
|
91
|
+
const original = Array.isArray(record?.pendingInputs) ? record.pendingInputs : [];
|
|
92
|
+
let pendingInputs = original.filter(item => item?.id && !queueTombstones[item.id]);
|
|
93
|
+
const existingById = new Map(
|
|
94
|
+
(Array.isArray(existingRecord?.pendingInputs) ? existingRecord.pendingInputs : [])
|
|
95
|
+
.filter(item => item?.id && !queueTombstones[item.id])
|
|
96
|
+
.map(item => [item.id, item])
|
|
97
|
+
);
|
|
98
|
+
const claims = options.queueClaims && typeof options.queueClaims === 'object'
|
|
99
|
+
? options.queueClaims
|
|
100
|
+
: {};
|
|
101
|
+
for (const claim of Object.values(claims)) {
|
|
102
|
+
if (!claim?.itemId || claim.ownerId === options.writerId) continue;
|
|
103
|
+
const index = pendingInputs.findIndex(item => item.id === claim.itemId);
|
|
104
|
+
const authoritative = existingById.get(claim.itemId);
|
|
105
|
+
if (authoritative && index >= 0) pendingInputs[index] = authoritative;
|
|
106
|
+
else if (authoritative) pendingInputs.push(authoritative);
|
|
107
|
+
else if (index >= 0) pendingInputs.splice(index, 1);
|
|
108
|
+
}
|
|
109
|
+
const unchanged = pendingInputs.length === original.length
|
|
110
|
+
&& pendingInputs.every((item, index) => item === original[index]);
|
|
111
|
+
return unchanged ? record : { ...record, pendingInputs };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
56
115
|
loadTombstones,
|
|
57
116
|
getDeletedSessionIds,
|
|
58
117
|
addTombstones,
|
|
59
118
|
isVisibleRecord,
|
|
60
119
|
writeSessions
|
|
61
120
|
};
|
|
62
|
-
})
|
|
121
|
+
});
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
(function initCodexOverleafUpdateNotice(root) {
|
|
1
|
+
(function initCodexOverleafUpdateNotice(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(require('../shared/managedUpdateProjection'));
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('UpdateNotice', ['ManagedUpdateProjection'], factory);
|
|
6
|
+
}
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function updateNoticeFactory(UpdateProjection) {
|
|
2
8
|
'use strict';
|
|
3
9
|
|
|
4
10
|
let notice = null;
|
|
@@ -11,7 +17,7 @@
|
|
|
11
17
|
let settingsActionInFlight = false;
|
|
12
18
|
let restartWatchdog = null;
|
|
13
19
|
let manualCommandCopied = false;
|
|
14
|
-
const ACTIVE_UPDATE_STATES = new Set(
|
|
20
|
+
const ACTIVE_UPDATE_STATES = new Set(UpdateProjection.activePhases('panel'));
|
|
15
21
|
const RESTART_WATCHDOG_MS = 30 * 1000;
|
|
16
22
|
|
|
17
23
|
function mount(panelLike, options = {}) {
|
|
@@ -467,5 +473,5 @@
|
|
|
467
473
|
return /^zh\b/i.test(locale) ? chinese : english;
|
|
468
474
|
}
|
|
469
475
|
|
|
470
|
-
|
|
471
|
-
})
|
|
476
|
+
return Object.freeze({ mount });
|
|
477
|
+
});
|