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,263 @@
|
|
|
1
|
+
(function initCodexOverleafSessionPanel() {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function create(options = {}) {
|
|
5
|
+
const container = options.container;
|
|
6
|
+
if (!container) {
|
|
7
|
+
throw new Error('CodexOverleafSessionPanel requires a container');
|
|
8
|
+
}
|
|
9
|
+
const instance = {
|
|
10
|
+
container,
|
|
11
|
+
callbacks: options.callbacks || {},
|
|
12
|
+
i18n: options.i18n || {},
|
|
13
|
+
deriveSessionTitle: options.deriveSessionTitle,
|
|
14
|
+
isDisplayableSession: options.isDisplayableSession || (() => true),
|
|
15
|
+
selectVisibleSessionsForList: options.selectVisibleSessionsForList,
|
|
16
|
+
isSessionRunning: options.isSessionRunning || (() => false),
|
|
17
|
+
formatSessionTime: options.formatSessionTime || defaultFormatSessionTime,
|
|
18
|
+
maxVisible: options.maxVisible || 3,
|
|
19
|
+
pinnedSessionIds: options.pinnedSessionIds || [],
|
|
20
|
+
sessions: options.sessions || [],
|
|
21
|
+
activeSessionId: options.activeSessionId || null,
|
|
22
|
+
showAll: false
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
container.innerHTML = `
|
|
26
|
+
<section class="codex-task-section">
|
|
27
|
+
<div class="codex-section-head">
|
|
28
|
+
<span data-i18n="sessionsHead">Sessions</span>
|
|
29
|
+
<span data-session-count></span>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="codex-session-list" data-session-list></div>
|
|
32
|
+
<button type="button" class="codex-view-all" data-view-all hidden></button>
|
|
33
|
+
</section>
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
container.querySelector('[data-view-all]')?.addEventListener('click', () => {
|
|
37
|
+
instance.showAll = true;
|
|
38
|
+
render(instance);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
render(instance);
|
|
42
|
+
return {
|
|
43
|
+
update: (sessions, activeSessionId, updateOptions = {}) => update(instance, sessions, activeSessionId, updateOptions),
|
|
44
|
+
destroy: () => destroy(instance),
|
|
45
|
+
_instance: instance
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function update(target, sessions, activeSessionId, updateOptions = {}) {
|
|
50
|
+
const instance = target?._instance || target;
|
|
51
|
+
if (!instance) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
instance.sessions = Array.isArray(sessions) ? sessions : [];
|
|
55
|
+
instance.activeSessionId = activeSessionId || null;
|
|
56
|
+
instance.pinnedSessionIds = updateOptions.pinnedSessionIds || instance.pinnedSessionIds || [];
|
|
57
|
+
// Preserve the expanded ("View all") state across re-renders (delete, rename,
|
|
58
|
+
// a composer keystroke, a run start) — only change it when a caller passes an
|
|
59
|
+
// explicit value. Previously every update snapped the list back to 3 rows.
|
|
60
|
+
if (updateOptions.showAll !== undefined) {
|
|
61
|
+
instance.showAll = updateOptions.showAll === true;
|
|
62
|
+
}
|
|
63
|
+
render(instance);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function render(instance) {
|
|
67
|
+
const list = instance.container.querySelector('[data-session-list]');
|
|
68
|
+
if (!list) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const sessions = (instance.sessions || []).filter(instance.isDisplayableSession);
|
|
73
|
+
const visibleSessions = getVisibleSessions(instance, sessions);
|
|
74
|
+
const count = instance.container.querySelector('[data-session-count]');
|
|
75
|
+
if (count) {
|
|
76
|
+
count.textContent = sessions.length ? `${sessions.length}` : '';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
list.replaceChildren();
|
|
80
|
+
if (!visibleSessions.length) {
|
|
81
|
+
const hint = document.createElement('div');
|
|
82
|
+
hint.className = 'codex-session-empty-hint';
|
|
83
|
+
hint.textContent = t(instance, 'sessionListEmpty');
|
|
84
|
+
list.append(hint);
|
|
85
|
+
}
|
|
86
|
+
for (const session of visibleSessions) {
|
|
87
|
+
list.append(renderSessionRow(instance, session));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const viewAll = instance.container.querySelector('[data-view-all]');
|
|
91
|
+
if (viewAll) {
|
|
92
|
+
viewAll.hidden = instance.showAll || sessions.length <= instance.maxVisible;
|
|
93
|
+
viewAll.textContent = t(instance, 'viewAllSessions', { count: sessions.length });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getVisibleSessions(instance, sessions) {
|
|
98
|
+
if (typeof instance.selectVisibleSessionsForList === 'function') {
|
|
99
|
+
return instance.selectVisibleSessionsForList(instance.sessions, instance.activeSessionId, {
|
|
100
|
+
showAll: instance.showAll,
|
|
101
|
+
maxVisible: instance.maxVisible,
|
|
102
|
+
pinnedSessionIds: instance.pinnedSessionIds
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return instance.showAll ? sessions : sessions.slice(0, instance.maxVisible);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function renderSessionRow(instance, session) {
|
|
109
|
+
const row = document.createElement('div');
|
|
110
|
+
const isRunningSession = instance.isSessionRunning(session);
|
|
111
|
+
row.className = 'codex-session-row';
|
|
112
|
+
row.dataset.active = session.id === instance.activeSessionId ? 'true' : 'false';
|
|
113
|
+
row.dataset.running = isRunningSession ? 'true' : 'false';
|
|
114
|
+
|
|
115
|
+
const switchButton = document.createElement('button');
|
|
116
|
+
switchButton.type = 'button';
|
|
117
|
+
switchButton.className = 'codex-session-switch';
|
|
118
|
+
const titleNode = document.createElement('span');
|
|
119
|
+
titleNode.className = 'codex-session-row-title';
|
|
120
|
+
const timeNode = document.createElement('time');
|
|
121
|
+
switchButton.append(titleNode, timeNode);
|
|
122
|
+
|
|
123
|
+
const titleInput = document.createElement('input');
|
|
124
|
+
titleInput.type = 'text';
|
|
125
|
+
titleInput.className = 'codex-session-title-input';
|
|
126
|
+
titleInput.setAttribute('aria-label', t(instance, 'renameSession'));
|
|
127
|
+
titleInput.maxLength = 80;
|
|
128
|
+
titleInput.hidden = true;
|
|
129
|
+
|
|
130
|
+
const renameButton = document.createElement('button');
|
|
131
|
+
renameButton.type = 'button';
|
|
132
|
+
renameButton.className = 'codex-session-rename';
|
|
133
|
+
renameButton.title = t(instance, 'renameSession');
|
|
134
|
+
renameButton.setAttribute('aria-label', t(instance, 'renameSession'));
|
|
135
|
+
renameButton.textContent = '✎';
|
|
136
|
+
|
|
137
|
+
const deleteButton = document.createElement('button');
|
|
138
|
+
deleteButton.type = 'button';
|
|
139
|
+
deleteButton.className = 'codex-session-delete';
|
|
140
|
+
deleteButton.textContent = '×';
|
|
141
|
+
|
|
142
|
+
row.append(switchButton, titleInput, renameButton, deleteButton);
|
|
143
|
+
const displayTitle = getDisplayTitle(instance, session);
|
|
144
|
+
switchButton.title = displayTitle;
|
|
145
|
+
titleNode.textContent = displayTitle;
|
|
146
|
+
timeNode.textContent = instance.formatSessionTime(session.updatedAt || session.createdAt);
|
|
147
|
+
const absoluteTs = session.updatedAt || session.createdAt;
|
|
148
|
+
if (absoluteTs) {
|
|
149
|
+
timeNode.title = new Date(absoluteTs).toLocaleString();
|
|
150
|
+
}
|
|
151
|
+
switchButton.addEventListener('click', () => instance.callbacks.onSelect?.(session.id));
|
|
152
|
+
// Keyboard nav: Up/Down move focus between rows, Enter/Space switch (native
|
|
153
|
+
// button activation -> onSelect), Delete/Backspace delete the focused row.
|
|
154
|
+
switchButton.addEventListener('keydown', event => {
|
|
155
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
const switches = Array.from(instance.container.querySelectorAll('.codex-session-switch'));
|
|
158
|
+
const index = switches.indexOf(switchButton);
|
|
159
|
+
const next = event.key === 'ArrowDown' ? switches[index + 1] : switches[index - 1];
|
|
160
|
+
next?.focus();
|
|
161
|
+
} else if ((event.key === 'Delete' || event.key === 'Backspace') && !deleteButton.disabled) {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
instance.callbacks.onDelete?.(session.id);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
renameButton.addEventListener('click', event => {
|
|
167
|
+
event.stopPropagation();
|
|
168
|
+
beginRename(instance, row, session);
|
|
169
|
+
});
|
|
170
|
+
deleteButton.disabled = isRunningSession;
|
|
171
|
+
deleteButton.title = isRunningSession ? t(instance, 'deleteRunningSession') : t(instance, 'deleteSession');
|
|
172
|
+
deleteButton.setAttribute('aria-label', isRunningSession ? t(instance, 'deleteRunningSession') : t(instance, 'deleteSession'));
|
|
173
|
+
deleteButton.addEventListener('click', event => {
|
|
174
|
+
event.stopPropagation();
|
|
175
|
+
instance.callbacks.onDelete?.(session.id);
|
|
176
|
+
});
|
|
177
|
+
return row;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function beginRename(instance, row, session) {
|
|
181
|
+
const input = row?.querySelector('.codex-session-title-input');
|
|
182
|
+
const switchButton = row?.querySelector('.codex-session-switch');
|
|
183
|
+
if (!session || !input || !switchButton) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let settled = false;
|
|
188
|
+
row.dataset.editing = 'true';
|
|
189
|
+
input.value = getDisplayTitle(instance, session);
|
|
190
|
+
input.hidden = false;
|
|
191
|
+
switchButton.hidden = true;
|
|
192
|
+
input.focus();
|
|
193
|
+
input.select();
|
|
194
|
+
|
|
195
|
+
const cleanup = () => {
|
|
196
|
+
input.removeEventListener('keydown', onKeydown);
|
|
197
|
+
input.removeEventListener('blur', onBlur);
|
|
198
|
+
};
|
|
199
|
+
const finish = commit => {
|
|
200
|
+
if (settled) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
settled = true;
|
|
204
|
+
cleanup();
|
|
205
|
+
if (!commit) {
|
|
206
|
+
render(instance);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
instance.callbacks.onRename?.(session.id, input.value);
|
|
210
|
+
};
|
|
211
|
+
const onKeydown = event => {
|
|
212
|
+
if (event.key === 'Enter') {
|
|
213
|
+
event.preventDefault();
|
|
214
|
+
finish(true);
|
|
215
|
+
} else if (event.key === 'Escape') {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
finish(false);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const onBlur = () => finish(true);
|
|
221
|
+
input.addEventListener('keydown', onKeydown);
|
|
222
|
+
input.addEventListener('blur', onBlur);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function getDisplayTitle(target, session) {
|
|
226
|
+
const instance = target?._instance || target;
|
|
227
|
+
const title = typeof session?.title === 'string' ? session.title.trim() : '';
|
|
228
|
+
if (title && title !== 'New task') {
|
|
229
|
+
return title;
|
|
230
|
+
}
|
|
231
|
+
if (typeof instance?.deriveSessionTitle === 'function') {
|
|
232
|
+
return instance.deriveSessionTitle(session?.runs, session?.task) || t(instance, 'newSessionFallback');
|
|
233
|
+
}
|
|
234
|
+
return session?.task || t(instance, 'newSessionFallback');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function t(instance, key, params) {
|
|
238
|
+
if (typeof instance?.i18n === 'function') {
|
|
239
|
+
return instance.i18n(key, params);
|
|
240
|
+
}
|
|
241
|
+
if (typeof instance?.i18n?.tr === 'function') {
|
|
242
|
+
return instance.i18n.tr(key, params);
|
|
243
|
+
}
|
|
244
|
+
if (typeof instance?.i18n?.t === 'function') {
|
|
245
|
+
return instance.i18n.t(key, params);
|
|
246
|
+
}
|
|
247
|
+
return key;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function defaultFormatSessionTime(timestamp) {
|
|
251
|
+
return timestamp ? String(timestamp) : '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function destroy(instance) {
|
|
255
|
+
instance.container.textContent = '';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
window.CodexOverleafSessionPanel = {
|
|
259
|
+
create,
|
|
260
|
+
update,
|
|
261
|
+
getDisplayTitle
|
|
262
|
+
};
|
|
263
|
+
})();
|