codex-overleaf-link 1.8.6 → 1.9.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 (72) hide show
  1. package/README.md +45 -21
  2. package/bin/codex-overleaf-link.mjs +58 -0
  3. package/extension/assets/icons/codex-overleaf-dialog-icon.png +0 -0
  4. package/extension/assets/icons/codex-overleaf-icon.png +0 -0
  5. package/extension/assets/icons/icon128.png +0 -0
  6. package/extension/assets/icons/icon16.png +0 -0
  7. package/extension/assets/icons/icon32.png +0 -0
  8. package/extension/assets/icons/icon48.png +0 -0
  9. package/extension/bootstrap/background.js +404 -0
  10. package/extension/bootstrap/manifest.template.json +51 -0
  11. package/extension/bootstrap/popup.css +26 -0
  12. package/extension/bootstrap/popup.html +38 -0
  13. package/extension/bootstrap/popup.js +44 -0
  14. package/extension/bootstrap/runtimeContext.js +2 -0
  15. package/extension/runtime-manifest.json +60 -0
  16. package/extension/src/background.js +532 -0
  17. package/extension/src/content/applyResultFormatters.js +387 -0
  18. package/extension/src/content/composerAttachments.js +522 -0
  19. package/extension/src/content/composerPanel.js +254 -0
  20. package/extension/src/content/contentRuntime.js +8895 -0
  21. package/extension/src/content/contextTray.js +686 -0
  22. package/extension/src/content/diagnosticsController.js +659 -0
  23. package/extension/src/content/diagnosticsPanel.js +342 -0
  24. package/extension/src/content/diffReviewPanel.js +797 -0
  25. package/extension/src/content/localSkillsPanel.js +280 -0
  26. package/extension/src/content/markdownText.js +606 -0
  27. package/extension/src/content/mirrorHealth.js +92 -0
  28. package/extension/src/content/modelPicker.js +489 -0
  29. package/extension/src/content/nativeChannel.js +62 -0
  30. package/extension/src/content/otWarmMirror.js +964 -0
  31. package/extension/src/content/otWarmMirrorController.js +174 -0
  32. package/extension/src/content/panelMaintenance.js +367 -0
  33. package/extension/src/content/panelRenderer.js +253 -0
  34. package/extension/src/content/recentProjects.js +1063 -0
  35. package/extension/src/content/reviewHunks.js +247 -0
  36. package/extension/src/content/runController.js +242 -0
  37. package/extension/src/content/runTimelineView.js +1126 -0
  38. package/extension/src/content/sessionManager.js +487 -0
  39. package/extension/src/content/sessionPanel.js +263 -0
  40. package/extension/src/content/settingsPanel.js +527 -0
  41. package/extension/src/content/themeController.js +91 -0
  42. package/extension/src/content/updateIdle.js +67 -0
  43. package/extension/src/content/writebackController.js +300 -0
  44. package/extension/src/content/writebackOrchestrator.js +850 -0
  45. package/extension/src/contentScript.js +8 -0
  46. package/extension/src/page/compileBridge.js +460 -0
  47. package/extension/src/page/overleafCapabilities.js +100 -0
  48. package/extension/src/page/overleafEditor.js +282 -0
  49. package/extension/src/page/overleafProjectSnapshot.js +234 -0
  50. package/extension/src/page/overleafRealtimeObserver.js +491 -0
  51. package/extension/src/page/pageBridgeCapability.js +71 -0
  52. package/extension/src/page/snapshotRouter.js +948 -0
  53. package/extension/src/page/trackedChangesLifecycle.js +1506 -0
  54. package/extension/src/page/treeOperations.js +1584 -0
  55. package/extension/src/page/writeGuard.js +142 -0
  56. package/extension/src/page/writebackRouter.js +1934 -0
  57. package/extension/src/pageBridge.js +2224 -0
  58. package/extension/src/popup.js +247 -0
  59. package/extension/src/shared/compatibility.js +1 -1
  60. package/extension/styles/panel.css +4048 -0
  61. package/native-host/src/index.js +9 -2
  62. package/native-host/src/managedInstall.js +393 -0
  63. package/native-host/src/managedLauncherRuntime.js +71 -0
  64. package/native-host/src/nativeHostPlatform.js +16 -0
  65. package/native-host/src/runtimeInstaller.js +6 -1
  66. package/native-host/src/taskRunnerRuntime.js +8 -0
  67. package/native-host/src/updateArchive.js +253 -0
  68. package/native-host/src/updateManager.js +548 -0
  69. package/native-host/src/updateTrust.js +156 -0
  70. package/package.json +11 -2
  71. package/scripts/install-managed.mjs +64 -0
  72. package/scripts/uninstall-managed.mjs +48 -0
