codex-overleaf-link 2.3.6 → 2.4.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.
- package/README.md +59 -34
- package/README.zh-CN.md +543 -0
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +3 -0
- package/extension/src/content/activeTurnControl.js +1 -0
- package/extension/src/content/assetTransferBroker.js +2 -0
- package/extension/src/content/contentRuntime.js +53 -50
- package/extension/src/content/contextTray.js +4 -0
- package/extension/src/content/diagnosticsController.js +4 -0
- package/extension/src/content/diagnosticsPanel.js +128 -4
- package/extension/src/content/generated/content.bundle.js +583 -112
- package/extension/src/content/generated/content.bundle.meta.json +4 -3
- package/extension/src/content/legacyGlobalRegistry.js +1 -0
- package/extension/src/content/modelPicker.js +21 -9
- package/extension/src/content/modelPickerSupport.js +38 -1
- package/extension/src/content/nativeCompatibilityController.js +4 -1
- package/extension/src/content/otWarmMirror.js +34 -30
- package/extension/src/content/otWarmMirrorController.js +20 -0
- package/extension/src/content/pageBridgeClient.js +2 -0
- package/extension/src/content/runResultActions.js +16 -1
- package/extension/src/content/runTimelineView.js +1 -1
- package/extension/src/content/sessionForkController.js +20 -11
- package/extension/src/content/trackedChangeCaptureController.js +153 -0
- package/extension/src/content/writebackOrchestrator.js +31 -0
- package/extension/src/page/binaryAssetUploader.js +101 -29
- package/extension/src/page/overleafRealtimeObserver.js +4 -1
- package/extension/src/page/textFileCreator.js +493 -0
- package/extension/src/page/trackedChangeCapture.js +342 -0
- package/extension/src/page/trackedChangesLifecycle.js +145 -149
- package/extension/src/page/treeOperations.js +30 -2
- package/extension/src/page/writebackRouter.js +94 -48
- package/extension/src/pageBridge.js +62 -25
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/pageRpcContract.js +2 -1
- package/extension/src/shared/sessionState.js +6 -2
- package/extension/src/shared/storageDb.js +10 -12
- package/extension/src/shared/storageRunActions.js +29 -1
- package/extension/src/shared/writebackSettlement.js +19 -5
- package/extension/styles/panel.css +55 -10
- package/native-host/src/taskRunnerRuntime.js +25 -0
- package/package.json +5 -1
|
@@ -392,54 +392,45 @@
|
|
|
392
392
|
: result;
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
async function
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
395
|
+
async function confirmNoTraceUndoEditing() {
|
|
396
|
+
let changed = false;
|
|
397
|
+
let toggle = { ok: true, changed: false, enabled: false };
|
|
398
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
399
|
+
if (!isEditingConfirmedForNoTraceUndo(getReviewingState({}))) {
|
|
400
|
+
toggle = await setReviewingEnabled(false, { waitMs: 1800 });
|
|
401
|
+
if (!toggle.ok) return toggle;
|
|
402
|
+
changed = changed || toggle.changed === true;
|
|
403
|
+
}
|
|
404
|
+
await delay(180);
|
|
405
|
+
if (!isEditingConfirmedForNoTraceUndo(getReviewingState({}))) continue;
|
|
406
|
+
await delay(180);
|
|
407
|
+
if (isEditingConfirmedForNoTraceUndo(getReviewingState({})))
|
|
408
|
+
return { ...toggle, ok: true, changed, enabled: false };
|
|
409
|
+
}
|
|
410
|
+
return { ok: false, code: 'editing_not_confirmed',
|
|
411
|
+
reason: 'Overleaf Editing mode did not remain confirmed before undo; no undo write was started.' };
|
|
412
|
+
}
|
|
414
413
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
414
|
+
async function applyOperationsWithNoTraceUndo(operations, options = {}) {
|
|
415
|
+
const disabled = await confirmNoTraceUndoEditing();
|
|
416
|
+
if (!disabled.ok) return buildNoTraceUndoBlockedResult(operations, disabled);
|
|
417
|
+
const result = await applyOperationsCore(operations, {
|
|
418
|
+
...options, noTraceUndo: true, trackReviewingChanges: false
|
|
419
|
+
});
|
|
420
|
+
const reviewingPolicy = { policy: 'no-trace-undo', disabled: disabled.changed === true, restored: false,
|
|
421
|
+
reason: disabled.changed ? 'left_editing_after_undo' : 'editing_already_confirmed' };
|
|
422
|
+
if (disabled.changed) {
|
|
423
|
+
reviewingPolicy.leftEditing = true;
|
|
424
|
+
reviewingPolicy.disable = summarizeReviewingToggleResult(disabled);
|
|
424
425
|
}
|
|
425
|
-
|
|
426
|
-
return {
|
|
427
|
-
...result,
|
|
428
|
-
reviewingPolicy: {
|
|
429
|
-
policy: 'no-trace-undo',
|
|
430
|
-
disabled: true,
|
|
431
|
-
restored: false,
|
|
432
|
-
leftEditing: true,
|
|
433
|
-
reason: 'left_editing_after_undo',
|
|
434
|
-
disable: summarizeReviewingToggleResult(disabled)
|
|
435
|
-
}
|
|
436
|
-
};
|
|
426
|
+
return { ...result, reviewingPolicy };
|
|
437
427
|
}
|
|
438
428
|
|
|
439
429
|
async function applyOperationsCore(operations, options = {}) {
|
|
440
430
|
const applied = [];
|
|
441
431
|
const skipped = [];
|
|
442
432
|
const trackedChanges = [];
|
|
433
|
+
const trackedChangeCaptures = [];
|
|
443
434
|
const safeBaseFiles = normalizeBaseFilesForSafety(options.baseFiles);
|
|
444
435
|
const baseFileLookup = window.CodexOverleafStaleGuard?.buildBaseFileLookup(safeBaseFiles);
|
|
445
436
|
const baseBinaryFileLookup = buildBaseBinaryFileLookup(safeBaseFiles);
|
|
@@ -574,8 +565,10 @@
|
|
|
574
565
|
let raceResult;
|
|
575
566
|
if (operation.type === 'edit') {
|
|
576
567
|
raceResult = await raceOpAgainstCancellation(applyEditOperation(operation, {
|
|
577
|
-
baseFileLookup,
|
|
578
|
-
|
|
568
|
+
baseFileLookup, runProjectId,
|
|
569
|
+
recheckWriteProject: writeGuardSurface ? () => writeGuardSurface.runWriteGuard({ runProjectId }) : null,
|
|
570
|
+
trackReviewingChanges: options.trackReviewingChanges === true,
|
|
571
|
+
noTraceUndo: options.noTraceUndo === true
|
|
579
572
|
}));
|
|
580
573
|
} else if (['binary-create', 'overwrite-binary'].includes(operation.type)) {
|
|
581
574
|
raceResult = await raceOpAgainstCancellation(applyBinaryAssetOperation(operation, { baseFileLookup, baseBinaryFileLookup }));
|
|
@@ -604,6 +597,7 @@
|
|
|
604
597
|
break;
|
|
605
598
|
}
|
|
606
599
|
const result = raceResult;
|
|
600
|
+
if (result.trackedChangeCapture) trackedChangeCaptures.push(result.trackedChangeCapture);
|
|
607
601
|
if (operation.type === 'edit' && result.ok && Array.isArray(result.trackedChanges)) {
|
|
608
602
|
trackedChanges.push(...result.trackedChanges);
|
|
609
603
|
}
|
|
@@ -618,7 +612,8 @@
|
|
|
618
612
|
ok: skipped.length === 0,
|
|
619
613
|
applied,
|
|
620
614
|
skipped,
|
|
621
|
-
trackedChanges: mergeTrackedChangeRefs(trackedChanges)
|
|
615
|
+
trackedChanges: trackedChangeCaptures.some(capture => capture.state !== 'observed') ? [] : mergeTrackedChangeRefs(trackedChanges),
|
|
616
|
+
trackedChangeCaptures
|
|
622
617
|
};
|
|
623
618
|
}
|
|
624
619
|
|
|
@@ -653,12 +648,17 @@
|
|
|
653
648
|
return editorReady;
|
|
654
649
|
}
|
|
655
650
|
|
|
651
|
+
if (options.noTraceUndo === true) {
|
|
652
|
+
const editing = await confirmNoTraceUndoEditing();
|
|
653
|
+
if (!editing.ok) return buildNoTraceUndoBlockedResult([operation], editing).skipped[0].result;
|
|
654
|
+
}
|
|
656
655
|
const trackReviewingChanges = options.trackReviewingChanges === true;
|
|
657
656
|
const forbidTrackedChanges = options.forbidTrackedChanges === true;
|
|
658
657
|
const observeTrackedChanges = trackReviewingChanges || forbidTrackedChanges;
|
|
659
658
|
const operationPaths = collectOperationPaths([operation]);
|
|
659
|
+
const captureBaseline = trackReviewingChanges ? prepareTrackedChangeCapture(operation.path) : null;
|
|
660
660
|
const trackedBefore = observeTrackedChanges
|
|
661
|
-
? collectTrackedChangeRefsForPaths(operationPaths)
|
|
661
|
+
? (captureBaseline?.refs || collectTrackedChangeRefsForPaths(operationPaths))
|
|
662
662
|
: [];
|
|
663
663
|
let current = editorReady.text;
|
|
664
664
|
let freshness = window.CodexOverleafStaleGuard?.checkOperationFreshness(
|
|
@@ -752,6 +752,25 @@
|
|
|
752
752
|
};
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
+
if (options.noTraceUndo === true) {
|
|
756
|
+
const blocked = typeof options.recheckWriteProject === 'function'
|
|
757
|
+
? await options.recheckWriteProject()
|
|
758
|
+
: { ok: false, code: 'editor_project_id_unavailable',
|
|
759
|
+
failure: buildPageFailure('editor_project_id_unavailable', { file: operation.path,
|
|
760
|
+
operationType: 'undo', changedDocument: false,
|
|
761
|
+
userMessage: 'The project identity guard is unavailable; the undo write was not applied.' }) };
|
|
762
|
+
if (blocked) return blocked.skipped?.[0]?.result || blocked;
|
|
763
|
+
if (getActiveFilePath() !== operation.path || readActiveEditorText() !== current) {
|
|
764
|
+
const reason = 'The editor changed while confirming Editing mode; the undo write was not applied.';
|
|
765
|
+
return { ok: false, code: 'stale_source_changed', reason,
|
|
766
|
+
failure: buildPageFailure('stale_source_changed', { file: operation.path,
|
|
767
|
+
operationType: 'undo', changedDocument: false, userMessage: reason }) };
|
|
768
|
+
}
|
|
769
|
+
if (!isEditingConfirmedForNoTraceUndo(getReviewingState({})))
|
|
770
|
+
return buildNoTraceUndoBlockedResult([operation], { ok: false, code: 'editing_not_confirmed',
|
|
771
|
+
reason: 'Overleaf left Editing mode before the undo write.' }).skipped[0].result;
|
|
772
|
+
}
|
|
773
|
+
|
|
755
774
|
const result = Array.isArray(operation.patches) && operation.patches.length
|
|
756
775
|
? replaceActiveEditorPatches(operation.patches, nextContent)
|
|
757
776
|
: replaceActiveEditorText(nextContent);
|
|
@@ -784,6 +803,7 @@
|
|
|
784
803
|
return decorated;
|
|
785
804
|
}
|
|
786
805
|
const trackedChanges = [];
|
|
806
|
+
let trackedChangeCapture = null;
|
|
787
807
|
if (observeTrackedChanges) {
|
|
788
808
|
// Overleaf renders review markers asynchronously after the editor text
|
|
789
809
|
// has already changed. A single 120 ms snapshot intermittently missed
|
|
@@ -791,10 +811,13 @@
|
|
|
791
811
|
// Poll both paths: normal Reviewing writes get a shorter capture window,
|
|
792
812
|
// while Accept replay keeps the longer safety window used to prove that
|
|
793
813
|
// it did not create fresh tracked changes.
|
|
794
|
-
const trackedDiff =
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
814
|
+
const trackedDiff = trackReviewingChanges
|
|
815
|
+
? await captureTrackedWrite({ trackedBefore, captureBaseline, operation, beforeContent: current,
|
|
816
|
+
postContent: nextContent, runProjectId: options.runProjectId })
|
|
817
|
+
: await waitForTrackedChangeDiff(trackedBefore, operationPaths, {
|
|
818
|
+
waitMs: 3600, intervalMs: 180, stopOnFirst: true
|
|
819
|
+
});
|
|
820
|
+
trackedChangeCapture = trackedDiff.capture || null;
|
|
798
821
|
trackedChanges.push(...trackedDiff.trackedChanges);
|
|
799
822
|
if (forbidTrackedChanges && trackedChanges.length > 0) {
|
|
800
823
|
return {
|
|
@@ -831,6 +854,7 @@
|
|
|
831
854
|
...result,
|
|
832
855
|
verified: true,
|
|
833
856
|
verifiedContent: nextContent,
|
|
857
|
+
trackedChangeCapture,
|
|
834
858
|
trackedChanges: mergeTrackedChangeRefs(trackedChanges)
|
|
835
859
|
};
|
|
836
860
|
}
|
|
@@ -1352,6 +1376,8 @@
|
|
|
1352
1376
|
}
|
|
1353
1377
|
|
|
1354
1378
|
async function applyFileTreeOperation(operation, options = {}) {
|
|
1379
|
+
const initialProjectId = treeOperations.getProjectId?.();
|
|
1380
|
+
const initialCancellationSequence = readWriteCancellationSequence();
|
|
1355
1381
|
const freshness = await checkFileTreeOperationFreshness(operation, options.baseFileLookup);
|
|
1356
1382
|
if (!freshness.ok) {
|
|
1357
1383
|
return freshness;
|
|
@@ -1400,8 +1426,21 @@
|
|
|
1400
1426
|
}
|
|
1401
1427
|
}
|
|
1402
1428
|
|
|
1429
|
+
const nativeOperation = { create: deps.createTextFile, delete: deps.deleteTextFile }[operation.type];
|
|
1430
|
+
if (typeof nativeOperation === 'function' && (operation.type === 'delete' || typeof operation.content === 'string')) {
|
|
1431
|
+
const isCurrent = () => Boolean(initialProjectId) && treeOperations.getProjectId?.() === initialProjectId
|
|
1432
|
+
&& readWriteCancellationSequence() === initialCancellationSequence;
|
|
1433
|
+
const created = await nativeOperation(operation, { isCurrent, expectedContent: options.baseFileLookup?.get(operation.path) });
|
|
1434
|
+
if (!created.ok) return created;
|
|
1435
|
+
if (!isCurrent() || created.verified !== true || created.verification !== 'overleaf-zip') {
|
|
1436
|
+
return { ...fileTreeVerificationFailed(operation, 'Server receipt is missing or stale.'), changedDocument: true };
|
|
1437
|
+
}
|
|
1438
|
+
recordFileTreeOperationSuccess(operation, options.baseFileLookup);
|
|
1439
|
+
return { ok: true, method: created.method, verified: true };
|
|
1440
|
+
}
|
|
1403
1441
|
return {
|
|
1404
1442
|
ok: false,
|
|
1443
|
+
code: 'file_tree_controls_unavailable',
|
|
1405
1444
|
reason: 'No supported Overleaf file-tree method was detected'
|
|
1406
1445
|
};
|
|
1407
1446
|
}
|
|
@@ -1847,6 +1886,8 @@
|
|
|
1847
1886
|
acceptTrackedChanges,
|
|
1848
1887
|
rejectTrackedChanges,
|
|
1849
1888
|
collectTrackedChangeRefsForPaths,
|
|
1889
|
+
prepareTrackedChangeCapture, getTrackedChangeCaptureStatus,
|
|
1890
|
+
captureTrackedWrite, reconcileTrackedChangeCapture,
|
|
1850
1891
|
waitForTrackedChangeDiff
|
|
1851
1892
|
} = trackedChangesLifecycleModule.create({
|
|
1852
1893
|
window,
|
|
@@ -1868,6 +1909,9 @@
|
|
|
1868
1909
|
buildPageFailure,
|
|
1869
1910
|
delay,
|
|
1870
1911
|
getActiveFilePath,
|
|
1912
|
+
getProjectId: () => treeOperations.getProjectId?.() || '',
|
|
1913
|
+
getCodeMirrorEditorView: deps.getCodeMirrorEditorView,
|
|
1914
|
+
getTrackedChangeDocumentId: deps.getTrackedChangeDocumentId,
|
|
1871
1915
|
getReviewingState,
|
|
1872
1916
|
isEditorUndoControl,
|
|
1873
1917
|
isInsideCodexPanel,
|
|
@@ -1885,6 +1929,8 @@
|
|
|
1885
1929
|
|
|
1886
1930
|
return {
|
|
1887
1931
|
applyOperations: applyOperationsForBridge,
|
|
1932
|
+
getTrackedChangeCaptureStatus,
|
|
1933
|
+
reconcileTrackedChangeCapture,
|
|
1888
1934
|
rejectTrackedChanges,
|
|
1889
1935
|
acceptTrackedChanges,
|
|
1890
1936
|
verifySaveState
|
|
@@ -67,7 +67,14 @@
|
|
|
67
67
|
treeOperations,
|
|
68
68
|
window
|
|
69
69
|
});
|
|
70
|
+
const textFileCreator = requirePageModule('CodexOverleafTextFileCreator').create({
|
|
71
|
+
window, document, treeOperations, snapshotRouter,
|
|
72
|
+
collectElements, ensureEditing, ensureReviewing, readActiveEditorText, readWriteCancellationSequence,
|
|
73
|
+
uploadHelpers: requirePageModule('CodexOverleafBinaryAssetUploader')
|
|
74
|
+
});
|
|
70
75
|
binaryAssetUploader = requirePageModule('CodexOverleafBinaryAssetUploader').create({
|
|
76
|
+
prepareUploadParent: textFileCreator.prepareUploadParent,
|
|
77
|
+
findFolderNode: textFileCreator.findFolderNode,
|
|
71
78
|
document,
|
|
72
79
|
snapshotRouter,
|
|
73
80
|
treeOperations,
|
|
@@ -86,6 +93,8 @@
|
|
|
86
93
|
window
|
|
87
94
|
});
|
|
88
95
|
writebackRouter = requirePageModule('CodexOverleafWritebackRouter').create({
|
|
96
|
+
createTextFile: textFileCreator.createFile,
|
|
97
|
+
deleteTextFile: textFileCreator.deleteFile,
|
|
89
98
|
activeEditorIdentityChanged,
|
|
90
99
|
clickNode,
|
|
91
100
|
collectElements,
|
|
@@ -93,8 +102,14 @@
|
|
|
93
102
|
compileBridge,
|
|
94
103
|
delay,
|
|
95
104
|
diagnosticsRevision: PAGE_BRIDGE_INSTALL_REVISION,
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
getCodeMirrorEditorView,
|
|
106
|
+
getTrackedChangeDocumentId: filePath => {
|
|
107
|
+
const ids = new Set(collectDocRecords({ includeWindowGlobals: true })
|
|
108
|
+
.filter(record => record.path === filePath && typeof record.id === 'string' && record.id).map(record => record.id));
|
|
109
|
+
return ids.size === 1 ? Array.from(ids)[0] : '';
|
|
110
|
+
},
|
|
111
|
+
ensureEditing: params => textFileCreator.ensureWriteMode(false, params),
|
|
112
|
+
ensureReviewing: params => textFileCreator.ensureWriteMode(true, params),
|
|
98
113
|
// Cross-world cancel: the router's per-op loop polls this each iteration
|
|
99
114
|
// and aborts remaining ops when the value bumps. content-side cancel
|
|
100
115
|
// invokes pageBridge.cancelActiveWrite which calls bumpWriteCancellationSequence.
|
|
@@ -205,20 +220,28 @@
|
|
|
205
220
|
return { ok: true };
|
|
206
221
|
},
|
|
207
222
|
createCheckpoint: params => createCheckpoint(params.label),
|
|
208
|
-
ensureReviewing,
|
|
209
|
-
ensureEditing,
|
|
210
|
-
applyOperations: params =>
|
|
223
|
+
ensureReviewing: params => textFileCreator.ensureWriteMode(true, params),
|
|
224
|
+
ensureEditing: params => textFileCreator.ensureWriteMode(false, params),
|
|
225
|
+
applyOperations: async params => {
|
|
226
|
+
if (params.reviewingPolicy === 'no-trace-undo') {
|
|
227
|
+
const prepared = await textFileCreator.prepareEditor(params);
|
|
228
|
+
if (prepared.ok !== true) return { applied: [], changedDocument: false,
|
|
229
|
+
skipped: (params.operations || []).map(operation => ({ operation, result: prepared })) };
|
|
230
|
+
}
|
|
231
|
+
return applyOperations(params.operations || [], {
|
|
211
232
|
baseFiles: params.baseFiles || null,
|
|
212
233
|
reviewingPolicy: params.reviewingPolicy || '',
|
|
213
234
|
requireReviewing: params.requireReviewing === true,
|
|
214
235
|
requireEditing: params.requireEditing === true,
|
|
215
236
|
runProjectId: typeof params.runProjectId === 'string' ? params.runProjectId : ''
|
|
216
|
-
})
|
|
237
|
+
});
|
|
238
|
+
},
|
|
217
239
|
binaryUploadBegin: params => binaryAssetUploader.begin(params),
|
|
218
240
|
binaryUploadAppend: params => binaryAssetUploader.append(params),
|
|
219
241
|
binaryUploadCommit: params => binaryAssetUploader.commit(params),
|
|
220
242
|
binaryUploadAbort: params => binaryAssetUploader.abort(params),
|
|
221
243
|
jumpToPosition,
|
|
244
|
+
reconcileTrackedChangeCapture: params => writebackRouter.reconcileTrackedChangeCapture(params),
|
|
222
245
|
rejectTrackedChanges,
|
|
223
246
|
acceptTrackedChanges,
|
|
224
247
|
triggerCompile: params => compileBridge.triggerCompile(params),
|
|
@@ -430,6 +453,7 @@
|
|
|
430
453
|
editor,
|
|
431
454
|
capabilities: pageRpcContract.withCapabilityReport(collectPageCapabilities(editor)),
|
|
432
455
|
editorDiagnostics: buildEditorDiagnostics(editor),
|
|
456
|
+
trackedChangeCapture: writebackRouter.getTrackedChangeCaptureStatus?.(),
|
|
433
457
|
projectDiagnostics: {
|
|
434
458
|
internalRootKeys: collectInternalRootKeys(),
|
|
435
459
|
docRecordCount: docRecords.length,
|
|
@@ -559,40 +583,26 @@
|
|
|
559
583
|
reason: 'jumpToPosition requires a non-empty file path'
|
|
560
584
|
};
|
|
561
585
|
}
|
|
562
|
-
if (!projectPathExists(filePath)) {
|
|
563
|
-
return {
|
|
564
|
-
ok: false,
|
|
565
|
-
code: 'path_not_found',
|
|
566
|
-
reason: `Could not find ${filePath} in the Overleaf project`,
|
|
567
|
-
path: filePath,
|
|
568
|
-
failure: buildPageBridgeFailure('target_file_not_found', {
|
|
569
|
-
file: filePath,
|
|
570
|
-
operationType: 'jump',
|
|
571
|
-
changedDocument: false,
|
|
572
|
-
userMessage: `Codex could not find ${filePath} in this Overleaf project.`,
|
|
573
|
-
evidence: { originalCode: 'path_not_found', writeStarted: false }
|
|
574
|
-
})
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
|
|
578
586
|
const navigating = getActiveFilePath() !== filePath;
|
|
579
587
|
const previousEditorIdentity = navigating ? getActiveEditorIdentity() : null;
|
|
580
588
|
|
|
581
589
|
if (navigating) {
|
|
582
590
|
const opened = await openFileByPath(filePath);
|
|
583
591
|
if (!opened.ok) {
|
|
592
|
+
const missing = !projectPathExists(filePath);
|
|
593
|
+
const code = missing ? 'path_not_found' : 'file_open_failed';
|
|
584
594
|
return {
|
|
585
595
|
ok: false,
|
|
586
|
-
code
|
|
596
|
+
code,
|
|
587
597
|
reason: opened.reason || `Could not open ${filePath}`,
|
|
588
598
|
path: filePath,
|
|
589
|
-
failure: buildPageBridgeFailure('target_file_open_failed', {
|
|
599
|
+
failure: buildPageBridgeFailure(missing ? 'target_file_not_found' : 'target_file_open_failed', {
|
|
590
600
|
file: filePath,
|
|
591
601
|
operationType: 'jump',
|
|
592
602
|
changedDocument: false,
|
|
593
603
|
userMessage: `Codex could not open ${filePath} in Overleaf.`,
|
|
594
604
|
technicalMessage: opened.reason || '',
|
|
595
|
-
evidence: { originalCode:
|
|
605
|
+
evidence: { originalCode: code, writeStarted: false }
|
|
596
606
|
})
|
|
597
607
|
};
|
|
598
608
|
}
|
|
@@ -830,10 +840,35 @@
|
|
|
830
840
|
return current.node !== previous.node;
|
|
831
841
|
}
|
|
832
842
|
|
|
843
|
+
function readCurrentEditorMode() {
|
|
844
|
+
const selector = '.toolbar-editor .review-mode-switcher > .review-mode-switcher-toggle-button';
|
|
845
|
+
const controls = uniqueNodes(collectElements(selector, 16)).filter(node =>
|
|
846
|
+
String(node.className || '').split(/\s+/).includes('review-mode-switcher-toggle-button')
|
|
847
|
+
&& node.ownerDocument === document && !isInsideCodexPanel(node));
|
|
848
|
+
if (!controls.length) return '';
|
|
849
|
+
const modes = controls.filter(node => {
|
|
850
|
+
const style = window.getComputedStyle?.(node);
|
|
851
|
+
return node.isConnected !== false && !node.hidden && !node.closest?.('[hidden],[aria-hidden="true"]')
|
|
852
|
+
&& (node.getClientRects?.().length || 0) > 0 && style?.visibility !== 'hidden'
|
|
853
|
+
&& style?.display !== 'none' && style?.opacity !== '0';
|
|
854
|
+
}).map(node => {
|
|
855
|
+
const classes = String(node.className || '').split(/\s+/);
|
|
856
|
+
const values = [node.getAttribute('aria-label'),
|
|
857
|
+
node.querySelector?.('.review-mode-switcher-toggle-label')?.textContent]
|
|
858
|
+
.map(value => normalizeReviewingSignalText(value).toLowerCase())
|
|
859
|
+
.filter(value => value === 'editing' || value === 'reviewing');
|
|
860
|
+
for (const mode of ['editing', 'reviewing']) if (classes.includes(mode)) values.push(mode);
|
|
861
|
+
return values.length && values.every(mode => mode === values[0]) ? values[0] : 'unknown';
|
|
862
|
+
});
|
|
863
|
+
return modes.length && modes.every(mode => mode === modes[0]) ? modes[0] : 'unknown';
|
|
864
|
+
}
|
|
865
|
+
|
|
833
866
|
function getReviewingState(params = {}) {
|
|
834
867
|
const signals = collectReviewingSignals({ ...params, manualOverride: false });
|
|
868
|
+
const editorMode = readCurrentEditorMode();
|
|
835
869
|
return {
|
|
836
870
|
signals,
|
|
871
|
+
...(editorMode ? { editorMode } : {}),
|
|
837
872
|
reviewing: window.CodexOverleafReviewing.detectReviewingFromSignals(signals)
|
|
838
873
|
};
|
|
839
874
|
}
|
|
@@ -986,6 +1021,7 @@
|
|
|
986
1021
|
}
|
|
987
1022
|
|
|
988
1023
|
function isReviewingConfirmedForWrite(state = {}) {
|
|
1024
|
+
if (state.editorMode) return state.editorMode === 'reviewing';
|
|
989
1025
|
const reviewing = state.reviewing || {};
|
|
990
1026
|
if (!reviewing.ok || reviewing.status === 'manual-override') {
|
|
991
1027
|
return false;
|
|
@@ -1015,6 +1051,7 @@
|
|
|
1015
1051
|
}
|
|
1016
1052
|
|
|
1017
1053
|
function isEditingConfirmedForNoTraceUndo(state = {}) {
|
|
1054
|
+
if (state.editorMode) return state.editorMode === 'editing';
|
|
1018
1055
|
if (isReviewingConfirmedForWrite(state)) {
|
|
1019
1056
|
return false;
|
|
1020
1057
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
13
|
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
14
|
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
-
const BUILD_TARGET_VERSION = '2.
|
|
15
|
+
const BUILD_TARGET_VERSION = '2.4.0';
|
|
16
16
|
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
17
|
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
18
|
'bridgePing',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
})(typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : this), function () {
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
var REVISION = '2026-
|
|
8
|
+
var REVISION = '2026-09-14-native-text-upload-v28';
|
|
9
9
|
var METHODS = freezeCatalog({
|
|
10
10
|
initializeCapability: entry('bridge.initialize', 'control', 'none', 'default', false, 'none'),
|
|
11
11
|
probe: entry('bridge.probe', 'read', 'none', 'default', false, 'safe'),
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
binaryUploadCommit: entry('asset.write', 'document', 'required', 'writeback', true, 'no_retry'),
|
|
23
23
|
binaryUploadAbort: entry('asset.stage', 'cache', 'none', 'default', false, 'idempotent'),
|
|
24
24
|
jumpToPosition: entry('editor.navigate', 'navigation', 'required', 'default', false, 'idempotent'),
|
|
25
|
+
reconcileTrackedChangeCapture: entry('tracked_changes.read', 'read', 'required', 'default', false, 'safe'),
|
|
25
26
|
rejectTrackedChanges: entry('tracked_changes.reject', 'document', 'required', 'lifecycle', true, 'no_retry'),
|
|
26
27
|
acceptTrackedChanges: entry('tracked_changes.accept', 'document', 'required', 'lifecycle', true, 'no_retry'),
|
|
27
28
|
triggerCompile: entry('compile.trigger', 'page_state', 'required', 'compile', false, 'no_retry'),
|
|
@@ -636,7 +636,7 @@
|
|
|
636
636
|
undoExpectedFiles: normalizeRunFiles(run.undoExpectedFiles),
|
|
637
637
|
undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
|
|
638
638
|
nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
|
|
639
|
-
codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
|
|
639
|
+
codexThreadId: sanitizeAssistantVisibleText(run.codexThreadId), codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
|
|
640
640
|
forkSnapshot: run.forkSnapshot === true,
|
|
641
641
|
forkSourceRunId: sanitizeAssistantVisibleText(run.forkSourceRunId),
|
|
642
642
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
@@ -656,6 +656,9 @@
|
|
|
656
656
|
if (run.changedDocument === true) {
|
|
657
657
|
normalized.changedDocument = true;
|
|
658
658
|
}
|
|
659
|
+
if (Array.isArray(run.trackedChangeCaptures) && run.trackedChangeCaptures.length && !run.forkSnapshot) {
|
|
660
|
+
normalized.trackedChangeCaptures = sanitizeAssistantVisibleValue(run.trackedChangeCaptures);
|
|
661
|
+
}
|
|
659
662
|
applyTrackedChangeStatus(normalized, run.trackedChangeStatus);
|
|
660
663
|
|
|
661
664
|
return normalized;
|
|
@@ -1215,7 +1218,7 @@
|
|
|
1215
1218
|
undoExpectedFiles: undoPayload.undoExpectedFiles,
|
|
1216
1219
|
undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status'),
|
|
1217
1220
|
nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
|
|
1218
|
-
codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
1221
|
+
codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
1219
1222
|
forkSnapshot: run.forkSnapshot === true,
|
|
1220
1223
|
forkSourceRunId: normalizeTextField(run.forkSourceRunId, 160),
|
|
1221
1224
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
@@ -1627,6 +1630,7 @@
|
|
|
1627
1630
|
setActiveSession,
|
|
1628
1631
|
updateActiveSession,
|
|
1629
1632
|
normalizeRuns,
|
|
1633
|
+
normalizeProjectReferenceFiles,
|
|
1630
1634
|
prepareStateForStorage,
|
|
1631
1635
|
estimateJsonBytes,
|
|
1632
1636
|
computeSafeTaskSummary
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
(function initStorageDb(root, factory) {
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
3
|
module.exports = factory(require('./storageRunActions'), require('./runInputQueue'),
|
|
4
|
-
require('./settlementFacts'), require('./storageSessionClaims'));
|
|
4
|
+
require('./settlementFacts'), require('./storageSessionClaims'), require('./sessionState'));
|
|
5
5
|
} else {
|
|
6
6
|
root.CodexOverleafModuleRegistry.define('StorageDb', [
|
|
7
7
|
'StorageRunActions',
|
|
8
8
|
'RunInputQueue',
|
|
9
9
|
'SettlementFacts',
|
|
10
|
-
'StorageSessionClaims'
|
|
10
|
+
'StorageSessionClaims',
|
|
11
|
+
'SessionState'
|
|
11
12
|
], factory);
|
|
12
13
|
}
|
|
13
|
-
})(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims) {
|
|
14
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function storageDbFactory(StorageRunActions, RunInputQueue, SettlementFacts, StorageSessionClaims, SessionState) {
|
|
14
15
|
'use strict';
|
|
15
|
-
if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
|
|
16
|
+
if (!StorageRunActions?.compactRunsForStorage || !StorageRunActions?.compactRunActionPayload || !StorageRunActions?.compactProviderSnapshot || !StorageRunActions?.compactRunExecutionSnapshot || !StorageRunActions?.compactStructuredEventValue || !StorageRunActions?.hashString || !RunInputQueue?.compactForStorage || !SettlementFacts?.compactSettlementFacts || !StorageSessionClaims?.createSessionClaimer) {
|
|
16
17
|
throw new Error('Codex Overleaf storage run-action helpers are unavailable.');
|
|
17
18
|
}
|
|
18
19
|
// IndexedDB versions are monotonic; v2.0 RC profiles have already opened v3.
|
|
@@ -279,6 +280,7 @@
|
|
|
279
280
|
codexThreadId: typeof input.codexThreadId === 'string' ? input.codexThreadId : '',
|
|
280
281
|
status: typeof input.status === 'string' && input.status ? input.status : 'active',
|
|
281
282
|
focusFiles: normalizePathList(input.focusFiles),
|
|
283
|
+
projectReferenceFiles: SessionState.normalizeProjectReferenceFiles(input.projectReferenceFiles),
|
|
282
284
|
pendingInputs: RunInputQueue.compactForStorage(input.pendingInputs, {
|
|
283
285
|
normalizeField: normalizeTextField,
|
|
284
286
|
normalizeDisplay: normalizeDisplayTextForStorage,
|
|
@@ -650,20 +652,15 @@
|
|
|
650
652
|
}
|
|
651
653
|
|
|
652
654
|
function compactRunsForStorage(runs, options) {
|
|
653
|
-
return StorageRunActions.compactRunsForStorage(
|
|
654
|
-
runs,
|
|
655
|
-
options,
|
|
656
|
-
SESSION_STORAGE_LIMITS.maxRunsPerSession,
|
|
657
|
-
compactRunForStorage
|
|
658
|
-
);
|
|
655
|
+
return StorageRunActions.compactRunsForStorage(runs, options, SESSION_STORAGE_LIMITS.maxRunsPerSession, compactRunForStorage);
|
|
659
656
|
}
|
|
660
|
-
|
|
661
657
|
function compactRunForStorage(run, keepActionPayload) {
|
|
662
658
|
var actionPayload = StorageRunActions.compactRunActionPayload(run, keepActionPayload);
|
|
663
659
|
var compact = {
|
|
664
660
|
id: run.id,
|
|
665
661
|
task: normalizeDisplayTextForStorage(run.task || 'untitled task', SESSION_STORAGE_LIMITS.taskChars),
|
|
666
662
|
mode: typeof run.mode === 'string' ? redactSecretLikeText(run.mode) : '', ...StorageRunActions.compactProviderSnapshot(run),
|
|
663
|
+
...StorageRunActions.compactRunExecutionSnapshot(run, function (text) { return normalizeTextField(text, 4096); }),
|
|
667
664
|
model: normalizeTextField(run.model, 80),
|
|
668
665
|
reasoningEffort: typeof run.reasoningEffort === 'string' ? redactSecretLikeText(run.reasoningEffort) : '',
|
|
669
666
|
speedTier: typeof run.speedTier === 'string' ? redactSecretLikeText(run.speedTier) : '',
|
|
@@ -679,9 +676,10 @@
|
|
|
679
676
|
undoBaseFiles: actionPayload.undoBaseFiles,
|
|
680
677
|
undoTrackedChanges: actionPayload.undoTrackedChanges,
|
|
681
678
|
undoExpectedFiles: actionPayload.undoExpectedFiles,
|
|
679
|
+
...(actionPayload.trackedChangeCaptures ? { trackedChangeCaptures: actionPayload.trackedChangeCaptures } : {}),
|
|
682
680
|
undoStatus: normalizeDisplayTextForStorage(run.undoStatus, SESSION_STORAGE_LIMITS.statusTextChars),
|
|
683
681
|
nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
|
|
684
|
-
codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
682
|
+
codexThreadId: normalizeTextField(run.codexThreadId, 160), codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
685
683
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
686
684
|
queueItemId: normalizeTextField(run.queueItemId, 160)
|
|
687
685
|
};
|
|
@@ -70,7 +70,13 @@
|
|
|
70
70
|
} catch (_error) {
|
|
71
71
|
return empty;
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
if (getUtf8ByteLength(serialized) > MAX_PERSISTED_ACTION_BYTES_PER_RUN) return empty;
|
|
74
|
+
const captures = cloneSerializableArray(run.trackedChangeCaptures);
|
|
75
|
+
if (captures.length) {
|
|
76
|
+
const extended = { ...payload, trackedChangeCaptures: captures };
|
|
77
|
+
if (getUtf8ByteLength(JSON.stringify(extended)) <= MAX_PERSISTED_ACTION_BYTES_PER_RUN) return extended;
|
|
78
|
+
}
|
|
79
|
+
return payload; // Optional capture evidence must never evict a mature Undo payload.
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
function emptyActionPayload() {
|
|
@@ -102,6 +108,27 @@
|
|
|
102
108
|
return value.length * 2;
|
|
103
109
|
}
|
|
104
110
|
|
|
111
|
+
// Persist only execution metadata; never infer a missing historical setting.
|
|
112
|
+
function compactRunExecutionSnapshot(run, normalizeString) {
|
|
113
|
+
var snapshot = run && run.executionSnapshot;
|
|
114
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return {};
|
|
115
|
+
var normalize = typeof normalizeString === 'function' ? normalizeString : String;
|
|
116
|
+
var compact = {};
|
|
117
|
+
var fields = ['schemaVersion', 'mode', 'providerId', 'providerRevision', 'model',
|
|
118
|
+
'reasoningEffort', 'speedTier', 'autoRecompile', 'requireReviewing', 'capturedAt', 'source'];
|
|
119
|
+
for (var key of fields) {
|
|
120
|
+
if (!Object.prototype.hasOwnProperty.call(snapshot, key)) continue;
|
|
121
|
+
var value = snapshot[key];
|
|
122
|
+
if (typeof value === 'string') compact[key] = normalize(value);
|
|
123
|
+
else if (value === null || typeof value === 'number' || typeof value === 'boolean') compact[key] = value;
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(snapshot.focusFiles)) {
|
|
126
|
+
compact.focusFiles = snapshot.focusFiles.slice(0, 100)
|
|
127
|
+
.filter(function (value) { return typeof value === 'string'; }).map(normalize);
|
|
128
|
+
}
|
|
129
|
+
return { executionSnapshot: compact };
|
|
130
|
+
}
|
|
131
|
+
|
|
105
132
|
function compactProviderSnapshot(value) {
|
|
106
133
|
return {
|
|
107
134
|
providerId: typeof value.providerId === 'string' && value.providerId ? value.providerId : 'builtin',
|
|
@@ -147,6 +174,7 @@
|
|
|
147
174
|
compactRunsForStorage: compactRunsForStorage,
|
|
148
175
|
compactRunActionPayload: compactRunActionPayload,
|
|
149
176
|
compactProviderSnapshot: compactProviderSnapshot,
|
|
177
|
+
compactRunExecutionSnapshot: compactRunExecutionSnapshot,
|
|
150
178
|
hashString: hashString
|
|
151
179
|
};
|
|
152
180
|
});
|