codex-overleaf-link 2.5.0 → 2.6.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 +155 -392
- package/README.zh-CN.md +154 -393
- package/extension/bootstrap/background.js +52 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/bootstrap/popup.html +1 -0
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/src/background.js +283 -55
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +129 -16
- package/extension/src/backgroundUpdateCoordinator.js +242 -29
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +171 -8
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +104 -47
- package/extension/src/content/composerPanel.js +110 -12
- package/extension/src/content/contentRuntime.js +1555 -926
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +48 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +11077 -3412
- package/extension/src/content/generated/content.bundle.meta.json +14 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +10 -0
- package/extension/src/content/localSkillsPanel.js +104 -14
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +47 -11
- package/extension/src/content/modelPicker.js +8 -4
- package/extension/src/content/nativeCompatibilityController.js +74 -30
- package/extension/src/content/otWarmMirror.js +5 -6
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +17 -4
- package/extension/src/content/panelMaintenance.js +46 -14
- package/extension/src/content/panelRenderer.js +57 -27
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +67 -6
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +73 -14
- package/extension/src/content/recentProjects.js +15 -8
- package/extension/src/content/runActivitySummary.js +160 -18
- package/extension/src/content/runCompletionMeta.js +532 -0
- package/extension/src/content/runController.js +39 -15
- package/extension/src/content/runFailureNotice.js +29 -13
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +157 -47
- package/extension/src/content/runRecoveryActions.js +147 -0
- package/extension/src/content/runResultActions.js +28 -143
- package/extension/src/content/runReviewController.js +633 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +367 -175
- package/extension/src/content/scopedPersistencePanelState.js +134 -20
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +153 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +50 -4
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +22 -5
- package/extension/src/content/settingsWorkbench.js +132 -89
- package/extension/src/content/sharedSessionViewSync.js +49 -11
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +118 -12
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +116 -94
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +1 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +6 -4
- package/extension/src/page/snapshotRouter.js +84 -39
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +114 -26
- package/extension/src/page/trackedChangeCapture.js +12 -2
- package/extension/src/page/trackedChangeOwnership.js +19 -1
- package/extension/src/page/trackedChangeReplay.js +41 -21
- package/extension/src/page/trackedChangesLifecycle.js +217 -199
- package/extension/src/page/treeOperations.js +7 -10
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +2 -0
- package/extension/src/page/writebackRouter.js +103 -155
- package/extension/src/pageBridge.js +126 -69
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +373 -17
- package/extension/src/shared/auditRecords.js +4 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +129 -66
- package/extension/src/shared/composerAttachmentDraft.js +156 -0
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +349 -4
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +590 -24
- package/extension/src/shared/lineReferences.js +38 -31
- package/extension/src/shared/managedUpdateProjection.js +2 -2
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +5 -4
- package/extension/src/shared/panelIcons.js +39 -0
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- package/extension/src/shared/reviewing.js +130 -1
- package/extension/src/shared/runActivityModel.js +70 -4
- package/extension/src/shared/runExecutionSnapshot.js +13 -0
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +132 -28
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +118 -27
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +166 -52
- package/extension/src/shared/storageDb.js +90 -26
- package/extension/src/shared/storageMigration.js +78 -112
- package/extension/src/shared/storageRunActions.js +267 -30
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +253 -32
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +17 -3
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +58 -40
- package/extension/styles/panel.css +1123 -493
- package/native-host/src/anthropicBridgeRequest.js +4 -2
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +269 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +20 -1
- package/native-host/src/chatCompletionsBridge.js +48 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexPromptAssembly.js +114 -127
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +234 -198
- package/native-host/src/codexSessionTiming.js +18 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +110 -35
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/processStopBarrier.js +42 -7
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerProfile.js +4 -2
- package/native-host/src/providerReasoning.js +2 -7
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +72 -14
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +36 -8
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +79 -66
- package/native-host/src/subagentTelemetry.js +19 -10
- package/native-host/src/subagentWorkspacePath.js +5 -2
- package/native-host/src/taskRunnerRuntime.js +92 -57
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +409 -46
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +5 -8
- package/scripts/install-managed.mjs +1 -0
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
const tx = typeof options.tx === 'function' ? options.tx : (en) => en;
|
|
7
7
|
const tr = typeof options.tr === 'function' ? options.tr : (key) => key;
|
|
8
8
|
const appendPlainLog = typeof options.appendPlainLog === 'function' ? options.appendPlainLog : () => {};
|
|
9
|
+
const onChange = typeof options.onChange === 'function' ? options.onChange : () => {};
|
|
10
|
+
const icons = options.icons || null;
|
|
9
11
|
const limits = options.limits || {};
|
|
10
12
|
const maxAttachments = Number(limits.maxAttachments) || 8;
|
|
11
13
|
const maxAttachmentBytes = Number(limits.maxAttachmentBytes) || 12 * 1024 * 1024;
|
|
@@ -15,6 +17,8 @@
|
|
|
15
17
|
let attachments = [];
|
|
16
18
|
const pendingAttachmentKeys = new Set();
|
|
17
19
|
let pendingAttachmentBytes = 0;
|
|
20
|
+
let attachmentGeneration = 0;
|
|
21
|
+
let attachmentRevision = 0;
|
|
18
22
|
|
|
19
23
|
function handlePaste(event) {
|
|
20
24
|
const files = collectFilesFromDataTransfer(event.clipboardData);
|
|
@@ -59,51 +63,68 @@
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
async function addFiles(files) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
66
|
+
const generation = attachmentGeneration;
|
|
67
|
+
try {
|
|
68
|
+
for (const file of Array.from(files || [])) {
|
|
69
|
+
if (generation !== attachmentGeneration) return;
|
|
70
|
+
const dedupeKey = buildAttachmentDedupeKey(file);
|
|
71
|
+
if (isDuplicateAttachmentFile(dedupeKey)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (attachments.length + pendingAttachmentKeys.size >= maxAttachments) {
|
|
75
|
+
appendPlainLog(tx(
|
|
76
|
+
`Attachment limit reached (${maxAttachments}).`,
|
|
77
|
+
`附件数量已达上限(${maxAttachments} 个)。`
|
|
78
|
+
));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
validateAttachmentFile(file);
|
|
82
|
+
const fileSize = Number(file.size) || 0;
|
|
83
|
+
if (!canReserveAttachmentBytes(fileSize)) {
|
|
84
|
+
appendPlainLog(tx(
|
|
85
|
+
`Attachment total limit reached (${formatFileSize(maxAttachmentTotalBytes)}). Remove attachments or use smaller files.`,
|
|
86
|
+
`附件总大小已达上限(${formatFileSize(maxAttachmentTotalBytes)})。请移除部分附件或使用更小的文件。`
|
|
87
|
+
));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
attachmentRevision += 1;
|
|
91
|
+
pendingAttachmentKeys.add(dedupeKey);
|
|
92
|
+
pendingAttachmentBytes += fileSize;
|
|
93
|
+
try {
|
|
94
|
+
const dataUrl = await readFileAsDataUrl(file, tx);
|
|
95
|
+
if (generation !== attachmentGeneration) return;
|
|
96
|
+
const preview = await buildAttachmentPreviewData(file, dataUrl);
|
|
97
|
+
if (generation !== attachmentGeneration) return;
|
|
98
|
+
const attachment = {
|
|
99
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
100
|
+
name: normalizeAttachmentName(file.name),
|
|
101
|
+
mimeType: String(file.type || ''),
|
|
102
|
+
size: Number(file.size) || 0,
|
|
103
|
+
kind: preview.kind,
|
|
104
|
+
previewDataUrl: preview.previewDataUrl,
|
|
105
|
+
contentBase64: extractBase64FromDataUrl(dataUrl),
|
|
106
|
+
dedupeKey
|
|
107
|
+
};
|
|
108
|
+
if (!attachments.some(existing => buildAttachmentDedupeKey(existing) === dedupeKey)) {
|
|
109
|
+
attachments.push(attachment);
|
|
110
|
+
attachmentRevision += 1;
|
|
111
|
+
onChange();
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (generation !== attachmentGeneration) return;
|
|
115
|
+
throw error;
|
|
116
|
+
} finally {
|
|
117
|
+
// Cleared work no longer owns reservations in the current composer.
|
|
118
|
+
if (generation === attachmentGeneration) {
|
|
119
|
+
pendingAttachmentBytes = Math.max(0, pendingAttachmentBytes - fileSize);
|
|
120
|
+
pendingAttachmentKeys.delete(dedupeKey);
|
|
121
|
+
}
|
|
100
122
|
}
|
|
101
|
-
} finally {
|
|
102
|
-
pendingAttachmentBytes = Math.max(0, pendingAttachmentBytes - fileSize);
|
|
103
|
-
pendingAttachmentKeys.delete(dedupeKey);
|
|
104
123
|
}
|
|
124
|
+
} finally {
|
|
125
|
+
// A partial batch may have accepted files before validation or reading failed.
|
|
126
|
+
if (generation === attachmentGeneration) renderComposerAttachments();
|
|
105
127
|
}
|
|
106
|
-
renderComposerAttachments();
|
|
107
128
|
}
|
|
108
129
|
|
|
109
130
|
function getAttachmentsForRun() {
|
|
@@ -139,20 +160,27 @@
|
|
|
139
160
|
root: getPanel() || document.body,
|
|
140
161
|
tr,
|
|
141
162
|
tx,
|
|
163
|
+
icons,
|
|
142
164
|
removable: true,
|
|
143
165
|
onRemove: removeAttachment
|
|
144
166
|
});
|
|
145
167
|
}
|
|
146
168
|
|
|
147
|
-
function clear() {
|
|
169
|
+
function clear(options = {}) {
|
|
170
|
+
attachmentGeneration += 1;
|
|
171
|
+
attachmentRevision += 1;
|
|
148
172
|
attachments = [];
|
|
149
173
|
pendingAttachmentKeys.clear();
|
|
174
|
+
pendingAttachmentBytes = 0;
|
|
150
175
|
renderComposerAttachments();
|
|
176
|
+
if (options.notify !== false) onChange();
|
|
151
177
|
}
|
|
152
178
|
|
|
153
179
|
function removeAttachment(id) {
|
|
180
|
+
const previousLength = attachments.length;
|
|
154
181
|
attachments = attachments.filter(attachment => attachment.id !== id);
|
|
155
182
|
renderComposerAttachments();
|
|
183
|
+
if (attachments.length !== previousLength) { attachmentRevision += 1; onChange(); }
|
|
156
184
|
}
|
|
157
185
|
|
|
158
186
|
function isDuplicateAttachmentFile(dedupeKey) {
|
|
@@ -260,14 +288,36 @@
|
|
|
260
288
|
if (!usable.length) {
|
|
261
289
|
return 0;
|
|
262
290
|
}
|
|
291
|
+
attachmentRevision += 1;
|
|
263
292
|
attachments = usable.map(item => ({ ...item }));
|
|
264
293
|
renderComposerAttachments();
|
|
265
294
|
return attachments.length;
|
|
266
295
|
}
|
|
267
296
|
|
|
297
|
+
// Only a just-rejected local-skill submission uses this in-memory recovery.
|
|
298
|
+
// It keeps the normal composer limits, without durable-draft truncation.
|
|
299
|
+
function restoreSubmittedAttachments(value, expectedRevision) {
|
|
300
|
+
if (expectedRevision !== attachmentRevision || attachments.length || pendingAttachmentBytes || pendingAttachmentKeys.size) return false;
|
|
301
|
+
if (!Array.isArray(value) || value.length > maxAttachments) return false;
|
|
302
|
+
let total = 0;
|
|
303
|
+
for (const item of value) {
|
|
304
|
+
const size = Number(item?.size);
|
|
305
|
+
if (!item?.name || typeof item.contentBase64 !== 'string' || !Number.isFinite(size) || size < 0 || size > maxAttachmentBytes) return false;
|
|
306
|
+
total += size;
|
|
307
|
+
}
|
|
308
|
+
if (total > maxAttachmentTotalBytes) return false;
|
|
309
|
+
attachments = value.map((item, index) => ({ ...item, id: `restored-${Date.now()}-${index}`, dedupeKey: buildAttachmentDedupeKey(item) }));
|
|
310
|
+
attachmentRevision += 1;
|
|
311
|
+
renderComposerAttachments();
|
|
312
|
+
onChange();
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
268
316
|
return {
|
|
269
317
|
serializePersistableAttachments,
|
|
270
318
|
restorePersistedAttachments,
|
|
319
|
+
restoreSubmittedAttachments,
|
|
320
|
+
getRevision: () => attachmentRevision,
|
|
271
321
|
handlePaste,
|
|
272
322
|
handleDragOver,
|
|
273
323
|
handleDragLeave,
|
|
@@ -285,6 +335,7 @@
|
|
|
285
335
|
root: getPanel() || document.body,
|
|
286
336
|
tr,
|
|
287
337
|
tx,
|
|
338
|
+
icons,
|
|
288
339
|
...options
|
|
289
340
|
}),
|
|
290
341
|
clear,
|
|
@@ -360,6 +411,12 @@
|
|
|
360
411
|
.slice(0, 180) || '';
|
|
361
412
|
}
|
|
362
413
|
|
|
414
|
+
function setCloseGlyph(button, icons) {
|
|
415
|
+
const glyph = icons?.svg?.('close', 'codex-close-glyph');
|
|
416
|
+
if (glyph) button.innerHTML = glyph;
|
|
417
|
+
else button.textContent = '×';
|
|
418
|
+
}
|
|
419
|
+
|
|
363
420
|
function renderAttachmentPreviewList(attachments = [], container, options = {}) {
|
|
364
421
|
if (!container) return;
|
|
365
422
|
container.replaceChildren();
|
|
@@ -407,7 +464,7 @@
|
|
|
407
464
|
const remove = document.createElement('button');
|
|
408
465
|
remove.type = 'button';
|
|
409
466
|
remove.className = 'codex-attachment-preview-remove';
|
|
410
|
-
remove.
|
|
467
|
+
setCloseGlyph(remove, options.icons);
|
|
411
468
|
const removeLabel = options.tx ? options.tx('Remove ' + label, '移除 ' + label) : 'Remove ' + label;
|
|
412
469
|
remove.title = removeLabel;
|
|
413
470
|
remove.setAttribute('aria-label', removeLabel);
|
|
@@ -438,7 +495,7 @@
|
|
|
438
495
|
const close = document.createElement('button');
|
|
439
496
|
close.type = 'button';
|
|
440
497
|
close.className = 'codex-attachment-preview-dialog-close';
|
|
441
|
-
close.
|
|
498
|
+
setCloseGlyph(close, options.icons);
|
|
442
499
|
close.title = options.tr ? options.tr('close') : 'Close';
|
|
443
500
|
close.setAttribute('aria-label', options.tr ? options.tr('close') : 'Close');
|
|
444
501
|
const image = document.createElement('img');
|
|
@@ -496,13 +553,13 @@
|
|
|
496
553
|
/\.(?:png|jpe?g|gif|webp|bmp|svg)$/i.test(String(attachment.name || ''));
|
|
497
554
|
}
|
|
498
555
|
|
|
499
|
-
function readFileAsDataUrl(file) {
|
|
556
|
+
function readFileAsDataUrl(file, tx = en => en) {
|
|
500
557
|
return new Promise((resolve, reject) => {
|
|
501
558
|
const reader = new FileReader();
|
|
502
559
|
reader.onload = () => {
|
|
503
560
|
resolve(String(reader.result || ''));
|
|
504
561
|
};
|
|
505
|
-
reader.onerror = () => reject(
|
|
562
|
+
reader.onerror = () => reject(new Error(tx('Could not read selected file.', '无法读取所选文件。'), { cause: reader.error }));
|
|
506
563
|
reader.readAsDataURL(file);
|
|
507
564
|
});
|
|
508
565
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
attachmentController: options.attachmentController || null,
|
|
14
14
|
listeners: []
|
|
15
15
|
};
|
|
16
|
+
const icon = name => options.icons?.svg(name) || '';
|
|
16
17
|
|
|
17
18
|
container.innerHTML = `
|
|
18
19
|
<div class="codex-pending-inputs" data-pending-inputs hidden></div>
|
|
@@ -20,9 +21,9 @@
|
|
|
20
21
|
<div class="codex-attachment-strip codex-attachment-preview-list" data-attachment-strip hidden></div>
|
|
21
22
|
<div class="codex-composer-selection" data-composer-selection hidden></div>
|
|
22
23
|
<div class="codex-composer-skill-context" data-composer-skill-context hidden>
|
|
23
|
-
<span class="codex-composer-skill-icon" aria-hidden="true"
|
|
24
|
+
<span class="codex-composer-skill-icon" aria-hidden="true">${icon('sparkle')}</span>
|
|
24
25
|
<span data-composer-skill-label></span>
|
|
25
|
-
<button type="button" data-composer-skill-clear title="Clear skill" aria-label="Clear skill"
|
|
26
|
+
<button type="button" data-composer-skill-clear title="Clear skill" aria-label="Clear skill">${icon('close')}</button>
|
|
26
27
|
</div>
|
|
27
28
|
<textarea data-task rows="3" placeholder="Ask Codex anything. Type @ to add context"></textarea>
|
|
28
29
|
<div class="codex-context-summary" data-context-summary hidden></div>
|
|
@@ -60,8 +61,43 @@
|
|
|
60
61
|
</div>
|
|
61
62
|
</div>
|
|
62
63
|
<div class="codex-composer-toolbar">
|
|
63
|
-
<button type="button" data-add-context title="Add @ context" aria-label="Add @ context" aria-expanded="false"
|
|
64
|
+
<button type="button" data-add-context title="Add @ context" aria-label="Add @ context" aria-expanded="false">${icon('plus')}</button>
|
|
64
65
|
<div class="codex-model-config" data-model-config>
|
|
66
|
+
<div class="codex-context-usage" data-context-usage data-unknown="true">
|
|
67
|
+
<button type="button" class="codex-context-usage-button" data-context-usage-toggle aria-expanded="false">
|
|
68
|
+
<svg class="codex-context-usage-ring" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
|
|
69
|
+
<circle class="codex-context-usage-track" cx="8" cy="8" r="6.4" pathLength="100"></circle>
|
|
70
|
+
<circle class="codex-context-usage-fill" cx="8" cy="8" r="2.55" pathLength="100"></circle>
|
|
71
|
+
<circle class="codex-context-usage-orbit" cx="8" cy="8" r="6.4" pathLength="100"></circle>
|
|
72
|
+
</svg>
|
|
73
|
+
</button>
|
|
74
|
+
<div class="codex-context-usage-popover" data-context-usage-popover role="region" tabindex="0" hidden>
|
|
75
|
+
<div class="codex-context-usage-head">
|
|
76
|
+
<svg class="codex-context-usage-empty-ring" data-context-usage-empty-ring viewBox="0 0 26 26" aria-hidden="true" focusable="false"><circle cx="13" cy="13" r="10.5"></circle><circle class="codex-context-usage-empty-dot" cx="13" cy="13" r="2"></circle></svg>
|
|
77
|
+
<div class="codex-context-usage-head-text">
|
|
78
|
+
<div class="codex-context-usage-title" data-context-usage-title></div>
|
|
79
|
+
<div class="codex-context-usage-amount" data-context-usage-amount></div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="codex-context-usage-figure" data-context-usage-figure>
|
|
82
|
+
<span class="codex-context-usage-spinner" data-context-usage-spinner hidden>${icon('spin')}</span>
|
|
83
|
+
<span class="codex-context-usage-chip" data-context-usage-chip hidden></span>
|
|
84
|
+
<span class="codex-context-usage-percent" data-context-usage-percent-wrap><span data-context-usage-percent></span><small data-context-usage-percent-sign>%</small></span>
|
|
85
|
+
<span class="codex-context-usage-level" data-context-usage-level hidden></span>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="codex-context-usage-note" data-context-usage-note hidden>${icon('compact')}<span data-context-usage-note-text></span></div>
|
|
89
|
+
<div class="codex-context-usage-bar" data-context-usage-bar aria-hidden="true"><i data-seg="cached"></i><i data-seg="input"></i><i data-seg="output"></i></div>
|
|
90
|
+
<div class="codex-context-usage-legend" data-context-usage-breakdown hidden>
|
|
91
|
+
<div class="codex-context-usage-row"><i class="codex-context-usage-swatch" data-seg="input"></i><span data-context-usage-input-label></span><b data-context-usage-input></b><em data-context-usage-input-share></em></div>
|
|
92
|
+
<div class="codex-context-usage-row" data-sub data-context-usage-cache><i class="codex-context-usage-swatch" data-seg="cached"></i><span data-context-usage-cached-label></span><b data-context-usage-cached></b><em data-context-usage-cache-rate></em></div>
|
|
93
|
+
<div class="codex-context-usage-row"><i class="codex-context-usage-swatch" data-seg="output"></i><span data-context-usage-output-label></span><b data-context-usage-output></b><em data-context-usage-output-share></em></div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="codex-context-usage-foot" data-context-usage-foot>
|
|
96
|
+
<span data-context-usage-reported hidden></span>
|
|
97
|
+
<button type="button" class="codex-context-usage-compact" data-context-usage-compact>${icon('compact')}<span data-context-usage-compact-label></span></button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
65
101
|
<button type="button" class="codex-model-config-button" data-model-config-toggle aria-haspopup="menu" aria-expanded="false">
|
|
66
102
|
<span class="codex-model-speed-indicator" data-speed-indicator hidden aria-hidden="true">
|
|
67
103
|
<svg viewBox="0 0 16 16" focusable="false">
|
|
@@ -99,7 +135,10 @@
|
|
|
99
135
|
</div>
|
|
100
136
|
</div>
|
|
101
137
|
</div>
|
|
102
|
-
<button type="submit" data-run data-action="send" title="Send" aria-label="Send"
|
|
138
|
+
<button type="submit" data-run data-action="send" title="Send" aria-label="Send">
|
|
139
|
+
<span class="codex-run-glyph" data-run-glyph="send">${icon('send')}</span>
|
|
140
|
+
<span class="codex-run-glyph" data-run-glyph="cancel">${icon('stop')}</span>
|
|
141
|
+
</button>
|
|
103
142
|
</div>
|
|
104
143
|
<div class="codex-slash-menu" data-slash-menu hidden>
|
|
105
144
|
<button type="button" data-slash-command="install-skill" data-slash-command-kind="installer">
|
|
@@ -119,9 +158,9 @@
|
|
|
119
158
|
</svg>
|
|
120
159
|
<span class="codex-context-sync-copy" data-context-sync-copy></span>
|
|
121
160
|
</span>
|
|
122
|
-
<button type="button" class="codex-attach-button" data-attach-file title="Attach files (PDF, images)" aria-label="Attach files (PDF, images)"
|
|
161
|
+
<button type="button" class="codex-attach-button" data-attach-file title="Attach files (PDF, images)" aria-label="Attach files (PDF, images)">${icon('attach')}</button>
|
|
123
162
|
<input type="file" data-attach-input multiple hidden>
|
|
124
|
-
<button type="button" data-context-refresh title="Refresh file list" aria-label="Refresh file list"
|
|
163
|
+
<button type="button" data-context-refresh title="Refresh file list" aria-label="Refresh file list">${icon('refresh')}</button>
|
|
125
164
|
</span>
|
|
126
165
|
</div>
|
|
127
166
|
<div class="codex-context-selection" data-context-selection></div>
|
|
@@ -132,9 +171,11 @@
|
|
|
132
171
|
`;
|
|
133
172
|
|
|
134
173
|
bindEvents(instance);
|
|
174
|
+
bindMenuLayout(instance);
|
|
135
175
|
|
|
136
176
|
return {
|
|
137
177
|
setRunning: running => setRunning(instance, running),
|
|
178
|
+
setSessionTransition: pending => setSessionTransition(instance, pending),
|
|
138
179
|
setModels: models => setModels(instance, models),
|
|
139
180
|
setMode: mode => setMode(instance, mode),
|
|
140
181
|
focus: () => focus(instance),
|
|
@@ -156,14 +197,31 @@
|
|
|
156
197
|
bind(instance, form, 'drop', event => instance.callbacks.onDrop?.(event));
|
|
157
198
|
bind(instance, instance.container.querySelector('[data-run]'), 'click', event => {
|
|
158
199
|
event.preventDefault();
|
|
159
|
-
if (instance.
|
|
200
|
+
if (instance.container.querySelector('[data-run]')?.dataset.action === 'cancel') {
|
|
160
201
|
instance.callbacks.onCancel?.();
|
|
161
202
|
return;
|
|
162
203
|
}
|
|
163
204
|
form?.requestSubmit?.();
|
|
164
205
|
});
|
|
165
206
|
bind(instance, task, 'keydown', event => instance.callbacks.onTaskKeydown?.(event));
|
|
166
|
-
bind(instance, task, 'input', event =>
|
|
207
|
+
bind(instance, task, 'input', event => {
|
|
208
|
+
if (acceptTaskInput(instance, task)) instance.callbacks.onTaskInput?.(event);
|
|
209
|
+
});
|
|
210
|
+
bind(instance, task, 'compositionstart', () => {
|
|
211
|
+
const composition = { scope: instance.callbacks.getTaskScope?.() };
|
|
212
|
+
composition.done = new Promise(resolve => { composition.resolve = resolve; });
|
|
213
|
+
instance.composition = composition;
|
|
214
|
+
});
|
|
215
|
+
bind(instance, task, 'compositionend', event => {
|
|
216
|
+
const composition = instance.composition;
|
|
217
|
+
if (acceptTaskInput(instance, task)) instance.callbacks.onInputChange?.(event);
|
|
218
|
+
// Chrome may emit the final input after compositionend. Keep its original
|
|
219
|
+
// owner until that event batch has finished, before a session can change.
|
|
220
|
+
if (composition) setTimeout(() => {
|
|
221
|
+
if (instance.composition === composition) instance.composition = null;
|
|
222
|
+
composition.resolve();
|
|
223
|
+
}, 0);
|
|
224
|
+
});
|
|
167
225
|
bind(instance, instance.container.querySelector('[data-slash-menu]'), 'click', event => instance.callbacks.onSlashMenuClick?.(event));
|
|
168
226
|
bind(instance, instance.container.querySelector('[data-composer-skill-clear]'), 'click', () => instance.callbacks.onClearSkillInvocation?.());
|
|
169
227
|
bind(instance, instance.container.querySelector('[data-add-context]'), 'click', () => instance.callbacks.onAddContext?.());
|
|
@@ -180,8 +238,11 @@
|
|
|
180
238
|
}
|
|
181
239
|
for (const selector of ['[data-model]', '[data-reasoning]', '[data-speed]', '[data-mode]', '[data-task]', '[data-require-reviewing]', '[data-auto-recompile]']) {
|
|
182
240
|
const element = instance.container.querySelector(selector);
|
|
183
|
-
|
|
184
|
-
|
|
241
|
+
const changed = event => {
|
|
242
|
+
if (element !== task || acceptTaskInput(instance, task)) instance.callbacks.onInputChange?.(event);
|
|
243
|
+
};
|
|
244
|
+
bind(instance, element, 'change', changed);
|
|
245
|
+
bind(instance, element, 'input', changed);
|
|
185
246
|
}
|
|
186
247
|
}
|
|
187
248
|
|
|
@@ -193,6 +254,41 @@
|
|
|
193
254
|
instance.listeners.push({ target, type, listener });
|
|
194
255
|
}
|
|
195
256
|
|
|
257
|
+
function bindMenuLayout(instance) {
|
|
258
|
+
const view = instance.container.ownerDocument?.defaultView || window;
|
|
259
|
+
const update = () => instance.callbacks.onMenuLayoutChange?.();
|
|
260
|
+
bind(instance, view, 'resize', update);
|
|
261
|
+
bind(instance, view.visualViewport, 'resize', update);
|
|
262
|
+
bind(instance, view.visualViewport, 'scroll', update);
|
|
263
|
+
const form = instance.container.querySelector('[data-composer-form]');
|
|
264
|
+
if (form && typeof view.ResizeObserver === 'function') {
|
|
265
|
+
instance.menuResizeObserver = new view.ResizeObserver(update);
|
|
266
|
+
instance.menuResizeObserver.observe(form);
|
|
267
|
+
instance.menuResizeObserver.observe(instance.container);
|
|
268
|
+
const panel = instance.container.closest?.('#codex-overleaf-panel');
|
|
269
|
+
if (panel && panel !== instance.container) instance.menuResizeObserver.observe(panel);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function acceptTaskInput(instance, task) {
|
|
274
|
+
if (instance.composition && instance.composition.scope !== instance.callbacks.getTaskScope?.()) {
|
|
275
|
+
task.value = instance.callbacks.getTaskValue?.() || '';
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return !instance.sessionTransition || Boolean(instance.composition);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function setSessionTransition(instance, pending) {
|
|
282
|
+
const task = instance.container.querySelector('[data-task]');
|
|
283
|
+
if (pending && !instance.sessionTransition) instance.previousReadOnly = task?.readOnly === true;
|
|
284
|
+
instance.sessionTransition = pending;
|
|
285
|
+
const composition = instance.composition;
|
|
286
|
+
if (pending && composition) task?.blur();
|
|
287
|
+
if (task) task.readOnly = pending || instance.previousReadOnly === true;
|
|
288
|
+
setRunning(instance, Boolean(instance.callbacks.isRunning?.()));
|
|
289
|
+
return pending ? composition?.done : undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
196
292
|
function setRunning(target, running) {
|
|
197
293
|
const instance = target?._instance || target;
|
|
198
294
|
const runButton = instance?.container?.querySelector('[data-run]');
|
|
@@ -200,14 +296,14 @@
|
|
|
200
296
|
return;
|
|
201
297
|
}
|
|
202
298
|
const hasInput = Boolean(String(instance?.container?.querySelector('[data-task]')?.value || '').trim());
|
|
203
|
-
const action = running && !hasInput ? 'cancel' : 'send';
|
|
299
|
+
const action = running && (!hasInput || instance.sessionTransition) ? 'cancel' : 'send';
|
|
204
300
|
const label = action === 'cancel'
|
|
205
301
|
? t(instance, 'cancelRun')
|
|
206
302
|
: running
|
|
207
303
|
? t(instance, 'queueNextInput')
|
|
208
304
|
: t(instance, 'send');
|
|
209
305
|
runButton.dataset.action = action;
|
|
210
|
-
runButton.disabled = action === 'send' && !hasInput;
|
|
306
|
+
runButton.disabled = action === 'send' && (!hasInput || instance.sessionTransition);
|
|
211
307
|
runButton.title = label;
|
|
212
308
|
runButton.setAttribute('aria-label', label);
|
|
213
309
|
}
|
|
@@ -276,6 +372,8 @@
|
|
|
276
372
|
}
|
|
277
373
|
|
|
278
374
|
function destroy(instance) {
|
|
375
|
+
instance.composition?.resolve();
|
|
376
|
+
instance.menuResizeObserver?.disconnect();
|
|
279
377
|
for (const { target, type, listener } of instance.listeners.splice(0)) {
|
|
280
378
|
target.removeEventListener?.(type, listener);
|
|
281
379
|
}
|