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
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
function codexIcon(name) {
|
|
11
11
|
const icons = {
|
|
12
|
+
archive: '<rect x="2.5" y="3" width="11" height="3.2" rx=".8"/><path d="M3.5 6.2v6.3a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V6.2"/><path d="M6.5 9h3"/>',
|
|
12
13
|
appearance: '<path d="M8 2.2v1.1"/><path d="M8 12.7v1.1"/><path d="M2.2 8h1.1"/><path d="M12.7 8h1.1"/><path d="m3.9 3.9.8.8"/><path d="m11.3 11.3.8.8"/><path d="m12.1 3.9-.8.8"/><path d="m4.7 11.3-.8.8"/><circle cx="8" cy="8" r="2.4"/>',
|
|
13
14
|
bolt: '<path d="M8.9 1.8 3.8 8.8h3.3l-.5 5.4 5.5-7.5H8.8z"/>',
|
|
15
|
+
close: '<path d="m4.6 4.6 6.8 6.8M11.4 4.6l-6.8 6.8"/>',
|
|
14
16
|
database: '<ellipse cx="8" cy="4" rx="4.5" ry="1.9"/><path d="M3.5 4v4c0 1 2 1.9 4.5 1.9s4.5-.9 4.5-1.9V4"/><path d="M3.5 8v4c0 1 2 1.9 4.5 1.9s4.5-.9 4.5-1.9V8"/>',
|
|
15
17
|
flask: '<path d="M6 2.5h4"/><path d="M7 2.5v4.1l-3.4 5.3A1.4 1.4 0 0 0 4.8 14h6.4a1.4 1.4 0 0 0 1.2-2.1L9 6.6V2.5"/><path d="M5.3 10.5h5.4"/>',
|
|
16
18
|
history: '<path d="M4.2 3.4h7.6"/><path d="M4.2 8h7.6"/><path d="M4.2 12.6h4.6"/><path d="M2.2 3.4h.1"/><path d="M2.2 8h.1"/><path d="M2.2 12.6h.1"/>',
|
|
@@ -36,7 +38,9 @@
|
|
|
36
38
|
container,
|
|
37
39
|
callbacks: options.callbacks || {},
|
|
38
40
|
i18n: options.i18n || null,
|
|
39
|
-
button: options.button || null
|
|
41
|
+
button: options.button || null,
|
|
42
|
+
// Names the open project in project-page scope lines; may return ''.
|
|
43
|
+
getProjectName: typeof options.getProjectName === 'function' ? options.getProjectName : null
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
container.innerHTML = `
|
|
@@ -211,6 +215,7 @@
|
|
|
211
215
|
<p class="codex-set-row-help" data-update-settings-summary data-i18n="softwareUpdatesHelp">Automatically checks signed stable releases for the Extension and Native Host.</p>
|
|
212
216
|
<p class="codex-set-note" data-update-settings-state aria-live="polite" hidden></p>
|
|
213
217
|
<button type="button" class="codex-set-btn" data-check-updates data-i18n="checkForUpdates">Check for updates</button>
|
|
218
|
+
<button type="button" class="codex-set-btn" data-restore-previous-version hidden>Restore previous version</button>
|
|
214
219
|
</div>
|
|
215
220
|
</div>
|
|
216
221
|
</details>
|
|
@@ -334,6 +339,7 @@
|
|
|
334
339
|
return host || null;
|
|
335
340
|
},
|
|
336
341
|
setProviderCloseGuard: guard => instance.workbench?.setProviderCloseGuard(guard),
|
|
342
|
+
revealGroup: key => instance.workbench?.revealGroup(key) || null,
|
|
337
343
|
loadState: state => loadState(instance, state),
|
|
338
344
|
readState: () => readState(instance),
|
|
339
345
|
setStatus: (text, status) => setStatus(instance, text, status),
|
|
@@ -464,7 +470,9 @@
|
|
|
464
470
|
};
|
|
465
471
|
}
|
|
466
472
|
|
|
467
|
-
|
|
473
|
+
// options.page names the workbench page a message belongs to; the workbench
|
|
474
|
+
// shows it only while that page is active. Untagged messages show everywhere.
|
|
475
|
+
function setStatus(target, text, status = 'info', options = {}) {
|
|
468
476
|
const instance = target?._instance || target;
|
|
469
477
|
const element = instance?.container?.querySelector('[data-project-settings-status]');
|
|
470
478
|
if (!element) {
|
|
@@ -472,6 +480,9 @@
|
|
|
472
480
|
}
|
|
473
481
|
element.textContent = text || '';
|
|
474
482
|
element.dataset.status = status;
|
|
483
|
+
if (options.page) element.dataset.statusPage = options.page;
|
|
484
|
+
else delete element.dataset.statusPage;
|
|
485
|
+
instance.workbench?.refresh();
|
|
475
486
|
}
|
|
476
487
|
|
|
477
488
|
function clearStatus(target) {
|
|
@@ -482,6 +493,8 @@
|
|
|
482
493
|
}
|
|
483
494
|
element.textContent = '';
|
|
484
495
|
delete element.dataset.status;
|
|
496
|
+
delete element.dataset.statusPage;
|
|
497
|
+
instance.workbench?.refresh();
|
|
485
498
|
}
|
|
486
499
|
|
|
487
500
|
function setSkillsSummary(target, text) {
|
|
@@ -4,21 +4,32 @@
|
|
|
4
4
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
['
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
// The sidebar is split by scope; a page's section decides whether its groups
|
|
8
|
+
// need an open project. Pages: [id, label key, icon].
|
|
9
|
+
const SECTIONS = [
|
|
10
|
+
['project', 'settingsScopeProjectTitle', [
|
|
11
|
+
['writing', 'settingsWorkbenchWriting', 'pen'],
|
|
12
|
+
['protection', 'settingsWorkbenchProtection', 'shield'],
|
|
13
|
+
['cache', 'otSettingsTitle', 'flask'],
|
|
14
|
+
['history', 'settingsWorkbenchChanges', 'history']
|
|
15
|
+
]],
|
|
16
|
+
['global', 'settingsScopeGlobalTitle', [
|
|
17
|
+
['appearance', 'appearanceTitle', 'appearance'],
|
|
18
|
+
['providers', 'settingsWorkbenchModels', 'bolt'],
|
|
19
|
+
['context', 'settingsWorkbenchContextSkills', 'database'],
|
|
20
|
+
['storage', 'storageTitle', 'archive'],
|
|
21
|
+
['updates', 'softwareUpdatesTitle', 'software']
|
|
22
|
+
]]
|
|
15
23
|
];
|
|
16
24
|
const GROUP_PAGES = {
|
|
17
|
-
|
|
18
|
-
experimental: '
|
|
19
|
-
|
|
25
|
+
'writing-style': 'writing', personalization: 'writing', protection: 'protection', privacy: 'protection',
|
|
26
|
+
experimental: 'cache', history: 'history', appearance: 'appearance', providers: 'providers',
|
|
27
|
+
'context-loading': 'context', skills: 'context', storage: 'storage', updates: 'updates'
|
|
20
28
|
};
|
|
21
|
-
|
|
29
|
+
// Groups without a mapping keep their original scope.
|
|
30
|
+
const FALLBACK_PAGES = { project: 'writing', global: 'context' };
|
|
31
|
+
// The skills list opens in place of the page that holds the Skills group.
|
|
32
|
+
const SKILLS_PAGE = 'context';
|
|
22
33
|
|
|
23
34
|
function mount(instance, options = {}) {
|
|
24
35
|
const container = instance.container, doc = container.ownerDocument;
|
|
@@ -50,93 +61,94 @@
|
|
|
50
61
|
const status = settings.querySelector('[data-project-settings-status]');
|
|
51
62
|
const dialog = make('dialog', 'codex-settings-workbench');
|
|
52
63
|
dialog.setAttribute('aria-labelledby', 'codex-settings-workbench-title');
|
|
53
|
-
const
|
|
54
|
-
const heading = make('div', 'codex-wb-heading');
|
|
55
|
-
const title = make('h2', '', 'settingsWorkbenchTitle');
|
|
64
|
+
const title = make('h2', 'codex-wb-title', 'settingsWorkbenchTitle');
|
|
56
65
|
title.id = 'codex-settings-workbench-title';
|
|
57
|
-
heading.append(title, make('p', '', 'settingsWorkbenchSubtitle'));
|
|
58
66
|
closeButton.classList.add('codex-wb-close');
|
|
59
|
-
closeButton.
|
|
60
|
-
|
|
67
|
+
if (options.icon) closeButton.innerHTML = options.icon('close'); // Static, repository-owned icon path.
|
|
68
|
+
else closeButton.textContent = '\u00d7';
|
|
61
69
|
originalHead?.remove();
|
|
62
70
|
|
|
63
71
|
const layout = make('div', 'codex-wb-layout');
|
|
64
72
|
const sidebar = make('aside', 'codex-wb-sidebar');
|
|
65
|
-
const filters = make('div', 'codex-wb-filters');
|
|
66
|
-
filters.setAttribute('role', 'group');
|
|
67
|
-
const allFilter = button('', 'settingsWorkbenchAll');
|
|
68
|
-
const projectFilter = button('', 'settingsScopeProjectTitle');
|
|
69
|
-
filters.append(allFilter, projectFilter);
|
|
70
73
|
const nav = make('nav', 'codex-wb-nav');
|
|
71
74
|
nav.setAttribute('role', 'tablist');
|
|
72
75
|
nav.setAttribute('aria-orientation', 'vertical');
|
|
73
|
-
|
|
74
|
-
brand.textContent = 'Codex Overleaf Link';
|
|
75
|
-
sidebar.append(filters, nav, brand);
|
|
76
|
+
sidebar.append(title, nav);
|
|
76
77
|
const main = make('div', 'codex-wb-main');
|
|
77
78
|
const body = make('div', 'codex-wb-body');
|
|
78
79
|
const footer = make('footer', 'codex-wb-footer');
|
|
79
80
|
if (status) { status.setAttribute('aria-live', 'polite'); footer.append(status); }
|
|
80
81
|
main.append(body, footer);
|
|
81
82
|
layout.append(sidebar, main);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
// The close button floats over the content's top-right corner.
|
|
84
|
+
dialog.append(layout, closeButton);
|
|
85
|
+
const pages = new Map(), navButtons = new Map(), pageScopes = new Map(), sectionLabels = new Map(), groups = [];
|
|
86
|
+
let active = 'writing', returnFocus = null, destroyed = false;
|
|
85
87
|
let providerRequested = false, providerCloseGuard = null, closing = null;
|
|
86
88
|
|
|
87
|
-
for (const [
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
for (const [scope, sectionKey, items] of SECTIONS) {
|
|
90
|
+
// Visual section heading; each tab references it as its description.
|
|
91
|
+
const label = make('div', 'codex-wb-nav-section', sectionKey);
|
|
92
|
+
label.id = 'codex-settings-nav-section-' + scope;
|
|
93
|
+
label.setAttribute('aria-hidden', 'true');
|
|
94
|
+
nav.append(label);
|
|
95
|
+
sectionLabels.set(scope, label);
|
|
96
|
+
for (const [id, key, icon] of items) {
|
|
97
|
+
const page = make('section', 'codex-wb-page');
|
|
98
|
+
page.id = 'codex-settings-page-' + id;
|
|
99
|
+
page.dataset.workbenchPage = id;
|
|
100
|
+
page.setAttribute('role', 'tabpanel');
|
|
101
|
+
page.setAttribute('aria-labelledby', 'codex-settings-nav-' + id);
|
|
102
|
+
page.hidden = true;
|
|
103
|
+
page.append(make('h3', 'codex-wb-page-title', key), make('p', 'codex-wb-page-scope'));
|
|
104
|
+
pages.set(id, page);
|
|
105
|
+
pageScopes.set(id, scope);
|
|
106
|
+
const tab = button('codex-wb-nav-button');
|
|
107
|
+
tab.id = 'codex-settings-nav-' + id;
|
|
108
|
+
tab.setAttribute('role', 'tab');
|
|
109
|
+
tab.setAttribute('aria-controls', page.id);
|
|
110
|
+
tab.setAttribute('aria-describedby', label.id);
|
|
111
|
+
if (options.icon) {
|
|
112
|
+
const glyph = make('span', 'codex-wb-nav-icon');
|
|
113
|
+
glyph.innerHTML = options.icon(icon); // Static, repository-owned icon paths only.
|
|
114
|
+
tab.append(glyph);
|
|
115
|
+
}
|
|
116
|
+
tab.append(make('span', '', key));
|
|
117
|
+
listen(tab, 'click', () => select(id));
|
|
118
|
+
nav.append(tab);
|
|
119
|
+
navButtons.set(id, tab);
|
|
104
120
|
}
|
|
105
|
-
tab.append(make('span', '', key));
|
|
106
|
-
listen(tab, 'click', () => select(id));
|
|
107
|
-
nav.append(tab);
|
|
108
|
-
navButtons.set(id, tab);
|
|
109
121
|
}
|
|
110
|
-
function addGroup(node, category
|
|
122
|
+
function addGroup(node, category) {
|
|
123
|
+
const projectOnly = pageScopes.get(category) === 'project';
|
|
111
124
|
node.hidden = true;
|
|
112
125
|
node.dataset.workbenchScope = projectOnly ? 'project' : 'global';
|
|
113
126
|
const summary = node.querySelector('summary');
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
if (summary) {
|
|
128
|
+
// Groups are plain sections: always open while their page is shown.
|
|
129
|
+
summary.tabIndex = -1;
|
|
130
|
+
listen(summary, 'click', event => event.preventDefault());
|
|
131
|
+
}
|
|
117
132
|
pages.get(category).append(node);
|
|
118
|
-
groups.push({ node, category, projectOnly
|
|
133
|
+
groups.push({ node, category, projectOnly });
|
|
119
134
|
}
|
|
135
|
+
const fallbackPage = scope => FALLBACK_PAGES[
|
|
136
|
+
scope && !scope.classList.contains('codex-project-settings-scope--global') ? 'project' : 'global'];
|
|
120
137
|
// Move existing controls rather than copying them. Their listeners, unsaved
|
|
121
138
|
// values, identity, validation and persistence callbacks remain attached.
|
|
122
139
|
for (const group of Array.from(settings.querySelectorAll('[data-set-group]'))) {
|
|
123
140
|
const key = group.dataset.setGroup;
|
|
124
|
-
|
|
125
|
-
const projectOnly = PROJECT_GROUPS.has(key)
|
|
126
|
-
|| (!GROUP_PAGES[key] && originalScope && !originalScope.classList.contains('codex-project-settings-scope--global'));
|
|
127
|
-
addGroup(group, GROUP_PAGES[key] || 'instructions', Boolean(projectOnly));
|
|
141
|
+
addGroup(group, GROUP_PAGES[key] || fallbackPage(group.closest('.codex-project-settings-scope')));
|
|
128
142
|
}
|
|
129
|
-
// Preserve non-card controls
|
|
130
|
-
// Scope headings are replaced by labels on the actual groups.
|
|
143
|
+
// Preserve non-card controls. The sidebar sections replace the scope headings.
|
|
131
144
|
for (const scope of Array.from(settings.querySelectorAll('.codex-project-settings-scope'))) {
|
|
132
145
|
scope.querySelectorAll('.codex-project-settings-scope-title').forEach(node => node.remove());
|
|
133
|
-
if (scope.children.length)
|
|
134
|
-
|
|
135
|
-
} else scope.remove();
|
|
146
|
+
if (scope.children.length) addGroup(scope, fallbackPage(scope));
|
|
147
|
+
else scope.remove();
|
|
136
148
|
}
|
|
137
149
|
for (const node of Array.from(settings.children)) {
|
|
138
150
|
if (node === status) continue;
|
|
139
|
-
addGroup(node,
|
|
151
|
+
addGroup(node, FALLBACK_PAGES.global);
|
|
140
152
|
}
|
|
141
153
|
const providerHost = make('div', 'codex-wb-provider-host');
|
|
142
154
|
providerHost.dataset.providerSettingsHost = '';
|
|
@@ -218,29 +230,54 @@
|
|
|
218
230
|
&& /^\/project\/[^/]+/.test(win?.location?.pathname || '');
|
|
219
231
|
}
|
|
220
232
|
function available(item) {
|
|
221
|
-
return
|
|
233
|
+
return !item.projectOnly || hasProject();
|
|
234
|
+
}
|
|
235
|
+
// A page whose only visible group repeats the page title hides that group's
|
|
236
|
+
// title; the group header keeps only its Saved mark.
|
|
237
|
+
function refreshSoleSections() {
|
|
238
|
+
for (const page of pages.values()) {
|
|
239
|
+
const pageGroups = Array.from(page.querySelectorAll('.codex-set-group'));
|
|
240
|
+
const visibleGroups = pageGroups.filter(node => !node.hidden);
|
|
241
|
+
const pageTitle = page.querySelector('.codex-wb-page-title')?.textContent.trim();
|
|
242
|
+
for (const node of pageGroups) {
|
|
243
|
+
const groupTitle = node.querySelector('.codex-set-group-head .codex-set-group-title');
|
|
244
|
+
const sole = visibleGroups.length === 1 && visibleGroups[0] === node
|
|
245
|
+
&& groupTitle && groupTitle.textContent.trim() === pageTitle;
|
|
246
|
+
if (sole) node.dataset.soleSection = 'true';
|
|
247
|
+
else delete node.dataset.soleSection;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Each page states its scope once, under its title.
|
|
252
|
+
function renderPageScope(page, scope, projectName) {
|
|
253
|
+
const line = page.querySelector('.codex-wb-page-scope');
|
|
254
|
+
if (!line) return;
|
|
255
|
+
const text = t(scope === 'project' ? 'settingsPageScopeProject' : 'settingsPageScopeGlobal');
|
|
256
|
+
if (scope !== 'project' || !projectName) { line.textContent = text; return; }
|
|
257
|
+
const name = doc.createElement('b');
|
|
258
|
+
name.textContent = projectName;
|
|
259
|
+
line.replaceChildren(text + ' \u00b7 ', name);
|
|
222
260
|
}
|
|
223
261
|
function refresh() {
|
|
224
262
|
if (destroyed) return;
|
|
225
|
-
if (
|
|
226
|
-
if (panel.dataset.view === 'skills') { active = 'instructions'; filter = 'all'; }
|
|
263
|
+
if (panel.dataset.view === 'skills') active = SKILLS_PAGE;
|
|
227
264
|
const availablePages = new Set(groups.filter(available).map(item => item.category));
|
|
228
|
-
if (!availablePages.has(active)) active =
|
|
265
|
+
if (!availablePages.has(active)) active = Array.from(navButtons.keys()).find(id => availablePages.has(id)) || 'appearance';
|
|
229
266
|
for (const node of dialog.querySelectorAll('[data-i18n]')) {
|
|
230
267
|
// Existing controls use their established translation pass; update the
|
|
231
268
|
// newly owned chrome only, without replacing controls or skill rows.
|
|
232
|
-
if (node.closest('.codex-wb-
|
|
269
|
+
if (node.closest('.codex-wb-nav, .codex-wb-footer')
|
|
270
|
+
|| node.classList.contains('codex-wb-title')
|
|
233
271
|
|| node.classList.contains('codex-wb-page-title')
|
|
234
|
-
|| node.classList.contains('codex-wb-scope-label')
|
|
235
272
|
|| node.classList.contains('codex-wb-theme-caption')
|
|
236
273
|
|| node.classList.contains('codex-wb-skills-caption')) node.textContent = t(node.dataset.i18n);
|
|
237
274
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
275
|
+
const projectName = hasProject() ? String(instance.getProjectName?.() || '').trim() : '';
|
|
276
|
+
for (const [id, page] of pages) renderPageScope(page, pageScopes.get(id), projectName);
|
|
277
|
+
for (const [scope, label] of sectionLabels) {
|
|
278
|
+
label.hidden = !Array.from(pageScopes).some(([id, pageScope]) => pageScope === scope && availablePages.has(id));
|
|
279
|
+
}
|
|
241
280
|
nav.setAttribute('aria-label', t('settingsWorkbenchCategories'));
|
|
242
|
-
allFilter.setAttribute('aria-pressed', String(filter === 'all'));
|
|
243
|
-
projectFilter.setAttribute('aria-pressed', String(filter === 'project'));
|
|
244
281
|
closeButton.title = t('settingsWorkbenchClose');
|
|
245
282
|
closeButton.setAttribute('aria-label', t('settingsWorkbenchClose'));
|
|
246
283
|
const skillsBack = skills?.querySelector('[data-skills-back]');
|
|
@@ -248,6 +285,7 @@
|
|
|
248
285
|
skillsBack.title = t('settingsBack');
|
|
249
286
|
skillsBack.setAttribute('aria-label', t('settingsBack'));
|
|
250
287
|
}
|
|
288
|
+
if (status) status.hidden = Boolean(status.dataset.statusPage) && status.dataset.statusPage !== active;
|
|
251
289
|
for (const [id, tab] of navButtons) {
|
|
252
290
|
tab.hidden = !availablePages.has(id);
|
|
253
291
|
tab.setAttribute('aria-selected', String(id === active));
|
|
@@ -256,12 +294,11 @@
|
|
|
256
294
|
}
|
|
257
295
|
for (const item of groups) {
|
|
258
296
|
const visible = available(item) && item.category === active && panel.dataset.view !== 'skills';
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
item.node.open = visible && item.expanded;
|
|
262
|
-
}
|
|
297
|
+
// Opening a group when its page is shown keeps the lazy toggle loaders (history, storage).
|
|
298
|
+
if (item.node.tagName === 'DETAILS' && item.node.open !== visible) item.node.open = visible;
|
|
263
299
|
item.node.hidden = !visible;
|
|
264
300
|
}
|
|
301
|
+
refreshSoleSections();
|
|
265
302
|
updateThemes();
|
|
266
303
|
}
|
|
267
304
|
function requestProviders() {
|
|
@@ -277,9 +314,17 @@
|
|
|
277
314
|
try { Promise.resolve(instance.callbacks.onProvidersOpen()).catch(failed); }
|
|
278
315
|
catch (error) { failed(error); }
|
|
279
316
|
}
|
|
317
|
+
// Shows the page that holds a group and returns the group, or null when the
|
|
318
|
+
// group is unavailable (for example a project group outside a project).
|
|
319
|
+
function revealGroup(key) {
|
|
320
|
+
const item = groups.find(entry => entry.node.dataset?.setGroup === key);
|
|
321
|
+
if (!item || !available(item)) return null;
|
|
322
|
+
active = item.category;
|
|
323
|
+
refresh();
|
|
324
|
+
return item.node;
|
|
325
|
+
}
|
|
280
326
|
function revealProviders() {
|
|
281
327
|
active = 'providers';
|
|
282
|
-
filter = 'all';
|
|
283
328
|
providerRequested = true;
|
|
284
329
|
pages.get('providers').dataset.providerEmbedded = 'true';
|
|
285
330
|
panel.dataset.view = 'settings';
|
|
@@ -330,13 +375,6 @@
|
|
|
330
375
|
if (['settings', 'skills'].includes(panel.dataset.view)) show();
|
|
331
376
|
else hide();
|
|
332
377
|
};
|
|
333
|
-
listen(allFilter, 'click', () => { filter = 'all'; refresh(); });
|
|
334
|
-
listen(projectFilter, 'click', () => {
|
|
335
|
-
filter = 'project';
|
|
336
|
-
if (panel.dataset.view === 'skills') instance.callbacks.onSkillsBack?.();
|
|
337
|
-
refresh();
|
|
338
|
-
body.scrollTop = 0;
|
|
339
|
-
});
|
|
340
378
|
listen(nav, 'keydown', event => {
|
|
341
379
|
if (!['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) return;
|
|
342
380
|
const tabs = Array.from(navButtons.values()).filter(tab => !tab.hidden);
|
|
@@ -363,7 +401,7 @@
|
|
|
363
401
|
observer.observe(panel, { attributes: true, attributeFilter: ['data-view', 'data-settings-scope', 'data-theme'] });
|
|
364
402
|
refresh();
|
|
365
403
|
return {
|
|
366
|
-
show, hide, refresh, revealProviders,
|
|
404
|
+
show, hide, refresh, revealGroup, revealProviders,
|
|
367
405
|
setProviderCloseGuard(guard) { providerCloseGuard = guard; },
|
|
368
406
|
destroy() {
|
|
369
407
|
destroyed = true;
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
function applySnapshot(options, sessions) {
|
|
11
11
|
const previous = options.getState(), scope = options.getScope();
|
|
12
|
-
const
|
|
12
|
+
const input = sessions.length ? { ...previous, sessions } : {
|
|
13
|
+
...previous, session: null, sessions: [], runs: [], activeSessionId: '',
|
|
14
|
+
task: '', focusFiles: [], selectionContext: null, composerAttachments: []
|
|
15
|
+
};
|
|
16
|
+
const next = options.normalize(input, { restoreRunningRuns: false });
|
|
13
17
|
const unchangedSnapshot = JSON.stringify(next);
|
|
14
18
|
options.setState(next);
|
|
15
19
|
try {
|
|
@@ -49,9 +53,10 @@
|
|
|
49
53
|
if (!scope.projectId || !scope.accountScopeId) { pending = false; return; }
|
|
50
54
|
if (saving || isBusy()) { schedule(1000); return; }
|
|
51
55
|
const local = getState();
|
|
52
|
-
// A draft or a not-yet-persisted edit
|
|
53
|
-
// history refresh.
|
|
54
|
-
if (
|
|
56
|
+
// A locally edited draft or a not-yet-persisted edit takes priority over
|
|
57
|
+
// passive history refresh. Callers may distinguish imported shared drafts.
|
|
58
|
+
if ((options.hasLocalDraft ? options.hasLocalDraft(local) : Boolean(local.task))
|
|
59
|
+
|| (local.sessions || []).some(session => StorageDb.hasUnpersistedSharedSession(session))) {
|
|
55
60
|
schedule(1500); return;
|
|
56
61
|
}
|
|
57
62
|
const capturedVersion = version, capturedSignature = signature(local);
|
|
@@ -87,6 +92,10 @@
|
|
|
87
92
|
}
|
|
88
93
|
const unsubscribe = Bridge.subscribe(change => {
|
|
89
94
|
const scope = getScope();
|
|
95
|
+
if (options.isDashboard?.()) {
|
|
96
|
+
if (scope.accountScopeId && change.accountScopeId === scope.accountScopeId) options.refreshDashboard?.();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
90
99
|
if (!change.projectId || change.projectId === scope.projectId) {
|
|
91
100
|
retryCount = 0; pending = true; schedule();
|
|
92
101
|
}
|
|
@@ -3,18 +3,31 @@
|
|
|
3
3
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function subagentActivityViewFactory(Agents, Activity) {
|
|
4
4
|
'use strict';
|
|
5
5
|
function create(deps) {
|
|
6
|
-
const { tx, getPanel, plain, make, icon, formatElapsed, renderBlocks } = deps;
|
|
6
|
+
const { tx, getPanel, plain, plainThought, make, icon, formatElapsed, renderBlocks, glyph = () => '' } = deps;
|
|
7
|
+
const withGlyph = (node, name, label) => {
|
|
8
|
+
node.insertAdjacentHTML('beforeend', glyph(name));
|
|
9
|
+
const text = make('span', ''); text.textContent = label; node.append(text);
|
|
10
|
+
return node;
|
|
11
|
+
};
|
|
7
12
|
let viewer = null;
|
|
8
13
|
function statusLabel(status) {
|
|
9
14
|
return ({ pending: tx('Starting', '启动中'), running: tx('Running', '运行中'),
|
|
10
15
|
completed: tx('Completed', '已完成'), failed: tx('Failed', '失败'),
|
|
11
16
|
cancelled: tx('Stopped', '已停止'), unknown: tx('Status unconfirmed', '状态未确认') })[status] || '';
|
|
12
17
|
}
|
|
18
|
+
// A row preview is one plain line: a final message keeps its words, not its
|
|
19
|
+
// Markdown ("- `main.tex` is …" reads "main.tex is …").
|
|
20
|
+
function plainMessage(value) {
|
|
21
|
+
return value.replace(/^\s*(?:#{1,6}\s+|>\s?|[-*+]\s+|\d+[.)]\s+)/gm, '')
|
|
22
|
+
.replace(/!?\[([^\]]*)\]\([^)\s]*\)/g, '$1').replace(/`([^`\n]+)`/g, '$1')
|
|
23
|
+
.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2').replace(/\s+/g, ' ').trim();
|
|
24
|
+
}
|
|
13
25
|
function latest(agent) {
|
|
14
26
|
const blocks = Activity.project({ status: agent.status, events: agent.events || [] }).blocks;
|
|
15
27
|
const last = blocks.at(-1);
|
|
16
28
|
const item = last?.kind === 'exploreGroup' ? last.items.at(-1) : last;
|
|
17
|
-
|
|
29
|
+
const text = plain(item?.meta?.target || item?.meta?.command || item?.event?.title || agent.task);
|
|
30
|
+
return (item?.kind === 'message' ? plainMessage(text) : text).slice(0, 180);
|
|
18
31
|
}
|
|
19
32
|
function elapsed(agent, run) {
|
|
20
33
|
const duration = Agents.elapsedMs(agent, run, Date.now());
|
|
@@ -42,9 +55,9 @@
|
|
|
42
55
|
shell.setAttribute('aria-label', tx('Subagent conversation', '子代理对话'));
|
|
43
56
|
const header = make('header', 'codex-subagent-view-head');
|
|
44
57
|
const back = make('button', 'codex-subagent-back');
|
|
45
|
-
back.type = 'button'; back
|
|
58
|
+
back.type = 'button'; withGlyph(back, 'chevronLeft', tx('Back to main task', '返回主任务'));
|
|
46
59
|
back.addEventListener('click', close);
|
|
47
|
-
const readonly = make('span', 'codex-subagent-readonly')
|
|
60
|
+
const readonly = withGlyph(make('span', 'codex-subagent-readonly'), 'lock', tx('Read only', '只读'));
|
|
48
61
|
header.append(back, readonly);
|
|
49
62
|
const parent = make('div', 'codex-subagent-parent');
|
|
50
63
|
const parentLabel = make('span', 'codex-subagent-parent-label');
|
|
@@ -112,7 +125,7 @@
|
|
|
112
125
|
const mark = icon('agent'), copy = make('span', 'run-subagent-copy');
|
|
113
126
|
const name = make('span', 'run-subagent-name'), activity = make('span', 'run-subagent-activity');
|
|
114
127
|
copy.append(name, activity);
|
|
115
|
-
const tail = make('span', 'run-subagent-tail'), status = make('span', ''), time = make('span', 'run-subagent-time');
|
|
128
|
+
const tail = make('span', 'run-subagent-tail'), status = make('span', 'run-subagent-status'), time = make('span', 'run-subagent-time');
|
|
116
129
|
tail.append(status, time);
|
|
117
130
|
el.append(mark, copy, tail);
|
|
118
131
|
el.addEventListener('click', () => open(card, agent.key, el));
|
|
@@ -154,15 +167,17 @@
|
|
|
154
167
|
const childStatus = Agents.effectiveStatus(agent, card.run);
|
|
155
168
|
viewer.title.textContent = plain(agent.title || agent.jobId || agent.threadId);
|
|
156
169
|
viewer.meta.textContent = [statusLabel(childStatus), elapsed(agent, card.run), agent.model, agent.reasoningEffort].filter(Boolean).join(' · ');
|
|
170
|
+
viewer.meta.dataset.state = childStatus;
|
|
157
171
|
viewer.prompt.textContent = plain(agent.task);
|
|
158
172
|
viewer.assignment.hidden = !viewer.prompt.textContent;
|
|
159
173
|
const parent = Activity.project(card.run), last = parent.blocks.at(-1);
|
|
160
174
|
const lastItem = last?.kind === 'exploreGroup' ? last.items.at(-1) : last;
|
|
161
|
-
const parentWork = plain(lastItem?.meta?.target || lastItem?.meta?.command || lastItem?.event?.title).slice(0, 150);
|
|
175
|
+
const parentWork = plainThought(plain(lastItem?.meta?.target || lastItem?.meta?.command || lastItem?.event?.title)).slice(0, 150);
|
|
162
176
|
viewer.parentLabel.textContent = tx('Main agent · ', '主 agent · ') + (card.run.status === 'running'
|
|
163
177
|
? parentWork || tx('Working', '处理中') : tx('Run ended', '本轮已结束'));
|
|
164
178
|
const running = (card.run.subagents || []).filter(item => ['pending', 'running'].includes(Agents.effectiveStatus(item, card.run))).length;
|
|
165
179
|
viewer.parentCount.textContent = tx(running + ' subagents active', running + ' 个子任务进行中');
|
|
180
|
+
viewer.parentCount.hidden = running === 0;
|
|
166
181
|
for (const sibling of card.run.subagents || []) {
|
|
167
182
|
let button = viewer.siblingRows.get(sibling.key);
|
|
168
183
|
if (!button) {
|
|
@@ -172,7 +187,8 @@
|
|
|
172
187
|
}
|
|
173
188
|
button.textContent = plain(sibling.title || sibling.jobId || sibling.threadId);
|
|
174
189
|
button.setAttribute('aria-pressed', String(sibling.key === agent.key));
|
|
175
|
-
button.
|
|
190
|
+
button.dataset.state = Agents.effectiveStatus(sibling, card.run);
|
|
191
|
+
button.title = statusLabel(button.dataset.state);
|
|
176
192
|
}
|
|
177
193
|
const childRun = { id: card.id + ':' + agent.key, status: ['pending', 'running'].includes(childStatus) ? 'running' : childStatus,
|
|
178
194
|
events: agent.events || [], mode: 'ask' };
|