codex-overleaf-link 2.3.5 → 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 +63 -31
- package/README.zh-CN.md +543 -0
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +7 -0
- package/extension/src/backgroundUpdateCoordinator.js +88 -4
- package/extension/src/content/activeTurnControl.js +1 -0
- package/extension/src/content/assetTransferBroker.js +2 -0
- package/extension/src/content/contentRuntime.js +77 -57
- package/extension/src/content/contextTray.js +4 -0
- package/extension/src/content/diagnosticsController.js +17 -3
- package/extension/src/content/diagnosticsPanel.js +129 -5
- package/extension/src/content/generated/content.bundle.js +1422 -348
- package/extension/src/content/generated/content.bundle.meta.json +7 -3
- package/extension/src/content/legacyGlobalRegistry.js +3 -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 +126 -92
- package/extension/src/content/otWarmMirrorController.js +122 -1
- package/extension/src/content/pageBridgeClient.js +2 -0
- package/extension/src/content/panelMaintenance.js +26 -15
- package/extension/src/content/providerSettingsCoordinator.js +1 -0
- package/extension/src/content/runFailureNotice.js +104 -0
- package/extension/src/content/runResultActions.js +16 -1
- package/extension/src/content/runScrollLayout.js +124 -0
- package/extension/src/content/runTimelineView.js +37 -11
- package/extension/src/content/sessionForkController.js +20 -11
- package/extension/src/content/settingsPanel.js +28 -11
- package/extension/src/content/trackedChangeCaptureController.js +153 -0
- package/extension/src/content/updateNotice.js +24 -4
- package/extension/src/content/writebackOrchestrator.js +31 -0
- package/extension/src/page/binaryAssetUploader.js +101 -29
- package/extension/src/page/overleafRealtimeObserver.js +120 -25
- 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/i18n.js +77 -37
- package/extension/src/shared/managedUpdateProjection.js +1 -1
- package/extension/src/shared/otText.js +0 -1
- package/extension/src/shared/pageRpcContract.js +2 -1
- package/extension/src/shared/sessionState.js +23 -23
- package/extension/src/shared/sessionTitle.js +120 -0
- package/extension/src/shared/storageDb.js +10 -12
- package/extension/src/shared/storageRunActions.js +29 -1
- package/extension/src/shared/updateRuntimeIdentity.js +32 -0
- package/extension/src/shared/writebackSettlement.js +19 -5
- package/extension/styles/panel.css +128 -24
- 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/taskRunnerRuntime.js +25 -0
- package/native-host/src/updateManager.js +40 -39
- 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',
|
|
@@ -40,20 +40,31 @@
|
|
|
40
40
|
diagnosticsPageSubtitle: 'Current file, write access, track changes',
|
|
41
41
|
diagnosticsSnapshotTitle: 'Check Project Read',
|
|
42
42
|
diagnosticsSnapshotSubtitle: 'Full project, assets, read source',
|
|
43
|
-
diagnosticsOtTitle: 'Check
|
|
43
|
+
diagnosticsOtTitle: 'Check OT Mirror',
|
|
44
44
|
diagnosticsOtSubtitle: 'Status, fresh files, fallback',
|
|
45
45
|
diagnosticsExportTitle: 'Export Diagnostics',
|
|
46
46
|
diagnosticsExportSubtitle: 'Redacted audit and environment bundle',
|
|
47
47
|
diagnosticsExportDone: 'Diagnostics bundle exported.',
|
|
48
|
-
diagnosticsOtSummaryEnabled: '
|
|
49
|
-
diagnosticsOtSummaryDisabled: '
|
|
50
|
-
diagnosticsOtNextStep: '
|
|
51
|
-
experimentalOtMenuTitle: '
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
diagnosticsOtSummaryEnabled: 'OT Mirror is enabled. Codex will use the OT-warmed mirror only when matching files are fresh; otherwise it falls back to the normal project read path.',
|
|
49
|
+
diagnosticsOtSummaryDisabled: 'OT Mirror is disabled, so Codex is using the normal project read path.',
|
|
50
|
+
diagnosticsOtNextStep: 'Observer status describes warming availability. File scope and freshness are checked again before each run.',
|
|
51
|
+
experimentalOtMenuTitle: 'OT Mirror',
|
|
52
|
+
otSettingsTitle: 'Project cache',
|
|
53
|
+
contextLoadingTitle: 'Context loading',
|
|
54
|
+
runFeedbackFailedTitle: 'Needs attention',
|
|
55
|
+
runFeedbackPartialTitle: 'Partially completed',
|
|
56
|
+
runFeedbackUnconfirmedTitle: 'Write status needs review',
|
|
57
|
+
runFeedbackGeneric: 'Open the run details for the reported cause and the next step.',
|
|
58
|
+
runFeedbackUnconfirmed: 'Some changes may already be in Overleaf. Check the document before retrying.',
|
|
59
|
+
runFeedbackUnconfirmedResult: 'Not fully confirmed. Review Overleaf before retrying.',
|
|
60
|
+
runFeedbackReviewFile: 'Review {file} in Overleaf',
|
|
61
|
+
runFeedbackReviewDetails: 'Review run details',
|
|
62
|
+
runFeedbackDetailsUnavailable: 'Detailed steps are unavailable for this older run.',
|
|
63
|
+
experimentalOtMenuSubtitle: 'Warms recent edits to the active text file locally. File scope and freshness are checked before reuse.',
|
|
64
|
+
experimentalOtConfirmTitle: 'Enable OT Mirror?',
|
|
65
|
+
experimentalOtConfirmMessage: 'Observes the active text editor and warms its local mirror. This is read-only with respect to Overleaf and does not upload assets. Each run checks file scope and freshness before reuse; normal project reading remains the fallback.',
|
|
55
66
|
experimentalOtConfirmEnable: 'Turn on',
|
|
56
|
-
experimentalOtEnabledToast: '
|
|
67
|
+
experimentalOtEnabledToast: 'OT Mirror requested for this project. Its status shows whether warming is available.',
|
|
57
68
|
diagnosticsSnapshotLoading: 'Checking whether the extension can read the full Overleaf project.',
|
|
58
69
|
diagnosticsSnapshotErrorSummary: 'Overleaf may still be loading, or the page API did not return project files yet.',
|
|
59
70
|
diagnosticsNativeLoading: 'Checking whether local Codex, Native Host, and LaTeX tools are available.',
|
|
@@ -326,18 +337,27 @@
|
|
|
326
337
|
requireReviewingTitle: 'When enabled, Codex checks or switches Overleaf Reviewing/Track Changes before writing. Deletes still require confirmation.',
|
|
327
338
|
autoCompile: 'Compile',
|
|
328
339
|
autoCompileTitle: 'After Codex writes, click Overleaf Recompile and record the compile result for this task. Ask mode will not trigger it.',
|
|
329
|
-
experimentalOtWarmMirror: '
|
|
340
|
+
experimentalOtWarmMirror: 'OT warm mirror',
|
|
330
341
|
experimentalOtWarmMirrorShort: 'OT',
|
|
331
|
-
experimentalOtWarmMirrorTitle: '
|
|
342
|
+
experimentalOtWarmMirrorTitle: 'Read-only local text warming. File scope and freshness are checked before reuse; normal project reading remains the fallback.',
|
|
332
343
|
otStatus: 'OT status',
|
|
333
|
-
otFreshFiles: 'OT
|
|
334
|
-
otFallback: '
|
|
344
|
+
otFreshFiles: 'Files with recent OT evidence',
|
|
345
|
+
otFallback: 'Read fallback',
|
|
335
346
|
otStatusOff: 'Off',
|
|
336
|
-
otStatusStarting: '
|
|
337
|
-
otStatusObserving: '
|
|
338
|
-
otStatusUnavailable: '
|
|
339
|
-
otStatusStale: '
|
|
340
|
-
otStatusInconsistent: '
|
|
347
|
+
otStatusStarting: 'Connecting',
|
|
348
|
+
otStatusObserving: 'Observing current editor',
|
|
349
|
+
otStatusUnavailable: 'Unavailable',
|
|
350
|
+
otStatusStale: 'Warming paused; awaiting verification',
|
|
351
|
+
otStatusInconsistent: 'Needs verification',
|
|
352
|
+
otFailureBridge: 'The page connection needs to be restored. Reload the extension and Overleaf before retrying.',
|
|
353
|
+
otFailureObserver: 'The editor observer is unavailable. The next run will try normal project reading.',
|
|
354
|
+
otFailureEditor: 'The active editor could not be read. Warming is paused; the next run will try normal project reading.',
|
|
355
|
+
otFailureFileLimit: 'This file exceeds the 1 MiB warming limit. The next run will try normal project reading.',
|
|
356
|
+
otFailureQueueLimit: 'The mirror queue reached its limit. Warming is paused; the next run will try normal project reading.',
|
|
357
|
+
otFailureExpired: 'The observation is too old to reuse. The next run will try normal project reading.',
|
|
358
|
+
otFailureMirror: 'The mirror update was not fully confirmed. The next run will try normal project reading.',
|
|
359
|
+
otFailureGeneric: 'Mirror warming could not continue. The next run will try normal project reading.',
|
|
360
|
+
otFailureNextStep: 'Resolve the reported cause, then enable OT again. If the page connection is unavailable, reload the extension and Overleaf first.',
|
|
341
361
|
modelSourceFallback: 'fallback',
|
|
342
362
|
modelSourceDiscovered: 'discovered',
|
|
343
363
|
modelSourceFailed: 'failed',
|
|
@@ -560,20 +580,31 @@
|
|
|
560
580
|
diagnosticsPageSubtitle: '当前文件、写入能力、留痕状态',
|
|
561
581
|
diagnosticsSnapshotTitle: '检查项目读取',
|
|
562
582
|
diagnosticsSnapshotSubtitle: '完整项目、资源文件、读取来源',
|
|
563
|
-
diagnosticsOtTitle: '
|
|
583
|
+
diagnosticsOtTitle: '检查 OT Mirror',
|
|
564
584
|
diagnosticsOtSubtitle: '状态、新鲜文件、回退路径',
|
|
565
585
|
diagnosticsExportTitle: '导出诊断信息',
|
|
566
586
|
diagnosticsExportSubtitle: '已脱敏的审计和环境信息包',
|
|
567
587
|
diagnosticsExportDone: '诊断信息已导出。',
|
|
568
|
-
diagnosticsOtSummaryEnabled: '
|
|
569
|
-
diagnosticsOtSummaryDisabled: '
|
|
570
|
-
diagnosticsOtNextStep: '
|
|
571
|
-
experimentalOtMenuTitle: '
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
588
|
+
diagnosticsOtSummaryEnabled: 'OT Mirror 已开启。只有匹配文件是新鲜状态时,Codex 才会使用 OT 预热镜像;否则会回退到常规项目读取路径。',
|
|
589
|
+
diagnosticsOtSummaryDisabled: 'OT Mirror 已关闭,Codex 会使用常规项目读取路径。',
|
|
590
|
+
diagnosticsOtNextStep: '观测状态说明预热是否可用;每次任务开始前仍会重新校验文件范围和新鲜度。',
|
|
591
|
+
experimentalOtMenuTitle: 'OT Mirror',
|
|
592
|
+
otSettingsTitle: '项目缓存',
|
|
593
|
+
contextLoadingTitle: '上下文加载',
|
|
594
|
+
runFeedbackFailedTitle: '需要处理',
|
|
595
|
+
runFeedbackPartialTitle: '部分完成',
|
|
596
|
+
runFeedbackUnconfirmedTitle: '写入状态待确认',
|
|
597
|
+
runFeedbackGeneric: '可在本轮详情中查看具体原因和后续处理方式。',
|
|
598
|
+
runFeedbackUnconfirmed: '部分改动可能已经写入 Overleaf,重试前请先检查文档。',
|
|
599
|
+
runFeedbackUnconfirmedResult: '结果尚未完全确认,请检查 Overleaf 后再决定是否重试。',
|
|
600
|
+
runFeedbackReviewFile: '在 Overleaf 检查 {file}',
|
|
601
|
+
runFeedbackReviewDetails: '查看运行步骤',
|
|
602
|
+
runFeedbackDetailsUnavailable: '这条历史记录没有可展开的详细步骤。',
|
|
603
|
+
experimentalOtMenuSubtitle: '在本地预热当前文本文件的最近改动,复用前重新校验文件范围和新鲜度。',
|
|
604
|
+
experimentalOtConfirmTitle: '开启 OT Mirror?',
|
|
605
|
+
experimentalOtConfirmMessage: '观测当前文本编辑器并预热本地镜像。此功能不会写回 Overleaf,也不负责上传资产。每次任务复用前都会校验文件范围和新鲜度,不满足条件时尝试常规项目读取。',
|
|
575
606
|
experimentalOtConfirmEnable: '开启',
|
|
576
|
-
experimentalOtEnabledToast: '
|
|
607
|
+
experimentalOtEnabledToast: '已为当前项目请求开启 OT Mirror,可用情况见状态提示。',
|
|
577
608
|
diagnosticsSnapshotLoading: '确认插件能否读取完整 Overleaf 项目。',
|
|
578
609
|
diagnosticsSnapshotErrorSummary: '这通常是 Overleaf 页面还没加载完成,或者当前页面接口暂时没有返回项目文件。',
|
|
579
610
|
diagnosticsNativeLoading: '确认本机 Codex、Native Host 和 LaTeX 工具是否可用。',
|
|
@@ -846,18 +877,27 @@
|
|
|
846
877
|
requireReviewingTitle: '开启后,写入前会确认并尝试切到 Overleaf Reviewing/Track Changes;删除仍需确认。',
|
|
847
878
|
autoCompile: '编译',
|
|
848
879
|
autoCompileTitle: 'Codex 写入后自动点击 Overleaf Recompile,并把编译结果记录到本轮任务。只问不改时不会触发。',
|
|
849
|
-
experimentalOtWarmMirror: '
|
|
880
|
+
experimentalOtWarmMirror: 'OT 预热镜像',
|
|
850
881
|
experimentalOtWarmMirrorShort: 'OT',
|
|
851
|
-
experimentalOtWarmMirrorTitle: '
|
|
882
|
+
experimentalOtWarmMirrorTitle: '只读本地文本预热;复用前校验文件范围和新鲜度,常规项目读取作为回退路径。',
|
|
852
883
|
otStatus: 'OT 状态',
|
|
853
|
-
otFreshFiles: 'OT
|
|
854
|
-
otFallback: '
|
|
855
|
-
otStatusOff: '
|
|
856
|
-
otStatusStarting: '
|
|
857
|
-
otStatusObserving: '
|
|
858
|
-
otStatusUnavailable: '
|
|
859
|
-
otStatusStale: '
|
|
860
|
-
otStatusInconsistent: '
|
|
884
|
+
otFreshFiles: '近期有 OT 校验证据的文件',
|
|
885
|
+
otFallback: '读取回退',
|
|
886
|
+
otStatusOff: '未开启',
|
|
887
|
+
otStatusStarting: '正在连接',
|
|
888
|
+
otStatusObserving: '正在观测当前编辑器',
|
|
889
|
+
otStatusUnavailable: '暂不可用',
|
|
890
|
+
otStatusStale: '预热暂缓,等待重新校验',
|
|
891
|
+
otStatusInconsistent: '需重新校验',
|
|
892
|
+
otFailureBridge: '页面连接需要恢复,请先重新加载扩展并刷新 Overleaf,再重试。',
|
|
893
|
+
otFailureObserver: '编辑器观察器暂不可用,下次任务将尝试常规项目读取。',
|
|
894
|
+
otFailureEditor: '当前编辑器内容尚不可读,预热已暂停;下次任务将尝试常规项目读取。',
|
|
895
|
+
otFailureFileLimit: '文件超过 1 MiB 预热上限,下次任务将尝试常规项目读取。',
|
|
896
|
+
otFailureQueueLimit: '镜像队列达到容量上限,预热已暂停;下次任务将尝试常规项目读取。',
|
|
897
|
+
otFailureExpired: '观测记录已过期,下次任务将尝试常规项目读取。',
|
|
898
|
+
otFailureMirror: '镜像更新尚未完整确认,下次任务将尝试常规项目读取。',
|
|
899
|
+
otFailureGeneric: '镜像预热未能继续,下次任务将尝试常规项目读取。',
|
|
900
|
+
otFailureNextStep: '处理所显示的原因后重新开启 OT;若页面连接不可用,请先重新加载扩展并刷新 Overleaf。',
|
|
861
901
|
modelSourceFallback: '备用列表',
|
|
862
902
|
modelSourceDiscovered: '已发现',
|
|
863
903
|
modelSourceFailed: '获取失败',
|
|
@@ -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'),
|