@@ -0,0 +1,282 @@
1
+ (function initOverleafEditor(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafEditorAdapter = factory();
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function overleafEditorFactory() {
8
+ 'use strict';
9
+
10
+ function create(deps = {}) {
11
+ function detectEditor() {
12
+ if (deps.getCodeMirrorEditorView?.()) {
13
+ return {
14
+ ok: true,
15
+ type: 'codemirror-view'
16
+ };
17
+ }
18
+ if (deps.findEditorTextArea?.()) {
19
+ return {
20
+ ok: true,
21
+ type: 'textarea'
22
+ };
23
+ }
24
+ if (deps.findEditorContentNode?.('.cm-content')) {
25
+ return {
26
+ ok: true,
27
+ type: 'codemirror'
28
+ };
29
+ }
30
+ if (deps.findEditorContentNode?.('[contenteditable="true"]')) {
31
+ return {
32
+ ok: true,
33
+ type: 'contenteditable'
34
+ };
35
+ }
36
+ return {
37
+ ok: false,
38
+ type: 'unknown'
39
+ };
40
+ }
41
+
42
+ function readActiveEditorText() {
43
+ const editorView = deps.getCodeMirrorEditorView?.();
44
+ if (editorView) {
45
+ return deps.getCodeMirrorDocText(editorView);
46
+ }
47
+
48
+ const active = deps.getDeepActiveElement?.();
49
+ if (active && active.tagName === 'TEXTAREA' && !deps.isInsideCodexPanel?.(active)) {
50
+ return active.value;
51
+ }
52
+
53
+ const textarea = deps.findEditorTextArea?.();
54
+ if (textarea) {
55
+ return textarea.value;
56
+ }
57
+
58
+ const cm = deps.findEditorContentNode?.('.cm-content');
59
+ if (cm) {
60
+ return cm.innerText || cm.textContent || '';
61
+ }
62
+
63
+ const editable = deps.findEditorContentNode?.('[contenteditable="true"]');
64
+ if (editable) {
65
+ return editable.innerText || editable.textContent || '';
66
+ }
67
+
68
+ return '';
69
+ }
70
+
71
+ function replaceActiveEditorText(text) {
72
+ const editorView = deps.getCodeMirrorEditorView?.();
73
+ if (editorView) {
74
+ const from = 0;
75
+ const to = deps.getCodeMirrorDocLength(editorView);
76
+ editorView.dispatch({
77
+ changes: {
78
+ from,
79
+ to,
80
+ insert: text
81
+ }
82
+ });
83
+ return {
84
+ ok: true,
85
+ method: 'codemirror-view'
86
+ };
87
+ }
88
+
89
+ const active = deps.getDeepActiveElement?.();
90
+ const textarea = active?.tagName === 'TEXTAREA' && !deps.isInsideCodexPanel?.(active)
91
+ ? active
92
+ : deps.findEditorTextArea?.();
93
+
94
+ if (textarea) {
95
+ textarea.focus();
96
+ textarea.value = text;
97
+ textarea.dispatchEvent(new deps.InputEvent('input', { bubbles: true, inputType: 'insertReplacementText', data: text }));
98
+ textarea.dispatchEvent(new deps.Event('change', { bubbles: true }));
99
+ return {
100
+ ok: true,
101
+ method: 'textarea'
102
+ };
103
+ }
104
+
105
+ const editable = deps.findEditorContentNode?.('.cm-content') || deps.findEditorContentNode?.('[contenteditable="true"]');
106
+ if (editable) {
107
+ editable.focus();
108
+ deps.document.execCommand('selectAll', false, null);
109
+ deps.document.execCommand('insertText', false, text);
110
+ editable.dispatchEvent(new deps.InputEvent('input', { bubbles: true, inputType: 'insertReplacementText', data: text }));
111
+ return {
112
+ ok: true,
113
+ method: 'contenteditable'
114
+ };
115
+ }
116
+
117
+ return {
118
+ ok: false,
119
+ reason: 'No editable surface was detected'
120
+ };
121
+ }
122
+
123
+ function replaceActiveEditorPatches(patches, nextContent) {
124
+ const normalized = deps.normalizeTextPatches(patches, readActiveEditorText().length);
125
+ if (!normalized.ok) {
126
+ return normalized;
127
+ }
128
+
129
+ const editorView = deps.getCodeMirrorEditorView?.();
130
+ if (editorView) {
131
+ editorView.dispatch({
132
+ changes: normalized.patches.map(patch => ({
133
+ from: patch.from,
134
+ to: patch.to,
135
+ insert: patch.insert
136
+ }))
137
+ });
138
+ return {
139
+ ok: true,
140
+ method: 'codemirror-view-patch'
141
+ };
142
+ }
143
+
144
+ const active = deps.getDeepActiveElement?.();
145
+ const textarea = active?.tagName === 'TEXTAREA' && !deps.isInsideCodexPanel?.(active)
146
+ ? active
147
+ : deps.findEditorTextArea?.();
148
+
149
+ if (textarea && typeof textarea.setRangeText === 'function') {
150
+ textarea.focus();
151
+ for (const patch of normalized.patches.slice().sort((left, right) => right.from - left.from)) {
152
+ textarea.setRangeText(patch.insert, patch.from, patch.to, 'end');
153
+ }
154
+ textarea.dispatchEvent(new deps.InputEvent('input', { bubbles: true, inputType: 'insertReplacementText', data: '' }));
155
+ textarea.dispatchEvent(new deps.Event('change', { bubbles: true }));
156
+ return {
157
+ ok: true,
158
+ method: 'textarea-patch'
159
+ };
160
+ }
161
+
162
+ const result = replaceActiveEditorText(nextContent);
163
+ return result.ok
164
+ ? {
165
+ ...result,
166
+ method: `${result.method}-patch-fallback`
167
+ }
168
+ : result;
169
+ }
170
+
171
+ function focusActiveEditorRange(from, to) {
172
+ const range = normalizeSelectionRange(from, to);
173
+ if (!range.ok) {
174
+ return range;
175
+ }
176
+
177
+ const editorView = deps.getCodeMirrorEditorView?.();
178
+ if (editorView) {
179
+ const length = getKnownCodeMirrorDocLength(editorView);
180
+ if (Number.isInteger(length) && !isSelectionRangeInBounds(range, length)) {
181
+ return rangeOutOfBounds(range, length);
182
+ }
183
+ editorView.focus?.();
184
+ editorView.dispatch({
185
+ selection: {
186
+ anchor: range.from,
187
+ head: range.to
188
+ },
189
+ scrollIntoView: true
190
+ });
191
+ return {
192
+ ok: true,
193
+ method: 'codemirror-view-selection'
194
+ };
195
+ }
196
+
197
+ const active = deps.getDeepActiveElement?.();
198
+ const textarea = active?.tagName === 'TEXTAREA' && !deps.isInsideCodexPanel?.(active)
199
+ ? active
200
+ : deps.findEditorTextArea?.();
201
+
202
+ if (textarea) {
203
+ const length = typeof textarea.value === 'string' ? textarea.value.length : null;
204
+ if (Number.isInteger(length) && !isSelectionRangeInBounds(range, length)) {
205
+ return rangeOutOfBounds(range, length);
206
+ }
207
+ textarea.focus();
208
+ textarea.setSelectionRange(range.from, range.to);
209
+ return {
210
+ ok: true,
211
+ method: 'textarea-selection'
212
+ };
213
+ }
214
+
215
+ const editable = deps.findEditorContentNode?.('.cm-content') || deps.findEditorContentNode?.('[contenteditable="true"]');
216
+ if (editable) {
217
+ editable.focus();
218
+ return {
219
+ ok: true,
220
+ method: 'selection-fallback'
221
+ };
222
+ }
223
+
224
+ return {
225
+ ok: false,
226
+ code: 'editor_not_found',
227
+ reason: 'No editable surface was detected'
228
+ };
229
+ }
230
+
231
+ function normalizeSelectionRange(from, to) {
232
+ if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < 0) {
233
+ return {
234
+ ok: false,
235
+ code: 'invalid_range',
236
+ reason: 'Selection offsets must be non-negative integers'
237
+ };
238
+ }
239
+ return {
240
+ ok: true,
241
+ from,
242
+ to
243
+ };
244
+ }
245
+
246
+ function getKnownCodeMirrorDocLength(editorView) {
247
+ try {
248
+ const length = deps.getCodeMirrorDocLength?.(editorView);
249
+ return Number.isInteger(length) ? length : null;
250
+ } catch (_error) {
251
+ return null;
252
+ }
253
+ }
254
+
255
+ function isSelectionRangeInBounds(range, length) {
256
+ return range.from <= length && range.to <= length;
257
+ }
258
+
259
+ function rangeOutOfBounds(range, length) {
260
+ return {
261
+ ok: false,
262
+ code: 'range_out_of_bounds',
263
+ reason: `Selection offsets are out of range for document length ${length}`,
264
+ from: range.from,
265
+ to: range.to,
266
+ length
267
+ };
268
+ }
269
+
270
+ return {
271
+ detectEditor,
272
+ focusActiveEditorRange,
273
+ readActiveEditorText,
274
+ replaceActiveEditorPatches,
275
+ replaceActiveEditorText
276
+ };
277
+ }
278
+
279
+ return {
280
+ create
281
+ };
282
+ });
@@ -0,0 +1,234 @@
1
+ (function initOverleafProjectSnapshot(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafProjectSnapshot = factory();
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function overleafProjectSnapshotFactory() {
8
+ 'use strict';
9
+
10
+ const SNAPSHOT_DEFAULT_MAX_AGE_MS = 2500;
11
+ const SNAPSHOT_MAX_CACHE_MS = 15000;
12
+ const FILE_LIST_DEFAULT_MAX_AGE_MS = 300000;
13
+
14
+ function create(deps = {}) {
15
+ const pageWindow = deps.window || window;
16
+ const snapshotCache = createKeyedCache();
17
+ const fileListCache = createCache();
18
+
19
+ async function getProjectSnapshot(params = {}) {
20
+ const cacheKey = getSnapshotCacheKey(params);
21
+ const maxAgeMs = normalizeSnapshotMaxAge(params.maxAgeMs);
22
+ const now = Date.now();
23
+ const cacheEntry = snapshotCache.entries.get(cacheKey);
24
+
25
+ if (!params.force && cacheEntry?.pending) {
26
+ return cacheEntry.pending;
27
+ }
28
+
29
+ if (!params.force && cacheEntry?.value && now - cacheEntry.capturedAt <= maxAgeMs) {
30
+ return withSnapshotCacheMetadata(cacheEntry.value, 'memory', cacheEntry.capturedAt);
31
+ }
32
+
33
+ const pending = deps.buildProjectSnapshot(params)
34
+ .then(snapshot => {
35
+ const capturedAt = Date.now();
36
+ const currentEntry = snapshotCache.entries.get(cacheKey);
37
+ if (currentEntry?.pending === pending) {
38
+ snapshotCache.entries.set(cacheKey, {
39
+ value: snapshot,
40
+ capturedAt,
41
+ pending: null
42
+ });
43
+ }
44
+ return withSnapshotCacheMetadata(snapshot, 'fresh', capturedAt);
45
+ })
46
+ .finally(() => {
47
+ const currentEntry = snapshotCache.entries.get(cacheKey);
48
+ if (currentEntry?.pending === pending) {
49
+ currentEntry.pending = null;
50
+ }
51
+ });
52
+
53
+ snapshotCache.entries.set(cacheKey, {
54
+ value: cacheEntry?.value || null,
55
+ capturedAt: cacheEntry?.capturedAt || 0,
56
+ pending
57
+ });
58
+ return pending;
59
+ }
60
+
61
+ async function getProjectFileList(params = {}) {
62
+ const cacheKey = getProjectFileListCacheKey(params);
63
+ const maxAgeMs = normalizeFileListMaxAge(params.maxAgeMs);
64
+ const now = Date.now();
65
+
66
+ if (fileListCache.key === cacheKey && fileListCache.pending) {
67
+ return fileListCache.pending;
68
+ }
69
+
70
+ if (!params.force && fileListCache.key === cacheKey && fileListCache.value && now - fileListCache.capturedAt <= maxAgeMs) {
71
+ return withFileListCacheMetadata(fileListCache.value, 'memory', fileListCache.capturedAt);
72
+ }
73
+
74
+ const pending = deps.buildProjectFileList(params)
75
+ .then(fileList => {
76
+ const capturedAt = Date.now();
77
+ if (shouldCacheProjectFileList(fileList, params)) {
78
+ fileListCache.key = cacheKey;
79
+ fileListCache.value = fileList;
80
+ fileListCache.capturedAt = capturedAt;
81
+ } else if (fileListCache.key === cacheKey) {
82
+ fileListCache.value = null;
83
+ fileListCache.capturedAt = 0;
84
+ }
85
+ return withFileListCacheMetadata(fileList, 'fresh', capturedAt);
86
+ })
87
+ .finally(() => {
88
+ if (fileListCache.pending === pending) {
89
+ fileListCache.pending = null;
90
+ }
91
+ });
92
+
93
+ fileListCache.key = cacheKey;
94
+ fileListCache.pending = pending;
95
+ return pending;
96
+ }
97
+
98
+ function invalidateProjectSnapshot() {
99
+ resetKeyedCache(snapshotCache);
100
+ }
101
+
102
+ function getSnapshotCacheKey(params = {}) {
103
+ const focusKey = Array.isArray(params.focusFiles)
104
+ ? params.focusFiles.map(path => deps.normalizePath?.(path)).filter(Boolean).sort().join(',')
105
+ : '';
106
+ return [
107
+ pageWindow.location.origin,
108
+ deps.getProjectId?.() || pageWindow.location.pathname || pageWindow.location.href,
109
+ params.preferLightweight ? 'lightweight' : 'full',
110
+ params.zipOnly ? 'zip-only' : 'zip-or-page',
111
+ params.allowZipFallback === false ? 'no-zip-fallback' : 'zip-fallback',
112
+ params.allowEditorNavigation === false ? 'no-editor-navigation' : params.allowEditorNavigation === true ? 'editor-navigation' : 'default-editor-navigation',
113
+ params.requireFullProject ? 'require-full-project' : 'allow-partial-project',
114
+ params.restrictToRequestedPathsOnly === true ? 'requested-only' : 'allow-project-expansion',
115
+ params.includeBinaryFiles ? 'binary' : 'text',
116
+ params.includeContent === false ? 'list' : 'content',
117
+ `zip-timeout=${normalizeZipTimeoutKey(params.zipTimeoutMs)}`,
118
+ params.requestedPathsCacheKey || '',
119
+ focusKey
120
+ ].join(':');
121
+ }
122
+
123
+ function getProjectFileListCacheKey(params = {}) {
124
+ return [
125
+ pageWindow.location.origin,
126
+ deps.getProjectId?.() || pageWindow.location.pathname || pageWindow.location.href,
127
+ params.preferExact === false ? 'tree' : 'exact',
128
+ params.exactOnly === true || params.requireExact === true ? 'required' : 'fallback'
129
+ ].join(':');
130
+ }
131
+
132
+ return {
133
+ getProjectFileList,
134
+ getProjectSnapshot,
135
+ invalidateProjectSnapshot
136
+ };
137
+ }
138
+
139
+ function shouldCacheProjectFileList(fileList, params = {}) {
140
+ if (params.preferExact === false) {
141
+ return true;
142
+ }
143
+ return fileList?.capabilities?.method === 'overleaf-zip-file-list';
144
+ }
145
+
146
+ function createCache() {
147
+ return {
148
+ key: '',
149
+ capturedAt: 0,
150
+ value: null,
151
+ pending: null
152
+ };
153
+ }
154
+
155
+ function createKeyedCache() {
156
+ return {
157
+ entries: new Map()
158
+ };
159
+ }
160
+
161
+ function resetKeyedCache(cache) {
162
+ cache.entries.clear();
163
+ }
164
+
165
+ function resetCache(cache) {
166
+ cache.key = '';
167
+ cache.capturedAt = 0;
168
+ cache.value = null;
169
+ cache.pending = null;
170
+ }
171
+
172
+ function normalizeSnapshotMaxAge(value) {
173
+ if (value === 0) {
174
+ return 0;
175
+ }
176
+ const number = Number(value);
177
+ if (!Number.isFinite(number)) {
178
+ return SNAPSHOT_DEFAULT_MAX_AGE_MS;
179
+ }
180
+ return Math.max(0, Math.min(number, SNAPSHOT_MAX_CACHE_MS));
181
+ }
182
+
183
+ function normalizeFileListMaxAge(value) {
184
+ if (value === 0) {
185
+ return 0;
186
+ }
187
+ const number = Number(value);
188
+ if (!Number.isFinite(number)) {
189
+ return FILE_LIST_DEFAULT_MAX_AGE_MS;
190
+ }
191
+ return Math.max(0, Math.min(number, FILE_LIST_DEFAULT_MAX_AGE_MS));
192
+ }
193
+
194
+ function normalizeZipTimeoutKey(value) {
195
+ const parsed = Number(value);
196
+ return Number.isFinite(parsed) ? String(parsed) : 'default';
197
+ }
198
+
199
+ function withSnapshotCacheMetadata(snapshot, cacheState, capturedAt) {
200
+ const capabilities = snapshot?.capabilities || {};
201
+ return {
202
+ ...snapshot,
203
+ capabilities: {
204
+ ...capabilities,
205
+ diagnostics: {
206
+ ...(capabilities.diagnostics || {}),
207
+ snapshotCache: cacheState,
208
+ snapshotCapturedAt: new Date(capturedAt).toISOString()
209
+ }
210
+ }
211
+ };
212
+ }
213
+
214
+ function withFileListCacheMetadata(fileList, cacheState, capturedAt) {
215
+ const capabilities = fileList?.capabilities || {};
216
+ return {
217
+ ...fileList,
218
+ capabilities: {
219
+ ...capabilities,
220
+ diagnostics: {
221
+ ...(capabilities.diagnostics || {}),
222
+ fileListCache: cacheState,
223
+ fileListCapturedAt: new Date(capturedAt).toISOString()
224
+ }
225
+ }
226
+ };
227
+ }
228
+
229
+ return {
230
+ create,
231
+ normalizeFileListMaxAge,
232
+ normalizeSnapshotMaxAge
233
+ };
234
+ });