xtralab 0.15.0 → 0.15.2
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 +65 -30
- package/lib/about/icons.d.ts +0 -5
- package/lib/about/icons.js +0 -5
- package/lib/about/index.d.ts +4 -8
- package/lib/about/index.js +6 -17
- package/lib/agentSessions.d.ts +9 -29
- package/lib/agentSessions.js +2 -7
- package/lib/askAgent/editorSelection.d.ts +20 -26
- package/lib/askAgent/editorSelection.js +11 -26
- package/lib/askAgent/icons.d.ts +2 -3
- package/lib/askAgent/icons.js +2 -3
- package/lib/askAgent/index.d.ts +4 -26
- package/lib/askAgent/index.js +54 -158
- package/lib/askAgent/popup.d.ts +12 -16
- package/lib/askAgent/popup.js +18 -37
- package/lib/askAgent/prompt.d.ts +3 -5
- package/lib/askAgent/prompt.js +6 -19
- package/lib/askAgent/queue.d.ts +11 -21
- package/lib/askAgent/queue.js +9 -19
- package/lib/askAgent/queuePanel.d.ts +13 -18
- package/lib/askAgent/queuePanel.js +15 -37
- package/lib/askAgent/targetPicker.d.ts +2 -9
- package/lib/askAgent/targetPicker.js +6 -11
- package/lib/askAgent/tokens.d.ts +32 -46
- package/lib/askAgent/tokens.js +4 -6
- package/lib/commandBar/index.d.ts +4 -13
- package/lib/commandBar/index.js +21 -39
- package/lib/customPanel/index.js +15 -0
- package/lib/editorBreadcrumbs/index.d.ts +3 -6
- package/lib/editorBreadcrumbs/index.js +10 -6
- package/lib/editorBreadcrumbs/widget.d.ts +17 -4
- package/lib/editorBreadcrumbs/widget.js +11 -17
- package/lib/editorIndent/index.d.ts +4 -12
- package/lib/editorIndent/index.js +8 -23
- package/lib/fileBrowser/commands.d.ts +29 -19
- package/lib/fileBrowser/commands.js +83 -97
- package/lib/fileBrowser/contents.d.ts +3 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
- package/lib/fileBrowser/dragAndDrop.js +2 -7
- package/lib/fileBrowser/fileBrowser.d.ts +19 -9
- package/lib/fileBrowser/fileBrowser.js +70 -222
- package/lib/fileBrowser/gitStatus.d.ts +16 -10
- package/lib/fileBrowser/gitStatus.js +31 -30
- package/lib/fileBrowser/gitignore.d.ts +4 -17
- package/lib/fileBrowser/gitignore.js +4 -25
- package/lib/fileBrowser/icons.d.ts +7 -17
- package/lib/fileBrowser/icons.js +25 -80
- package/lib/fileBrowser/index.js +2 -4
- package/lib/fileBrowser/widget.d.ts +141 -48
- package/lib/fileBrowser/widget.js +102 -4
- package/lib/fileTypeIcons/index.js +0 -10
- package/lib/git/api.d.ts +5 -13
- package/lib/git/api.js +10 -36
- package/lib/git/askRequest.d.ts +4 -8
- package/lib/git/askRequest.js +6 -20
- package/lib/git/commands.d.ts +45 -5
- package/lib/git/commands.js +20 -13
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffPreferences.d.ts +90 -0
- package/lib/git/diffPreferences.js +107 -0
- package/lib/git/diffProvider.js +10 -2
- package/lib/git/diffSurface.d.ts +64 -63
- package/lib/git/diffSurface.js +72 -213
- package/lib/git/diffTheme.d.ts +7 -0
- package/lib/git/diffTheme.js +23 -0
- package/lib/git/diffWidget.d.ts +70 -14
- package/lib/git/diffWidget.js +100 -94
- package/lib/git/imageDiff.d.ts +14 -6
- package/lib/git/imageDiff.js +19 -75
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +44 -10
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +55 -197
- package/lib/git/tokens.d.ts +73 -31
- package/lib/git/tokens.js +2 -3
- package/lib/highlight/index.d.ts +4 -10
- package/lib/highlight/index.js +10 -41
- package/lib/index.d.ts +0 -9
- package/lib/index.js +0 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +15 -57
- package/lib/launcher/availability.d.ts +3 -9
- package/lib/launcher/availability.js +3 -9
- package/lib/launcher/commands.d.ts +7 -32
- package/lib/launcher/commands.js +14 -45
- package/lib/launcher/dashboard.d.ts +19 -24
- package/lib/launcher/dashboard.js +21 -67
- package/lib/launcher/editorRegistry.d.ts +10 -22
- package/lib/launcher/editorRegistry.js +14 -18
- package/lib/launcher/editors.d.ts +39 -45
- package/lib/launcher/editors.js +8 -34
- package/lib/launcher/icons.d.ts +0 -12
- package/lib/launcher/icons.js +5 -39
- package/lib/launcher/index.js +12 -55
- package/lib/launcher/invocation.d.ts +4 -10
- package/lib/launcher/invocation.js +7 -22
- package/lib/launcher/registry.d.ts +9 -7
- package/lib/launcher/registry.js +9 -7
- package/lib/launcher/tokens.d.ts +10 -22
- package/lib/launcher/tokens.js +5 -8
- package/lib/menuBar/index.d.ts +3 -10
- package/lib/menuBar/index.js +11 -28
- package/lib/menus/index.d.ts +3 -9
- package/lib/menus/index.js +14 -30
- package/lib/omnibox/files.d.ts +2 -4
- package/lib/omnibox/files.js +5 -11
- package/lib/omnibox/index.d.ts +4 -14
- package/lib/omnibox/index.js +9 -27
- package/lib/omnibox/model.d.ts +38 -10
- package/lib/omnibox/model.js +15 -33
- package/lib/omnibox/recents.d.ts +15 -17
- package/lib/omnibox/recents.js +12 -17
- package/lib/omnibox/tokens.d.ts +5 -10
- package/lib/omnibox/tokens.js +2 -6
- package/lib/omnibox/widget.d.ts +11 -7
- package/lib/omnibox/widget.js +8 -16
- package/lib/searchReplace/index.d.ts +4 -20
- package/lib/searchReplace/index.js +7 -27
- package/lib/showOutput/index.d.ts +3 -12
- package/lib/showOutput/index.js +6 -22
- package/lib/sidebar/index.d.ts +4 -7
- package/lib/sidebar/index.js +5 -12
- package/lib/terminalNotifications/index.d.ts +4 -10
- package/lib/terminalNotifications/index.js +6 -20
- package/lib/terminals/agentTerminals.d.ts +12 -5
- package/lib/terminals/agentTerminals.js +25 -44
- package/lib/terminals/detection.d.ts +4 -8
- package/lib/terminals/detection.js +4 -8
- package/lib/terminals/index.d.ts +0 -35
- package/lib/terminals/index.js +12 -101
- package/lib/terminals/model.d.ts +64 -139
- package/lib/terminals/model.js +80 -220
- package/lib/terminals/tokens.d.ts +11 -22
- package/lib/terminals/tokens.js +2 -2
- package/lib/terminals/widget.d.ts +43 -13
- package/lib/terminals/widget.js +33 -18
- package/lib/topBar/icons.d.ts +3 -8
- package/lib/topBar/icons.js +3 -8
- package/lib/topBar/index.d.ts +4 -11
- package/lib/topBar/index.js +10 -38
- package/lib/walkthrough/index.d.ts +4 -9
- package/lib/walkthrough/index.js +6 -20
- package/lib/walkthrough/panel.d.ts +40 -9
- package/lib/walkthrough/panel.js +4 -10
- package/package.json +5 -3
- package/schema/git-diff-preferences.json +42 -0
- package/src/about/icons.ts +0 -5
- package/src/about/index.tsx +6 -17
- package/src/agentSessions.ts +9 -29
- package/src/askAgent/editorSelection.ts +22 -29
- package/src/askAgent/icons.ts +2 -3
- package/src/askAgent/index.ts +54 -158
- package/src/askAgent/popup.tsx +23 -45
- package/src/askAgent/prompt.ts +6 -19
- package/src/askAgent/queue.ts +13 -24
- package/src/askAgent/queuePanel.tsx +21 -46
- package/src/askAgent/targetPicker.tsx +5 -15
- package/src/askAgent/tokens.ts +32 -46
- package/src/commandBar/index.ts +24 -39
- package/src/customPanel/index.ts +15 -0
- package/src/editorBreadcrumbs/index.ts +10 -6
- package/src/editorBreadcrumbs/widget.ts +23 -17
- package/src/editorIndent/index.ts +17 -23
- package/src/fileBrowser/commands.ts +119 -96
- package/src/fileBrowser/contents.ts +3 -0
- package/src/fileBrowser/dragAndDrop.ts +17 -7
- package/src/fileBrowser/fileBrowser.tsx +86 -230
- package/src/fileBrowser/gitStatus.ts +36 -33
- package/src/fileBrowser/gitignore.ts +4 -25
- package/src/fileBrowser/icons.ts +25 -80
- package/src/fileBrowser/index.ts +2 -4
- package/src/fileBrowser/widget.tsx +171 -50
- package/src/fileTypeIcons/index.ts +0 -10
- package/src/git/api.ts +10 -36
- package/src/git/askRequest.ts +6 -20
- package/src/git/commands.ts +83 -11
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffPreferences.ts +192 -0
- package/src/git/diffProvider.tsx +11 -2
- package/src/git/diffSurface.tsx +165 -286
- package/src/git/diffTheme.ts +24 -0
- package/src/git/diffWidget.tsx +194 -134
- package/src/git/imageDiff.tsx +56 -84
- package/src/git/index.ts +54 -10
- package/src/git/notebookDiff.tsx +214 -240
- package/src/git/tokens.ts +73 -31
- package/src/highlight/index.ts +10 -41
- package/src/index.ts +0 -9
- package/src/launcher/agents.ts +64 -95
- package/src/launcher/availability.ts +3 -9
- package/src/launcher/commands.ts +14 -51
- package/src/launcher/dashboard.tsx +49 -88
- package/src/launcher/editorRegistry.ts +21 -29
- package/src/launcher/editors.ts +39 -58
- package/src/launcher/icons.ts +5 -39
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/tokens.ts +10 -22
- package/src/menuBar/index.ts +11 -28
- package/src/menus/index.ts +14 -30
- package/src/omnibox/files.ts +10 -14
- package/src/omnibox/index.ts +9 -27
- package/src/omnibox/model.ts +50 -38
- package/src/omnibox/recents.ts +17 -22
- package/src/omnibox/tokens.ts +5 -10
- package/src/omnibox/widget.tsx +14 -19
- package/src/searchReplace/index.ts +7 -27
- package/src/showOutput/index.ts +6 -22
- package/src/sidebar/index.ts +20 -12
- package/src/terminalNotifications/index.ts +42 -25
- package/src/terminals/agentTerminals.ts +25 -49
- package/src/terminals/detection.ts +4 -8
- package/src/terminals/index.ts +12 -101
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +48 -31
- package/src/topBar/icons.ts +3 -8
- package/src/topBar/index.ts +21 -44
- package/src/walkthrough/index.ts +6 -20
- package/src/walkthrough/panel.ts +40 -10
- package/style/about.css +2 -7
- package/style/askAgent.css +8 -40
- package/style/base.css +9 -45
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +1 -4
- package/style/git.css +26 -186
- package/style/highlight.css +1 -3
- package/style/launcher.css +6 -70
- package/style/omnibox.css +2 -14
- package/style/showOutput.css +0 -6
- package/style/sidebar.css +29 -9
- package/style/tabs.css +5 -13
- package/style/terminals.css +6 -53
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
package/src/launcher/editors.ts
CHANGED
|
@@ -3,11 +3,10 @@ import { LabIcon, textEditorIcon } from '@jupyterlab/ui-components';
|
|
|
3
3
|
import { BUILTIN_EDITOR_ICONS } from './icons';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A terminal text editor
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the terminals panel to badge running editors.
|
|
6
|
+
* A terminal text editor on the launcher's "Open" section, launched by typing
|
|
7
|
+
* its command into a fresh terminal. The launcher shows one tile
|
|
8
|
+
* ({@link resolveEditor}); the full list badges running editors in the
|
|
9
|
+
* terminals panel.
|
|
11
10
|
*/
|
|
12
11
|
export interface IEditor {
|
|
13
12
|
/**
|
|
@@ -31,57 +30,59 @@ export interface IEditor {
|
|
|
31
30
|
*/
|
|
32
31
|
icon: LabIcon;
|
|
33
32
|
/**
|
|
34
|
-
* Preference order
|
|
35
|
-
* ascending rank, that qualifies wins (Neovim before Vim by default).
|
|
33
|
+
* Preference order: the first qualifying candidate by rank wins the tile.
|
|
36
34
|
*/
|
|
37
35
|
rank: number;
|
|
38
36
|
/**
|
|
39
|
-
* When false,
|
|
40
|
-
*
|
|
41
|
-
* still resolves when typed in a real terminal. Defaults to true.
|
|
37
|
+
* When false, skip the `which`-based availability check — for aliases or
|
|
38
|
+
* shell functions not on PATH. Defaults to true.
|
|
42
39
|
*/
|
|
43
40
|
requireAvailable: boolean;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
|
-
* The settings-side shape: every field except `id` is optional
|
|
48
|
-
* override a single field on a built-in editor
|
|
49
|
-
*
|
|
50
|
-
* editors. Mirrors `IAgentSettings` minus `promptArgs`.
|
|
44
|
+
* The settings-side shape: every field except `id` is optional so a user can
|
|
45
|
+
* override a single field on a built-in editor; new ids define new editors.
|
|
46
|
+
* Mirrors `IAgentSettings` minus `promptArgs`.
|
|
51
47
|
*/
|
|
52
48
|
export interface IEditorSettings {
|
|
49
|
+
/**
|
|
50
|
+
* Id of the editor to override; a new id defines a new editor.
|
|
51
|
+
*/
|
|
53
52
|
id: string;
|
|
53
|
+
/**
|
|
54
|
+
* See {@link IEditor.label}.
|
|
55
|
+
*/
|
|
54
56
|
label?: string;
|
|
57
|
+
/**
|
|
58
|
+
* See {@link IEditor.caption}.
|
|
59
|
+
*/
|
|
55
60
|
caption?: string;
|
|
61
|
+
/**
|
|
62
|
+
* See {@link IEditor.command}.
|
|
63
|
+
*/
|
|
56
64
|
command?: string;
|
|
57
65
|
/**
|
|
58
|
-
* Inline SVG
|
|
59
|
-
*
|
|
60
|
-
* set (in which case it overrides the built-in).
|
|
66
|
+
* Inline SVG icon. Required for new ids; overrides the built-in when set on
|
|
67
|
+
* a built-in id.
|
|
61
68
|
*/
|
|
62
69
|
iconSvg?: string;
|
|
70
|
+
/**
|
|
71
|
+
* See {@link IEditor.rank}.
|
|
72
|
+
*/
|
|
63
73
|
rank?: number;
|
|
64
74
|
/**
|
|
65
|
-
* When false, the editor
|
|
66
|
-
*
|
|
67
|
-
* the editor tile entirely. Defaults to true.
|
|
75
|
+
* When false, hides the editor. Disable both built-ins to drop the editor
|
|
76
|
+
* tile entirely.
|
|
68
77
|
*/
|
|
69
78
|
enabled?: boolean;
|
|
70
79
|
/**
|
|
71
|
-
* See {@link IEditor.requireAvailable}. Defaults to true
|
|
72
|
-
*
|
|
73
|
-
* the `command` (a user-chosen command — often a shell alias — is trusted and
|
|
74
|
-
* always shown). Set it explicitly to force the check on or off.
|
|
80
|
+
* See {@link IEditor.requireAvailable}. Defaults to true, but flips to false
|
|
81
|
+
* once `command` is overridden (a user-chosen alias is trusted).
|
|
75
82
|
*/
|
|
76
83
|
requireAvailable?: boolean;
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
/**
|
|
80
|
-
* Built-in editor candidates in preference order. The launcher shows the first
|
|
81
|
-
* one whose command resolves on the server's `$PATH`, so Neovim wins over Vim
|
|
82
|
-
* when both are installed. Users override, disable, or extend this list
|
|
83
|
-
* through the `editors` setting; see {@link mergeEditors}.
|
|
84
|
-
*/
|
|
85
86
|
const DEFAULTS: IEditor[] = [
|
|
86
87
|
{
|
|
87
88
|
id: 'nvim',
|
|
@@ -104,11 +105,8 @@ const DEFAULTS: IEditor[] = [
|
|
|
104
105
|
];
|
|
105
106
|
|
|
106
107
|
/**
|
|
107
|
-
* The built-in editors projected into the
|
|
108
|
-
*
|
|
109
|
-
* {@link defaultAgentSettings}: {@link registerLauncherSchemaDefaults} injects
|
|
110
|
-
* this as the `editors` setting's schema default so the Settings Editor shows
|
|
111
|
-
* the shipped list.
|
|
108
|
+
* The built-in editors projected into the settings shape (no runtime LabIcon),
|
|
109
|
+
* injected as the `editors` schema default so the Settings Editor shows them.
|
|
112
110
|
*/
|
|
113
111
|
export function defaultEditorSettings(): IEditorSettings[] {
|
|
114
112
|
return DEFAULTS.map(editor => ({
|
|
@@ -132,13 +130,9 @@ function resolveEditorIcon(id: string, iconSvg: string | undefined): LabIcon {
|
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
/**
|
|
135
|
-
* Merge
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* callers don't need to re-check the flag). The result is sorted by rank, which
|
|
139
|
-
* is also the launcher's tile-preference order. Mirrors `mergeAgents`, including
|
|
140
|
-
* turning `requireAvailable` off when the user overrides a built-in's `command`
|
|
141
|
-
* so an aliased editor still shows.
|
|
133
|
+
* Merge the built-in editors with the user's `editors` settings; the result is
|
|
134
|
+
* sorted by rank, the tile-preference order. Mirrors `mergeAgents`, including
|
|
135
|
+
* turning `requireAvailable` off when a built-in's `command` is overridden.
|
|
142
136
|
*/
|
|
143
137
|
export function mergeEditors(overrides: IEditorSettings[]): IEditor[] {
|
|
144
138
|
const overrideById = new Map(overrides.map(entry => [entry.id, entry]));
|
|
@@ -164,10 +158,6 @@ export function mergeEditors(overrides: IEditorSettings[]): IEditor[] {
|
|
|
164
158
|
? resolveEditorIcon(base.id, override.iconSvg)
|
|
165
159
|
: base.icon,
|
|
166
160
|
rank: override.rank ?? base.rank,
|
|
167
|
-
// See `mergeAgents`: once the user points a built-in editor at their own
|
|
168
|
-
// command (e.g. a shell alias `which` can't resolve), stop requiring
|
|
169
|
-
// availability so the tile still shows. The check stays on only while the
|
|
170
|
-
// command is xtralab's default.
|
|
171
161
|
requireAvailable:
|
|
172
162
|
command === base.command
|
|
173
163
|
? (override.requireAvailable ?? base.requireAvailable)
|
|
@@ -175,9 +165,6 @@ export function mergeEditors(overrides: IEditorSettings[]): IEditor[] {
|
|
|
175
165
|
});
|
|
176
166
|
}
|
|
177
167
|
|
|
178
|
-
// What remains in `overrideById` are ids that don't match a built-in — treat
|
|
179
|
-
// them as new editors the user is adding. Skip disabled ones; the settings
|
|
180
|
-
// schema validates the shape, so missing fields just fall back here.
|
|
181
168
|
let nextRank =
|
|
182
169
|
merged.reduce((max, editor) => Math.max(max, editor.rank), -1) + 1;
|
|
183
170
|
for (const entry of overrideById.values()) {
|
|
@@ -202,15 +189,9 @@ export function mergeEditors(overrides: IEditorSettings[]): IEditor[] {
|
|
|
202
189
|
}
|
|
203
190
|
|
|
204
191
|
/**
|
|
205
|
-
* Pick the single
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* opts out of the check (`requireAvailable: false`) — Neovim before Vim by
|
|
209
|
-
* default — or `null` when none qualifies.
|
|
210
|
-
*
|
|
211
|
-
* When `available` is `null` (the endpoint couldn't be reached), a
|
|
212
|
-
* `requireAvailable` editor is not shown; an entry with
|
|
213
|
-
* `requireAvailable: false` is shown regardless.
|
|
192
|
+
* Pick the single tile: the first editor, by rank, that is available or opts
|
|
193
|
+
* out of the check, else `null`. When `available` is `null` (probe failed),
|
|
194
|
+
* only `requireAvailable: false` entries qualify.
|
|
214
195
|
*/
|
|
215
196
|
export function resolveEditor(
|
|
216
197
|
editors: IEditor[],
|
package/src/launcher/icons.ts
CHANGED
|
@@ -1,33 +1,11 @@
|
|
|
1
1
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Brand icons for the
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* reads as a multi-color Google mark. Pi's mark is the official `logo-auto.svg`
|
|
10
|
-
* from https://pi.dev (MIT, `earendil-works/pi`). Small adjustments are applied
|
|
11
|
-
* to play nicely with JupyterLab themes:
|
|
12
|
-
* - Monochrome marks (Codex, Copilot, Goose, Pi) use `currentColor` so they
|
|
13
|
-
* inherit the surrounding launcher card's text color rather than
|
|
14
|
-
* hard-coding black, which disappears on the dark theme. For Pi this
|
|
15
|
-
* replaces the upstream `prefers-color-scheme` style block, which follows
|
|
16
|
-
* the OS scheme rather than the active JupyterLab theme.
|
|
17
|
-
* - The Goose mark upstream is rendered on a white rounded rect; we drop
|
|
18
|
-
* the rect so the silhouette can sit on the launcher card directly.
|
|
19
|
-
* - SVG ids that would collide if two copies of the same artwork ended up
|
|
20
|
-
* on the page (gradient/mask/filter defs) are namespaced under
|
|
21
|
-
* `xtralab-…`.
|
|
22
|
-
*
|
|
23
|
-
* Brand-colored icons (Antigravity, Claude, Kiro, Mistral Vibe, OpenCode) keep
|
|
24
|
-
* their upstream colors; the dark/light contrast of the launcher card behind
|
|
25
|
-
* them is the same as on `jupyter-ai-acp-client`.
|
|
26
|
-
*
|
|
27
|
-
* The Vim and Neovim marks belong to the launcher's "Open" editor tile rather
|
|
28
|
-
* than the agent grid. They are the brand silhouettes from Simple Icons
|
|
29
|
-
* (https://simpleicons.org, CC0), each filled with its brand green so the mark
|
|
30
|
-
* reads on both the light and dark themes.
|
|
4
|
+
* Brand icons for the launcher cards: `jupyter-ai-contrib/jupyter-ai-acp-client`
|
|
5
|
+
* (BSD-3-Clause), except Antigravity (`@lobehub/icons-static-svg`, MIT),
|
|
6
|
+
* Pi (pi.dev, MIT) and Vim/Neovim (Simple Icons, CC0). Monochrome marks use
|
|
7
|
+
* `currentColor` to follow the active theme; svg ids are namespaced under
|
|
8
|
+
* `xtralab-…` so two copies of one mark on a page don't collide.
|
|
31
9
|
*/
|
|
32
10
|
|
|
33
11
|
const antigravityIcon = new LabIcon({
|
|
@@ -204,12 +182,6 @@ const vimIcon = new LabIcon({
|
|
|
204
182
|
</svg>`
|
|
205
183
|
});
|
|
206
184
|
|
|
207
|
-
/**
|
|
208
|
-
* Built-in icons keyed by the agent's id, used to resolve the icon for an
|
|
209
|
-
* agent declared without an inline `iconSvg` override (i.e. one of xtralab's
|
|
210
|
-
* defaults). Custom agents added via settings can either supply their own
|
|
211
|
-
* `iconSvg` or fall back to the generic terminal icon.
|
|
212
|
-
*/
|
|
213
185
|
export const BUILTIN_AGENT_ICONS: Record<string, LabIcon> = {
|
|
214
186
|
antigravity: antigravityIcon,
|
|
215
187
|
claude: claudeIcon,
|
|
@@ -222,12 +194,6 @@ export const BUILTIN_AGENT_ICONS: Record<string, LabIcon> = {
|
|
|
222
194
|
pi: piIcon
|
|
223
195
|
};
|
|
224
196
|
|
|
225
|
-
/**
|
|
226
|
-
* Built-in icons keyed by the editor's id, used to resolve the icon for a
|
|
227
|
-
* built-in editor (Neovim, Vim) declared without an inline `iconSvg`. Custom
|
|
228
|
-
* editors added via settings can supply their own `iconSvg` or fall back to
|
|
229
|
-
* the generic text-editor icon.
|
|
230
|
-
*/
|
|
231
197
|
export const BUILTIN_EDITOR_ICONS: Record<string, LabIcon> = {
|
|
232
198
|
nvim: neovimIcon,
|
|
233
199
|
vim: vimIcon
|
package/src/launcher/index.ts
CHANGED
|
@@ -25,26 +25,10 @@ import { IAgentRegistry } from './tokens';
|
|
|
25
25
|
const PLUGIN_ID = 'xtralab:launcher';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* The xtralab launcher plugin
|
|
29
|
-
* (
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* changed files (clickable into the diff viewer) below them. Clicking an
|
|
33
|
-
* agent opens a fresh terminal and pipes the agent's command into it; if
|
|
34
|
-
* the prompt textarea is non-empty, the prompt is shell-quoted and
|
|
35
|
-
* spliced into the launch line per the agent's `promptArgs` recipe.
|
|
36
|
-
*
|
|
37
|
-
* The agent list is the merge of xtralab's defaults with the user's
|
|
38
|
-
* `xtralab:launcher` settings, then filtered by a server-side `which`
|
|
39
|
-
* check so users only see agents that are actually installed. Agents with
|
|
40
|
-
* `requireAvailable: false` skip the filter, as does any built-in whose
|
|
41
|
-
* `command` the user has overridden (a user-chosen command — often a shell
|
|
42
|
-
* alias the server can't resolve — is trusted and always shown).
|
|
43
|
-
*
|
|
44
|
-
* The plugin deliberately does NOT provide the `ILauncher` token: other
|
|
45
|
-
* extensions register notebook/console/terminal cards on it as a side
|
|
46
|
-
* effect, which would defeat the point of the agent-only launcher. If an
|
|
47
|
-
* extension needs to surface itself, we'll add it here explicitly.
|
|
28
|
+
* The xtralab launcher plugin: replaces the stock JupyterLab launcher
|
|
29
|
+
* (disabled via `jupyterlab.disabledExtensions`) with an agent-focused
|
|
30
|
+
* dashboard. It deliberately does NOT provide `ILauncher` — other extensions
|
|
31
|
+
* would register their cards on it and defeat the agent-only design.
|
|
48
32
|
*/
|
|
49
33
|
const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
50
34
|
id: PLUGIN_ID,
|
|
@@ -72,23 +56,13 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
72
56
|
const { commands, shell } = app;
|
|
73
57
|
const trans = (translator ?? nullTranslator).load('jupyterlab');
|
|
74
58
|
|
|
75
|
-
// The shared agent registry this plugin provides on `IAgentRegistry`.
|
|
76
|
-
// It holds the active agent list (updated whenever settings change), so
|
|
77
|
-
// every freshly-created launcher widget — and any other plugin that
|
|
78
|
-
// consumes the token, e.g. the terminals panel — renders the latest list.
|
|
79
59
|
const registry = new AgentRegistry();
|
|
80
60
|
|
|
81
|
-
// Track command registrations so a settings change can wipe them
|
|
82
|
-
// before re-registering — without this the command palette would
|
|
83
|
-
// accumulate stale entries when the agent list shrinks.
|
|
84
61
|
let registered: IDisposable | null = null;
|
|
85
62
|
|
|
86
63
|
const applyAgents = async (overrides: IAgentSettings[]): Promise<void> => {
|
|
87
64
|
const agents = mergeAgents(overrides);
|
|
88
65
|
|
|
89
|
-
// Probe the server's `$PATH` so we only surface agents that are actually
|
|
90
|
-
// installed. Agents with `requireAvailable: false` opt out (their command
|
|
91
|
-
// may be a shell alias that `which` can't see) and are kept regardless.
|
|
92
66
|
const probe = Array.from(
|
|
93
67
|
new Set(
|
|
94
68
|
agents
|
|
@@ -131,8 +105,6 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
131
105
|
});
|
|
132
106
|
} catch (reason) {
|
|
133
107
|
console.error('xtralab: failed to load launcher settings', reason);
|
|
134
|
-
// Settings load failed — fall back to defaults so the launcher
|
|
135
|
-
// still has cards instead of going silent.
|
|
136
108
|
await applyAgents([]);
|
|
137
109
|
}
|
|
138
110
|
} else {
|
|
@@ -144,11 +116,8 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
144
116
|
execute: (args: ReadonlyPartialJSONObject) => {
|
|
145
117
|
const id = Private.nextId();
|
|
146
118
|
const onAgentLaunch = (item: Widget): void => {
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// agent feels like the launcher transformed into the terminal.
|
|
150
|
-
// Disposing the inner ReactWidget cascades to the MainAreaWidget
|
|
151
|
-
// host via its `content.disposed` connection.
|
|
119
|
+
// Slot the widget where this launcher sits; disposing the inner
|
|
120
|
+
// ReactWidget cascades to its MainAreaWidget host via `content.disposed`.
|
|
152
121
|
if (find(shell.widgets('main'), w => w === item)) {
|
|
153
122
|
shell.add(item, 'main', { ref: id });
|
|
154
123
|
launcher.dispose();
|
|
@@ -160,9 +129,6 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
160
129
|
editor: editorRegistry?.current ?? null,
|
|
161
130
|
agentSessions,
|
|
162
131
|
onAgentLaunch,
|
|
163
|
-
// Empty repoPath/cwd matches the JupyterLab convention used by
|
|
164
|
-
// the git panel and the stock launcher: let the server resolve
|
|
165
|
-
// the working tree from its root directory.
|
|
166
132
|
repoPath: '',
|
|
167
133
|
cwd: '',
|
|
168
134
|
trans
|
|
@@ -171,9 +137,7 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
171
137
|
launcher.title.label = trans.__('Launcher');
|
|
172
138
|
|
|
173
139
|
const main = new MainAreaWidget({ content: launcher });
|
|
174
|
-
//
|
|
175
|
-
// main area: closing it would leave the user staring at an empty
|
|
176
|
-
// shell with no way back.
|
|
140
|
+
// Closing the only main-area widget would leave an empty shell.
|
|
177
141
|
main.title.closable = !!Array.from(shell.widgets('main')).length;
|
|
178
142
|
main.id = id;
|
|
179
143
|
|
|
@@ -210,9 +174,7 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
210
174
|
labShell.layoutModified.connect(() => {
|
|
211
175
|
maybeCreate();
|
|
212
176
|
});
|
|
213
|
-
//
|
|
214
|
-
// empty (fresh start, no restored widgets) the connect above won't
|
|
215
|
-
// fire on its own — kick it off here.
|
|
177
|
+
// If the restored layout is already empty the connect never fires.
|
|
216
178
|
maybeCreate();
|
|
217
179
|
});
|
|
218
180
|
|
|
@@ -230,14 +192,9 @@ const plugin: JupyterFrontEndPlugin<IAgentRegistry> = {
|
|
|
230
192
|
};
|
|
231
193
|
|
|
232
194
|
/**
|
|
233
|
-
* Drop agents whose command isn't on `$PATH`,
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* {@link fetchAvailableCommands}.
|
|
237
|
-
*
|
|
238
|
-
* When `available` is `null` (the endpoint couldn't be reached) we fail open
|
|
239
|
-
* and return the input unchanged — better to show an unreachable agent than
|
|
240
|
-
* to hide the entire launcher because the server extension didn't load.
|
|
195
|
+
* Drop agents whose command isn't on `$PATH`, keeping `requireAvailable:
|
|
196
|
+
* false` entries. When `available` is `null` (probe failed), fail open and
|
|
197
|
+
* return the input unchanged.
|
|
241
198
|
*/
|
|
242
199
|
function filterAgents(
|
|
243
200
|
agents: IAgent[],
|
|
@@ -255,7 +212,7 @@ namespace Private {
|
|
|
255
212
|
let counter = 0;
|
|
256
213
|
|
|
257
214
|
/**
|
|
258
|
-
*
|
|
215
|
+
* Generate a unique id for a launcher widget.
|
|
259
216
|
*/
|
|
260
217
|
export function nextId(): string {
|
|
261
218
|
return `launcher-${counter++}`;
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import type { IAgent } from './agents';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Quote a string
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* The distinction matters: an actual newline byte (0x0A) sent into a PTY
|
|
9
|
-
* is interpreted by the line discipline as Enter regardless of whether
|
|
10
|
-
* it sits inside `'...'` quotes, so the shell never sees the closing
|
|
11
|
-
* quote. We therefore use ANSI-C `$'...'` quoting and emit `\n` (and
|
|
12
|
-
* `\r`) as their two-character escape sequences; the shell expands them
|
|
13
|
-
* to real newlines after the line is submitted, when readline is no
|
|
14
|
-
* longer involved. Backslashes and single quotes are escaped first so
|
|
15
|
-
* the source text round-trips unchanged.
|
|
4
|
+
* Quote a string for a command line typed into an interactive terminal. A raw
|
|
5
|
+
* newline sent to a PTY acts as Enter even inside '…' quotes, so ANSI-C
|
|
6
|
+
* `$'…'` quoting emits `\n`/`\r` as escapes the shell expands after submit.
|
|
16
7
|
*/
|
|
17
8
|
function shellQuote(value: string): string {
|
|
18
9
|
const escaped = value
|
|
@@ -24,16 +15,10 @@ function shellQuote(value: string): string {
|
|
|
24
15
|
}
|
|
25
16
|
|
|
26
17
|
/**
|
|
27
|
-
* Compose the literal command line typed into a fresh terminal for `agent
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* Returns `agent.command` unchanged when the agent declares no prompt
|
|
33
|
-
* support (`promptArgs === undefined`), so a stray prompt typed into the
|
|
34
|
-
* launcher does not silently mutate the launch line for that agent. The
|
|
35
|
-
* launcher UI separately prevents the click in that case so the dropped
|
|
36
|
-
* prompt is never surprising to the user.
|
|
18
|
+
* Compose the literal command line typed into a fresh terminal for `agent`:
|
|
19
|
+
* the shell-quoted prompt is spliced in per the `promptArgs` recipe. Returns
|
|
20
|
+
* bare `agent.command` when the prompt is empty or the agent has no prompt
|
|
21
|
+
* support.
|
|
37
22
|
*/
|
|
38
23
|
export function buildAgentInvocation(agent: IAgent, prompt: string): string {
|
|
39
24
|
const trimmed = prompt.trim();
|
package/src/launcher/registry.ts
CHANGED
|
@@ -4,24 +4,26 @@ import type { IAgent } from './agents';
|
|
|
4
4
|
import type { IAgentRegistry } from './tokens';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* whenever the launcher recomputes it (on a settings change). The launcher
|
|
10
|
-
* is the only writer, so the write side ({@link setAgents}) is kept off the
|
|
11
|
-
* shared token.
|
|
7
|
+
* Concrete {@link IAgentRegistry} the launcher plugin provides. The launcher
|
|
8
|
+
* is the only writer, so {@link setAgents} is kept off the shared token.
|
|
12
9
|
*/
|
|
13
10
|
export class AgentRegistry implements IAgentRegistry {
|
|
11
|
+
/**
|
|
12
|
+
* The current agents, filtered by availability and sorted by rank.
|
|
13
|
+
*/
|
|
14
14
|
get agents(): IAgent[] {
|
|
15
15
|
return this._agents;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Emitted whenever {@link agents} changes.
|
|
20
|
+
*/
|
|
18
21
|
get changed(): ISignal<IAgentRegistry, void> {
|
|
19
22
|
return this._changed;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
|
-
* Replace the agent list and notify observers.
|
|
24
|
-
* after merging the user's settings and filtering by availability.
|
|
26
|
+
* Replace the agent list and notify observers.
|
|
25
27
|
*/
|
|
26
28
|
setAgents(agents: IAgent[]): void {
|
|
27
29
|
this._agents = agents;
|
package/src/launcher/tokens.ts
CHANGED
|
@@ -4,35 +4,26 @@ import type { ISignal } from '@lumino/signaling';
|
|
|
4
4
|
import type { IAgent } from './agents';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* A read-only, observable view of the launcher's available agents
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the user's `xtralab:launcher` settings, then filtered by a server-side
|
|
11
|
-
* `which` check — and registers an `xtralab:start-agent:<id>` command for
|
|
12
|
-
* each entry. This token shares that list, and (via {@link agentCommandId})
|
|
13
|
-
* the command id that launches each agent, so other plugins can surface the
|
|
14
|
-
* same agents without re-deriving the list or duplicating the icons. The
|
|
15
|
-
* terminals panel uses it to build its "new terminal" dropdown.
|
|
7
|
+
* A read-only, observable view of the launcher's available agents — the
|
|
8
|
+
* merged, availability-filtered list the launcher renders — shared so other
|
|
9
|
+
* plugins (e.g. the terminals panel) can surface the same agents and icons.
|
|
16
10
|
*/
|
|
17
11
|
export interface IAgentRegistry {
|
|
18
12
|
/**
|
|
19
|
-
* The current agents,
|
|
20
|
-
* rank — the same array the launcher renders.
|
|
13
|
+
* The current agents, filtered by availability and sorted by rank.
|
|
21
14
|
*/
|
|
22
15
|
readonly agents: IAgent[];
|
|
23
16
|
|
|
24
17
|
/**
|
|
25
|
-
* Emitted whenever {@link agents} changes
|
|
26
|
-
* launcher settings). Consumers that cache the list should re-read it
|
|
27
|
-
* here; consumers that read it on demand can ignore the signal.
|
|
18
|
+
* Emitted whenever {@link agents} changes.
|
|
28
19
|
*/
|
|
29
20
|
readonly changed: ISignal<IAgentRegistry, void>;
|
|
30
21
|
}
|
|
31
22
|
|
|
32
23
|
/**
|
|
33
|
-
* DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher
|
|
34
|
-
*
|
|
35
|
-
* disabled
|
|
24
|
+
* DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher`;
|
|
25
|
+
* consumers depend on it optionally so they survive the launcher being
|
|
26
|
+
* disabled.
|
|
36
27
|
*/
|
|
37
28
|
export const IAgentRegistry = new Token<IAgentRegistry>(
|
|
38
29
|
'xtralab:IAgentRegistry',
|
|
@@ -40,11 +31,8 @@ export const IAgentRegistry = new Token<IAgentRegistry>(
|
|
|
40
31
|
);
|
|
41
32
|
|
|
42
33
|
/**
|
|
43
|
-
* The
|
|
44
|
-
*
|
|
45
|
-
* command-registration internals — so the launcher (which registers the
|
|
46
|
-
* commands) and any consumer (which references them, e.g. in a menu) agree
|
|
47
|
-
* on the id.
|
|
34
|
+
* The command id that launches a given agent in a new terminal, defined in
|
|
35
|
+
* the shared contract module so the launcher and consumers agree on it.
|
|
48
36
|
*/
|
|
49
37
|
export function agentCommandId(agentId: string): string {
|
|
50
38
|
return `xtralab:start-agent:${agentId}`;
|
package/src/menuBar/index.ts
CHANGED
|
@@ -18,25 +18,15 @@ const OPEN_COMMAND = 'xtralab:open-main-menu';
|
|
|
18
18
|
const TOGGLE_COMMAND = 'xtralab:toggle-menu-bar';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* document `pointerdown` listener in the capture phase, which runs before
|
|
24
|
-
* the button's own mousedown handler — so without this window, pressing the
|
|
25
|
-
* button while the popup is open would close it and instantly reopen it.
|
|
21
|
+
* Guard after the popup closes: Lumino menus close on a capture-phase document
|
|
22
|
+
* pointerdown, before the button's mousedown, which would instantly reopen.
|
|
26
23
|
*/
|
|
27
24
|
const REOPEN_GUARD_MS = 250;
|
|
28
25
|
|
|
29
26
|
/**
|
|
30
|
-
* Collapse the main menu bar into a
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* menus as a vertical popup. The popup reuses the live `RankedMenu` instances
|
|
34
|
-
* owned by the `MainMenu`, so menus added or removed at runtime are reflected
|
|
35
|
-
* without duplication.
|
|
36
|
-
*
|
|
37
|
-
* Hiding covers the `jp-menu-panel` container too, which has its own
|
|
38
|
-
* `min-height` and would otherwise leave an empty strip. The "Show Menu Bar"
|
|
39
|
-
* toggle restores the classic bar.
|
|
27
|
+
* Collapse the main menu bar into a hamburger button whose popup reuses the
|
|
28
|
+
* live `RankedMenu` instances owned by `MainMenu`. Hiding covers the
|
|
29
|
+
* `jp-menu-panel` container too — its `min-height` would leave an empty strip.
|
|
40
30
|
*/
|
|
41
31
|
const plugin: JupyterFrontEndPlugin<void> = {
|
|
42
32
|
id: PLUGIN_ID,
|
|
@@ -84,9 +74,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
84
74
|
|
|
85
75
|
const openMenu = (): void => {
|
|
86
76
|
if (visible) {
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
// compete with the bar for the same Menu instances.
|
|
77
|
+
// With the classic bar on screen, act like keyboard menu activation —
|
|
78
|
+
// a popup would compete with the bar for the same Menu instances.
|
|
90
79
|
mainMenu.activeIndex = 0;
|
|
91
80
|
mainMenu.openActiveMenu();
|
|
92
81
|
return;
|
|
@@ -95,13 +84,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
95
84
|
return;
|
|
96
85
|
}
|
|
97
86
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
// Menu rather than a MenuSvg: MenuSvg.insertItem re-wraps each
|
|
101
|
-
// submenu's renderer and insertItem on every call, which would pile
|
|
102
|
-
// wrappers onto the shared menus across reopens. The svg renderer and
|
|
103
|
-
// themed-container class are applied directly instead, matching how
|
|
104
|
-
// the real menus are constructed in MainMenu.
|
|
87
|
+
// A plain Menu rebuilt on every open; MenuSvg.insertItem would re-wrap
|
|
88
|
+
// each submenu's renderer per reopen, piling wrappers onto the shared menus.
|
|
105
89
|
if (popup === null) {
|
|
106
90
|
popup = new Menu({ commands, renderer: MenuSvg.defaultRenderer });
|
|
107
91
|
popup.addClass('jp-ThemedContainer');
|
|
@@ -183,9 +167,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
183
167
|
loaded.changed.connect(update);
|
|
184
168
|
})
|
|
185
169
|
.catch(reason => {
|
|
186
|
-
// Without settings the collapsed state cannot be managed
|
|
187
|
-
//
|
|
188
|
-
// the app with no reachable menus.
|
|
170
|
+
// Without settings the collapsed state cannot be managed and the button
|
|
171
|
+
// was never added; reveal the stock bar so menus stay reachable.
|
|
189
172
|
applyVisibility(true);
|
|
190
173
|
console.error(`Failed to load settings for ${PLUGIN_ID}`, reason);
|
|
191
174
|
});
|