codex-overleaf-link 2.5.1 → 2.6.5
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 +149 -398
- package/README.zh-CN.md +153 -404
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +148 -3
- package/extension/src/backgroundSharedSessions.js +55 -2
- package/extension/src/backgroundUpdateCoordinator.js +128 -12
- package/extension/src/content/activeTurnControl.js +164 -7
- package/extension/src/content/composerAttachments.js +46 -10
- package/extension/src/content/composerPanel.js +92 -12
- package/extension/src/content/contentRuntime.js +1114 -321
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsController.js +77 -34
- package/extension/src/content/diagnosticsExport.js +1 -1
- package/extension/src/content/diagnosticsPanel.js +147 -42
- package/extension/src/content/generated/content.bundle.js +5761 -1237
- package/extension/src/content/generated/content.bundle.meta.json +6 -3
- package/extension/src/content/legacyGlobalRegistry.js +4 -0
- package/extension/src/content/localSkillsPanel.js +6 -4
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +53 -14
- package/extension/src/content/modelPicker.js +29 -19
- package/extension/src/content/nativeCompatibilityController.js +52 -19
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/pageBridgeClient.js +7 -7
- package/extension/src/content/panelMaintenance.js +10 -2
- package/extension/src/content/panelRenderer.js +55 -28
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectSettingsCoordinator.js +30 -6
- package/extension/src/content/providerSettingsDialog.js +28 -19
- package/extension/src/content/recentProjects.js +30 -19
- package/extension/src/content/runActivitySummary.js +122 -14
- package/extension/src/content/runCompletionMeta.js +194 -6
- package/extension/src/content/runController.js +2 -1
- package/extension/src/content/runFailureNotice.js +8 -1
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +27 -13
- package/extension/src/content/runRecoveryActions.js +2 -1
- package/extension/src/content/runResultActions.js +73 -24
- package/extension/src/content/runReviewController.js +102 -37
- package/extension/src/content/runTimelineView.js +224 -25
- package/extension/src/content/scopedPersistencePanelState.js +124 -46
- package/extension/src/content/selectionContextView.js +30 -3
- package/extension/src/content/sensitivePreflight.js +14 -1
- package/extension/src/content/sessionManager.js +49 -3
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +15 -2
- package/extension/src/content/settingsWorkbench.js +127 -89
- package/extension/src/content/sharedSessionViewSync.js +13 -4
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateNotice.js +60 -6
- package/extension/src/content/writebackOrchestrator.js +301 -72
- package/extension/src/content/writingStyleSettings.js +70 -17
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/overleafEditor.js +12 -3
- package/extension/src/page/overleafProjectSnapshot.js +56 -3
- package/extension/src/page/snapshotRouter.js +75 -66
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +9 -1
- package/extension/src/page/treeOperations.js +200 -19
- package/extension/src/page/writebackRouter.js +172 -113
- package/extension/src/pageBridge.js +124 -73
- package/extension/src/popup.js +1 -1
- package/extension/src/shared/agentTranscript.js +372 -18
- package/extension/src/shared/auditRecords.js +3 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +58 -17
- package/extension/src/shared/composerAttachmentDraft.js +27 -7
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/failureReasons.js +57 -3
- package/extension/src/shared/i18n.js +315 -29
- package/extension/src/shared/lineReferences.js +38 -31
- package/extension/src/shared/pageRpcContract.js +1 -1
- package/extension/src/shared/panelIcons.js +39 -0
- package/extension/src/shared/projectFiles.js +2 -1
- 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/scopedPersistenceBrowserAdapter.js +34 -0
- package/extension/src/shared/sessionState.js +63 -4
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/sharedSessionBridge.js +81 -13
- package/extension/src/shared/staleGuard.js +130 -63
- package/extension/src/shared/storageDb.js +35 -3
- package/extension/src/shared/storageMigration.js +111 -2
- package/extension/src/shared/storageRunActions.js +62 -8
- package/extension/src/shared/undoOperations.js +126 -28
- package/extension/src/shared/updateRuntimeIdentity.js +16 -2
- package/extension/src/shared/writebackSettlement.js +21 -3
- package/extension/styles/panel.css +1293 -549
- package/native-host/src/bootstrapUpdate.js +52 -16
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +10 -1
- package/native-host/src/chatCompletionsBridge.js +29 -2
- package/native-host/src/codexPromptAssembly.js +86 -118
- package/native-host/src/codexSessionRunner.js +127 -17
- package/native-host/src/debugLog.js +27 -3
- package/native-host/src/index.js +42 -11
- package/native-host/src/localSkills.js +1 -1
- package/native-host/src/mirrorWorkspace.js +47 -3
- package/native-host/src/nativeEnvironment.js +27 -5
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/skills/reviewer-response/SKILL.md +65 -0
- package/native-host/src/taskRunnerRuntime.js +28 -8
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/updateManager.js +144 -21
- package/native-host/src/writingStyleRuntime.js +1 -1
- package/package.json +5 -8
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
plus: '<path d="M8 3.2v9.6"/><path d="M3.2 8h9.6"/>',
|
|
13
13
|
panel: '<rect x="2.3" y="2.3" width="11.4" height="11.4" rx="2"/><path d="M9.2 2.5v11"/><path d="M5.1 6.1h1.5M5.1 8h1.5M5.1 9.9h1.5"/>',
|
|
14
14
|
collapse: '<path d="m6.1 3.3 4.7 4.7-4.7 4.7"/>',
|
|
15
|
+
chevronDown: '<path d="m4.4 6.4 3.6 3.6 3.6-3.6"/>',
|
|
15
16
|
settings: '<path d="M3.4 5.2h5.9"/><path d="M11.7 5.2h.9"/><path d="M10.1 3.6v3.2"/><path d="M3.4 10.8h1"/><path d="M6.8 10.8h5.8"/><path d="M5.9 9.2v3.2"/>'
|
|
16
17
|
};
|
|
17
18
|
const safeName = Object.prototype.hasOwnProperty.call(icons, name) ? name : 'settings';
|
|
@@ -53,8 +54,15 @@
|
|
|
53
54
|
<div class="codex-panel-resize-handle" data-panel-resize-handle title="Drag to resize the Codex panel. Double click to reset." aria-label="Drag to resize the Codex panel. Double click to reset." role="separator"></div>
|
|
54
55
|
<div class="codex-vscode-head" data-panel-header>
|
|
55
56
|
<div class="codex-vscode-title">CODEX</div>
|
|
57
|
+
<div class="codex-thread-title codex-head-session" data-session-label>
|
|
58
|
+
<button type="button" class="codex-thread-title-trigger" data-session-menu-trigger aria-haspopup="menu" aria-expanded="false">
|
|
59
|
+
<span class="codex-thread-title-text" data-session-label-text></span>
|
|
60
|
+
<span class="codex-thread-title-chevron">${codexIcon('chevronDown')}</span>
|
|
61
|
+
</button>
|
|
62
|
+
<input type="text" class="codex-thread-title-input" data-session-rename-input maxlength="80" hidden>
|
|
63
|
+
<div class="codex-session-menu" data-session-menu role="menu" hidden></div>
|
|
64
|
+
</div>
|
|
56
65
|
<div class="codex-vscode-head-actions" aria-label="Codex actions">
|
|
57
|
-
<button type="button" data-refresh title="Refresh current file status. This will not sync or modify files." aria-label="Refresh current file status. This will not sync or modify files.">${codexIcon('refresh')}</button>
|
|
58
66
|
<div data-diagnostics-slot></div>
|
|
59
67
|
<button type="button" data-new-session title="New Session" aria-label="New Session">${codexIcon('plus')}</button>
|
|
60
68
|
<button type="button" data-custom-instructions-settings title="Project Settings" aria-label="Project Settings" aria-expanded="false">${codexIcon('settings')}</button>
|
|
@@ -86,7 +94,6 @@
|
|
|
86
94
|
|
|
87
95
|
bind(instance, panelEl, 'click', event => event.stopPropagation());
|
|
88
96
|
bind(instance, panelEl, 'mousedown', event => event.stopPropagation());
|
|
89
|
-
bind(instance, panelEl.querySelector('[data-refresh]'), 'click', () => callbacks.onRefresh?.());
|
|
90
97
|
bind(instance, panelEl.querySelector('[data-new-session]'), 'click', () => callbacks.onNewSession?.());
|
|
91
98
|
bind(instance, panelEl.querySelector('[data-custom-instructions-settings]'), 'click', () => callbacks.onSettingsClick?.());
|
|
92
99
|
bind(instance, panelEl.querySelector('[data-close-panel]'), 'click', () => callbacks.onClosePanel?.());
|
|
@@ -221,7 +228,10 @@
|
|
|
221
228
|
return compact;
|
|
222
229
|
}
|
|
223
230
|
|
|
224
|
-
function setVisible(
|
|
231
|
+
function setVisible(target, visible) {
|
|
232
|
+
const instance = target?._instance;
|
|
233
|
+
const panelEl = instance?.panelEl || target;
|
|
234
|
+
if (visible && instance) updateCompactMode(instance);
|
|
225
235
|
panelEl?.classList?.toggle('is-open', Boolean(visible));
|
|
226
236
|
document.documentElement.classList.toggle(ROOT_CLASS, Boolean(visible));
|
|
227
237
|
const launcherEl = panelEl?.ownerDocument?.getElementById?.('codex-overleaf-launcher');
|
|
@@ -320,7 +330,7 @@
|
|
|
320
330
|
}
|
|
321
331
|
const panel = options.getPanel?.();
|
|
322
332
|
if (!panel) {
|
|
323
|
-
return Promise.resolve(false);
|
|
333
|
+
return Promise.resolve(config.choices?.length ? 'cancel' : false);
|
|
324
334
|
}
|
|
325
335
|
dismiss(false);
|
|
326
336
|
const tr = options.tr || (key => key);
|
|
@@ -329,6 +339,8 @@
|
|
|
329
339
|
const confirmLabel = config.confirmLabel || tr('confirmDefaultConfirm');
|
|
330
340
|
const cancelLabel = config.cancelLabel || tr('confirmDefaultCancel');
|
|
331
341
|
const destructive = config.destructive === true;
|
|
342
|
+
const choices = Array.isArray(config.choices) && config.choices.length ? config.choices : null;
|
|
343
|
+
const cancelled = choices ? 'cancel' : false;
|
|
332
344
|
const doc = panel.ownerDocument || document;
|
|
333
345
|
|
|
334
346
|
return new Promise(resolve => {
|
|
@@ -369,54 +381,69 @@
|
|
|
369
381
|
body.textContent = String(message);
|
|
370
382
|
const actions = doc.createElement('div');
|
|
371
383
|
actions.className = 'codex-plugin-confirm-actions';
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
384
|
+
const buttons = (choices || [
|
|
385
|
+
{ value: false, label: cancelLabel }, { value: true, label: confirmLabel }
|
|
386
|
+
]).map(choice => {
|
|
387
|
+
const button = doc.createElement('button');
|
|
388
|
+
button.type = 'button';
|
|
389
|
+
const cancel = choice.value === false || choice.value === 'cancel';
|
|
390
|
+
// Only one primary action: other affirmative choices can ask for the secondary style.
|
|
391
|
+
button.className = cancel || choice.secondary === true ? 'codex-plugin-confirm-cancel' : 'codex-plugin-confirm-confirm';
|
|
392
|
+
if (destructive && !cancel) button.dataset.destructive = 'true';
|
|
393
|
+
button.textContent = choice.label;
|
|
394
|
+
button.addEventListener('click', () => cleanup(choice.value));
|
|
395
|
+
actions.append(button);
|
|
396
|
+
return button;
|
|
397
|
+
});
|
|
382
398
|
card.append(head, body, actions);
|
|
383
399
|
overlay.append(card);
|
|
384
400
|
panel.append(overlay);
|
|
385
401
|
overlay.showModal?.();
|
|
386
402
|
|
|
403
|
+
let timeout, ownerCheck, removalObserver;
|
|
404
|
+
const onAbort = () => cleanup(cancelled);
|
|
387
405
|
const cleanup = value => {
|
|
388
406
|
if (settled) return;
|
|
389
407
|
settled = true;
|
|
390
408
|
activeResolve = null;
|
|
391
409
|
doc.removeEventListener('keydown', onKeydown, true);
|
|
410
|
+
config.signal?.removeEventListener('abort', onAbort);
|
|
411
|
+
clearTimeout(timeout);
|
|
412
|
+
clearInterval(ownerCheck);
|
|
413
|
+
removalObserver?.disconnect();
|
|
392
414
|
overlay.close?.();
|
|
393
415
|
overlay.remove();
|
|
394
416
|
returnFocus?.focus?.();
|
|
395
|
-
resolve(value);
|
|
417
|
+
resolve(choices && !choices.some(choice => choice.value === value) ? cancelled : value);
|
|
396
418
|
};
|
|
397
419
|
activeResolve = cleanup;
|
|
398
420
|
const onKeydown = event => {
|
|
399
421
|
if (event.key === 'Escape') {
|
|
400
|
-
event.preventDefault();
|
|
401
|
-
cleanup(false);
|
|
422
|
+
event.preventDefault(); cleanup(cancelled);
|
|
402
423
|
} else if (event.key === 'Enter') {
|
|
403
424
|
event.preventDefault();
|
|
404
|
-
if (
|
|
405
|
-
else if (
|
|
406
|
-
else if (event.target === confirm) cleanup(true);
|
|
425
|
+
if (buttons.includes(event.target)) event.target.click();
|
|
426
|
+
else if (!choices && !destructive) cleanup(true);
|
|
407
427
|
} else if (event.key === 'Tab') {
|
|
408
428
|
event.preventDefault();
|
|
409
|
-
|
|
429
|
+
const index = buttons.indexOf(event.target);
|
|
430
|
+
buttons[(index + (event.shiftKey ? buttons.length - 1 : 1)) % buttons.length].focus();
|
|
410
431
|
}
|
|
411
432
|
};
|
|
412
|
-
overlay.addEventListener('click', event => {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
overlay.addEventListener('cancel', event => { event.preventDefault(); cleanup(false); });
|
|
416
|
-
cancel.addEventListener('click', () => cleanup(false));
|
|
417
|
-
confirm.addEventListener('click', () => cleanup(true));
|
|
433
|
+
overlay.addEventListener('click', event => { if (event.target === overlay) cleanup(cancelled); });
|
|
434
|
+
overlay.addEventListener('cancel', event => { event.preventDefault(); cleanup(cancelled); });
|
|
435
|
+
overlay.addEventListener('close', () => cleanup(cancelled));
|
|
418
436
|
doc.addEventListener('keydown', onKeydown, true);
|
|
419
|
-
(
|
|
437
|
+
config.signal?.addEventListener('abort', onAbort, { once: true });
|
|
438
|
+
if (config.timeoutMs > 0) timeout = setTimeout(onAbort, config.timeoutMs);
|
|
439
|
+
if (config.isCurrent) ownerCheck = setInterval(() => { if (!config.isCurrent()) onAbort(); }, 100);
|
|
440
|
+
const Observer = doc.defaultView?.MutationObserver;
|
|
441
|
+
if (choices && Observer) {
|
|
442
|
+
removalObserver = new Observer(() => { if (!overlay.isConnected) onAbort(); });
|
|
443
|
+
removalObserver.observe(panel, { childList: true });
|
|
444
|
+
}
|
|
445
|
+
(choices ? buttons[0] : buttons[destructive ? 0 : 1]).focus();
|
|
446
|
+
if (config.signal?.aborted || config.isCurrent?.() === false) onAbort();
|
|
420
447
|
});
|
|
421
448
|
}
|
|
422
449
|
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
// Pull a queued message back into the composer. A typed draft is never overwritten.
|
|
116
116
|
function pullBack(item) {
|
|
117
117
|
const task = container?.parentElement?.querySelector('[data-task]');
|
|
118
|
-
if (!task || (item.status || 'queued') !== 'queued') return;
|
|
118
|
+
if (!task || task.readOnly || (item.status || 'queued') !== 'queued') return;
|
|
119
119
|
if (String(task.value || '').trim()) { options.onToast?.(tr('queuedInputEditBlocked')); return; }
|
|
120
120
|
options.onRemove?.(item.id);
|
|
121
121
|
task.value = item.text;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
(function initCodexOverleafProjectSettingsCoordinator() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
// Skill messages belong to the settings workbench page that holds the Skills group.
|
|
5
|
+
const SKILLS_STATUS = Object.freeze({ page: 'context' });
|
|
6
|
+
|
|
4
7
|
function create(deps = {}) {
|
|
5
8
|
const {
|
|
6
9
|
CodexOverleafTheme,
|
|
@@ -100,7 +103,7 @@
|
|
|
100
103
|
refreshLocalSkills().catch(error => setProjectSettingsStatus(tx(
|
|
101
104
|
`Could not list local skills: ${error.message}`,
|
|
102
105
|
`无法列出本地技能:${error.message}`
|
|
103
|
-
), 'failed'));
|
|
106
|
+
), 'failed', SKILLS_STATUS));
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
function toggleCustomInstructionsSettings() {
|
|
@@ -128,7 +131,7 @@
|
|
|
128
131
|
refreshLocalSkills().catch(error => setProjectSettingsStatus(tx(
|
|
129
132
|
`Could not list local skills: ${error.message}`,
|
|
130
133
|
`无法列出本地技能:${error.message}`
|
|
131
|
-
), 'failed'));
|
|
134
|
+
), 'failed', SKILLS_STATUS));
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
function closeSkillsView() {
|
|
@@ -242,6 +245,25 @@
|
|
|
242
245
|
});
|
|
243
246
|
}
|
|
244
247
|
|
|
248
|
+
function getReviewingAvailability() {
|
|
249
|
+
return getState()?.reviewingAvailabilityByProject?.[getCurrentProjectId()] || null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function observeReviewingAvailability(status, evidence) {
|
|
253
|
+
const projectId = getCurrentProjectId();
|
|
254
|
+
if (!projectId || !['available', 'unavailable', 'unknown'].includes(status)) return;
|
|
255
|
+
const previous = getReviewingAvailability();
|
|
256
|
+
// A remembered failure must not renew its own expiry.
|
|
257
|
+
if (previous?.status === status && previous.evidence === evidence && status === 'available') return;
|
|
258
|
+
// Preserve ordering even when a retry and its previous observation share a millisecond.
|
|
259
|
+
const observedAt = Math.max(Date.now(), Number.isFinite(previous?.observedAt) ? previous.observedAt + 1 : 0);
|
|
260
|
+
setState({ ...getState(), reviewingAvailabilityByProject: {
|
|
261
|
+
...getState()?.reviewingAvailabilityByProject,
|
|
262
|
+
[projectId]: { status, evidence, observedAt }
|
|
263
|
+
} });
|
|
264
|
+
saveStateSoon(0);
|
|
265
|
+
}
|
|
266
|
+
|
|
245
267
|
function getThemePreference() {
|
|
246
268
|
return CodexOverleafTheme.normalizeThemePreference(getState()?.theme);
|
|
247
269
|
}
|
|
@@ -316,11 +338,11 @@
|
|
|
316
338
|
const pending = deps.updateGlobalPreferences({ codexLocalSkillEnabled: { [skillId]: Boolean(enabled) } });
|
|
317
339
|
Promise.resolve(pending).then(() => saveStateSoon()).catch(() => {
|
|
318
340
|
renderLocalSkillList();
|
|
319
|
-
setProjectSettingsStatus(tx('Could not save the local skill selection. Try again.', '无法保存本地技能选择,请重试。'), 'failed');
|
|
341
|
+
setProjectSettingsStatus(tx('Could not save the local skill selection. Try again.', '无法保存本地技能选择,请重试。'), 'failed', SKILLS_STATUS);
|
|
320
342
|
});
|
|
321
343
|
} catch (_error) {
|
|
322
344
|
renderLocalSkillList();
|
|
323
|
-
setProjectSettingsStatus(tx('Could not save the local skill selection. Try again.', '无法保存本地技能选择,请重试。'), 'failed');
|
|
345
|
+
setProjectSettingsStatus(tx('Could not save the local skill selection. Try again.', '无法保存本地技能选择,请重试。'), 'failed', SKILLS_STATUS);
|
|
324
346
|
}
|
|
325
347
|
} else {
|
|
326
348
|
setState({ ...getState(), codexLocalSkillEnabled: { ...getCodexLocalSkillEnabled(), [skillId]: Boolean(enabled) } });
|
|
@@ -348,8 +370,8 @@
|
|
|
348
370
|
renderLocalSkillList();
|
|
349
371
|
}
|
|
350
372
|
|
|
351
|
-
function setProjectSettingsStatus(text, status = 'info') {
|
|
352
|
-
SettingsPanel.setStatus(getSettingsPanelInstance(), text, status);
|
|
373
|
+
function setProjectSettingsStatus(text, status = 'info', options) {
|
|
374
|
+
SettingsPanel.setStatus(getSettingsPanelInstance(), text, status, options);
|
|
353
375
|
}
|
|
354
376
|
|
|
355
377
|
function clearProjectSettingsStatus() {
|
|
@@ -378,6 +400,8 @@
|
|
|
378
400
|
getGovernanceRulesForCurrentProject,
|
|
379
401
|
getSkillLoadingSettings,
|
|
380
402
|
getThemePreference,
|
|
403
|
+
getReviewingAvailability,
|
|
404
|
+
observeReviewingAvailability,
|
|
381
405
|
isCodexOverleafSkillEnabled,
|
|
382
406
|
normalizeCustomInstructionsByProject,
|
|
383
407
|
normalizeGovernanceRules,
|
|
@@ -613,18 +613,7 @@
|
|
|
613
613
|
actions.innerHTML = instance.embedded ? '' : `<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(tx('Close', '关闭'))}</button>`;
|
|
614
614
|
return;
|
|
615
615
|
}
|
|
616
|
-
const
|
|
617
|
-
const isNew = !provider.id;
|
|
618
|
-
const destinationChanged = active && getCurrentBaseUrl(instance) !== provider.endpointDisclosureBaseUrl;
|
|
619
|
-
const actionState = getFooterActionState({
|
|
620
|
-
isNew,
|
|
621
|
-
active,
|
|
622
|
-
dirty: instance.dirty,
|
|
623
|
-
destinationChanged,
|
|
624
|
-
canSave: canSaveCurrentDraft(instance),
|
|
625
|
-
hasProject: instance.callbacks.hasCurrentProject?.() !== false,
|
|
626
|
-
canActivate: canActivateCurrentProvider(instance)
|
|
627
|
-
});
|
|
616
|
+
const actionState = getCurrentFooterActionState(instance);
|
|
628
617
|
const saveDisabled = actionState.saveEnabled ? '' : ' disabled aria-disabled="true"';
|
|
629
618
|
const useDisabled = actionState.useEnabled ? '' : ' disabled aria-disabled="true"';
|
|
630
619
|
actions.innerHTML = `
|
|
@@ -638,6 +627,21 @@
|
|
|
638
627
|
`;
|
|
639
628
|
}
|
|
640
629
|
|
|
630
|
+
function getCurrentFooterActionState(instance) {
|
|
631
|
+
const provider = getSelectedProvider(instance);
|
|
632
|
+
const active = Boolean(provider?.id && provider.id === getCurrentProjectProviderId(instance));
|
|
633
|
+
return getFooterActionState({
|
|
634
|
+
isNew: !provider?.id,
|
|
635
|
+
active,
|
|
636
|
+
dirty: instance.dirty,
|
|
637
|
+
destinationChanged: active && getCurrentBaseUrl(instance) !== provider.endpointDisclosureBaseUrl,
|
|
638
|
+
disclosureAccepted: instance.root.querySelector('[data-provider-disclosure]')?.checked === true,
|
|
639
|
+
canSave: canSaveCurrentDraft(instance),
|
|
640
|
+
hasProject: instance.callbacks.hasCurrentProject?.() !== false,
|
|
641
|
+
canActivate: canActivateCurrentProvider(instance)
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
641
645
|
function getFooterActionState(options = {}) {
|
|
642
646
|
const isNew = options.isNew === true;
|
|
643
647
|
const active = options.active === true;
|
|
@@ -645,9 +649,9 @@
|
|
|
645
649
|
return {
|
|
646
650
|
showSave: isNew || dirty,
|
|
647
651
|
showSaveAndUse: options.hasProject !== false && (isNew || dirty) && (!active || options.destinationChanged === true),
|
|
648
|
-
showUse: options.hasProject !== false && !isNew && !active && !dirty,
|
|
652
|
+
showUse: options.hasProject !== false && !isNew && (!active || options.destinationChanged === true) && !dirty,
|
|
649
653
|
saveEnabled: options.canSave === true,
|
|
650
|
-
useEnabled: options.canActivate === true
|
|
654
|
+
useEnabled: options.canActivate === true && (!options.destinationChanged || options.disclosureAccepted === true)
|
|
651
655
|
};
|
|
652
656
|
}
|
|
653
657
|
|
|
@@ -837,6 +841,11 @@
|
|
|
837
841
|
refreshTestState(instance);
|
|
838
842
|
return;
|
|
839
843
|
}
|
|
844
|
+
if (event.target.matches('[data-provider-disclosure]')) {
|
|
845
|
+
renderFooter(instance);
|
|
846
|
+
applyBusyState(instance);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
840
849
|
if (event.target.matches('[data-provider-field]')) {
|
|
841
850
|
const field = event.target.dataset.providerField;
|
|
842
851
|
if (event.target.dataset.providerField === 'apiKey') {
|
|
@@ -1023,8 +1032,7 @@
|
|
|
1023
1032
|
|
|
1024
1033
|
function applyBusyState(instance) {
|
|
1025
1034
|
const busy = Boolean(instance.busy && instance.busy !== 'failed');
|
|
1026
|
-
const
|
|
1027
|
-
const canActivate = canActivateCurrentProvider(instance);
|
|
1035
|
+
const actionState = getCurrentFooterActionState(instance);
|
|
1028
1036
|
instance.root.dataset.busy = instance.busy || '';
|
|
1029
1037
|
for (const element of instance.root.querySelectorAll('input, select, textarea, button')) {
|
|
1030
1038
|
if (element.matches('[data-provider-confirm-answer]')) {
|
|
@@ -1034,9 +1042,9 @@
|
|
|
1034
1042
|
} else if (element.matches('[data-provider-action="cancel-test"]')) {
|
|
1035
1043
|
element.disabled = false;
|
|
1036
1044
|
} else if (element.matches('[data-provider-action="save"], [data-provider-action="save-use"]')) {
|
|
1037
|
-
element.disabled = busy || !
|
|
1045
|
+
element.disabled = busy || !actionState.saveEnabled;
|
|
1038
1046
|
} else if (element.matches('[data-provider-action="use"]')) {
|
|
1039
|
-
element.disabled = busy || !
|
|
1047
|
+
element.disabled = busy || !actionState.useEnabled;
|
|
1040
1048
|
} else {
|
|
1041
1049
|
element.disabled = busy;
|
|
1042
1050
|
}
|
|
@@ -1296,7 +1304,8 @@
|
|
|
1296
1304
|
|
|
1297
1305
|
function renderTemplatePicker(instance, detail) {
|
|
1298
1306
|
const tx = instance.tx;
|
|
1299
|
-
detail.innerHTML = '<div class="codex-provider-template-head"><button type="button" class="codex-provider-inline-button" data-provider-action="templates-back">'
|
|
1307
|
+
detail.innerHTML = '<div class="codex-provider-template-head"><button type="button" class="codex-provider-inline-button codex-provider-back-button" data-provider-action="templates-back">'
|
|
1308
|
+
+ '<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="m9.8 3.6-4.4 4.4 4.4 4.4"/></svg>'
|
|
1300
1309
|
+ escapeHtml(tx('Back', '返回')) + '</button><h3>' + escapeHtml(tx('Add a connection', '添加连接')) + '</h3></div>'
|
|
1301
1310
|
+ '<p class="codex-provider-help">' + escapeHtml(tx(
|
|
1302
1311
|
'Service shortcuts default to Anthropic Messages. Chat Completions remains available; preset addresses follow the selected protocol. Add the matching API key and model IDs separately.',
|
|
@@ -199,10 +199,11 @@
|
|
|
199
199
|
} catch (_error) { /* swallow */ }
|
|
200
200
|
try {
|
|
201
201
|
var docTitle = (document.title || '').trim();
|
|
202
|
-
// Overleaf
|
|
203
|
-
// "<Project Name> - Online LaTeX Editor";
|
|
202
|
+
// Overleaf formats the title as "<Project Name> - Overleaf" or
|
|
203
|
+
// "<Project Name> - Online LaTeX Editor Overleaf" (localized); drop the
|
|
204
|
+
// last " - " segment when it names Overleaf.
|
|
204
205
|
if (docTitle) {
|
|
205
|
-
var stripped = docTitle.replace(/\s
|
|
206
|
+
var stripped = docTitle.replace(/\s+[-–|]\s+[^-–|]*Overleaf[^-–|]*$/i, '').trim();
|
|
206
207
|
if (stripped && stripped.toLowerCase() !== 'overleaf') {
|
|
207
208
|
return stripped;
|
|
208
209
|
}
|
|
@@ -454,10 +455,10 @@
|
|
|
454
455
|
};
|
|
455
456
|
}
|
|
456
457
|
|
|
457
|
-
async function refreshDashboardView() {
|
|
458
|
+
async function refreshDashboardView(options = {}) {
|
|
458
459
|
var panelEl = getPanel();
|
|
459
460
|
if (panelEl && panelEl.dataset.view === 'recent-projects') {
|
|
460
|
-
await renderRecentProjectsVariant(dashboardViewOptions());
|
|
461
|
+
await renderRecentProjectsVariant(Object.assign(dashboardViewOptions(), options));
|
|
461
462
|
}
|
|
462
463
|
}
|
|
463
464
|
|
|
@@ -633,7 +634,7 @@
|
|
|
633
634
|
: {};
|
|
634
635
|
map[projectId] = record.id;
|
|
635
636
|
prefs.activeSessionByProject = map;
|
|
636
|
-
await Migration.savePrefs(
|
|
637
|
+
await Migration.savePrefs({ activeSessionByProject: map }, accountScopeId, projectId);
|
|
637
638
|
}
|
|
638
639
|
} catch (_error) { /* fall through: opening the project still works */ }
|
|
639
640
|
openProjectFromRow(projectId);
|
|
@@ -785,19 +786,31 @@
|
|
|
785
786
|
input.focus();
|
|
786
787
|
input.select();
|
|
787
788
|
var settled = false;
|
|
788
|
-
var finish = function (commit) {
|
|
789
|
+
var finish = async function (commit) {
|
|
789
790
|
if (settled) {
|
|
790
791
|
return;
|
|
791
792
|
}
|
|
792
793
|
settled = true;
|
|
793
794
|
var nextRaw = input.value;
|
|
794
|
-
input.remove();
|
|
795
|
-
titleEl.hidden = false;
|
|
796
795
|
// Unchanged input is a cancel: never rewrites the title source.
|
|
797
796
|
if (!commit || nextRaw.trim() === seed.trim()) {
|
|
797
|
+
input.remove();
|
|
798
|
+
titleEl.hidden = false;
|
|
798
799
|
return;
|
|
799
800
|
}
|
|
800
|
-
|
|
801
|
+
input.readOnly = true;
|
|
802
|
+
try {
|
|
803
|
+
await commitDashboardSessionRename(sessionsEl, projectId, record, nextRaw);
|
|
804
|
+
} catch (_error) {
|
|
805
|
+
settled = false;
|
|
806
|
+
input.readOnly = false;
|
|
807
|
+
showPluginToast(tr('recentProjects_renameFailed'), { status: 'failed', sticky: true });
|
|
808
|
+
input.focus();
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
input.remove();
|
|
812
|
+
titleEl.hidden = false;
|
|
813
|
+
await refreshDashboardView().catch(function () { /* keep last render */ });
|
|
801
814
|
};
|
|
802
815
|
input.addEventListener('keydown', function (event) {
|
|
803
816
|
if (event.key === 'Enter') {
|
|
@@ -832,15 +845,12 @@
|
|
|
832
845
|
return SessionState.renameSession(state, record.id, rawTitle, { placeholderTitle: tr('newSessionFallback') });
|
|
833
846
|
});
|
|
834
847
|
} catch (_error) { /* blob may be absent; record update still proceeds */ }
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
842
|
-
} catch (_error) { /* swallow */ }
|
|
843
|
-
await refreshDashboardView();
|
|
848
|
+
if (StorageDb) {
|
|
849
|
+
await StorageDb.putRecord('sessions', StorageDb.buildSessionRecord(Object.assign({}, record, {
|
|
850
|
+
title: next.title,
|
|
851
|
+
titleSource: next.titleSource
|
|
852
|
+
}), { preserveRunActionPayload: true }));
|
|
853
|
+
}
|
|
844
854
|
}
|
|
845
855
|
|
|
846
856
|
function ensureRecentProjectsRoot() {
|
|
@@ -1070,6 +1080,7 @@
|
|
|
1070
1080
|
openProjectFromRow,
|
|
1071
1081
|
renderRecentProjectRow,
|
|
1072
1082
|
renderRecentProjectsVariant,
|
|
1083
|
+
refreshDashboardView,
|
|
1073
1084
|
mountDashboardShell,
|
|
1074
1085
|
resetProjectNameCache,
|
|
1075
1086
|
renderPerProjectVariant
|