xtralab 0.4.0 → 0.6.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.
Files changed (96) hide show
  1. package/README.md +110 -73
  2. package/lib/agentSessions.d.ts +50 -0
  3. package/lib/agentSessions.js +45 -0
  4. package/lib/fileBrowser/index.js +5 -1
  5. package/lib/git/commands.d.ts +12 -13
  6. package/lib/git/commands.js +7 -28
  7. package/lib/git/diffProvider.d.ts +8 -0
  8. package/lib/git/diffProvider.js +314 -0
  9. package/lib/git/diffSurface.d.ts +115 -0
  10. package/lib/git/diffSurface.js +342 -0
  11. package/lib/git/diffWidget.d.ts +8 -13
  12. package/lib/git/diffWidget.js +65 -311
  13. package/lib/git/imageDiff.d.ts +23 -0
  14. package/lib/git/imageDiff.js +256 -0
  15. package/lib/git/index.d.ts +10 -8
  16. package/lib/git/index.js +37 -52
  17. package/lib/index.d.ts +4 -2
  18. package/lib/index.js +14 -8
  19. package/lib/launcher/agents.d.ts +1 -1
  20. package/lib/launcher/agents.js +22 -13
  21. package/lib/launcher/commands.d.ts +32 -6
  22. package/lib/launcher/commands.js +81 -57
  23. package/lib/launcher/dashboard.d.ts +15 -0
  24. package/lib/launcher/dashboard.js +119 -5
  25. package/lib/launcher/editorRegistry.d.ts +43 -0
  26. package/lib/launcher/editorRegistry.js +96 -0
  27. package/lib/launcher/editors.d.ts +78 -0
  28. package/lib/launcher/editors.js +110 -0
  29. package/lib/launcher/icons.d.ts +25 -6
  30. package/lib/launcher/icons.js +116 -19
  31. package/lib/launcher/index.d.ts +2 -22
  32. package/lib/launcher/index.js +48 -25
  33. package/lib/launcher/registry.d.ts +21 -0
  34. package/lib/launcher/registry.js +28 -0
  35. package/lib/launcher/tokens.d.ts +41 -0
  36. package/lib/launcher/tokens.js +17 -0
  37. package/lib/terminals/detection.d.ts +11 -0
  38. package/lib/terminals/detection.js +50 -0
  39. package/lib/terminals/index.d.ts +27 -0
  40. package/lib/terminals/index.js +194 -0
  41. package/lib/terminals/model.d.ts +191 -0
  42. package/lib/terminals/model.js +418 -0
  43. package/lib/terminals/widget.d.ts +72 -0
  44. package/lib/terminals/widget.js +93 -0
  45. package/lib/topBar/icons.d.ts +13 -0
  46. package/lib/topBar/icons.js +27 -0
  47. package/lib/topBar/index.d.ts +16 -0
  48. package/lib/topBar/index.js +86 -0
  49. package/package.json +52 -35
  50. package/schema/launcher.json +56 -1
  51. package/src/agentSessions.ts +94 -0
  52. package/src/fileBrowser/index.ts +5 -1
  53. package/src/git/commands.ts +12 -38
  54. package/src/git/diffProvider.tsx +462 -0
  55. package/src/git/diffSurface.tsx +588 -0
  56. package/src/git/diffWidget.tsx +105 -465
  57. package/src/git/imageDiff.tsx +469 -0
  58. package/src/git/index.ts +38 -68
  59. package/src/index.ts +14 -8
  60. package/src/launcher/agents.ts +23 -14
  61. package/src/launcher/commands.ts +98 -60
  62. package/src/launcher/dashboard.tsx +205 -3
  63. package/src/launcher/editorRegistry.ts +149 -0
  64. package/src/launcher/editors.ts +183 -0
  65. package/src/launcher/icons.ts +120 -20
  66. package/src/launcher/index.ts +62 -27
  67. package/src/launcher/registry.ts +33 -0
  68. package/src/launcher/tokens.ts +51 -0
  69. package/src/terminals/detection.ts +65 -0
  70. package/src/terminals/index.ts +222 -0
  71. package/src/terminals/model.ts +497 -0
  72. package/src/terminals/widget.tsx +240 -0
  73. package/src/topBar/icons.ts +30 -0
  74. package/src/topBar/index.ts +121 -0
  75. package/style/git.css +267 -266
  76. package/style/index.css +2 -1
  77. package/style/index.js +2 -1
  78. package/style/launcher.css +124 -2
  79. package/style/terminals.css +235 -0
  80. package/style/topBar.css +38 -0
  81. package/lib/git/icons.d.ts +0 -14
  82. package/lib/git/icons.js +0 -19
  83. package/lib/git/panel.d.ts +0 -29
  84. package/lib/git/panel.js +0 -42
  85. package/lib/git/panelComponent.d.ts +0 -28
  86. package/lib/git/panelComponent.js +0 -320
  87. package/lib/statusBar/index.d.ts +0 -14
  88. package/lib/statusBar/index.js +0 -89
  89. package/lib/statusBar/widget.d.ts +0 -121
  90. package/lib/statusBar/widget.js +0 -237
  91. package/src/git/icons.ts +0 -20
  92. package/src/git/panel.tsx +0 -56
  93. package/src/git/panelComponent.tsx +0 -575
  94. package/src/statusBar/index.ts +0 -115
  95. package/src/statusBar/widget.tsx +0 -318
  96. package/style/statusBar.css +0 -95
