codex-overleaf-link 1.8.5 → 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.
- package/README.md +45 -21
- package/bin/codex-overleaf-link.mjs +58 -0
- package/extension/assets/icons/codex-overleaf-dialog-icon.png +0 -0
- package/extension/assets/icons/codex-overleaf-icon.png +0 -0
- package/extension/assets/icons/icon128.png +0 -0
- package/extension/assets/icons/icon16.png +0 -0
- package/extension/assets/icons/icon32.png +0 -0
- package/extension/assets/icons/icon48.png +0 -0
- package/extension/bootstrap/background.js +404 -0
- package/extension/bootstrap/manifest.template.json +51 -0
- package/extension/bootstrap/popup.css +26 -0
- package/extension/bootstrap/popup.html +38 -0
- package/extension/bootstrap/popup.js +44 -0
- package/extension/bootstrap/runtimeContext.js +2 -0
- package/extension/runtime-manifest.json +60 -0
- package/extension/src/background.js +532 -0
- package/extension/src/content/applyResultFormatters.js +387 -0
- package/extension/src/content/composerAttachments.js +522 -0
- package/extension/src/content/composerPanel.js +254 -0
- package/extension/src/content/contentRuntime.js +8895 -0
- package/extension/src/content/contextTray.js +686 -0
- package/extension/src/content/diagnosticsController.js +659 -0
- package/extension/src/content/diagnosticsPanel.js +342 -0
- package/extension/src/content/diffReviewPanel.js +797 -0
- package/extension/src/content/localSkillsPanel.js +280 -0
- package/extension/src/content/markdownText.js +606 -0
- package/extension/src/content/mirrorHealth.js +92 -0
- package/extension/src/content/modelPicker.js +489 -0
- package/extension/src/content/nativeChannel.js +62 -0
- package/extension/src/content/otWarmMirror.js +964 -0
- package/extension/src/content/otWarmMirrorController.js +174 -0
- package/extension/src/content/panelMaintenance.js +367 -0
- package/extension/src/content/panelRenderer.js +253 -0
- package/extension/src/content/recentProjects.js +1063 -0
- package/extension/src/content/reviewHunks.js +247 -0
- package/extension/src/content/runController.js +242 -0
- package/extension/src/content/runTimelineView.js +1126 -0
- package/extension/src/content/sessionManager.js +487 -0
- package/extension/src/content/sessionPanel.js +263 -0
- package/extension/src/content/settingsPanel.js +527 -0
- package/extension/src/content/themeController.js +91 -0
- package/extension/src/content/updateIdle.js +67 -0
- package/extension/src/content/writebackController.js +300 -0
- package/extension/src/content/writebackOrchestrator.js +850 -0
- package/extension/src/contentScript.js +8 -0
- package/extension/src/page/compileBridge.js +460 -0
- package/extension/src/page/overleafCapabilities.js +100 -0
- package/extension/src/page/overleafEditor.js +282 -0
- package/extension/src/page/overleafProjectSnapshot.js +234 -0
- package/extension/src/page/overleafRealtimeObserver.js +491 -0
- package/extension/src/page/pageBridgeCapability.js +71 -0
- package/extension/src/page/snapshotRouter.js +948 -0
- package/extension/src/page/trackedChangesLifecycle.js +1506 -0
- package/extension/src/page/treeOperations.js +1584 -0
- package/extension/src/page/writeGuard.js +142 -0
- package/extension/src/page/writebackRouter.js +1934 -0
- package/extension/src/pageBridge.js +2224 -0
- package/extension/src/popup.js +247 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/styles/panel.css +4048 -0
- package/native-host/src/index.js +9 -2
- package/native-host/src/launcher.js +2 -0
- package/native-host/src/managedInstall.js +393 -0
- package/native-host/src/managedLauncherRuntime.js +71 -0
- package/native-host/src/nativeEnvironment.js +4 -0
- package/native-host/src/nativeHostPlatform.js +16 -0
- package/native-host/src/runtimeInstaller.js +6 -1
- package/native-host/src/taskRunnerRuntime.js +8 -0
- package/native-host/src/updateArchive.js +253 -0
- package/native-host/src/updateManager.js +548 -0
- package/native-host/src/updateTrust.js +156 -0
- package/package.json +11 -2
- package/scripts/install-managed.mjs +64 -0
- 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
|
+
});
|