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.
Files changed (41) hide show
  1. package/README.md +59 -34
  2. package/README.zh-CN.md +543 -0
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/src/background.js +3 -0
  5. package/extension/src/content/activeTurnControl.js +1 -0
  6. package/extension/src/content/assetTransferBroker.js +2 -0
  7. package/extension/src/content/contentRuntime.js +53 -50
  8. package/extension/src/content/contextTray.js +4 -0
  9. package/extension/src/content/diagnosticsController.js +4 -0
  10. package/extension/src/content/diagnosticsPanel.js +128 -4
  11. package/extension/src/content/generated/content.bundle.js +583 -112
  12. package/extension/src/content/generated/content.bundle.meta.json +4 -3
  13. package/extension/src/content/legacyGlobalRegistry.js +1 -0
  14. package/extension/src/content/modelPicker.js +21 -9
  15. package/extension/src/content/modelPickerSupport.js +38 -1
  16. package/extension/src/content/nativeCompatibilityController.js +4 -1
  17. package/extension/src/content/otWarmMirror.js +34 -30
  18. package/extension/src/content/otWarmMirrorController.js +20 -0
  19. package/extension/src/content/pageBridgeClient.js +2 -0
  20. package/extension/src/content/runResultActions.js +16 -1
  21. package/extension/src/content/runTimelineView.js +1 -1
  22. package/extension/src/content/sessionForkController.js +20 -11
  23. package/extension/src/content/trackedChangeCaptureController.js +153 -0
  24. package/extension/src/content/writebackOrchestrator.js +31 -0
  25. package/extension/src/page/binaryAssetUploader.js +101 -29
  26. package/extension/src/page/overleafRealtimeObserver.js +4 -1
  27. package/extension/src/page/textFileCreator.js +493 -0
  28. package/extension/src/page/trackedChangeCapture.js +342 -0
  29. package/extension/src/page/trackedChangesLifecycle.js +145 -149
  30. package/extension/src/page/treeOperations.js +30 -2
  31. package/extension/src/page/writebackRouter.js +94 -48
  32. package/extension/src/pageBridge.js +62 -25
  33. package/extension/src/shared/compatibility.js +1 -1
  34. package/extension/src/shared/pageRpcContract.js +2 -1
  35. package/extension/src/shared/sessionState.js +6 -2
  36. package/extension/src/shared/storageDb.js +10 -12
  37. package/extension/src/shared/storageRunActions.js +29 -1
  38. package/extension/src/shared/writebackSettlement.js +19 -5
  39. package/extension/styles/panel.css +55 -10
  40. package/native-host/src/taskRunnerRuntime.js +25 -0
  41. package/package.json +5 -1
@@ -397,7 +397,7 @@
397
397
  if (!result || typeof result !== 'object' || result.ok === false) return result;
398
398
  if (Array.isArray(result.skipped) && result.skipped.length > 0) return result;
399
399
  const expectedFiles = Array.isArray(run?.undoExpectedFiles) ? run.undoExpectedFiles : [];
400
- if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result)) return result;
400
+ if (!expectedFiles.length || isUndoVerifiedContentMatching(run, result, options)) return result;
401
401
  const path = expectedFiles.find(entry => typeof entry?.path === 'string')?.path || '';
402
402
  appendSyntheticFailure(result, options.buildFailure?.('undo_not_verified', { path, type: 'undo' }, {
403
403
  changedDocument: true,
@@ -407,15 +407,18 @@
407
407
  return result;
408
408
  }
409
409
 
410
- function isUndoVerifiedContentMatching(run, result) {
410
+ function isUndoVerifiedContentMatching(run, result, options = {}) {
411
411
  const expectedByPath = new Map((run?.undoExpectedFiles || [])
412
412
  .filter(file => typeof file?.path === 'string' && typeof file?.content === 'string')
413
413
  .map(file => [normalizeSettlementPath(file.path), file.content])
414
414
  .filter(([path]) => path));
415
415
  if (!expectedByPath.size) return true;
416
+ const postByPath = new Map((Array.isArray(options?.postFiles) ? options.postFiles : [])
417
+ .filter(file => typeof file?.path === 'string' && typeof file?.content === 'string')
418
+ .map(file => [normalizeSettlementPath(file.path), file.content]));
416
419
  const applied = Array.isArray(result?.applied) ? result.applied : [];
417
420
  return Array.from(expectedByPath).every(([path, expected]) =>
418
- applied.some(entry => isVerifiedUndoEntryForPath(entry, path, expected))
421
+ applied.some(entry => isVerifiedUndoEntryForPath(entry, path, expected, postByPath.get(path)))
419
422
  );
420
423
  }
421
424
 
@@ -438,7 +441,7 @@
438
441
  ));
439
442
  }
440
443
 
