xtralab 0.5.0 → 0.7.0
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 +138 -76
- package/lib/agentSessions.d.ts +50 -0
- package/lib/agentSessions.js +45 -0
- package/lib/fileBrowser/index.js +5 -1
- package/lib/git/imageDiff.js +80 -8
- package/lib/index.d.ts +3 -2
- package/lib/index.js +11 -6
- package/lib/launcher/agents.d.ts +1 -1
- package/lib/launcher/agents.js +22 -13
- package/lib/launcher/commands.d.ts +32 -6
- package/lib/launcher/commands.js +81 -57
- package/lib/launcher/dashboard.d.ts +15 -0
- package/lib/launcher/dashboard.js +139 -10
- package/lib/launcher/editorRegistry.d.ts +43 -0
- package/lib/launcher/editorRegistry.js +96 -0
- package/lib/launcher/editors.d.ts +78 -0
- package/lib/launcher/editors.js +110 -0
- package/lib/launcher/icons.d.ts +25 -6
- package/lib/launcher/icons.js +116 -19
- package/lib/launcher/index.d.ts +2 -22
- package/lib/launcher/index.js +48 -25
- package/lib/launcher/registry.d.ts +21 -0
- package/lib/launcher/registry.js +28 -0
- package/lib/launcher/tokens.d.ts +41 -0
- package/lib/launcher/tokens.js +17 -0
- package/lib/terminals/detection.d.ts +11 -0
- package/lib/terminals/detection.js +50 -0
- package/lib/terminals/index.d.ts +27 -0
- package/lib/terminals/index.js +194 -0
- package/lib/terminals/model.d.ts +191 -0
- package/lib/terminals/model.js +418 -0
- package/lib/terminals/widget.d.ts +72 -0
- package/lib/terminals/widget.js +93 -0
- package/lib/topBar/icons.d.ts +13 -0
- package/lib/topBar/icons.js +27 -0
- package/lib/topBar/index.d.ts +16 -0
- package/lib/topBar/index.js +98 -0
- package/package.json +36 -33
- package/schema/launcher.json +56 -1
- package/src/agentSessions.ts +94 -0
- package/src/fileBrowser/index.ts +5 -1
- package/src/git/imageDiff.tsx +127 -26
- package/src/index.ts +11 -6
- package/src/launcher/agents.ts +23 -14
- package/src/launcher/commands.ts +98 -60
- package/src/launcher/dashboard.tsx +227 -9
- package/src/launcher/editorRegistry.ts +149 -0
- package/src/launcher/editors.ts +183 -0
- package/src/launcher/icons.ts +120 -20
- package/src/launcher/index.ts +62 -27
- package/src/launcher/registry.ts +33 -0
- package/src/launcher/tokens.ts +51 -0
- package/src/terminals/detection.ts +65 -0
- package/src/terminals/index.ts +222 -0
- package/src/terminals/model.ts +497 -0
- package/src/terminals/widget.tsx +240 -0
- package/src/topBar/icons.ts +30 -0
- package/src/topBar/index.ts +134 -0
- package/style/git.css +109 -0
- package/style/index.css +2 -1
- package/style/index.js +2 -1
- package/style/launcher.css +124 -2
- package/style/terminals.css +235 -0
- package/style/topBar.css +38 -0
- package/lib/statusBar/index.d.ts +0 -14
- package/lib/statusBar/index.js +0 -89
- package/lib/statusBar/widget.d.ts +0 -121
- package/lib/statusBar/widget.js +0 -237
- package/src/statusBar/index.ts +0 -115
- package/src/statusBar/widget.tsx +0 -318
- package/style/statusBar.css +0 -95
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Left-sidebar panel listing running terminal sessions. Each row shows
|
|
3
|
+
* the session's real (program-published) title and an inline shutdown
|
|
4
|
+
* button that fades in on hover; the header carries the panel title and
|
|
5
|
+
* a "new terminal" button. Colors and spacing pull from `--jp-*` custom
|
|
6
|
+
* properties so the panel tracks the active JupyterLab theme.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
.jp-xtralab-Terminals {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
min-width: 200px;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
background-color: var(--jp-layout-color1);
|
|
15
|
+
color: var(--jp-ui-font-color1);
|
|
16
|
+
font-size: var(--jp-ui-font-size1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.jp-xtralab-Terminals-body {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex: 1 1 auto;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
min-height: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.jp-xtralab-Terminals-header {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex: 0 0 auto;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
padding: 8px 12px;
|
|
32
|
+
border-bottom: var(--jp-border-width) solid var(--jp-border-color2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.jp-xtralab-Terminals-title {
|
|
36
|
+
margin: 0;
|
|
37
|
+
font-size: var(--jp-ui-font-size0);
|
|
38
|
+
font-weight: 600;
|
|
39
|
+
letter-spacing: 1px;
|
|
40
|
+
text-transform: uppercase;
|
|
41
|
+
color: var(--jp-ui-font-color1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.jp-xtralab-Terminals-actions {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex: 0 0 auto;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.jp-xtralab-Terminals-action {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
width: 20px;
|
|
56
|
+
height: 20px;
|
|
57
|
+
padding: 0;
|
|
58
|
+
background: transparent;
|
|
59
|
+
border: none;
|
|
60
|
+
border-radius: 2px;
|
|
61
|
+
color: var(--jp-ui-font-color2);
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.jp-xtralab-Terminals-action:hover {
|
|
66
|
+
background-color: var(--jp-layout-color2);
|
|
67
|
+
color: var(--jp-ui-font-color1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.jp-xtralab-Terminals-action:focus-visible {
|
|
71
|
+
outline: var(--jp-border-width) solid var(--jp-brand-color1);
|
|
72
|
+
outline-offset: -1px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* "Shut down all" is disabled when there are no running terminals. Dim the
|
|
77
|
+
* whole control with opacity rather than `color`: the stop glyph is an SVG
|
|
78
|
+
* whose fill comes from its own `jp-icon3` class (not `currentColor`), so a
|
|
79
|
+
* `color` change alone leaves the icon looking fully active — which is why the
|
|
80
|
+
* button did not read as disabled.
|
|
81
|
+
*/
|
|
82
|
+
.jp-xtralab-Terminals-action:disabled {
|
|
83
|
+
cursor: default;
|
|
84
|
+
opacity: 0.4;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.jp-xtralab-Terminals-action:disabled:hover {
|
|
88
|
+
background: transparent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.jp-xtralab-Terminals-empty {
|
|
92
|
+
margin: 0;
|
|
93
|
+
padding: 12px;
|
|
94
|
+
color: var(--jp-ui-font-color2);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.jp-xtralab-Terminals-list {
|
|
98
|
+
flex: 1 1 auto;
|
|
99
|
+
margin: 0;
|
|
100
|
+
padding: 4px 0;
|
|
101
|
+
overflow-y: auto;
|
|
102
|
+
list-style: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.jp-xtralab-Terminals-item {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: stretch;
|
|
108
|
+
height: 28px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.jp-xtralab-Terminals-item:hover {
|
|
112
|
+
background-color: var(--jp-layout-color2);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/*
|
|
116
|
+
* The main click target: terminal icon + label, filling the row so the
|
|
117
|
+
* whole strip activates the session. The close button sits to its right
|
|
118
|
+
* and intercepts its own clicks via the React handler.
|
|
119
|
+
*/
|
|
120
|
+
.jp-xtralab-Terminals-item-activate {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex: 1 1 auto;
|
|
123
|
+
align-items: center;
|
|
124
|
+
gap: 6px;
|
|
125
|
+
min-width: 0;
|
|
126
|
+
padding: 0 4px 0 12px;
|
|
127
|
+
background: transparent;
|
|
128
|
+
border: none;
|
|
129
|
+
color: inherit;
|
|
130
|
+
font: inherit;
|
|
131
|
+
text-align: left;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.jp-xtralab-Terminals-item-activate:focus-visible {
|
|
136
|
+
outline: var(--jp-border-width) solid var(--jp-brand-color1);
|
|
137
|
+
outline-offset: -2px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.jp-xtralab-Terminals-item-label {
|
|
141
|
+
overflow: hidden;
|
|
142
|
+
text-overflow: ellipsis;
|
|
143
|
+
white-space: nowrap;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
* Close button. Hidden by default so the row reads as plain text; fades
|
|
148
|
+
* in when the row is hovered or the button itself takes focus.
|
|
149
|
+
*/
|
|
150
|
+
.jp-xtralab-Terminals-item-close {
|
|
151
|
+
display: flex;
|
|
152
|
+
flex: 0 0 auto;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
width: 24px;
|
|
156
|
+
margin-right: 4px;
|
|
157
|
+
background: transparent;
|
|
158
|
+
border: none;
|
|
159
|
+
color: var(--jp-ui-font-color2);
|
|
160
|
+
cursor: pointer;
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transition: opacity 80ms linear;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.jp-xtralab-Terminals-item-close:hover {
|
|
166
|
+
color: var(--jp-ui-font-color1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.jp-xtralab-Terminals-item-close:focus-visible {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
outline: var(--jp-border-width) solid var(--jp-brand-color1);
|
|
172
|
+
outline-offset: -2px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Higher specificity than the bare-button rules above, so it comes
|
|
176
|
+
* last to keep stylelint's no-descending-specificity rule happy. */
|
|
177
|
+
.jp-xtralab-Terminals-item:hover .jp-xtralab-Terminals-item-close {
|
|
178
|
+
opacity: 1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Keep the row/header icons at the standard 16px sidebar size. */
|
|
182
|
+
.jp-xtralab-Terminals-item svg,
|
|
183
|
+
.jp-xtralab-Terminals-action svg {
|
|
184
|
+
width: 16px;
|
|
185
|
+
height: 16px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* The terminal that is currently the active widget in the main area — the
|
|
190
|
+
* running-session counterpart of the file browser highlighting the open
|
|
191
|
+
* document. It reuses the file listing's brand-color treatment so "this is
|
|
192
|
+
* the focused terminal" reads at a glance, and the panel highlights nothing
|
|
193
|
+
* while a notebook (or any non-terminal widget) holds the focus instead.
|
|
194
|
+
* Driven from `widget.tsx` via the terminal tracker + the shell's current
|
|
195
|
+
* widget.
|
|
196
|
+
*/
|
|
197
|
+
.jp-xtralab-Terminals-item.jp-mod-current {
|
|
198
|
+
color: var(--jp-ui-inverse-font-color1);
|
|
199
|
+
background-color: var(--jp-brand-color1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Hovering the current row keeps the highlight rather than falling back to
|
|
203
|
+
* the neutral hover background. */
|
|
204
|
+
.jp-xtralab-Terminals-item.jp-mod-current:hover {
|
|
205
|
+
background-color: var(--jp-brand-color1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
* Invert the monochrome terminal glyph against the brand background, exactly
|
|
210
|
+
* as the file listing does for its selected rows. Colored agent logos carry
|
|
211
|
+
* no selectable classes, so they keep their own colors.
|
|
212
|
+
*/
|
|
213
|
+
.jp-xtralab-Terminals-item.jp-mod-current .jp-icon-selectable[fill] {
|
|
214
|
+
fill: #fff;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.jp-xtralab-Terminals-item.jp-mod-current .jp-icon-selectable-inverse[fill] {
|
|
218
|
+
fill: var(--jp-brand-color1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Keep the inline shutdown button legible on the brand background, including
|
|
222
|
+
* while it is itself hovered. */
|
|
223
|
+
.jp-xtralab-Terminals-item.jp-mod-current .jp-xtralab-Terminals-item-close,
|
|
224
|
+
.jp-xtralab-Terminals-item.jp-mod-current
|
|
225
|
+
.jp-xtralab-Terminals-item-close:hover {
|
|
226
|
+
color: var(--jp-ui-inverse-font-color1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Focus rings need a color that contrasts with the brand background. */
|
|
230
|
+
.jp-xtralab-Terminals-item.jp-mod-current
|
|
231
|
+
.jp-xtralab-Terminals-item-activate:focus-visible,
|
|
232
|
+
.jp-xtralab-Terminals-item.jp-mod-current
|
|
233
|
+
.jp-xtralab-Terminals-item-close:focus-visible {
|
|
234
|
+
outline-color: var(--jp-layout-color1);
|
|
235
|
+
}
|
package/style/topBar.css
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Sidebar toggle buttons in the top bar (xtralab:top-bar). */
|
|
2
|
+
|
|
3
|
+
.xtralab-TopBarButton {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The right-area button is added to the flex container `#jp-top-panel` after
|
|
10
|
+
* the main menu (a high rank). `margin-left: auto` consumes the free space to
|
|
11
|
+
* its left so it floats to the far right, mirroring the left button at the
|
|
12
|
+
* leading edge.
|
|
13
|
+
*/
|
|
14
|
+
.xtralab-TopBarButton-right {
|
|
15
|
+
margin-left: auto;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* The buttons are FAST `<jp-button appearance="stealth">` elements. The design
|
|
20
|
+
* system paints state chrome directly on the button host that reads as an
|
|
21
|
+
* unwanted box/border in the top bar (see
|
|
22
|
+
* `@jupyter/web-components/.../styles/patterns/button.js`):
|
|
23
|
+
* - `:host([aria-pressed='true'])` (the matching sidebar is open) adds an
|
|
24
|
+
* inset `box-shadow`;
|
|
25
|
+
* - `:host([disabled])` (e.g. the right area is empty) fills the host with
|
|
26
|
+
* `neutralFillRest`.
|
|
27
|
+
* Neutralize both so the control stays a flat icon like the menu.
|
|
28
|
+
* The hover/active stealth fills are left untouched as a normal affordance.
|
|
29
|
+
* `!important` is needed to override the (non-important) `:host(...)` rules
|
|
30
|
+
* defined inside the component's shadow DOM.
|
|
31
|
+
*/
|
|
32
|
+
.xtralab-TopBarButton .jp-ToolbarButtonComponent {
|
|
33
|
+
box-shadow: none !important;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.xtralab-TopBarButton .jp-ToolbarButtonComponent[disabled] {
|
|
37
|
+
background: transparent !important;
|
|
38
|
+
}
|
package/lib/statusBar/index.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
|
-
/**
|
|
3
|
-
* Surfaces every running terminal session as its own item on the left
|
|
4
|
-
* side of the status bar. The list is driven by
|
|
5
|
-
* `serviceManager.terminals.runningChanged`, not by the widget tracker,
|
|
6
|
-
* so a session that has its tab closed but is still running on the
|
|
7
|
-
* backend keeps its slot — the typical state for an agent the user has
|
|
8
|
-
* stepped away from. Clicking an item activates the existing tab if one
|
|
9
|
-
* is open, or reopens the session in a fresh terminal widget. The
|
|
10
|
-
* inline `×` button shuts the session down on the server, which
|
|
11
|
-
* cascades through the widget if it is currently open.
|
|
12
|
-
*/
|
|
13
|
-
declare const plugin: JupyterFrontEndPlugin<void>;
|
|
14
|
-
export default plugin;
|
package/lib/statusBar/index.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { IStatusBar } from '@jupyterlab/statusbar';
|
|
2
|
-
import { ITerminalTracker } from '@jupyterlab/terminal';
|
|
3
|
-
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
4
|
-
import { DisposableSet } from '@lumino/disposable';
|
|
5
|
-
import { SessionRegistry, TerminalSessionItem } from './widget';
|
|
6
|
-
const PLUGIN_ID = 'xtralab:statusBar';
|
|
7
|
-
/**
|
|
8
|
-
* Surfaces every running terminal session as its own item on the left
|
|
9
|
-
* side of the status bar. The list is driven by
|
|
10
|
-
* `serviceManager.terminals.runningChanged`, not by the widget tracker,
|
|
11
|
-
* so a session that has its tab closed but is still running on the
|
|
12
|
-
* backend keeps its slot — the typical state for an agent the user has
|
|
13
|
-
* stepped away from. Clicking an item activates the existing tab if one
|
|
14
|
-
* is open, or reopens the session in a fresh terminal widget. The
|
|
15
|
-
* inline `×` button shuts the session down on the server, which
|
|
16
|
-
* cascades through the widget if it is currently open.
|
|
17
|
-
*/
|
|
18
|
-
const plugin = {
|
|
19
|
-
id: PLUGIN_ID,
|
|
20
|
-
description: 'Surface each running terminal session as its own status bar item so users can jump to (or reopen) backgrounded agents from anywhere in the workspace.',
|
|
21
|
-
autoStart: true,
|
|
22
|
-
requires: [IStatusBar, ITerminalTracker],
|
|
23
|
-
optional: [ITranslator],
|
|
24
|
-
activate: (app, statusBar, tracker, translator) => {
|
|
25
|
-
const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
|
|
26
|
-
const registry = new SessionRegistry(app.serviceManager, tracker);
|
|
27
|
-
// One DisposableSet per session — registerStatusItem returns an
|
|
28
|
-
// IDisposable for the registration, and the item widget itself
|
|
29
|
-
// implements IDisposable. Bundling them lets a single `dispose()`
|
|
30
|
-
// tear the slot down completely when the session goes away.
|
|
31
|
-
const items = new Map();
|
|
32
|
-
const onActivate = (name) => {
|
|
33
|
-
// `terminal:open` is the upstream entry point that handles both
|
|
34
|
-
// sides of this: if a widget is already attached to the named
|
|
35
|
-
// session it activates that tab, otherwise it spins up a fresh
|
|
36
|
-
// widget connected to the live session. Reusing it keeps the
|
|
37
|
-
// path identical to a click in the running-sessions sidebar.
|
|
38
|
-
void app.commands.execute('terminal:open', { name });
|
|
39
|
-
};
|
|
40
|
-
const onShutdown = (name) => {
|
|
41
|
-
void app.serviceManager.terminals.shutdown(name).catch(reason => {
|
|
42
|
-
console.error(`xtralab: failed to shut down terminal ${name}: ${reason}`);
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
const sync = () => {
|
|
46
|
-
const live = new Set(registry.sessionNames());
|
|
47
|
-
// Add items for newly-seen sessions. We register each at its
|
|
48
|
-
// stable rank from the registry so re-creating an item between
|
|
49
|
-
// syncs (e.g. if we ever decide to swap representations) lands
|
|
50
|
-
// in the same slot.
|
|
51
|
-
for (const name of live) {
|
|
52
|
-
if (items.has(name)) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
const item = new TerminalSessionItem({
|
|
56
|
-
registry,
|
|
57
|
-
sessionName: name,
|
|
58
|
-
trans,
|
|
59
|
-
onActivate,
|
|
60
|
-
onShutdown
|
|
61
|
-
});
|
|
62
|
-
const registration = statusBar.registerStatusItem(`${PLUGIN_ID}:${name}`, {
|
|
63
|
-
item,
|
|
64
|
-
align: 'left',
|
|
65
|
-
rank: registry.rankFor(name)
|
|
66
|
-
});
|
|
67
|
-
const set = new DisposableSet();
|
|
68
|
-
set.add(registration);
|
|
69
|
-
set.add(item);
|
|
70
|
-
items.set(name, set);
|
|
71
|
-
}
|
|
72
|
-
// Remove items for sessions the server no longer knows about.
|
|
73
|
-
for (const [name, disposable] of Array.from(items.entries())) {
|
|
74
|
-
if (!live.has(name)) {
|
|
75
|
-
disposable.dispose();
|
|
76
|
-
items.delete(name);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
registry.stateChanged.connect(sync);
|
|
81
|
-
void app.serviceManager.terminals.ready.then(() => {
|
|
82
|
-
// The first `runningChanged` may have fired before our connect,
|
|
83
|
-
// depending on how the manager initialises — refresh once after
|
|
84
|
-
// it is ready so any pre-existing sessions get their items.
|
|
85
|
-
sync();
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
export default plugin;
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import type { MainAreaWidget } from '@jupyterlab/apputils';
|
|
2
|
-
import type { ServiceManager } from '@jupyterlab/services';
|
|
3
|
-
import type { ITerminal, ITerminalTracker } from '@jupyterlab/terminal';
|
|
4
|
-
import { type TranslationBundle } from '@jupyterlab/translation';
|
|
5
|
-
import { VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
|
|
6
|
-
import * as React from 'react';
|
|
7
|
-
/**
|
|
8
|
-
* The widget shape every entry in `ITerminalTracker` takes — kept here
|
|
9
|
-
* so the registry's call sites read cleanly.
|
|
10
|
-
*/
|
|
11
|
-
export type TerminalWidget = MainAreaWidget<ITerminal.ITerminal>;
|
|
12
|
-
/**
|
|
13
|
-
* Source-of-truth model for the status bar. Each running terminal
|
|
14
|
-
* session known to the server is one entry; the registry caches the
|
|
15
|
-
* last `widget.title.label` we observed so the agent name (or any
|
|
16
|
-
* xterm-published title) survives the user closing the tab while the
|
|
17
|
-
* session continues running on the backend.
|
|
18
|
-
*
|
|
19
|
-
* Two upstream signals feed the model:
|
|
20
|
-
* - `serviceManager.terminals.runningChanged` is the authoritative
|
|
21
|
-
* list of live sessions; everything not in it has been shut down
|
|
22
|
-
* server-side and we must drop it.
|
|
23
|
-
* - `tracker.widgetAdded` plus the per-widget `title.changed` /
|
|
24
|
-
* `disposed` signals keep our label cache in sync with whatever
|
|
25
|
-
* xterm/the launcher has set on the open tabs.
|
|
26
|
-
*
|
|
27
|
-
* `stateChanged` is reused for both kinds of update so consumers
|
|
28
|
-
* (the per-session status item, the plugin's add/remove sync) can
|
|
29
|
-
* subscribe once.
|
|
30
|
-
*/
|
|
31
|
-
export declare class SessionRegistry extends VDomModel {
|
|
32
|
-
constructor(serviceManager: ServiceManager.IManager, tracker: ITerminalTracker);
|
|
33
|
-
/**
|
|
34
|
-
* Names of all live sessions, ordered by their stable rank so the
|
|
35
|
-
* status bar items keep their slots as new sessions are added.
|
|
36
|
-
*/
|
|
37
|
-
sessionNames(): string[];
|
|
38
|
-
/**
|
|
39
|
-
* True iff the named session is still on the server. Used by the
|
|
40
|
-
* per-session item to render `null` during the brief window between
|
|
41
|
-
* the session's shutdown and the plugin disposing the item.
|
|
42
|
-
*/
|
|
43
|
-
has(name: string): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Display name for the session. The cache wins because it only
|
|
46
|
-
* holds "real" labels (the launcher's agent name or an xterm escape
|
|
47
|
-
* sequence — see `_cacheLabel` for the filter); reading it first
|
|
48
|
-
* prevents the transient `Terminal {name}` an XTerm widget shows
|
|
49
|
-
* during reconnect from flickering into the status bar. The live
|
|
50
|
-
* widget title is used when no cache exists, and the
|
|
51
|
-
* `Terminal {name}` fallback covers sessions we have never seen a
|
|
52
|
-
* widget for (e.g. surviving a lab reload).
|
|
53
|
-
*/
|
|
54
|
-
labelFor(name: string): string;
|
|
55
|
-
/**
|
|
56
|
-
* Return the open widget for a session, if any. The status item
|
|
57
|
-
* uses this to switch behaviour between "activate existing tab"
|
|
58
|
-
* and "open a new tab connected to the live session".
|
|
59
|
-
*/
|
|
60
|
-
widgetFor(name: string): TerminalWidget | null;
|
|
61
|
-
/**
|
|
62
|
-
* Stable per-session rank assigned in observation order. Reused
|
|
63
|
-
* across `registerStatusItem` calls so an item that is briefly
|
|
64
|
-
* unregistered (e.g. while a widget is being recreated) lands back
|
|
65
|
-
* in the same slot. Cleaned up when the session is shut down so the
|
|
66
|
-
* counter does not grow without bound across long sessions.
|
|
67
|
-
*/
|
|
68
|
-
rankFor(name: string): number;
|
|
69
|
-
dispose(): void;
|
|
70
|
-
private _refreshLive;
|
|
71
|
-
private _onRunningChanged;
|
|
72
|
-
private _onWidgetAdded;
|
|
73
|
-
private _trackWidget;
|
|
74
|
-
private _untrackWidget;
|
|
75
|
-
private _onTitleChanged;
|
|
76
|
-
/**
|
|
77
|
-
* Update the cached label for a session, but only when the new
|
|
78
|
-
* label is "real" — non-empty and not one of the transient defaults
|
|
79
|
-
* the XTerm widget cycles through during reconnect (`'...'` while
|
|
80
|
-
* the websocket is opening, `'Terminal {name}'` once
|
|
81
|
-
* `_initialConnection` fires). Without the filter, briefly
|
|
82
|
-
* reopening a tab while an agent has yet to re-emit its xterm
|
|
83
|
-
* title escape sequence would clobber the cached agent name with
|
|
84
|
-
* `Terminal 1`, which is exactly the regression we are guarding
|
|
85
|
-
* against. Live widget titles still display whatever the widget
|
|
86
|
-
* currently holds because `labelFor` consults the widget first;
|
|
87
|
-
* the filter only affects what survives a tab close.
|
|
88
|
-
*/
|
|
89
|
-
private _cacheLabel;
|
|
90
|
-
private _onWidgetDisposed;
|
|
91
|
-
private _terminals;
|
|
92
|
-
private _tracker;
|
|
93
|
-
private _labels;
|
|
94
|
-
private _ranks;
|
|
95
|
-
private _live;
|
|
96
|
-
private _nextRank;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* One status bar item per live terminal session. Renders a terminal
|
|
100
|
-
* icon plus the session's label, with an inline shutdown button that
|
|
101
|
-
* appears on hover/focus. The item delegates click handling to the
|
|
102
|
-
* plugin so the lifecycle (create/dispose) and the user-facing
|
|
103
|
-
* behaviour (activate vs. reopen, shutdown) live in one place.
|
|
104
|
-
*/
|
|
105
|
-
export declare class TerminalSessionItem extends VDomRenderer<SessionRegistry> {
|
|
106
|
-
constructor(options: TerminalSessionItem.IOptions);
|
|
107
|
-
render(): React.ReactElement | null;
|
|
108
|
-
private _sessionName;
|
|
109
|
-
private _trans;
|
|
110
|
-
private _onActivate;
|
|
111
|
-
private _onShutdown;
|
|
112
|
-
}
|
|
113
|
-
export declare namespace TerminalSessionItem {
|
|
114
|
-
interface IOptions {
|
|
115
|
-
registry: SessionRegistry;
|
|
116
|
-
sessionName: string;
|
|
117
|
-
trans?: TranslationBundle;
|
|
118
|
-
onActivate: (sessionName: string) => void;
|
|
119
|
-
onShutdown: (sessionName: string) => void;
|
|
120
|
-
}
|
|
121
|
-
}
|