codex-overleaf-link 2.1.5 → 2.2.1
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 +27 -23
- package/extension/bootstrap/background.js +14 -1
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +10 -0
- package/extension/src/background.js +507 -8
- package/extension/src/backgroundUpdateCoordinator.js +284 -19
- package/extension/src/content/activeTurnControl.js +179 -0
- package/extension/src/content/composerPanel.js +14 -5
- package/extension/src/content/contentRuntime.js +330 -309
- package/extension/src/content/diagnosticsPanel.js +132 -1
- package/extension/src/content/markdownDomRenderer.js +451 -0
- package/extension/src/content/markdownText.js +86 -7
- package/extension/src/content/mathText.js +49 -4
- package/extension/src/content/modelPicker.js +14 -5
- package/extension/src/content/nativeChannel.js +7 -1
- package/extension/src/content/panelRenderer.js +112 -12
- package/extension/src/content/pendingInputView.js +106 -0
- package/extension/src/content/projectProviderSelection.js +68 -0
- package/extension/src/content/projectSessionCleanup.js +4 -0
- package/extension/src/content/providerSettingsCoordinator.js +170 -22
- package/extension/src/content/providerSettingsDialog.js +2 -2
- package/extension/src/content/recentProjects.js +7 -2
- package/extension/src/content/runGuidanceController.js +70 -0
- package/extension/src/content/runGuidanceView.js +48 -0
- package/extension/src/content/runQueueScheduler.js +260 -0
- package/extension/src/content/runTimelineView.js +33 -80
- package/extension/src/content/sessionManager.js +2 -2
- package/extension/src/content/sessionPersistence.js +62 -0
- package/extension/src/content/settingsPanel.js +1 -1
- package/extension/src/content/updateNotice.js +80 -1
- package/extension/src/page/writebackRouter.js +3 -9
- package/extension/src/shared/compatibility.js +2 -1
- package/extension/src/shared/i18n.js +38 -0
- package/extension/src/shared/providerProfiles.js +3 -2
- package/extension/src/shared/runInputQueue.js +253 -0
- package/extension/src/shared/sessionState.js +45 -22
- package/extension/src/shared/storageDb.js +39 -44
- package/extension/src/shared/storageMigration.js +123 -3
- package/extension/src/shared/storageRunActions.js +34 -1
- package/extension/src/shared/updateConsent.js +15 -4
- package/extension/styles/panel.css +319 -20
- package/extension/vendor/katex/katex.min.css +1 -1
- package/extension/vendor/markdown-it/LICENSE +22 -0
- package/extension/vendor/markdown-it/SOURCE.json +7 -0
- package/extension/vendor/markdown-it/VERSION +1 -0
- package/extension/vendor/markdown-it/markdown-it.min.js +3 -0
- package/native-host/src/anthropicBridgeRequest.js +3 -3
- package/native-host/src/anthropicBridgeResponse.js +58 -17
- package/native-host/src/anthropicMessagesBridge.js +156 -23
- package/native-host/src/chatBridgeRequest.js +1 -1
- package/native-host/src/chatBridgeResponse.js +12 -8
- package/native-host/src/chatCompletionsBridge.js +22 -40
- package/native-host/src/codexPromptAssembly.js +3 -1
- package/native-host/src/codexProviderLaunch.js +6 -3
- package/native-host/src/codexSessionRunner.js +65 -65
- package/native-host/src/codexSessionTiming.js +45 -0
- package/native-host/src/index.js +58 -3
- package/native-host/src/managedInstall.js +45 -7
- package/native-host/src/mirrorWorkspace.js +15 -8
- package/native-host/src/providerProfile.js +2 -2
- package/native-host/src/providerStreamLifecycle.js +100 -0
- package/native-host/src/responsesPassthroughBridge.js +636 -0
- package/native-host/src/taskRunnerRuntime.js +81 -2
- package/native-host/src/updateArchive.js +4 -3
- package/native-host/src/updateManager.js +306 -19
- package/native-host/src/updateTrust.js +1 -1
- package/package.json +3 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
getSelectedModel: options.getSelectedModel || (() => ''),
|
|
11
11
|
getSelectedProviderId: options.getSelectedProviderId || (() => 'builtin'),
|
|
12
12
|
setSelectedProviderId: options.setSelectedProviderId || (() => {}),
|
|
13
|
+
confirmProviderSwitch: options.confirmProviderSwitch || (() => true),
|
|
13
14
|
clearSelectedModel: options.clearSelectedModel || (() => {}),
|
|
14
15
|
refreshModelOptions: options.refreshModelOptions || (() => {}),
|
|
15
16
|
persistInputs: options.persistInputs || (() => {}),
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
setupCrossTabRefresh(instance, options.window || window);
|
|
39
40
|
return {
|
|
40
41
|
open: () => open(instance),
|
|
41
|
-
refreshSummary: () =>
|
|
42
|
+
refreshSummary: () => refreshSummary(instance),
|
|
42
43
|
ensureLoaded: () => ensureLoaded(instance),
|
|
43
44
|
getRunSelection: providerId => instance.loaded
|
|
44
45
|
? Profiles.buildRunSelection(instance.catalog, providerId || instance.getSelectedProviderId())
|
|
@@ -93,9 +94,45 @@
|
|
|
93
94
|
return instance.catalog;
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
async function ensureLoadedWithRetry(instance) {
|
|
98
|
+
try {
|
|
99
|
+
return await ensureLoaded(instance);
|
|
100
|
+
} catch (_firstError) {
|
|
101
|
+
// Extension reloads can race the first Native Messaging connection.
|
|
102
|
+
// Keep the persisted project selection visible, then retry once after
|
|
103
|
+
// the service worker and Native Host have had a chance to reconnect.
|
|
104
|
+
updateSettingsSummary(instance);
|
|
105
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
106
|
+
try {
|
|
107
|
+
const result = await refresh(instance);
|
|
108
|
+
return result?.catalog || instance.catalog;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
updateSettingsSummary(instance);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function refreshSummary(instance) {
|
|
117
|
+
// Project state hydrates before the Native Host catalog is guaranteed to
|
|
118
|
+
// be ready. Project the persisted selection immediately so Settings never
|
|
119
|
+
// claims Built-in Codex (or stays on a loading placeholder) while the
|
|
120
|
+
// authoritative catalog request is still pending.
|
|
121
|
+
updateSettingsSummary(instance);
|
|
122
|
+
try {
|
|
123
|
+
await ensureLoadedWithRetry(instance);
|
|
124
|
+
} catch (_error) {
|
|
125
|
+
// This card describes the project's saved selection. Native connection
|
|
126
|
+
// failures are surfaced when Configure is opened, without replacing the
|
|
127
|
+
// selection with a misleading Built-in/unavailable state.
|
|
128
|
+
}
|
|
129
|
+
updateSettingsSummary(instance);
|
|
130
|
+
return instance.catalog;
|
|
131
|
+
}
|
|
132
|
+
|
|
96
133
|
async function syncSessionProvider(instance) {
|
|
97
134
|
try {
|
|
98
|
-
await
|
|
135
|
+
await ensureLoadedWithRetry(instance);
|
|
99
136
|
} catch (_error) {
|
|
100
137
|
return instance.refreshModelOptions(
|
|
101
138
|
window.CodexOverleafProviderProfiles.buildRunSelection(instance.catalog, instance.getSelectedProviderId())
|
|
@@ -109,22 +146,78 @@
|
|
|
109
146
|
const requestedProviderId = typeof change.sessionProviderId === 'string' && change.sessionProviderId
|
|
110
147
|
? change.sessionProviderId
|
|
111
148
|
: previousProviderId;
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
149
|
+
const projectProviderChanged = requestedProviderId !== previousProviderId;
|
|
150
|
+
if (projectProviderChanged && change.providerSwitchApproved !== true) {
|
|
114
151
|
const requestedProvider = window.CodexOverleafProviderProfiles.getProviderById(catalog, requestedProviderId);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
:
|
|
118
|
-
|
|
152
|
+
const approved = await instance.confirmProviderSwitch({
|
|
153
|
+
providerId: requestedProviderId,
|
|
154
|
+
providerName: requestedProvider?.name || requestedProviderId
|
|
155
|
+
});
|
|
156
|
+
if (!approved) {
|
|
157
|
+
return { cancelled: true };
|
|
158
|
+
}
|
|
119
159
|
}
|
|
120
160
|
const changedProviderIds = Array.isArray(change.changedProviderIds) ? change.changedProviderIds : [];
|
|
121
|
-
if (!change.forceSessionRefresh && !
|
|
161
|
+
if (!change.forceSessionRefresh && !projectProviderChanged && !changedProviderIds.includes(requestedProviderId)) {
|
|
122
162
|
return;
|
|
123
163
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
164
|
+
const loadResult = change.modelsPreloaded === true
|
|
165
|
+
? { stale: false, selectedModel: change.preloadedModelId || '' }
|
|
166
|
+
: await instance.refreshModelOptions(
|
|
167
|
+
window.CodexOverleafProviderProfiles.buildRunSelection(catalog, requestedProviderId),
|
|
168
|
+
{ persist: false }
|
|
169
|
+
);
|
|
170
|
+
if (loadResult?.stale || loadResult?.error) {
|
|
171
|
+
const error = createClientError(
|
|
172
|
+
loadResult?.error?.code || 'provider_model_catalog_unavailable',
|
|
173
|
+
loadResult?.error?.message || 'The provider model catalog could not be loaded.'
|
|
174
|
+
);
|
|
175
|
+
error.details = loadResult?.error || {};
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
if (projectProviderChanged) {
|
|
179
|
+
await instance.setSelectedProviderId(requestedProviderId, loadResult?.selectedModel || '');
|
|
180
|
+
}
|
|
127
181
|
await instance.persistInputs();
|
|
182
|
+
updateSettingsSummary(instance);
|
|
183
|
+
return { providerId: requestedProviderId, selectedModel: loadResult?.selectedModel || '' };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function prepareProviderActivation(instance, providerId) {
|
|
187
|
+
const previousProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
188
|
+
const provider = window.CodexOverleafProviderProfiles.getProviderById(instance.catalog, providerId);
|
|
189
|
+
if (providerId !== previousProviderId) {
|
|
190
|
+
const approved = await instance.confirmProviderSwitch({
|
|
191
|
+
providerId,
|
|
192
|
+
providerName: provider?.name || providerId
|
|
193
|
+
});
|
|
194
|
+
if (!approved) {
|
|
195
|
+
return { cancelled: true, previousProviderId };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const result = await instance.refreshModelOptions(
|
|
199
|
+
window.CodexOverleafProviderProfiles.buildRunSelection(instance.catalog, providerId),
|
|
200
|
+
{ persist: false }
|
|
201
|
+
);
|
|
202
|
+
if (result?.stale || result?.error) {
|
|
203
|
+
if (providerId !== previousProviderId) {
|
|
204
|
+
await instance.refreshModelOptions(
|
|
205
|
+
window.CodexOverleafProviderProfiles.buildRunSelection(instance.catalog, previousProviderId),
|
|
206
|
+
{ persist: false }
|
|
207
|
+
).catch(() => {});
|
|
208
|
+
}
|
|
209
|
+
const error = createClientError(
|
|
210
|
+
result?.error?.code || 'provider_model_catalog_unavailable',
|
|
211
|
+
result?.error?.message || 'The provider model catalog could not be loaded.'
|
|
212
|
+
);
|
|
213
|
+
error.details = result?.error || {};
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
cancelled: false,
|
|
218
|
+
previousProviderId,
|
|
219
|
+
selectedModel: result?.selectedModel || ''
|
|
220
|
+
};
|
|
128
221
|
}
|
|
129
222
|
|
|
130
223
|
async function testProvider(instance, context) {
|
|
@@ -264,8 +357,13 @@
|
|
|
264
357
|
}
|
|
265
358
|
|
|
266
359
|
async function activateProvider(instance, context) {
|
|
267
|
-
|
|
360
|
+
let preparation;
|
|
268
361
|
try {
|
|
362
|
+
preparation = await prepareProviderActivation(instance, context.profileId);
|
|
363
|
+
if (preparation.cancelled) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
instance.dialog.setBusy('saving', instance.tx('Activating provider…', '正在启用模型服务…'));
|
|
269
367
|
const result = await request(instance, 'codex.providers.activate', {
|
|
270
368
|
providerId: context.profileId,
|
|
271
369
|
expectedRevision: context.expectedRevision,
|
|
@@ -276,30 +374,59 @@
|
|
|
276
374
|
updateDialog: true,
|
|
277
375
|
selectedId: context.profileId
|
|
278
376
|
});
|
|
279
|
-
await notifyChanged(instance, applied.change, {
|
|
377
|
+
await notifyChanged(instance, applied.change, {
|
|
378
|
+
sessionProviderId: context.profileId,
|
|
379
|
+
providerSwitchApproved: true,
|
|
380
|
+
modelsPreloaded: true,
|
|
381
|
+
preloadedModelId: preparation.selectedModel,
|
|
382
|
+
requireProjection: true
|
|
383
|
+
});
|
|
280
384
|
instance.dialog.setBusy('', '');
|
|
281
385
|
instance.dialog.setStatus({
|
|
282
386
|
tone: 'success',
|
|
283
387
|
title: instance.tx('Provider activated.', '模型服务已启用。')
|
|
284
388
|
});
|
|
285
389
|
} catch (error) {
|
|
390
|
+
if (preparation?.previousProviderId && preparation.previousProviderId !== context.profileId) {
|
|
391
|
+
await instance.refreshModelOptions(
|
|
392
|
+
window.CodexOverleafProviderProfiles.buildRunSelection(instance.catalog, preparation.previousProviderId),
|
|
393
|
+
{ persist: false }
|
|
394
|
+
).catch(() => {});
|
|
395
|
+
}
|
|
286
396
|
instance.dialog.setBusy('failed', formatError(instance, error));
|
|
287
397
|
instance.dialog.setStatus({ tone: 'failed', title: formatError(instance, error) });
|
|
288
398
|
}
|
|
289
399
|
}
|
|
290
400
|
|
|
291
401
|
async function activateBuiltin(instance) {
|
|
292
|
-
|
|
402
|
+
let preparation;
|
|
293
403
|
try {
|
|
404
|
+
preparation = await prepareProviderActivation(instance, 'builtin');
|
|
405
|
+
if (preparation.cancelled) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
instance.dialog.setBusy('saving', instance.tx('Activating built-in Codex…', '正在启用内置 Codex…'));
|
|
294
409
|
const result = await request(instance, 'codex.providers.activate', {
|
|
295
410
|
providerId: 'builtin',
|
|
296
411
|
expectedRevision: 0
|
|
297
412
|
});
|
|
298
413
|
const applied = applyCatalog(instance, result, { updateDialog: true, selectedId: 'builtin' });
|
|
299
|
-
await notifyChanged(instance, applied.change, {
|
|
414
|
+
await notifyChanged(instance, applied.change, {
|
|
415
|
+
sessionProviderId: 'builtin',
|
|
416
|
+
providerSwitchApproved: true,
|
|
417
|
+
modelsPreloaded: true,
|
|
418
|
+
preloadedModelId: preparation.selectedModel,
|
|
419
|
+
requireProjection: true
|
|
420
|
+
});
|
|
300
421
|
instance.dialog.setBusy('', '');
|
|
301
422
|
instance.dialog.setStatus({ tone: 'success', title: instance.tx('Built-in Codex is active.', '已启用内置 Codex。') });
|
|
302
423
|
} catch (error) {
|
|
424
|
+
if (preparation?.previousProviderId && preparation.previousProviderId !== 'builtin') {
|
|
425
|
+
await instance.refreshModelOptions(
|
|
426
|
+
window.CodexOverleafProviderProfiles.buildRunSelection(instance.catalog, preparation.previousProviderId),
|
|
427
|
+
{ persist: false }
|
|
428
|
+
).catch(() => {});
|
|
429
|
+
}
|
|
303
430
|
instance.dialog.setBusy('failed', formatError(instance, error));
|
|
304
431
|
}
|
|
305
432
|
}
|
|
@@ -340,13 +467,31 @@
|
|
|
340
467
|
}
|
|
341
468
|
|
|
342
469
|
function updateSettingsSummary(instance, override = {}) {
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
470
|
+
const selectedProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
471
|
+
const catalogProvider = window.CodexOverleafProviderProfiles.getProviderById(
|
|
472
|
+
instance.catalog,
|
|
473
|
+
selectedProviderId
|
|
474
|
+
);
|
|
475
|
+
const selectedProvider = catalogProvider?.id === selectedProviderId
|
|
476
|
+
? catalogProvider
|
|
477
|
+
: (selectedProviderId === 'builtin'
|
|
478
|
+
? window.CodexOverleafProviderProfiles.getActiveProvider(instance.catalog)
|
|
479
|
+
: {
|
|
480
|
+
id: selectedProviderId,
|
|
481
|
+
name: selectedProviderId,
|
|
482
|
+
kind: 'custom',
|
|
483
|
+
defaultModelId: instance.getSelectedModel() || '',
|
|
484
|
+
wireApiPreference: '',
|
|
485
|
+
resolvedWireApi: ''
|
|
486
|
+
});
|
|
487
|
+
const summary = override.summary || (selectedProvider.kind === 'builtin'
|
|
488
|
+
? instance.tx('Built-in Codex · current project', '内置 Codex · 当前项目')
|
|
489
|
+
: `${selectedProvider.name} · ${instance.getSelectedModel() || selectedProvider.defaultModelId || instance.tx('No model', '未配置模型')} · ${instance.tx('Current project', '当前项目')}`);
|
|
347
490
|
instance.getSettingsPanelInstance()?.setProviderSummary?.({
|
|
348
491
|
summary,
|
|
349
|
-
tone: override.tone || (
|
|
492
|
+
tone: override.tone || (selectedProvider.kind === 'custom'
|
|
493
|
+
&& selectedProvider.wireApiPreference === 'auto'
|
|
494
|
+
&& !selectedProvider.resolvedWireApi ? 'warning' : 'ok')
|
|
350
495
|
});
|
|
351
496
|
}
|
|
352
497
|
|
|
@@ -358,7 +503,10 @@
|
|
|
358
503
|
}
|
|
359
504
|
try {
|
|
360
505
|
await instance.onProviderChanged(instance.catalog, { ...change, ...localChange });
|
|
361
|
-
} catch (
|
|
506
|
+
} catch (error) {
|
|
507
|
+
if (localChange.requireProjection === true) {
|
|
508
|
+
throw error;
|
|
509
|
+
}
|
|
362
510
|
// The authoritative catalog is already saved. A later refresh can retry
|
|
363
511
|
// the local model projection without rolling back the provider change.
|
|
364
512
|
}
|
|
@@ -381,7 +381,7 @@
|
|
|
381
381
|
</label>
|
|
382
382
|
<label class="codex-provider-field">
|
|
383
383
|
<span>${escapeHtml(tx('Maximum output tokens', '最大输出 Token'))}</span>
|
|
384
|
-
<input type="number" data-provider-field="maxOutputTokens" min="256" max="
|
|
384
|
+
<input type="number" data-provider-field="maxOutputTokens" min="256" max="65536" step="256" value="${Number(draft.maxOutputTokens) || 65536}">
|
|
385
385
|
</label>
|
|
386
386
|
<div class="codex-provider-field codex-provider-field--wide">
|
|
387
387
|
<span>${escapeHtml(tx('Anthropic compatibility', 'Anthropic 兼容能力'))}</span>
|
|
@@ -736,7 +736,7 @@
|
|
|
736
736
|
,anthropicThinkingMode: get('anthropicThinkingMode')?.value || 'budget'
|
|
737
737
|
,anthropicPromptCaching: Boolean(get('anthropicPromptCaching')?.checked)
|
|
738
738
|
,impersonateClaudeCode: Boolean(get('impersonateClaudeCode')?.checked)
|
|
739
|
-
,maxOutputTokens: Number(get('maxOutputTokens')?.value ||
|
|
739
|
+
,maxOutputTokens: Number(get('maxOutputTokens')?.value || 65536)
|
|
740
740
|
};
|
|
741
741
|
const apiKey = String(get('apiKey')?.value || '');
|
|
742
742
|
const secretMutation = instance.secretAction === 'replace' && apiKey
|
|
@@ -510,9 +510,11 @@
|
|
|
510
510
|
return [];
|
|
511
511
|
}
|
|
512
512
|
var records = await StorageDb.getAllByIndex('sessions', 'projectId', projectId);
|
|
513
|
+
var Persistence = window.CodexOverleafSessionPersistence;
|
|
514
|
+
var deletedIds = await Persistence.getDeletedSessionIds(projectId);
|
|
513
515
|
var scope = getCachedAccountScopeId();
|
|
514
516
|
return (records || [])
|
|
515
|
-
.filter(function (record) { return
|
|
517
|
+
.filter(function (record) { return Persistence.isVisibleRecord(record, deletedIds, scope); })
|
|
516
518
|
.sort(function (a, b) {
|
|
517
519
|
return String(b.updatedAt || b.createdAt || '').localeCompare(String(a.updatedAt || a.createdAt || ''));
|
|
518
520
|
});
|
|
@@ -729,6 +731,7 @@
|
|
|
729
731
|
if (!approved) {
|
|
730
732
|
return;
|
|
731
733
|
}
|
|
734
|
+
await window.CodexOverleafSessionPersistence.addTombstones(projectId, [record.id]);
|
|
732
735
|
try {
|
|
733
736
|
await mutateProjectPanelState(projectId, function (state) {
|
|
734
737
|
return SessionState.deleteSession(state, record.id);
|
|
@@ -962,9 +965,11 @@
|
|
|
962
965
|
try {
|
|
963
966
|
var StorageDb = window.CodexOverleafStorageDb;
|
|
964
967
|
if (StorageDb) {
|
|
968
|
+
var deletedSessionIdsByProject = await window.CodexOverleafSessionPersistence.loadTombstones();
|
|
965
969
|
rows = await StorageDb.listRecentProjectsAcrossAccount({
|
|
966
970
|
accountScopeId: accountScopeId,
|
|
967
|
-
limit: showAll ? 500 : pageLimit + 1
|
|
971
|
+
limit: showAll ? 500 : pageLimit + 1,
|
|
972
|
+
deletedSessionIdsByProject: deletedSessionIdsByProject
|
|
968
973
|
});
|
|
969
974
|
}
|
|
970
975
|
} catch (_error) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
(function initCodexOverleafRunGuidanceController() {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function create(deps = {}) {
|
|
5
|
+
function upsert(text, target = {}) {
|
|
6
|
+
const record = deps.findRunRecord?.(target.recordId, target.sessionId);
|
|
7
|
+
const guidanceId = String(target.guidanceId || '').trim();
|
|
8
|
+
if (!record || !guidanceId) return null;
|
|
9
|
+
const event = {
|
|
10
|
+
title: String(text || '').trim(),
|
|
11
|
+
status: target.status || 'queued',
|
|
12
|
+
kind: 'guidance',
|
|
13
|
+
guidanceId,
|
|
14
|
+
timestamp: new Date().toISOString()
|
|
15
|
+
};
|
|
16
|
+
const existing = (record.events || []).find(entry => entry.guidanceId === guidanceId);
|
|
17
|
+
if (!existing) {
|
|
18
|
+
deps.appendRunEvent?.(event, record);
|
|
19
|
+
return event;
|
|
20
|
+
}
|
|
21
|
+
Object.assign(existing, event, { timestamp: existing.timestamp || event.timestamp });
|
|
22
|
+
const view = deps.getCurrentRunView?.();
|
|
23
|
+
if (view?.recordId === target.recordId) {
|
|
24
|
+
view.root?.querySelector(`[data-guidance-id="${deps.cssEscape(guidanceId)}"]`)
|
|
25
|
+
?.replaceWith(deps.renderRunEvent(existing));
|
|
26
|
+
}
|
|
27
|
+
return existing;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function remove(guidanceId, target = {}) {
|
|
31
|
+
const record = deps.findRunRecord?.(target.recordId, target.sessionId);
|
|
32
|
+
if (!record || !guidanceId) return false;
|
|
33
|
+
const before = (record.events || []).length;
|
|
34
|
+
record.events = (record.events || []).filter(event => event.guidanceId !== guidanceId);
|
|
35
|
+
const view = deps.getCurrentRunView?.();
|
|
36
|
+
if (view?.recordId === target.recordId) {
|
|
37
|
+
view.root?.querySelector(`[data-guidance-id="${deps.cssEscape(guidanceId)}"]`)?.remove();
|
|
38
|
+
}
|
|
39
|
+
return record.events.length !== before;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function settleView(view) {
|
|
43
|
+
const guidance = view?.guidance || view?.root?.querySelector('[data-run-guidance]');
|
|
44
|
+
if (!guidance || !view?.events) return;
|
|
45
|
+
view.guidance = guidance;
|
|
46
|
+
for (const row of view.events.querySelectorAll('[data-kind="guidance"]')) guidance.append(row);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function appendToView(event, view) {
|
|
50
|
+
const text = window.CodexOverleafRunGuidanceView?.getGuidanceText(event);
|
|
51
|
+
if (!text) return false;
|
|
52
|
+
const guidance = view.guidance || view.root?.querySelector('[data-run-guidance]');
|
|
53
|
+
const target = view.terminalStatus ? guidance : view.events;
|
|
54
|
+
if (!target) return false;
|
|
55
|
+
view.guidance = guidance;
|
|
56
|
+
const rendered = deps.renderRunEvent(event);
|
|
57
|
+
const existing = event.guidanceId
|
|
58
|
+
? view.root?.querySelector(`[data-guidance-id="${deps.cssEscape(event.guidanceId)}"]`)
|
|
59
|
+
: null;
|
|
60
|
+
if (existing) existing.replaceWith(rendered);
|
|
61
|
+
else target.append(rendered);
|
|
62
|
+
deps.bumpUnreadIfDetached?.();
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { upsert, remove, settleView, appendToView };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
window.CodexOverleafRunGuidanceController = { create };
|
|
70
|
+
})();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
(function initCodexOverleafRunGuidanceView() {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const LEGACY_GUIDANCE_TITLES = new Set([
|
|
5
|
+
'Follow-up guidance was delivered to the active Codex turn.',
|
|
6
|
+
'\u540e\u7eed\u5f15\u5bfc\u5df2\u53d1\u9001\u5230\u5f53\u524d Codex turn\u3002'
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
const GUIDANCE_DELIVERY_PREFIX = /^(?:Guidance delivered|Follow-up guidance delivered|引导已发送|后续引导已发送)\s*[::]\s*/i;
|
|
10
|
+
|
|
11
|
+
function normalizeGuidanceText(value) {
|
|
12
|
+
return String(value || '').trim().replace(GUIDANCE_DELIVERY_PREFIX, '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getGuidanceText(event = {}) {
|
|
16
|
+
const detailText = typeof event.detail === 'string' ? event.detail.trim() : '';
|
|
17
|
+
if (event.kind === 'guidance') {
|
|
18
|
+
return normalizeGuidanceText(detailText || event.title);
|
|
19
|
+
}
|
|
20
|
+
return detailText && LEGACY_GUIDANCE_TITLES.has(String(event.title || ''))
|
|
21
|
+
? normalizeGuidanceText(detailText)
|
|
22
|
+
: '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderGuidanceMessage(event, guidanceText, tx) {
|
|
26
|
+
const row = document.createElement('div');
|
|
27
|
+
row.className = 'run-guidance-message run-user-message';
|
|
28
|
+
row.dataset.kind = 'guidance';
|
|
29
|
+
row.dataset.status = event.status || 'completed';
|
|
30
|
+
if (event.guidanceId) {
|
|
31
|
+
row.dataset.guidanceId = event.guidanceId;
|
|
32
|
+
}
|
|
33
|
+
row.setAttribute('role', 'group');
|
|
34
|
+
row.setAttribute('aria-label', tx('Follow-up guidance', '\u540e\u7eed\u5f15\u5bfc'));
|
|
35
|
+
|
|
36
|
+
const text = document.createElement('div');
|
|
37
|
+
text.className = 'run-guidance-text';
|
|
38
|
+
text.textContent = guidanceText;
|
|
39
|
+
|
|
40
|
+
row.append(text);
|
|
41
|
+
return row;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
window.CodexOverleafRunGuidanceView = {
|
|
45
|
+
getGuidanceText,
|
|
46
|
+
renderGuidanceMessage
|
|
47
|
+
};
|
|
48
|
+
})();
|