441
- function isVerifiedUndoEntryForPath(entry, path, expectedContent) {
444
+ function isVerifiedUndoEntryForPath(entry, path, expectedContent, postContent) {
442
445
  const inner = entry?.result || {};
443
446
  if (inner.ok === false) return false;
444
447
  const normalizedPath = normalizeSettlementPath(path);
@@ -456,7 +459,18 @@
456
459
  && inner.verifiedContent === expectedContent;
457
460
  }
458
461
  if (key === `snapshot-undo:${normalizedPath}`) {
459
- return inner.verifiedContent === expectedContent;
462
+ if (inner.verifiedContent === expectedContent) return true;
463
+ const proof = inner.undoRebaseProof, before = proof?.beforeUndoContent;
464
+ if (inner.verified !== true || proof?.version !== 1
465
+ || normalizeSettlementPath(proof.path) !== normalizedPath
466
+ || typeof before !== 'string' || typeof postContent !== 'string' || !postContent
467
+ || typeof inner.verifiedContent !== 'string') return false;
468
+ // The page observed prefix + post + suffix before Undo. Prove that only
469
+ // this run's post-image became its pre-image, preserving both envelopes.
470
+ const start = before.indexOf(postContent);
471
+ if (start < 0 || before.indexOf(postContent, start + 1) >= 0) return false;
472
+ return inner.verifiedContent === before.slice(0, start) + expectedContent
473
+ + before.slice(start + postContent.length);
460
474
  }
461
475
  return false;
462
476
  }
@@ -1029,11 +1029,21 @@
1029
1029
  }
1030
1030
 
1031
1031
  #codex-overleaf-panel .codex-diagnostics-menu {
1032
- position: absolute;
1032
+ position: fixed;
1033
1033
  z-index: 2147483647;
1034
- top: 30px;
1035
- right: 0;
1036
- width: 260px;
1034
+ top: 44px;
1035
+ right: 12px;
1036
+ bottom: auto;
1037
+ left: auto;
1038
+ margin: 0;
1039
+ box-sizing: border-box;
1040
+ width: min(300px, calc(100vw - 24px));
1041
+ max-width: calc(100vw - 24px);
1042
+ max-height: calc(100vh - 24px);
1043
+ overflow: auto;
1044
+ overscroll-behavior: contain;
1045
+ overflow-wrap: anywhere;
1046
+ color: var(--tl-fg-2);
1037
1047
  border: 1px solid var(--tl-border-strong);
1038
1048
  border-radius: 6px;
1039
1049
  background: var(--tl-surface-2);
@@ -1084,15 +1094,30 @@
1084
1094
  z-index: 2147483647;
1085
1095
  top: 44px;
1086
1096
  right: 12px;
1087
- width: min(316px, calc(var(--codex-overleaf-panel-width) - 24px));
1088
- max-height: min(520px, calc(100vh - 64px));
1097
+ bottom: auto;
1098
+ left: auto;
1099
+ margin: 0;
1100
+ padding: 0;
1101
+ box-sizing: border-box;
1102
+ width: min(560px, calc(100vw - 24px));
1103
+ max-width: calc(100vw - 24px);
1104
+ max-height: min(640px, calc(100vh - 24px));
1089
1105
  overflow: auto;
1106
+ overscroll-behavior: contain;
1107
+ overflow-wrap: anywhere;
1108
+ color: var(--tl-fg-2);
1090
1109
  border: 1px solid var(--tl-border-strong);
1091
1110
  border-radius: 8px;
1092
1111
  background: var(--tl-surface-1);
1093
1112
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.48);
1094
1113
  }
1095
1114
 
1115
+ #codex-overleaf-panel .codex-diagnostics-menu::backdrop,
1116
+ #codex-overleaf-panel .codex-diagnostics-result::backdrop {
1117
+ background: transparent;
1118
+ pointer-events: none;
1119
+ }
1120
+
1096
1121
  #codex-overleaf-panel .codex-diagnostics-result[hidden] {
1097
1122
  display: none;
1098
1123
  }
@@ -1821,9 +1846,12 @@
1821
1846
  #codex-overleaf-panel .codex-plugin-confirm::backdrop { background: transparent; }
1822
1847
 
1823
1848
  #codex-overleaf-panel .codex-plugin-confirm-card {
1849
+ display: flex;
1850
+ flex-direction: column;
1851
+ box-sizing: border-box;
1824
1852
  width: 100%;
1825
1853
  max-height: min(520px, calc(100vh - 28px));
1826
- overflow: hidden;
1854
+ overflow: auto;
1827
1855
  border: 1px solid var(--tl-border-strong);
1828
1856
  border-radius: 8px;
1829
1857
  background: var(--tl-surface-1);
@@ -1832,6 +1860,7 @@
1832
1860
 
1833
1861
  #codex-overleaf-panel .codex-plugin-confirm-head {
1834
1862
  display: flex;
1863
+ flex: 0 0 auto;
1835
1864
  align-items: center;
1836
1865
  gap: 10px;
1837
1866
  padding: 14px 14px 12px;
@@ -1860,6 +1889,8 @@
1860
1889
  }
1861
1890
 
1862
1891
  #codex-overleaf-panel .codex-plugin-confirm-body {
