xtralab 0.14.1 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- 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.d.ts +7 -0
- package/lib/customPanel/index.js +89 -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 +28 -19
- package/lib/fileBrowser/commands.js +51 -98
- package/lib/fileBrowser/contents.d.ts +14 -0
- package/lib/fileBrowser/contents.js +28 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
- package/lib/fileBrowser/dragAndDrop.js +110 -0
- package/lib/fileBrowser/fileBrowser.d.ts +21 -9
- package/lib/fileBrowser/fileBrowser.js +211 -405
- package/lib/fileBrowser/gitStatus.d.ts +4 -9
- package/lib/fileBrowser/gitStatus.js +7 -17
- 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 +41 -10
- package/lib/fileBrowser/widget.d.ts +170 -53
- package/lib/fileBrowser/widget.js +242 -18
- 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 +40 -5
- package/lib/git/commands.js +16 -10
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffSurface.d.ts +41 -55
- package/lib/git/diffSurface.js +42 -117
- package/lib/git/diffWidget.d.ts +81 -2
- package/lib/git/diffWidget.js +62 -12
- package/lib/git/imageDiff.d.ts +5 -6
- package/lib/git/imageDiff.js +18 -48
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +3 -8
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +52 -195
- 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 +2 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +25 -54
- 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 +14 -37
- 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/schemaDefaults.d.ts +2 -9
- package/lib/launcher/schemaDefaults.js +37 -13
- 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 -13
- package/lib/sidebar/index.js +73 -46
- 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 -33
- package/lib/terminals/index.js +24 -102
- 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 +72 -38
- package/lib/terminals/widget.js +173 -73
- 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 +2 -2
- package/schema/launcher.json +2 -2
- package/schema/sidebar.json +47 -7
- 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 +119 -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 +70 -98
- package/src/fileBrowser/contents.ts +33 -0
- package/src/fileBrowser/dragAndDrop.ts +188 -0
- package/src/fileBrowser/fileBrowser.tsx +259 -440
- package/src/fileBrowser/gitStatus.ts +7 -17
- package/src/fileBrowser/gitignore.ts +4 -25
- package/src/fileBrowser/icons.ts +25 -80
- package/src/fileBrowser/index.ts +43 -10
- package/src/fileBrowser/widget.tsx +344 -64
- 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 +70 -10
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffSurface.tsx +71 -159
- package/src/git/diffWidget.tsx +104 -14
- package/src/git/imageDiff.tsx +45 -51
- package/src/git/index.ts +3 -8
- package/src/git/notebookDiff.tsx +211 -238
- package/src/git/tokens.ts +73 -31
- package/src/highlight/index.ts +10 -41
- package/src/index.ts +2 -9
- package/src/launcher/agents.ts +74 -92
- 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 +15 -37
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/schemaDefaults.ts +43 -13
- 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 +103 -52
- 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 +31 -102
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +229 -113
- 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 +25 -73
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +6 -0
- package/style/git.css +22 -184
- package/style/highlight.css +1 -3
- package/style/index.js +1 -0
- 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 +18 -123
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
package/src/terminals/tokens.ts
CHANGED
|
@@ -12,16 +12,13 @@ export interface IAgentTerminalSession {
|
|
|
12
12
|
name: string;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* The agent identifier
|
|
16
|
-
*
|
|
17
|
-
* is an alias (e.g. `ccm` spawning `claude`). Matches an `IAgent` by
|
|
18
|
-
* either field.
|
|
15
|
+
* The agent identifier detection reported: the configured `command`, or the
|
|
16
|
+
* canonical `id` when the command is an alias (e.g. `ccm` spawning `claude`).
|
|
19
17
|
*/
|
|
20
18
|
command: string;
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
|
-
* Display label — the session's real
|
|
24
|
-
* whatever the running program published via an xterm escape sequence).
|
|
21
|
+
* Display label — the session's real (program-published) title.
|
|
25
22
|
*/
|
|
26
23
|
label: string;
|
|
27
24
|
|
|
@@ -35,12 +32,9 @@ export interface IAgentTerminalSession {
|
|
|
35
32
|
/**
|
|
36
33
|
* Running agent terminals, and a way to send a prompt into one.
|
|
37
34
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* TUI can meaningfully receive pasted prompt text. `sendPrompt` re-validates
|
|
42
|
-
* against the server before writing, since pasting prose into a shell prompt
|
|
43
|
-
* would execute it.
|
|
35
|
+
* Only sessions with a detection-*confirmed* running agent are listed — never
|
|
36
|
+
* optimistic launch tags: pasted prompt text must land in an agent's TUI, not
|
|
37
|
+
* a shell prompt where it would be executed.
|
|
44
38
|
*/
|
|
45
39
|
export interface IAgentTerminals {
|
|
46
40
|
/**
|
|
@@ -54,22 +48,17 @@ export interface IAgentTerminals {
|
|
|
54
48
|
readonly changed: ISignal<IAgentTerminals, void>;
|
|
55
49
|
|
|
56
50
|
/**
|
|
57
|
-
* Paste `prompt` into the named session
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* the session's websocket, bracketed-paste-wrapped so a multi-line prompt
|
|
61
|
-
* arrives as one block in the agent's input box, without revealing or
|
|
62
|
-
* focusing the terminal's tab — the user keeps working where they are,
|
|
63
|
-
* and the agent CLIs queue the prompt themselves when they are busy.
|
|
64
|
-
* Rejects when the session is gone or no agent is running in it anymore.
|
|
51
|
+
* Paste `prompt` (bracketed-paste-wrapped) into the named session and press
|
|
52
|
+
* Enter, writing straight to its websocket without revealing or focusing
|
|
53
|
+
* its tab. Rejects when the session is gone or no agent runs in it anymore.
|
|
65
54
|
*/
|
|
66
55
|
sendPrompt(name: string, prompt: string): Promise<void>;
|
|
67
56
|
}
|
|
68
57
|
|
|
69
58
|
/**
|
|
70
59
|
* DI token for {@link IAgentTerminals}. Provided by `xtralab:terminals`;
|
|
71
|
-
* consumed
|
|
72
|
-
*
|
|
60
|
+
* consumed optionally by `xtralab:ask-agent`, so each side works without
|
|
61
|
+
* the other.
|
|
73
62
|
*/
|
|
74
63
|
export const IAgentTerminals = new Token<IAgentTerminals>(
|
|
75
64
|
'xtralab:IAgentTerminals',
|
package/src/terminals/widget.tsx
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IMovableSectionDestination,
|
|
3
|
+
IMovableSectionSource,
|
|
4
|
+
ISectionEntry
|
|
5
|
+
} from '@jupyterlab/apputils';
|
|
1
6
|
import {
|
|
2
7
|
type TranslationBundle,
|
|
3
8
|
nullTranslator
|
|
@@ -6,106 +11,226 @@ import {
|
|
|
6
11
|
addIcon,
|
|
7
12
|
closeIcon,
|
|
8
13
|
LabIcon,
|
|
14
|
+
PanelWithToolbar,
|
|
9
15
|
ReactWidget,
|
|
16
|
+
SidePanel,
|
|
10
17
|
stopIcon,
|
|
11
18
|
terminalIcon,
|
|
19
|
+
ToolbarButton,
|
|
12
20
|
UseSignal
|
|
13
21
|
} from '@jupyterlab/ui-components';
|
|
22
|
+
import { ISignal, Signal } from '@lumino/signaling';
|
|
23
|
+
import { AccordionPanel, Widget } from '@lumino/widgets';
|
|
14
24
|
import * as React from 'react';
|
|
15
25
|
|
|
16
26
|
import { SessionRegistry } from './model';
|
|
17
27
|
|
|
28
|
+
const RUNNING_TERMINALS_ID = 'xtralab-running-terminals';
|
|
29
|
+
|
|
18
30
|
/**
|
|
19
|
-
*
|
|
20
|
-
* the sidebar visibility toggle would target.
|
|
31
|
+
* Persisted by the movable-sections plugin — must stay stable across releases.
|
|
21
32
|
*/
|
|
22
|
-
const
|
|
33
|
+
const TERMINALS_SECTION_ID = 'xtralab-terminals-section';
|
|
23
34
|
|
|
24
35
|
/**
|
|
25
|
-
* Left-sidebar panel listing every running terminal session
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* running so the user can jump back to a backgrounded agent — including
|
|
29
|
-
* sessions whose tab has been closed but which are still alive on the
|
|
30
|
-
* server. Each row is labelled with the session's title and, for a session
|
|
31
|
-
* running a coding agent, a smaller line below it showing the agent's latest
|
|
32
|
-
* line of output (from {@link SessionRegistry.activityFor}).
|
|
33
|
-
*
|
|
34
|
-
* Backed by {@link SessionRegistry}: a `UseSignal` re-renders the list
|
|
35
|
-
* whenever the registry emits `stateChanged`, so it tracks
|
|
36
|
-
* `runningChanged` and the agent-title cache without any local state.
|
|
37
|
-
* Click behavior (activate vs. reopen, shutdown, new terminal) is
|
|
38
|
-
* delegated to plugin-supplied callbacks so the widget never imports
|
|
39
|
-
* `app`.
|
|
36
|
+
* Left-sidebar panel listing every running terminal session, including the
|
|
37
|
+
* sessions whose tab is closed but which are still alive on the server. The
|
|
38
|
+
* list is a single movable "Terminals" section.
|
|
40
39
|
*/
|
|
41
|
-
export class RunningTerminals
|
|
40
|
+
export class RunningTerminals
|
|
41
|
+
extends SidePanel
|
|
42
|
+
implements IMovableSectionSource, IMovableSectionDestination
|
|
43
|
+
{
|
|
42
44
|
constructor(options: RunningTerminals.IOptions) {
|
|
43
45
|
super();
|
|
44
46
|
this._registry = options.registry;
|
|
45
|
-
|
|
46
|
-
this._iconForCommand = options.iconForCommand;
|
|
47
|
-
this._onActivate = options.onActivate;
|
|
48
|
-
this._onShutdown = options.onShutdown;
|
|
49
|
-
this._onShutdownAll = options.onShutdownAll;
|
|
50
|
-
this._onCreate = options.onCreate;
|
|
47
|
+
const trans = options.trans ?? nullTranslator.load('jupyterlab');
|
|
51
48
|
|
|
52
49
|
this.id = RUNNING_TERMINALS_ID;
|
|
53
50
|
this.title.icon = terminalIcon;
|
|
54
|
-
this.title.caption =
|
|
51
|
+
this.title.caption = trans.__('Running Terminals');
|
|
55
52
|
this.addClass('jp-xtralab-Terminals');
|
|
53
|
+
|
|
54
|
+
const section = (this._section = new PanelWithToolbar());
|
|
55
|
+
section.id = TERMINALS_SECTION_ID;
|
|
56
|
+
section.title.label = trans.__('Terminals');
|
|
57
|
+
section.addClass('jp-xtralab-Terminals-section');
|
|
58
|
+
|
|
59
|
+
const shutdownAll = new ToolbarButton({
|
|
60
|
+
icon: stopIcon,
|
|
61
|
+
onClick: options.onShutdownAll,
|
|
62
|
+
tooltip: trans.__('Shut Down All Terminals'),
|
|
63
|
+
enabled: this._registry.sessionNames().length > 0
|
|
64
|
+
});
|
|
65
|
+
this._registry.stateChanged.connect(() => {
|
|
66
|
+
shutdownAll.enabled = this._registry.sessionNames().length > 0;
|
|
67
|
+
});
|
|
68
|
+
section.toolbar.addItem('shutdown-all', shutdownAll);
|
|
69
|
+
|
|
70
|
+
const newTerminal = new ToolbarButton({
|
|
71
|
+
icon: addIcon,
|
|
72
|
+
onClick: () => {
|
|
73
|
+
const rect = newTerminal.node.getBoundingClientRect();
|
|
74
|
+
options.onCreate({ x: rect.left, y: rect.bottom });
|
|
75
|
+
},
|
|
76
|
+
tooltip: trans.__('New Terminal')
|
|
77
|
+
});
|
|
78
|
+
section.toolbar.addItem('new-terminal', newTerminal);
|
|
79
|
+
|
|
80
|
+
section.addWidget(
|
|
81
|
+
new TerminalsListing({
|
|
82
|
+
registry: this._registry,
|
|
83
|
+
trans,
|
|
84
|
+
iconForCommand: options.iconForCommand,
|
|
85
|
+
onActivate: options.onActivate,
|
|
86
|
+
onShutdown: options.onShutdown
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
this.addWidget(section);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The accordion panel hosting the sections; read by the move plugin.
|
|
95
|
+
*/
|
|
96
|
+
get accordionPanel(): AccordionPanel {
|
|
97
|
+
return this.content as AccordionPanel;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A signal emitted when a section is announced to the move plugin.
|
|
102
|
+
*/
|
|
103
|
+
get sectionAdded(): ISignal<this, ISectionEntry> {
|
|
104
|
+
return this._sectionAdded;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The hosted section widgets, excluding the panel's own Terminals section.
|
|
109
|
+
*/
|
|
110
|
+
get sections(): ReadonlyArray<Widget> {
|
|
111
|
+
return this.accordionPanel.widgets.filter(w => w !== this._section);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the movable sections: just the Terminals section, while attached here.
|
|
116
|
+
*/
|
|
117
|
+
getSections(): ReadonlyArray<ISectionEntry> {
|
|
118
|
+
const entry = this._sectionEntry();
|
|
119
|
+
return entry ? [entry] : [];
|
|
56
120
|
}
|
|
57
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Detach the Terminals section for the move plugin and return it; `null`
|
|
124
|
+
* for an unknown id or when the section is already hosted elsewhere.
|
|
125
|
+
*/
|
|
126
|
+
removeSectionById(sectionId: string): Widget | null {
|
|
127
|
+
if (
|
|
128
|
+
sectionId !== TERMINALS_SECTION_ID ||
|
|
129
|
+
this._section.parent !== this.content
|
|
130
|
+
) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
this._section.parent = null;
|
|
134
|
+
return this._section;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Re-attach the Terminals section after it moves back to this panel.
|
|
139
|
+
*/
|
|
140
|
+
reinsertSection(widget: Widget): void {
|
|
141
|
+
this.addWidget(widget);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Host a section moved in from another sidebar panel.
|
|
146
|
+
*/
|
|
147
|
+
addSection(widget: Widget): void {
|
|
148
|
+
this.addWidget(widget);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Detach a hosted section when it moves back to its own panel.
|
|
153
|
+
*/
|
|
154
|
+
removeSectionWidget(widget: Widget): void {
|
|
155
|
+
if (widget.parent === this.content) {
|
|
156
|
+
widget.parent = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The layout restorer stores a section as collapsed whenever its panel is off
|
|
162
|
+
* screen at save time, so it can come back collapsed without the user asking.
|
|
163
|
+
*/
|
|
164
|
+
expandOwnSection(): void {
|
|
165
|
+
const accordion = this.accordionPanel;
|
|
166
|
+
const index = Array.from(accordion.widgets).indexOf(this._section);
|
|
167
|
+
if (index >= 0) {
|
|
168
|
+
accordion.expand(index);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The move plugin reconciles persisted moves only against sections announced
|
|
174
|
+
* after `registerSource`, and this panel builds its section in the constructor.
|
|
175
|
+
*/
|
|
176
|
+
announceSections(): void {
|
|
177
|
+
const entry = this._sectionEntry();
|
|
178
|
+
if (entry) {
|
|
179
|
+
this._sectionAdded.emit(entry);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Dispose of the panel and the registry it owns.
|
|
185
|
+
*/
|
|
58
186
|
dispose(): void {
|
|
59
187
|
if (this.isDisposed) {
|
|
60
188
|
return;
|
|
61
189
|
}
|
|
62
|
-
// The panel owns the registry
|
|
63
|
-
// subscriptions before the React tree goes away.
|
|
190
|
+
// The panel owns the registry; tear down its subscriptions first.
|
|
64
191
|
this._registry.dispose();
|
|
65
192
|
super.dispose();
|
|
66
193
|
}
|
|
67
194
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/>
|
|
81
|
-
)}
|
|
82
|
-
</UseSignal>
|
|
83
|
-
);
|
|
195
|
+
private _sectionEntry(): ISectionEntry | null {
|
|
196
|
+
const accordion = this.accordionPanel;
|
|
197
|
+
const index = Array.from(accordion.widgets).indexOf(this._section);
|
|
198
|
+
const titleNode = accordion.titles[index] as HTMLElement | undefined;
|
|
199
|
+
if (index < 0 || !titleNode) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
id: TERMINALS_SECTION_ID,
|
|
204
|
+
titleNode,
|
|
205
|
+
widget: this._section
|
|
206
|
+
};
|
|
84
207
|
}
|
|
85
208
|
|
|
86
209
|
private _registry: SessionRegistry;
|
|
87
|
-
private
|
|
88
|
-
private
|
|
89
|
-
private _onActivate: (sessionName: string) => void;
|
|
90
|
-
private _onShutdown: (sessionName: string) => void;
|
|
91
|
-
private _onShutdownAll: () => void;
|
|
92
|
-
private _onCreate: (anchor: { x: number; y: number }) => void;
|
|
210
|
+
private _section: PanelWithToolbar;
|
|
211
|
+
private _sectionAdded = new Signal<this, ISectionEntry>(this);
|
|
93
212
|
}
|
|
94
213
|
|
|
95
214
|
export namespace RunningTerminals {
|
|
215
|
+
/**
|
|
216
|
+
* The instantiation options for a {@link RunningTerminals} panel.
|
|
217
|
+
*/
|
|
96
218
|
export interface IOptions {
|
|
219
|
+
/**
|
|
220
|
+
* The session registry the panel renders and takes ownership of.
|
|
221
|
+
*/
|
|
97
222
|
registry: SessionRegistry;
|
|
223
|
+
/**
|
|
224
|
+
* The translation bundle for the panel's labels; untranslated if omitted.
|
|
225
|
+
*/
|
|
98
226
|
trans?: TranslationBundle;
|
|
99
227
|
/**
|
|
100
|
-
* Resolve
|
|
101
|
-
*
|
|
102
|
-
* the agent's logo, or the plain terminal icon. Supplied by the plugin
|
|
103
|
-
* so the widget never imports the agent list.
|
|
228
|
+
* Resolve a row's running-agent command to its icon; supplied by the
|
|
229
|
+
* plugin so the widget never imports the agent list.
|
|
104
230
|
*/
|
|
105
231
|
iconForCommand: (command: string | null) => LabIcon;
|
|
106
232
|
/**
|
|
107
|
-
* Activate the named session's open tab, or reopen it in a fresh
|
|
108
|
-
* terminal widget if no tab is currently attached.
|
|
233
|
+
* Activate the named session's open tab, or reopen it in a fresh widget.
|
|
109
234
|
*/
|
|
110
235
|
onActivate: (sessionName: string) => void;
|
|
111
236
|
/**
|
|
@@ -113,78 +238,74 @@ export namespace RunningTerminals {
|
|
|
113
238
|
*/
|
|
114
239
|
onShutdown: (sessionName: string) => void;
|
|
115
240
|
/**
|
|
116
|
-
* Shut down every running terminal
|
|
117
|
-
* confirm with the user first, since it tears down all live sessions.
|
|
241
|
+
* Shut down every running terminal; the plugin confirms with the user first.
|
|
118
242
|
*/
|
|
119
243
|
onShutdownAll: () => void;
|
|
120
244
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* there — a menu of agents plus a plain terminal, or just a new
|
|
124
|
-
* terminal when no agents are available — so the panel only reports
|
|
125
|
-
* where the button is and never imports the command/menu machinery.
|
|
245
|
+
* Handle the "+" button, anchored at its bottom-left in viewport
|
|
246
|
+
* coordinates; the plugin decides what to show there.
|
|
126
247
|
*/
|
|
127
248
|
onCreate: (anchor: { x: number; y: number }) => void;
|
|
128
249
|
}
|
|
129
250
|
}
|
|
130
251
|
|
|
252
|
+
/**
|
|
253
|
+
* The body of the Terminals section.
|
|
254
|
+
*/
|
|
255
|
+
class TerminalsListing extends ReactWidget {
|
|
256
|
+
constructor(options: {
|
|
257
|
+
registry: SessionRegistry;
|
|
258
|
+
trans: TranslationBundle;
|
|
259
|
+
iconForCommand: (command: string | null) => LabIcon;
|
|
260
|
+
onActivate: (sessionName: string) => void;
|
|
261
|
+
onShutdown: (sessionName: string) => void;
|
|
262
|
+
}) {
|
|
263
|
+
super();
|
|
264
|
+
this._options = options;
|
|
265
|
+
this.addClass('jp-xtralab-Terminals-listing');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Render the listing, re-rendering on every registry state change.
|
|
270
|
+
*/
|
|
271
|
+
protected render(): React.ReactElement {
|
|
272
|
+
return (
|
|
273
|
+
<UseSignal signal={this._options.registry.stateChanged}>
|
|
274
|
+
{() => (
|
|
275
|
+
<RunningTerminalsComponent
|
|
276
|
+
registry={this._options.registry}
|
|
277
|
+
trans={this._options.trans}
|
|
278
|
+
iconForCommand={this._options.iconForCommand}
|
|
279
|
+
onActivate={this._options.onActivate}
|
|
280
|
+
onShutdown={this._options.onShutdown}
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
283
|
+
</UseSignal>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private _options: {
|
|
288
|
+
registry: SessionRegistry;
|
|
289
|
+
trans: TranslationBundle;
|
|
290
|
+
iconForCommand: (command: string | null) => LabIcon;
|
|
291
|
+
onActivate: (sessionName: string) => void;
|
|
292
|
+
onShutdown: (sessionName: string) => void;
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
131
296
|
function RunningTerminalsComponent(props: {
|
|
132
297
|
registry: SessionRegistry;
|
|
133
298
|
trans: TranslationBundle;
|
|
134
299
|
iconForCommand: (command: string | null) => LabIcon;
|
|
135
300
|
onActivate: (sessionName: string) => void;
|
|
136
301
|
onShutdown: (sessionName: string) => void;
|
|
137
|
-
onShutdownAll: () => void;
|
|
138
|
-
onCreate: (anchor: { x: number; y: number }) => void;
|
|
139
302
|
}): React.ReactElement {
|
|
140
|
-
const {
|
|
141
|
-
registry,
|
|
142
|
-
trans,
|
|
143
|
-
iconForCommand,
|
|
144
|
-
onActivate,
|
|
145
|
-
onShutdown,
|
|
146
|
-
onShutdownAll,
|
|
147
|
-
onCreate
|
|
148
|
-
} = props;
|
|
303
|
+
const { registry, trans, iconForCommand, onActivate, onShutdown } = props;
|
|
149
304
|
const names = registry.sessionNames();
|
|
150
|
-
// The session whose terminal is the current widget in the main area, so its
|
|
151
|
-
// row can be highlighted. `null` when the current tab is a notebook or any
|
|
152
|
-
// other non-terminal widget.
|
|
153
305
|
const currentName = registry.currentSessionName();
|
|
154
306
|
|
|
155
307
|
return (
|
|
156
308
|
<div className="jp-xtralab-Terminals-body">
|
|
157
|
-
<div className="jp-xtralab-Terminals-header">
|
|
158
|
-
<h2 className="jp-xtralab-Terminals-title">{trans.__('Terminals')}</h2>
|
|
159
|
-
<div className="jp-xtralab-Terminals-actions">
|
|
160
|
-
<button
|
|
161
|
-
type="button"
|
|
162
|
-
className="jp-xtralab-Terminals-action jp-xtralab-Terminals-shutdown-all"
|
|
163
|
-
onClick={onShutdownAll}
|
|
164
|
-
disabled={names.length === 0}
|
|
165
|
-
title={trans.__('Shut Down All Terminals')}
|
|
166
|
-
aria-label={trans.__('Shut Down All Terminals')}
|
|
167
|
-
>
|
|
168
|
-
<stopIcon.react tag="span" verticalAlign="middle" />
|
|
169
|
-
</button>
|
|
170
|
-
<button
|
|
171
|
-
type="button"
|
|
172
|
-
className="jp-xtralab-Terminals-action jp-xtralab-Terminals-new"
|
|
173
|
-
onClick={event => {
|
|
174
|
-
// Anchor whatever the plugin shows (usually an agent menu) to
|
|
175
|
-
// the button's bottom-left, in viewport coordinates — what
|
|
176
|
-
// `Menu.open(x, y)` expects.
|
|
177
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
178
|
-
onCreate({ x: rect.left, y: rect.bottom });
|
|
179
|
-
}}
|
|
180
|
-
title={trans.__('New Terminal')}
|
|
181
|
-
aria-label={trans.__('New Terminal')}
|
|
182
|
-
aria-haspopup="menu"
|
|
183
|
-
>
|
|
184
|
-
<addIcon.react tag="span" verticalAlign="middle" />
|
|
185
|
-
</button>
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
309
|
{names.length === 0 ? (
|
|
189
310
|
<p className="jp-xtralab-Terminals-empty">
|
|
190
311
|
{trans.__('No running terminals.')}
|
|
@@ -197,15 +318,10 @@ function RunningTerminalsComponent(props: {
|
|
|
197
318
|
const tooltip = hasWidget
|
|
198
319
|
? trans.__('Activate %1', label)
|
|
199
320
|
: trans.__('Reopen %1', label);
|
|
200
|
-
// The running agent's logo (e.g. Claude), or the plain terminal
|
|
201
|
-
// icon when nothing recognised is running in the session.
|
|
202
321
|
const RowIcon = iconForCommand(
|
|
203
322
|
registry.agentCommandFor(name)
|
|
204
323
|
).react;
|
|
205
324
|
const isCurrent = name === currentName;
|
|
206
|
-
// The latest line of output from the session's agent, shown as a
|
|
207
|
-
// smaller line under the title; `null` for rows with nothing to
|
|
208
|
-
// surface (no agent running, or no open tab to read a live buffer).
|
|
209
325
|
const activity = registry.activityFor(name);
|
|
210
326
|
return (
|
|
211
327
|
<li
|
package/src/topBar/icons.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Mirror-symmetric "toggle sidebar" marks
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* by a divider, with the controlled side shaded so left vs. right reads at a
|
|
8
|
-
* glance. The artwork is hand-authored from basic geometry (rounded rect +
|
|
9
|
-
* divider + a shaded panel) so there is no third-party licensing to track,
|
|
10
|
-
* and every stroke/fill uses `currentColor` so the icons inherit the top
|
|
11
|
-
* bar's text color across the light and dark themes.
|
|
4
|
+
* Mirror-symmetric "toggle sidebar" marks in the macOS / VS Code visual
|
|
5
|
+
* language. Hand-authored from basic geometry (no third-party licensing);
|
|
6
|
+
* every stroke/fill uses `currentColor` to follow the top bar's text color.
|
|
12
7
|
*/
|
|
13
8
|
|
|
14
9
|
export const leftSidebarIcon = new LabIcon({
|
package/src/topBar/index.ts
CHANGED
|
@@ -10,16 +10,12 @@ import { leftSidebarIcon, rightSidebarIcon } from './icons';
|
|
|
10
10
|
|
|
11
11
|
const PLUGIN_ID = 'xtralab:top-bar';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Upstream commands from `@jupyterlab/application-extension` that collapse or
|
|
15
|
-
* expand the side areas. We bind the buttons to these rather than
|
|
16
|
-
* reimplementing the toggle, so behavior (and the `isToggled`/`isEnabled`
|
|
17
|
-
* state the buttons reflect) stays in lockstep with the View menu entries
|
|
18
|
-
* and their keyboard shortcuts.
|
|
19
|
-
*/
|
|
20
13
|
const TOGGLE_LEFT_AREA = 'application:toggle-left-area';
|
|
21
14
|
const TOGGLE_RIGHT_AREA = 'application:toggle-right-area';
|
|
22
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A specification for one sidebar toggle button in the top area.
|
|
18
|
+
*/
|
|
23
19
|
interface IButtonSpec {
|
|
24
20
|
/**
|
|
25
21
|
* Stable widget id (required by `LabShell.add`).
|
|
@@ -29,15 +25,17 @@ interface IButtonSpec {
|
|
|
29
25
|
* Command the button triggers and mirrors the state of.
|
|
30
26
|
*/
|
|
31
27
|
command: string;
|
|
28
|
+
/**
|
|
29
|
+
* The icon rendered on the button.
|
|
30
|
+
*/
|
|
32
31
|
icon: LabIcon;
|
|
32
|
+
/**
|
|
33
|
+
* The localized tooltip of the button.
|
|
34
|
+
*/
|
|
33
35
|
caption: (trans: ReturnType<ITranslator['load']>) => string;
|
|
34
36
|
/**
|
|
35
|
-
* Rank
|
|
36
|
-
*
|
|
37
|
-
* rank puts the right button after the menu, where `margin-left: auto`
|
|
38
|
-
* (style/topBar.css) floats it to the far edge. xtralab's shipped config
|
|
39
|
-
* disables the upstream Jupyter logo that otherwise occupies rank 0, so the
|
|
40
|
-
* leading edge is free.
|
|
37
|
+
* Rank in the `top` area: the menu bar sits at 100 and the rank-0 upstream
|
|
38
|
+
* logo is disabled; `margin-left: auto` (topBar.css) floats the right button.
|
|
41
39
|
*/
|
|
42
40
|
rank: number;
|
|
43
41
|
/**
|
|
@@ -66,17 +64,10 @@ const BUTTONS: IButtonSpec[] = [
|
|
|
66
64
|
];
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
|
-
* Add
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Each is a `CommandToolbarButton` wrapping an existing
|
|
74
|
-
* `application:toggle-{left,right}-area` command, so clicking it runs the
|
|
75
|
-
* same toggle as the View menu and the button reflects the command's state —
|
|
76
|
-
* pressed while the matching sidebar is open, and disabled when the area is
|
|
77
|
-
* empty. Note xtralab ships nothing in the right area by default (the
|
|
78
|
-
* property inspector, table of contents and debugger are all disabled), so
|
|
79
|
-
* the right button stays disabled until a widget is moved or added there.
|
|
67
|
+
* Add macOS-style sidebar toggle buttons to the top bar: left at the leading
|
|
68
|
+
* edge, right at the far edge. Each wraps the upstream toggle command and
|
|
69
|
+
* reflects its state — pressed while the sidebar is open, disabled while the
|
|
70
|
+
* area is empty (as the right area is by default).
|
|
80
71
|
*/
|
|
81
72
|
const plugin: JupyterFrontEndPlugin<void> = {
|
|
82
73
|
id: PLUGIN_ID,
|
|
@@ -92,40 +83,26 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
92
83
|
const { commands } = app;
|
|
93
84
|
const trans = (translator ?? nullTranslator).load('jupyterlab');
|
|
94
85
|
|
|
95
|
-
// The upstream toggle
|
|
96
|
-
//
|
|
97
|
-
// move in or out of a side area. `CommandToolbarButton` only re-renders on
|
|
98
|
-
// `commandChanged` for its own id, so the buttons need an explicit notify
|
|
99
|
-
// whenever the shell layout shifts — `layoutModified` covers each side
|
|
100
|
-
// handler's `_updated` (add/remove, expand/collapse), keeping both
|
|
101
|
-
// `isEnabled` and `isToggled` in sync.
|
|
86
|
+
// The upstream toggle commands don't fire `commandChanged` on layout
|
|
87
|
+
// shifts, and `CommandToolbarButton` only re-renders on that signal.
|
|
102
88
|
labShell.layoutModified.connect(() => {
|
|
103
89
|
for (const spec of BUTTONS) {
|
|
104
90
|
commands.notifyCommandChanged(spec.command);
|
|
105
91
|
}
|
|
106
92
|
});
|
|
107
93
|
|
|
108
|
-
// Defer to `app.restored`
|
|
109
|
-
//
|
|
110
|
-
// exists and only re-renders on `commandChanged` of type `changed` /
|
|
111
|
-
// `many-changed` (not `added`), so mounting earlier could leave a button
|
|
112
|
-
// blank until the next unrelated command change.
|
|
94
|
+
// Defer to `app.restored`: `CommandToolbarButton` renders nothing until
|
|
95
|
+
// its command exists and doesn't re-render on the `added` change type.
|
|
113
96
|
void app.restored.then(() => {
|
|
114
97
|
for (const spec of BUTTONS) {
|
|
115
98
|
const button = new CommandToolbarButton({
|
|
116
99
|
commands,
|
|
117
100
|
id: spec.command,
|
|
118
101
|
icon: spec.icon,
|
|
119
|
-
// Empty label keeps the button icon-only: the override is forwarded
|
|
120
|
-
// verbatim and the component skips the label span when it is falsy.
|
|
121
102
|
label: '',
|
|
122
103
|
caption: spec.caption(trans),
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
// leaving the stealth button showing a focus fill/outline (the
|
|
126
|
-
// stray "border") until you click elsewhere — unwanted chrome for a
|
|
127
|
-
// title-bar control. Keyboard Tab focus still works, so this stays
|
|
128
|
-
// accessible.
|
|
104
|
+
// ToolbarButtonComponent otherwise focuses the button on click,
|
|
105
|
+
// leaving a stray focus fill on this title-bar control; Tab still works.
|
|
129
106
|
noFocusOnClick: true
|
|
130
107
|
});
|
|
131
108
|
button.id = spec.id;
|