@@ -1,237 +0,0 @@
1
- import { nullTranslator } from '@jupyterlab/translation';
2
- import { closeIcon, terminalIcon, VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
3
- import * as React from 'react';
4
- /**
5
- * Source-of-truth model for the status bar. Each running terminal
6
- * session known to the server is one entry; the registry caches the
7
- * last `widget.title.label` we observed so the agent name (or any
8
- * xterm-published title) survives the user closing the tab while the
9
- * session continues running on the backend.
10
- *
11
- * Two upstream signals feed the model:
12
- * - `serviceManager.terminals.runningChanged` is the authoritative
13
- * list of live sessions; everything not in it has been shut down
14
- * server-side and we must drop it.
15
- * - `tracker.widgetAdded` plus the per-widget `title.changed` /
16
- * `disposed` signals keep our label cache in sync with whatever
17
- * xterm/the launcher has set on the open tabs.
18
- *
19
- * `stateChanged` is reused for both kinds of update so consumers
20
- * (the per-session status item, the plugin's add/remove sync) can
21
- * subscribe once.
22
- */
23
- export class SessionRegistry extends VDomModel {
24
- constructor(serviceManager, tracker) {
25
- super();
26
- this._labels = new Map();
27
- this._ranks = new Map();
28
- this._live = new Set();
29
- this._nextRank = 100;
30
- this._terminals = serviceManager.terminals;
31
- this._tracker = tracker;
32
- this._terminals.runningChanged.connect(this._onRunningChanged, this);
33
- this._tracker.widgetAdded.connect(this._onWidgetAdded, this);
34
- this._tracker.forEach(widget => this._trackWidget(widget));
35
- this._refreshLive();
36
- }
37
- /**
38
- * Names of all live sessions, ordered by their stable rank so the
39
- * status bar items keep their slots as new sessions are added.
40
- */
41
- sessionNames() {
42
- const names = Array.from(this._live);
43
- names.sort((a, b) => this.rankFor(a) - this.rankFor(b));
44
- return names;
45
- }
46
- /**
47
- * True iff the named session is still on the server. Used by the
48
- * per-session item to render `null` during the brief window between
49
- * the session's shutdown and the plugin disposing the item.
50
- */
51
- has(name) {
52
- return this._live.has(name);
53
- }
54
- /**
55
- * Display name for the session. The cache wins because it only
56
- * holds "real" labels (the launcher's agent name or an xterm escape
57
- * sequence — see `_cacheLabel` for the filter); reading it first
58
- * prevents the transient `Terminal {name}` an XTerm widget shows
59
- * during reconnect from flickering into the status bar. The live
60
- * widget title is used when no cache exists, and the
61
- * `Terminal {name}` fallback covers sessions we have never seen a
62
- * widget for (e.g. surviving a lab reload).
63
- */
64
- labelFor(name) {
65
- const cached = this._labels.get(name);
66
- if (cached) {
67
- return cached;
68
- }
69
- const widget = this.widgetFor(name);
70
- if (widget && widget.title.label) {
71
- return widget.title.label;
72
- }
73
- return `Terminal ${name}`;
74
- }
75
- /**
76
- * Return the open widget for a session, if any. The status item
77
- * uses this to switch behaviour between "activate existing tab"
78
- * and "open a new tab connected to the live session".
79
- */
80
- widgetFor(name) {
81
- var _a;
82
- return ((_a = this._tracker.find(widget => widget.content.session.name === name)) !== null && _a !== void 0 ? _a : null);
83
- }
84
- /**
85
- * Stable per-session rank assigned in observation order. Reused
86
- * across `registerStatusItem` calls so an item that is briefly
87
- * unregistered (e.g. while a widget is being recreated) lands back
88
- * in the same slot. Cleaned up when the session is shut down so the
89
- * counter does not grow without bound across long sessions.
90
- */
91
- rankFor(name) {
92
- let rank = this._ranks.get(name);
93
- if (rank === undefined) {
94
- rank = this._nextRank++;
95
- this._ranks.set(name, rank);
96
- }
97
- return rank;
98
- }
99
- dispose() {
100
- if (this.isDisposed) {
101
- return;
102
- }
103
- this._terminals.runningChanged.disconnect(this._onRunningChanged, this);
104
- this._tracker.widgetAdded.disconnect(this._onWidgetAdded, this);
105
- this._tracker.forEach(widget => this._untrackWidget(widget));
106
- super.dispose();
107
- }
108
- _refreshLive() {
109
- const next = new Set();
110
- for (const model of this._terminals.running()) {
111
- next.add(model.name);
112
- }
113
- this._live = next;
114
- // Drop cache + rank entries for sessions that have been shut down
115
- // server-side. Without this prune the rank counter would keep
116
- // climbing across long-running labs as terminals come and go.
117
- for (const name of Array.from(this._labels.keys())) {
118
- if (!next.has(name)) {
119
- this._labels.delete(name);
120
- }
121
- }
122
- for (const name of Array.from(this._ranks.keys())) {
123
- if (!next.has(name)) {
124
- this._ranks.delete(name);
125
- }
126
- }
127
- }
128
- _onRunningChanged(_, sessions) {
129
- this._live = new Set(sessions.map(model => model.name));
130
- for (const name of Array.from(this._labels.keys())) {
131
- if (!this._live.has(name)) {
132
- this._labels.delete(name);
133
- }
134
- }
135
- for (const name of Array.from(this._ranks.keys())) {
136
- if (!this._live.has(name)) {
137
- this._ranks.delete(name);
138
- }
139
- }
140
- this.stateChanged.emit();
141
- }
142
- _onWidgetAdded(_, widget) {
143
- this._trackWidget(widget);
144
- this.stateChanged.emit();
145
- }
146
- _trackWidget(widget) {
147
- const name = widget.content.session.name;
148
- this._cacheLabel(name, widget.title.label);
149
- widget.title.changed.connect(this._onTitleChanged, this);
150
- widget.disposed.connect(this._onWidgetDisposed, this);
151
- }
152
- _untrackWidget(widget) {
153
- widget.title.changed.disconnect(this._onTitleChanged, this);
154
- widget.disposed.disconnect(this._onWidgetDisposed, this);
155
- }
156
- _onTitleChanged(title) {
157
- var _a;
158
- const owner = title.owner;
159
- const session = (_a = owner.content) === null || _a === void 0 ? void 0 : _a.session;
160
- if (!session) {
161
- return;
162
- }
163
- this._cacheLabel(session.name, title.label);
164
- this.stateChanged.emit();
165
- }
166
- /**
167
- * Update the cached label for a session, but only when the new
168
- * label is "real" — non-empty and not one of the transient defaults
169
- * the XTerm widget cycles through during reconnect (`'...'` while
170
- * the websocket is opening, `'Terminal {name}'` once
171
- * `_initialConnection` fires). Without the filter, briefly
172
- * reopening a tab while an agent has yet to re-emit its xterm
173
- * title escape sequence would clobber the cached agent name with
174
- * `Terminal 1`, which is exactly the regression we are guarding
175
- * against. Live widget titles still display whatever the widget
176
- * currently holds because `labelFor` consults the widget first;
177
- * the filter only affects what survives a tab close.
178
- */
179
- _cacheLabel(name, label) {
180
- if (!label) {
181
- return;
182
- }
183
- if (label === '...' || label === `Terminal ${name}`) {
184
- return;
185
- }
186
- this._labels.set(name, label);
187
- }
188
- _onWidgetDisposed(widget) {
189
- // The widget is gone but the session may still be running on the
190
- // server — keep the cached label so the status item keeps the
191
- // agent's name when the user reopens the tab. We only clean up
192
- // our subscriptions here; the cache is purged by `runningChanged`
193
- // once the session itself goes away.
194
- this._untrackWidget(widget);
195
- this.stateChanged.emit();
196
- }
197
- }
198
- /**
199
- * One status bar item per live terminal session. Renders a terminal
200
- * icon plus the session's label, with an inline shutdown button that
201
- * appears on hover/focus. The item delegates click handling to the
202
- * plugin so the lifecycle (create/dispose) and the user-facing
203
- * behaviour (activate vs. reopen, shutdown) live in one place.
204
- */
205
- export class TerminalSessionItem extends VDomRenderer {
206
- constructor(options) {
207
- var _a;
208
- super(options.registry);
209
- this._sessionName = options.sessionName;
210
- this._trans = (_a = options.trans) !== null && _a !== void 0 ? _a : nullTranslator.load('jupyterlab');
211
- this._onActivate = options.onActivate;
212
- this._onShutdown = options.onShutdown;
213
- this.addClass('jp-mod-highlighted');
214
- this.addClass('jp-xtralab-StatusBar-Terminal');
215
- }
216
- render() {
217
- if (!this.model || !this.model.has(this._sessionName)) {
218
- return null;
219
- }
220
- const trans = this._trans;
221
- const label = this.model.labelFor(this._sessionName);
222
- const hasWidget = this.model.widgetFor(this._sessionName) !== null;
223
- const tooltip = hasWidget
224
- ? trans.__('Activate %1', label)
225
- : trans.__('Reopen %1', label);
226
- this.node.title = tooltip;
227
- return (React.createElement("div", { className: "jp-xtralab-StatusBar-Terminal-Container" },
228
- React.createElement("button", { type: "button", className: "jp-xtralab-StatusBar-Terminal-Activate", onClick: () => this._onActivate(this._sessionName), "aria-label": tooltip },
229
- React.createElement(terminalIcon.react, { tag: "span", verticalAlign: "middle", stylesheet: "statusBar" }),
230
- React.createElement("span", { className: "jp-xtralab-StatusBar-Terminal-Label" }, label)),
231
- React.createElement("button", { type: "button", className: "jp-xtralab-StatusBar-Terminal-Close", onClick: event => {
232
- event.stopPropagation();
233
- this._onShutdown(this._sessionName);
234
- }, title: trans.__('Shut down %1', label), "aria-label": trans.__('Shut down %1', label) },
235
- React.createElement(closeIcon.react, { tag: "span", verticalAlign: "middle", stylesheet: "statusBar" }))));
236
- }
237
- }
package/src/git/icons.ts DELETED
@@ -1,20 +0,0 @@
1
- import { LabIcon } from '@jupyterlab/ui-components';
2
-
3
- /**
4
- * The branch glyph used as the git panel's sidebar tab icon. Defined as a
5
- * `LabIcon` (rather than a CSS-only mask) so it picks up JupyterLab's icon
6
- * recoloring and inverse-color sidebar painting automatically.
7
- *
8
- * Source: `git-branch` from SVG Repo (https://www.svgrepo.com/svg/377352/git-branch),
9
- * part of the same Pixelarticons family as the xtralab file browser's `file-tree`
10
- * icon, so the two sidebar tab glyphs read as a matched set. The original
11
- * `fill="#000000"` is replaced with `fill="currentColor"` and the root element
12
- * is given the `jp-icon3` class so the icon picks up JupyterLab's theme color
13
- * and the sidebar's inverse-color painting automatically.
14
- */
15
- export const gitIcon = new LabIcon({
16
- name: 'xtralab:git',
17
- svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="jp-icon3" fill="currentColor">
18
- <path d="M5 2h2v12h3v3h7v-7h-3V2h8v8h-3v9h-9v3H2v-8h3V2zm15 6V4h-4v4h4zM8 19v-3H4v4h4v-1z"/>
19
- </svg>`
20
- });
package/src/git/panel.tsx DELETED
@@ -1,56 +0,0 @@
1
- import * as React from 'react';
2
-
3
- import { ReactWidget } from '@jupyterlab/ui-components';
4
- import { Signal, ISignal } from '@lumino/signaling';
5
-
6
- import { gitIcon } from './icons';
7
- import { GitPanelComponent, IGitPanelHandlers } from './panelComponent';
8
-
9
- /**
10
- * The id and CSS class used for the git panel widget. The id ends up as the
11
- * sidebar tab id (so layout restoration can find the widget) and the CSS
12
- * class lets per-panel styles attach without leaking to other widgets.
13
- */
14
- export const GIT_PANEL_ID = 'xtralab:git-panel';
15
- export const GIT_PANEL_CSS_CLASS = 'jp-xtralab-GitPanel';
16
-
17
- /**
18
- * The lumino host widget for the git changes panel. Owns the `refresh`
19
- * signal that the React component listens to, and exposes a typed callback
20
- * surface (`handlers`) the React component uses to react to user actions.
21
- */
22
- export class GitPanel extends ReactWidget {
23
- constructor(handlers: IGitPanelHandlers) {
24
- super();
25
- this.id = GIT_PANEL_ID;
26
- this.title.icon = gitIcon;
27
- this.title.caption = 'Git Changes';
28
- this.addClass(GIT_PANEL_CSS_CLASS);
29
- this._handlers = handlers;
30
- }
31
-
32
- /**
33
- * Public refresh hook called by the plugin when something happened that
34
- * invalidates the current view (the user clicked the toolbar refresh, the
35
- * file browser refreshed, a focused notebook saved, …).
36
- */
37
- refresh(): void {
38
- this._refreshRequested.emit();
39
- }
40
-
41
- get refreshRequested(): ISignal<this, void> {
42
- return this._refreshRequested;
43
- }
44
-
45
- protected render(): React.ReactElement {
46
- return (
47
- <GitPanelComponent
48
- handlers={this._handlers}
49
- refreshSignal={this._refreshRequested}
50
- />
51
- );
52
- }
53
-
54
- private _handlers: IGitPanelHandlers;
55
- private _refreshRequested = new Signal<this, void>(this);
56
- }