1892
+ flex: 0 1 auto;
1893
+ min-height: 0;
1863
1894
  max-height: 300px;
1864
1895
  overflow: auto;
1865
1896
  padding: 12px 14px;
@@ -1872,6 +1903,9 @@
1872
1903
 
1873
1904
  #codex-overleaf-panel .codex-plugin-confirm-actions {
1874
1905
  display: flex;
1906
+ flex: 0 0 auto;
1907
+ flex-wrap: wrap;
1908
+ align-items: stretch;
1875
1909
  justify-content: flex-end;
1876
1910
  gap: 8px;
1877
1911
  padding: 12px 14px 14px;
@@ -1879,12 +1913,23 @@
1879
1913
  }
1880
1914
 
1881
1915
  #codex-overleaf-panel .codex-plugin-confirm-actions button {
1882
- min-width: 72px;
1883
- height: 30px;
1916
+ display: flex;
1917
+ flex: 1 1 auto;
1918
+ align-items: center;
1919
+ justify-content: center;
1920
+ box-sizing: border-box;
1921
+ min-width: min(72px, 100%);
1922
+ max-width: 100%;
1923
+ min-height: 34px;
1924
+ height: auto;
1884
1925
  border: 1px solid var(--tl-border-strong);
1885
1926
  border-radius: 6px;
1886
- padding: 0 12px;
1927
+ padding: 7px 12px;
1887
1928
  font-weight: 650;
1929
+ line-height: 1.35;
1930
+ text-align: center;
1931
+ white-space: normal;
1932
+ overflow-wrap: anywhere;
1888
1933
  }
1889
1934
 
1890
1935
  #codex-overleaf-panel .codex-plugin-confirm-cancel {
@@ -82,6 +82,10 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
82
82
  return handleMirrorStatus(request, env);
83
83
  }
84
84
 
85
+ if (request.method === 'mirror.invalidate') {
86
+ return handleMirrorInvalidate(request, env);
87
+ }
88
+
85
89
  if (request.method === 'mirror.scanSensitive') {
86
90
  return handleMirrorScanSensitive(request, env);
87
91
  }
@@ -709,6 +713,27 @@ async function handleMirrorConfirmWriteback(request, env) {
709
713
  }
710
714
  }
711
715
 
716
+ function handleMirrorInvalidate(request, env) {
717
+ const projectId = request.params?.projectId;
718
+ if (typeof projectId !== 'string' || !projectId.trim()) {
719
+ return errorResponse(request.id, 'invalid_project_id', 'Mirror invalidation requires an explicit project id.');
720
+ }
721
+ const projectKey = resolveProjectKey({ projectId });
722
+ const lockToken = acquireProjectLock(projectKey);
723
+ if (!lockToken) {
724
+ return errorResponse(request.id, 'project_locked', `Project ${projectKey} is currently in use by codex.run`);
725
+ }
726
+ try {
727
+ const { markMirrorDirty } = require('./mirrorWorkspace');
728
+ markMirrorDirty({ projectId, rootDir: env.CODEX_OVERLEAF_MIRROR_ROOT, reason: 'overleaf_undo' });
729
+ return okResponse(request.id, { invalidated: true, projectKey });
730
+ } catch (error) {
731
+ return errorResponse(request.id, 'mirror_invalidation_failed', error.message);
732
+ } finally {
733
+ releaseProjectLock(projectKey, lockToken);
734
+ }
735
+ }
736
+
712
737
  function handleMirrorStatus(request, env) {
713
738
  const { getMirrorStatus } = require('./mirrorWorkspace');
714
739
  const params = request.params || {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.3.6",
3
+ "version": "2.4.0",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -17,9 +17,12 @@
17
17
  "build:content": "node scripts/build-content-bundle.mjs",
18
18
  "check:module-graph": "node scripts/check-content-module-graph.mjs",
19
19
  "test": "npm run check:module-graph && node scripts/run-tests.mjs",
20
+ "test:fast": "npm run check:module-graph && node scripts/run-tests.mjs --fast",
21
+ "test:focus": "npm run check:module-graph && node scripts/run-tests.mjs",
20
22
  "check:architecture": "node scripts/check-architecture-budget.mjs --enforce-target",
21
23
  "benchmark:large": "node scripts/benchmark-large-project.mjs --gate",
22
24
  "smoke:extension": "npm run build:content && node scripts/smoke-extension.mjs",
25
+ "smoke:prepare:chrome": "npm run build:content && npm run test:fast && npm run install:managed",
23
26
  "verify:release": "node scripts/verify-release.mjs",
24
27
  "verify:source": "node scripts/verify-release.mjs --source-hygiene-only",
25
28
  "verify:release-artifacts": "node scripts/verify-release-artifacts.mjs",
@@ -60,6 +63,7 @@
60
63
  "scripts/uninstall-native-host.mjs",
61
64
  "scripts/verify-npm-package.mjs",
62
65
  "README.md",
66
+ "README.zh-CN.md",
63
67
  "package.json"
64
68
  ],
65
69
  "devDependencies": {