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.
Files changed (236) hide show
  1. package/README.md +10 -0
  2. package/lib/about/icons.d.ts +0 -5
  3. package/lib/about/icons.js +0 -5
  4. package/lib/about/index.d.ts +4 -8
  5. package/lib/about/index.js +6 -17
  6. package/lib/agentSessions.d.ts +9 -29
  7. package/lib/agentSessions.js +2 -7
  8. package/lib/askAgent/editorSelection.d.ts +20 -26
  9. package/lib/askAgent/editorSelection.js +11 -26
  10. package/lib/askAgent/icons.d.ts +2 -3
  11. package/lib/askAgent/icons.js +2 -3
  12. package/lib/askAgent/index.d.ts +4 -26
  13. package/lib/askAgent/index.js +54 -158
  14. package/lib/askAgent/popup.d.ts +12 -16
  15. package/lib/askAgent/popup.js +18 -37
  16. package/lib/askAgent/prompt.d.ts +3 -5
  17. package/lib/askAgent/prompt.js +6 -19
  18. package/lib/askAgent/queue.d.ts +11 -21
  19. package/lib/askAgent/queue.js +9 -19
  20. package/lib/askAgent/queuePanel.d.ts +13 -18
  21. package/lib/askAgent/queuePanel.js +15 -37
  22. package/lib/askAgent/targetPicker.d.ts +2 -9
  23. package/lib/askAgent/targetPicker.js +6 -11
  24. package/lib/askAgent/tokens.d.ts +32 -46
  25. package/lib/askAgent/tokens.js +4 -6
  26. package/lib/commandBar/index.d.ts +4 -13
  27. package/lib/commandBar/index.js +21 -39
  28. package/lib/customPanel/index.d.ts +7 -0
  29. package/lib/customPanel/index.js +89 -0
  30. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  31. package/lib/editorBreadcrumbs/index.js +10 -6
  32. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  33. package/lib/editorBreadcrumbs/widget.js +11 -17
  34. package/lib/editorIndent/index.d.ts +4 -12
  35. package/lib/editorIndent/index.js +8 -23
  36. package/lib/fileBrowser/commands.d.ts +28 -19
  37. package/lib/fileBrowser/commands.js +51 -98
  38. package/lib/fileBrowser/contents.d.ts +14 -0
  39. package/lib/fileBrowser/contents.js +28 -0
  40. package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
  41. package/lib/fileBrowser/dragAndDrop.js +110 -0
  42. package/lib/fileBrowser/fileBrowser.d.ts +21 -9
  43. package/lib/fileBrowser/fileBrowser.js +211 -405
  44. package/lib/fileBrowser/gitStatus.d.ts +4 -9
  45. package/lib/fileBrowser/gitStatus.js +7 -17
  46. package/lib/fileBrowser/gitignore.d.ts +4 -17
  47. package/lib/fileBrowser/gitignore.js +4 -25
  48. package/lib/fileBrowser/icons.d.ts +7 -17
  49. package/lib/fileBrowser/icons.js +25 -80
  50. package/lib/fileBrowser/index.js +41 -10
  51. package/lib/fileBrowser/widget.d.ts +170 -53
  52. package/lib/fileBrowser/widget.js +242 -18
  53. package/lib/fileTypeIcons/index.js +0 -10
  54. package/lib/git/api.d.ts +5 -13
  55. package/lib/git/api.js +10 -36
  56. package/lib/git/askRequest.d.ts +4 -8
  57. package/lib/git/askRequest.js +6 -20
  58. package/lib/git/commands.d.ts +40 -5
  59. package/lib/git/commands.js +16 -10
  60. package/lib/git/diffModel.js +4 -0
  61. package/lib/git/diffSurface.d.ts +41 -55
  62. package/lib/git/diffSurface.js +42 -117
  63. package/lib/git/diffWidget.d.ts +81 -2
  64. package/lib/git/diffWidget.js +62 -12
  65. package/lib/git/imageDiff.d.ts +5 -6
  66. package/lib/git/imageDiff.js +18 -48
  67. package/lib/git/index.d.ts +0 -3
  68. package/lib/git/index.js +3 -8
  69. package/lib/git/notebookDiff.d.ts +110 -33
  70. package/lib/git/notebookDiff.js +52 -195
  71. package/lib/git/tokens.d.ts +73 -31
  72. package/lib/git/tokens.js +2 -3
  73. package/lib/highlight/index.d.ts +4 -10
  74. package/lib/highlight/index.js +10 -41
  75. package/lib/index.d.ts +0 -9
  76. package/lib/index.js +2 -9
  77. package/lib/launcher/agents.d.ts +55 -51
  78. package/lib/launcher/agents.js +25 -54
  79. package/lib/launcher/availability.d.ts +3 -9
  80. package/lib/launcher/availability.js +3 -9
  81. package/lib/launcher/commands.d.ts +7 -32
  82. package/lib/launcher/commands.js +14 -45
  83. package/lib/launcher/dashboard.d.ts +19 -24
  84. package/lib/launcher/dashboard.js +21 -67
  85. package/lib/launcher/editorRegistry.d.ts +10 -22
  86. package/lib/launcher/editorRegistry.js +14 -18
  87. package/lib/launcher/editors.d.ts +39 -45
  88. package/lib/launcher/editors.js +8 -34
  89. package/lib/launcher/icons.d.ts +0 -12
  90. package/lib/launcher/icons.js +14 -37
  91. package/lib/launcher/index.js +12 -55
  92. package/lib/launcher/invocation.d.ts +4 -10
  93. package/lib/launcher/invocation.js +7 -22
  94. package/lib/launcher/registry.d.ts +9 -7
  95. package/lib/launcher/registry.js +9 -7
  96. package/lib/launcher/schemaDefaults.d.ts +2 -9
  97. package/lib/launcher/schemaDefaults.js +37 -13
  98. package/lib/launcher/tokens.d.ts +10 -22
  99. package/lib/launcher/tokens.js +5 -8
  100. package/lib/menuBar/index.d.ts +3 -10
  101. package/lib/menuBar/index.js +11 -28
  102. package/lib/menus/index.d.ts +3 -9
  103. package/lib/menus/index.js +14 -30
  104. package/lib/omnibox/files.d.ts +2 -4
  105. package/lib/omnibox/files.js +5 -11
  106. package/lib/omnibox/index.d.ts +4 -14
  107. package/lib/omnibox/index.js +9 -27
  108. package/lib/omnibox/model.d.ts +38 -10
  109. package/lib/omnibox/model.js +15 -33
  110. package/lib/omnibox/recents.d.ts +15 -17
  111. package/lib/omnibox/recents.js +12 -17
  112. package/lib/omnibox/tokens.d.ts +5 -10
  113. package/lib/omnibox/tokens.js +2 -6
  114. package/lib/omnibox/widget.d.ts +11 -7
  115. package/lib/omnibox/widget.js +8 -16
  116. package/lib/searchReplace/index.d.ts +4 -20
  117. package/lib/searchReplace/index.js +7 -27
  118. package/lib/showOutput/index.d.ts +3 -12
  119. package/lib/showOutput/index.js +6 -22
  120. package/lib/sidebar/index.d.ts +4 -13
  121. package/lib/sidebar/index.js +73 -46
  122. package/lib/terminalNotifications/index.d.ts +4 -10
  123. package/lib/terminalNotifications/index.js +6 -20
  124. package/lib/terminals/agentTerminals.d.ts +12 -5
  125. package/lib/terminals/agentTerminals.js +25 -44
  126. package/lib/terminals/detection.d.ts +4 -8
  127. package/lib/terminals/detection.js +4 -8
  128. package/lib/terminals/index.d.ts +0 -33
  129. package/lib/terminals/index.js +24 -102
  130. package/lib/terminals/model.d.ts +64 -139
  131. package/lib/terminals/model.js +80 -220
  132. package/lib/terminals/tokens.d.ts +11 -22
  133. package/lib/terminals/tokens.js +2 -2
  134. package/lib/terminals/widget.d.ts +72 -38
  135. package/lib/terminals/widget.js +173 -73
  136. package/lib/topBar/icons.d.ts +3 -8
  137. package/lib/topBar/icons.js +3 -8
  138. package/lib/topBar/index.d.ts +4 -11
  139. package/lib/topBar/index.js +10 -38
  140. package/lib/walkthrough/index.d.ts +4 -9
  141. package/lib/walkthrough/index.js +6 -20
  142. package/lib/walkthrough/panel.d.ts +40 -9
  143. package/lib/walkthrough/panel.js +4 -10
  144. package/package.json +2 -2
  145. package/schema/launcher.json +2 -2
  146. package/schema/sidebar.json +47 -7
  147. package/src/about/icons.ts +0 -5
  148. package/src/about/index.tsx +6 -17
  149. package/src/agentSessions.ts +9 -29
  150. package/src/askAgent/editorSelection.ts +22 -29
  151. package/src/askAgent/icons.ts +2 -3
  152. package/src/askAgent/index.ts +54 -158
  153. package/src/askAgent/popup.tsx +23 -45
  154. package/src/askAgent/prompt.ts +6 -19
  155. package/src/askAgent/queue.ts +13 -24
  156. package/src/askAgent/queuePanel.tsx +21 -46
  157. package/src/askAgent/targetPicker.tsx +5 -15
  158. package/src/askAgent/tokens.ts +32 -46
  159. package/src/commandBar/index.ts +24 -39
  160. package/src/customPanel/index.ts +119 -0
  161. package/src/editorBreadcrumbs/index.ts +10 -6
  162. package/src/editorBreadcrumbs/widget.ts +23 -17
  163. package/src/editorIndent/index.ts +17 -23
  164. package/src/fileBrowser/commands.ts +70 -98
  165. package/src/fileBrowser/contents.ts +33 -0
  166. package/src/fileBrowser/dragAndDrop.ts +188 -0
  167. package/src/fileBrowser/fileBrowser.tsx +259 -440
  168. package/src/fileBrowser/gitStatus.ts +7 -17
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +43 -10
  172. package/src/fileBrowser/widget.tsx +344 -64
  173. package/src/fileTypeIcons/index.ts +0 -10
  174. package/src/git/api.ts +10 -36
  175. package/src/git/askRequest.ts +6 -20
  176. package/src/git/commands.ts +70 -10
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffSurface.tsx +71 -159
  179. package/src/git/diffWidget.tsx +104 -14
  180. package/src/git/imageDiff.tsx +45 -51
  181. package/src/git/index.ts +3 -8
  182. package/src/git/notebookDiff.tsx +211 -238
  183. package/src/git/tokens.ts +73 -31
  184. package/src/highlight/index.ts +10 -41
  185. package/src/index.ts +2 -9
  186. package/src/launcher/agents.ts +74 -92
  187. package/src/launcher/availability.ts +3 -9
  188. package/src/launcher/commands.ts +14 -51
  189. package/src/launcher/dashboard.tsx +49 -88
  190. package/src/launcher/editorRegistry.ts +21 -29
  191. package/src/launcher/editors.ts +39 -58
  192. package/src/launcher/icons.ts +15 -37
  193. package/src/launcher/index.ts +12 -55
  194. package/src/launcher/invocation.ts +7 -22
  195. package/src/launcher/registry.ts +9 -7
  196. package/src/launcher/schemaDefaults.ts +43 -13
  197. package/src/launcher/tokens.ts +10 -22
  198. package/src/menuBar/index.ts +11 -28
  199. package/src/menus/index.ts +14 -30
  200. package/src/omnibox/files.ts +10 -14
  201. package/src/omnibox/index.ts +9 -27
  202. package/src/omnibox/model.ts +50 -38
  203. package/src/omnibox/recents.ts +17 -22
  204. package/src/omnibox/tokens.ts +5 -10
  205. package/src/omnibox/widget.tsx +14 -19
  206. package/src/searchReplace/index.ts +7 -27
  207. package/src/showOutput/index.ts +6 -22
  208. package/src/sidebar/index.ts +103 -52
  209. package/src/terminalNotifications/index.ts +42 -25
  210. package/src/terminals/agentTerminals.ts +25 -49
  211. package/src/terminals/detection.ts +4 -8
  212. package/src/terminals/index.ts +31 -102
  213. package/src/terminals/model.ts +130 -246
  214. package/src/terminals/tokens.ts +11 -22
  215. package/src/terminals/widget.tsx +229 -113
  216. package/src/topBar/icons.ts +3 -8
  217. package/src/topBar/index.ts +21 -44
  218. package/src/walkthrough/index.ts +6 -20
  219. package/src/walkthrough/panel.ts +40 -10
  220. package/style/about.css +2 -7
  221. package/style/askAgent.css +8 -40
  222. package/style/base.css +25 -73
  223. package/style/chrome.css +108 -308
  224. package/style/commandBar.css +4 -20
  225. package/style/customPanel.css +6 -0
  226. package/style/git.css +22 -184
  227. package/style/highlight.css +1 -3
  228. package/style/index.js +1 -0
  229. package/style/launcher.css +6 -70
  230. package/style/omnibox.css +2 -14
  231. package/style/showOutput.css +0 -6
  232. package/style/sidebar.css +29 -9
  233. package/style/tabs.css +5 -13
  234. package/style/terminals.css +18 -123
  235. package/style/topBar.css +2 -22
  236. package/style/walkthrough.css +0 -8
@@ -1,50 +1,88 @@
1
+ import { IMovableSectionDestination, IMovableSectionSource, ISectionEntry } from '@jupyterlab/apputils';
1
2
  import { type TranslationBundle } from '@jupyterlab/translation';
2
- import { LabIcon, ReactWidget } from '@jupyterlab/ui-components';
3
- import * as React from 'react';
3
+ import { LabIcon, SidePanel } from '@jupyterlab/ui-components';
4
+ import { ISignal } from '@lumino/signaling';
5
+ import { AccordionPanel, Widget } from '@lumino/widgets';
4
6
  import { SessionRegistry } from './model';
5
7
  /**
6
- * Left-sidebar panel listing every running terminal session. It is the
7
- * sidebar counterpart of the launcher's terminal tiles: where the
8
- * launcher *starts* sessions, this panel surfaces the ones already
9
- * running so the user can jump back to a backgrounded agent — including
10
- * sessions whose tab has been closed but which are still alive on the
11
- * server. Each row is labelled with the session's title and, for a session
12
- * running a coding agent, a smaller line below it showing the agent's latest
13
- * line of output (from {@link SessionRegistry.activityFor}).
14
- *
15
- * Backed by {@link SessionRegistry}: a `UseSignal` re-renders the list
16
- * whenever the registry emits `stateChanged`, so it tracks
17
- * `runningChanged` and the agent-title cache without any local state.
18
- * Click behavior (activate vs. reopen, shutdown, new terminal) is
19
- * delegated to plugin-supplied callbacks so the widget never imports
20
- * `app`.
8
+ * Left-sidebar panel listing every running terminal session, including the
9
+ * sessions whose tab is closed but which are still alive on the server. The
10
+ * list is a single movable "Terminals" section.
21
11
  */
22
- export declare class RunningTerminals extends ReactWidget {
12
+ export declare class RunningTerminals extends SidePanel implements IMovableSectionSource, IMovableSectionDestination {
23
13
  constructor(options: RunningTerminals.IOptions);
14
+ /**
15
+ * The accordion panel hosting the sections; read by the move plugin.
16
+ */
17
+ get accordionPanel(): AccordionPanel;
18
+ /**
19
+ * A signal emitted when a section is announced to the move plugin.
20
+ */
21
+ get sectionAdded(): ISignal<this, ISectionEntry>;
22
+ /**
23
+ * The hosted section widgets, excluding the panel's own Terminals section.
24
+ */
25
+ get sections(): ReadonlyArray<Widget>;
26
+ /**
27
+ * Get the movable sections: just the Terminals section, while attached here.
28
+ */
29
+ getSections(): ReadonlyArray<ISectionEntry>;
30
+ /**
31
+ * Detach the Terminals section for the move plugin and return it; `null`
32
+ * for an unknown id or when the section is already hosted elsewhere.
33
+ */
34
+ removeSectionById(sectionId: string): Widget | null;
35
+ /**
36
+ * Re-attach the Terminals section after it moves back to this panel.
37
+ */
38
+ reinsertSection(widget: Widget): void;
39
+ /**
40
+ * Host a section moved in from another sidebar panel.
41
+ */
42
+ addSection(widget: Widget): void;
43
+ /**
44
+ * Detach a hosted section when it moves back to its own panel.
45
+ */
46
+ removeSectionWidget(widget: Widget): void;
47
+ /**
48
+ * The layout restorer stores a section as collapsed whenever its panel is off
49
+ * screen at save time, so it can come back collapsed without the user asking.
50
+ */
51
+ expandOwnSection(): void;
52
+ /**
53
+ * The move plugin reconciles persisted moves only against sections announced
54
+ * after `registerSource`, and this panel builds its section in the constructor.
55
+ */
56
+ announceSections(): void;
57
+ /**
58
+ * Dispose of the panel and the registry it owns.
59
+ */
24
60
  dispose(): void;
25
- protected render(): React.ReactElement;
61
+ private _sectionEntry;
26
62
  private _registry;
27
- private _trans;
28
- private _iconForCommand;
29
- private _onActivate;
30
- private _onShutdown;
31
- private _onShutdownAll;
32
- private _onCreate;
63
+ private _section;
64
+ private _sectionAdded;
33
65
  }
34
66
  export declare namespace RunningTerminals {
67
+ /**
68
+ * The instantiation options for a {@link RunningTerminals} panel.
69
+ */
35
70
  interface IOptions {
71
+ /**
72
+ * The session registry the panel renders and takes ownership of.
73
+ */
36
74
  registry: SessionRegistry;
75
+ /**
76
+ * The translation bundle for the panel's labels; untranslated if omitted.
77
+ */
37
78
  trans?: TranslationBundle;
38
79
  /**
39
- * Resolve the running-agent command for a row (from
40
- * `registry.agentCommandFor`) to the icon to show before its label —
41
- * the agent's logo, or the plain terminal icon. Supplied by the plugin
42
- * so the widget never imports the agent list.
80
+ * Resolve a row's running-agent command to its icon; supplied by the
81
+ * plugin so the widget never imports the agent list.
43
82
  */
44
83
  iconForCommand: (command: string | null) => LabIcon;
45
84
  /**
46
- * Activate the named session's open tab, or reopen it in a fresh
47
- * terminal widget if no tab is currently attached.
85
+ * Activate the named session's open tab, or reopen it in a fresh widget.
48
86
  */
49
87
  onActivate: (sessionName: string) => void;
50
88
  /**
@@ -52,16 +90,12 @@ export declare namespace RunningTerminals {
52
90
  */
53
91
  onShutdown: (sessionName: string) => void;
54
92
  /**
55
- * Shut down every running terminal at once. The plugin is expected to
56
- * confirm with the user first, since it tears down all live sessions.
93
+ * Shut down every running terminal; the plugin confirms with the user first.
57
94
  */
58
95
  onShutdownAll: () => void;
59
96
  /**
60
- * Activate the "+" button, anchored at the given viewport coordinates
61
- * (the bottom-left of the button). The plugin decides what to show
62
- * there — a menu of agents plus a plain terminal, or just a new
63
- * terminal when no agents are available — so the panel only reports
64
- * where the button is and never imports the command/menu machinery.
97
+ * Handle the "+" button, anchored at its bottom-left in viewport
98
+ * coordinates; the plugin decides what to show there.
65
99
  */
66
100
  onCreate: (anchor: {
67
101
  x: number;
@@ -1,101 +1,201 @@
1
1
  import { nullTranslator } from '@jupyterlab/translation';
2
- import { addIcon, closeIcon, ReactWidget, stopIcon, terminalIcon, UseSignal } from '@jupyterlab/ui-components';
2
+ import { addIcon, closeIcon, PanelWithToolbar, ReactWidget, SidePanel, stopIcon, terminalIcon, ToolbarButton, UseSignal } from '@jupyterlab/ui-components';
3
+ import { Signal } from '@lumino/signaling';
3
4
  import * as React from 'react';
5
+ const RUNNING_TERMINALS_ID = 'xtralab-running-terminals';
4
6
  /**
5
- * Id of the panel widget. Used for layout restoration and as the handle
6
- * the sidebar visibility toggle would target.
7
+ * Persisted by the movable-sections plugin — must stay stable across releases.
7
8
  */
8
- const RUNNING_TERMINALS_ID = 'xtralab-running-terminals';
9
+ const TERMINALS_SECTION_ID = 'xtralab-terminals-section';
9
10
  /**
10
- * Left-sidebar panel listing every running terminal session. It is the
11
- * sidebar counterpart of the launcher's terminal tiles: where the
12
- * launcher *starts* sessions, this panel surfaces the ones already
13
- * running so the user can jump back to a backgrounded agent — including
14
- * sessions whose tab has been closed but which are still alive on the
15
- * server. Each row is labelled with the session's title and, for a session
16
- * running a coding agent, a smaller line below it showing the agent's latest
17
- * line of output (from {@link SessionRegistry.activityFor}).
18
- *
19
- * Backed by {@link SessionRegistry}: a `UseSignal` re-renders the list
20
- * whenever the registry emits `stateChanged`, so it tracks
21
- * `runningChanged` and the agent-title cache without any local state.
22
- * Click behavior (activate vs. reopen, shutdown, new terminal) is
23
- * delegated to plugin-supplied callbacks so the widget never imports
24
- * `app`.
11
+ * Left-sidebar panel listing every running terminal session, including the
12
+ * sessions whose tab is closed but which are still alive on the server. The
13
+ * list is a single movable "Terminals" section.
25
14
  */
26
- export class RunningTerminals extends ReactWidget {
15
+ export class RunningTerminals extends SidePanel {
27
16
  constructor(options) {
28
17
  var _a;
29
18
  super();
19
+ this._sectionAdded = new Signal(this);
30
20
  this._registry = options.registry;
31
- this._trans = (_a = options.trans) !== null && _a !== void 0 ? _a : nullTranslator.load('jupyterlab');
32
- this._iconForCommand = options.iconForCommand;
33
- this._onActivate = options.onActivate;
34
- this._onShutdown = options.onShutdown;
35
- this._onShutdownAll = options.onShutdownAll;
36
- this._onCreate = options.onCreate;
21
+ const trans = (_a = options.trans) !== null && _a !== void 0 ? _a : nullTranslator.load('jupyterlab');
37
22
  this.id = RUNNING_TERMINALS_ID;
38
23
  this.title.icon = terminalIcon;
39
- this.title.caption = this._trans.__('Running Terminals');
24
+ this.title.caption = trans.__('Running Terminals');
40
25
  this.addClass('jp-xtralab-Terminals');
26
+ const section = (this._section = new PanelWithToolbar());
27
+ section.id = TERMINALS_SECTION_ID;
28
+ section.title.label = trans.__('Terminals');
29
+ section.addClass('jp-xtralab-Terminals-section');
30
+ const shutdownAll = new ToolbarButton({
31
+ icon: stopIcon,
32
+ onClick: options.onShutdownAll,
33
+ tooltip: trans.__('Shut Down All Terminals'),
34
+ enabled: this._registry.sessionNames().length > 0
35
+ });
36
+ this._registry.stateChanged.connect(() => {
37
+ shutdownAll.enabled = this._registry.sessionNames().length > 0;
38
+ });
39
+ section.toolbar.addItem('shutdown-all', shutdownAll);
40
+ const newTerminal = new ToolbarButton({
41
+ icon: addIcon,
42
+ onClick: () => {
43
+ const rect = newTerminal.node.getBoundingClientRect();
44
+ options.onCreate({ x: rect.left, y: rect.bottom });
45
+ },
46
+ tooltip: trans.__('New Terminal')
47
+ });
48
+ section.toolbar.addItem('new-terminal', newTerminal);
49
+ section.addWidget(new TerminalsListing({
50
+ registry: this._registry,
51
+ trans,
52
+ iconForCommand: options.iconForCommand,
53
+ onActivate: options.onActivate,
54
+ onShutdown: options.onShutdown
55
+ }));
56
+ this.addWidget(section);
57
+ }
58
+ /**
59
+ * The accordion panel hosting the sections; read by the move plugin.
60
+ */
61
+ get accordionPanel() {
62
+ return this.content;
63
+ }
64
+ /**
65
+ * A signal emitted when a section is announced to the move plugin.
66
+ */
67
+ get sectionAdded() {
68
+ return this._sectionAdded;
69
+ }
70
+ /**
71
+ * The hosted section widgets, excluding the panel's own Terminals section.
72
+ */
73
+ get sections() {
74
+ return this.accordionPanel.widgets.filter(w => w !== this._section);
75
+ }
76
+ /**
77
+ * Get the movable sections: just the Terminals section, while attached here.
78
+ */
79
+ getSections() {
80
+ const entry = this._sectionEntry();
81
+ return entry ? [entry] : [];
82
+ }
83
+ /**
84
+ * Detach the Terminals section for the move plugin and return it; `null`
85
+ * for an unknown id or when the section is already hosted elsewhere.
86
+ */
87
+ removeSectionById(sectionId) {
88
+ if (sectionId !== TERMINALS_SECTION_ID ||
89
+ this._section.parent !== this.content) {
90
+ return null;
91
+ }
92
+ this._section.parent = null;
93
+ return this._section;
94
+ }
95
+ /**
96
+ * Re-attach the Terminals section after it moves back to this panel.
97
+ */
98
+ reinsertSection(widget) {
99
+ this.addWidget(widget);
100
+ }
101
+ /**
102
+ * Host a section moved in from another sidebar panel.
103
+ */
104
+ addSection(widget) {
105
+ this.addWidget(widget);
106
+ }
107
+ /**
108
+ * Detach a hosted section when it moves back to its own panel.
109
+ */
110
+ removeSectionWidget(widget) {
111
+ if (widget.parent === this.content) {
112
+ widget.parent = null;
113
+ }
41
114
  }
115
+ /**
116
+ * The layout restorer stores a section as collapsed whenever its panel is off
117
+ * screen at save time, so it can come back collapsed without the user asking.
118
+ */
119
+ expandOwnSection() {
120
+ const accordion = this.accordionPanel;
121
+ const index = Array.from(accordion.widgets).indexOf(this._section);
122
+ if (index >= 0) {
123
+ accordion.expand(index);
124
+ }
125
+ }
126
+ /**
127
+ * The move plugin reconciles persisted moves only against sections announced
128
+ * after `registerSource`, and this panel builds its section in the constructor.
129
+ */
130
+ announceSections() {
131
+ const entry = this._sectionEntry();
132
+ if (entry) {
133
+ this._sectionAdded.emit(entry);
134
+ }
135
+ }
136
+ /**
137
+ * Dispose of the panel and the registry it owns.
138
+ */
42
139
  dispose() {
43
140
  if (this.isDisposed) {
44
141
  return;
45
142
  }
46
- // The panel owns the registry, so tear down its upstream
47
- // subscriptions before the React tree goes away.
143
+ // The panel owns the registry; tear down its subscriptions first.
48
144
  this._registry.dispose();
49
145
  super.dispose();
50
146
  }
147
+ _sectionEntry() {
148
+ const accordion = this.accordionPanel;
149
+ const index = Array.from(accordion.widgets).indexOf(this._section);
150
+ const titleNode = accordion.titles[index];
151
+ if (index < 0 || !titleNode) {
152
+ return null;
153
+ }
154
+ return {
155
+ id: TERMINALS_SECTION_ID,
156
+ titleNode,
157
+ widget: this._section
158
+ };
159
+ }
160
+ }
161
+ /**
162
+ * The body of the Terminals section.
163
+ */
164
+ class TerminalsListing extends ReactWidget {
165
+ constructor(options) {
166
+ super();
167
+ this._options = options;
168
+ this.addClass('jp-xtralab-Terminals-listing');
169
+ }
170
+ /**
171
+ * Render the listing, re-rendering on every registry state change.
172
+ */
51
173
  render() {
52
- return (React.createElement(UseSignal, { signal: this._registry.stateChanged }, () => (React.createElement(RunningTerminalsComponent, { registry: this._registry, trans: this._trans, iconForCommand: this._iconForCommand, onActivate: this._onActivate, onShutdown: this._onShutdown, onShutdownAll: this._onShutdownAll, onCreate: this._onCreate }))));
174
+ return (React.createElement(UseSignal, { signal: this._options.registry.stateChanged }, () => (React.createElement(RunningTerminalsComponent, { registry: this._options.registry, trans: this._options.trans, iconForCommand: this._options.iconForCommand, onActivate: this._options.onActivate, onShutdown: this._options.onShutdown }))));
53
175
  }
54
176
  }
55
177
  function RunningTerminalsComponent(props) {
56
- const { registry, trans, iconForCommand, onActivate, onShutdown, onShutdownAll, onCreate } = props;
178
+ const { registry, trans, iconForCommand, onActivate, onShutdown } = props;
57
179
  const names = registry.sessionNames();
58
- // The session whose terminal is the current widget in the main area, so its
59
- // row can be highlighted. `null` when the current tab is a notebook or any
60
- // other non-terminal widget.
61
180
  const currentName = registry.currentSessionName();
62
- return (React.createElement("div", { className: "jp-xtralab-Terminals-body" },
63
- React.createElement("div", { className: "jp-xtralab-Terminals-header" },
64
- React.createElement("h2", { className: "jp-xtralab-Terminals-title" }, trans.__('Terminals')),
65
- React.createElement("div", { className: "jp-xtralab-Terminals-actions" },
66
- React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-action jp-xtralab-Terminals-shutdown-all", onClick: onShutdownAll, disabled: names.length === 0, title: trans.__('Shut Down All Terminals'), "aria-label": trans.__('Shut Down All Terminals') },
67
- React.createElement(stopIcon.react, { tag: "span", verticalAlign: "middle" })),
68
- React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-action jp-xtralab-Terminals-new", onClick: event => {
69
- // Anchor whatever the plugin shows (usually an agent menu) to
70
- // the button's bottom-left, in viewport coordinates — what
71
- // `Menu.open(x, y)` expects.
72
- const rect = event.currentTarget.getBoundingClientRect();
73
- onCreate({ x: rect.left, y: rect.bottom });
74
- }, title: trans.__('New Terminal'), "aria-label": trans.__('New Terminal'), "aria-haspopup": "menu" },
75
- React.createElement(addIcon.react, { tag: "span", verticalAlign: "middle" })))),
76
- names.length === 0 ? (React.createElement("p", { className: "jp-xtralab-Terminals-empty" }, trans.__('No running terminals.'))) : (React.createElement("ul", { className: "jp-xtralab-Terminals-list" }, names.map(name => {
77
- const label = registry.labelFor(name);
78
- const hasWidget = registry.widgetFor(name) !== null;
79
- const tooltip = hasWidget
80
- ? trans.__('Activate %1', label)
81
- : trans.__('Reopen %1', label);
82
- // The running agent's logo (e.g. Claude), or the plain terminal
83
- // icon when nothing recognised is running in the session.
84
- const RowIcon = iconForCommand(registry.agentCommandFor(name)).react;
85
- const isCurrent = name === currentName;
86
- // The latest line of output from the session's agent, shown as a
87
- // smaller line under the title; `null` for rows with nothing to
88
- // surface (no agent running, or no open tab to read a live buffer).
89
- const activity = registry.activityFor(name);
90
- return (React.createElement("li", { key: name, className: isCurrent
91
- ? 'jp-xtralab-Terminals-item jp-mod-current'
92
- : 'jp-xtralab-Terminals-item' },
93
- React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-item-activate", onClick: () => onActivate(name), title: tooltip, "aria-label": tooltip, "aria-current": isCurrent || undefined },
94
- React.createElement(RowIcon, { tag: "span", className: "jp-xtralab-Terminals-item-icon", verticalAlign: "middle" }),
95
- React.createElement("span", { className: "jp-xtralab-Terminals-item-text" },
96
- React.createElement("span", { className: "jp-xtralab-Terminals-item-label" }, label),
97
- activity ? (React.createElement("span", { className: "jp-xtralab-Terminals-item-detail" }, activity)) : null)),
98
- React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-item-close", onClick: () => onShutdown(name), title: trans.__('Shut down %1', label), "aria-label": trans.__('Shut down %1', label) },
99
- React.createElement(closeIcon.react, { tag: "span", verticalAlign: "middle" }))));
100
- })))));
181
+ return (React.createElement("div", { className: "jp-xtralab-Terminals-body" }, names.length === 0 ? (React.createElement("p", { className: "jp-xtralab-Terminals-empty" }, trans.__('No running terminals.'))) : (React.createElement("ul", { className: "jp-xtralab-Terminals-list" }, names.map(name => {
182
+ const label = registry.labelFor(name);
183
+ const hasWidget = registry.widgetFor(name) !== null;
184
+ const tooltip = hasWidget
185
+ ? trans.__('Activate %1', label)
186
+ : trans.__('Reopen %1', label);
187
+ const RowIcon = iconForCommand(registry.agentCommandFor(name)).react;
188
+ const isCurrent = name === currentName;
189
+ const activity = registry.activityFor(name);
190
+ return (React.createElement("li", { key: name, className: isCurrent
191
+ ? 'jp-xtralab-Terminals-item jp-mod-current'
192
+ : 'jp-xtralab-Terminals-item' },
193
+ React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-item-activate", onClick: () => onActivate(name), title: tooltip, "aria-label": tooltip, "aria-current": isCurrent || undefined },
194
+ React.createElement(RowIcon, { tag: "span", className: "jp-xtralab-Terminals-item-icon", verticalAlign: "middle" }),
195
+ React.createElement("span", { className: "jp-xtralab-Terminals-item-text" },
196
+ React.createElement("span", { className: "jp-xtralab-Terminals-item-label" }, label),
197
+ activity ? (React.createElement("span", { className: "jp-xtralab-Terminals-item-detail" }, activity)) : null)),
198
+ React.createElement("button", { type: "button", className: "jp-xtralab-Terminals-item-close", onClick: () => onShutdown(name), title: trans.__('Shut down %1', label), "aria-label": trans.__('Shut down %1', label) },
199
+ React.createElement(closeIcon.react, { tag: "span", verticalAlign: "middle" }))));
200
+ })))));
101
201
  }
@@ -1,13 +1,8 @@
1
1
  import { LabIcon } from '@jupyterlab/ui-components';
2
2
  /**
3
- * Mirror-symmetric "toggle sidebar" marks for the top-bar buttons, in the
4
- * same visual language as the macOS / VS Code / svgrepo sidebar icons
5
- * (https://www.svgrepo.com/vectors/sidebar/): a rounded window outline split
6
- * by a divider, with the controlled side shaded so left vs. right reads at a
7
- * glance. The artwork is hand-authored from basic geometry (rounded rect +
8
- * divider + a shaded panel) so there is no third-party licensing to track,
9
- * and every stroke/fill uses `currentColor` so the icons inherit the top
10
- * bar's text color across the light and dark themes.
3
+ * Mirror-symmetric "toggle sidebar" marks in the macOS / VS Code visual
4
+ * language. Hand-authored from basic geometry (no third-party licensing);
5
+ * every stroke/fill uses `currentColor` to follow the top bar's text color.
11
6
  */
12
7
  export declare const leftSidebarIcon: LabIcon;
13
8
  export declare const rightSidebarIcon: LabIcon;
@@ -1,13 +1,8 @@
1
1
  import { LabIcon } from '@jupyterlab/ui-components';
2
2
  /**
3
- * Mirror-symmetric "toggle sidebar" marks for the top-bar buttons, in the
4
- * same visual language as the macOS / VS Code / svgrepo sidebar icons
5
- * (https://www.svgrepo.com/vectors/sidebar/): a rounded window outline split
6
- * by a divider, with the controlled side shaded so left vs. right reads at a
7
- * glance. The artwork is hand-authored from basic geometry (rounded rect +
8
- * divider + a shaded panel) so there is no third-party licensing to track,
9
- * and every stroke/fill uses `currentColor` so the icons inherit the top
10
- * bar's text color across the light and dark themes.
3
+ * Mirror-symmetric "toggle sidebar" marks in the macOS / VS Code visual
4
+ * language. Hand-authored from basic geometry (no third-party licensing);
5
+ * every stroke/fill uses `currentColor` to follow the top bar's text color.
11
6
  */
12
7
  export const leftSidebarIcon = new LabIcon({
13
8
  name: 'xtralab:sidebar-left',
@@ -1,16 +1,9 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Add two icon buttons to the top bar that toggle the left and right side
4
- * areas, mirroring the macOS-style sidebar buttons: a left-sidebar button at
5
- * the leading edge and a right-sidebar button at the far edge.
6
- *
7
- * Each is a `CommandToolbarButton` wrapping an existing
8
- * `application:toggle-{left,right}-area` command, so clicking it runs the
9
- * same toggle as the View menu and the button reflects the command's state —
10
- * pressed while the matching sidebar is open, and disabled when the area is
11
- * empty. Note xtralab ships nothing in the right area by default (the
12
- * property inspector, table of contents and debugger are all disabled), so
13
- * the right button stays disabled until a widget is moved or added there.
3
+ * Add macOS-style sidebar toggle buttons to the top bar: left at the leading
4
+ * edge, right at the far edge. Each wraps the upstream toggle command and
5
+ * reflects its state — pressed while the sidebar is open, disabled while the
6
+ * area is empty (as the right area is by default).
14
7
  */
15
8
  declare const plugin: JupyterFrontEndPlugin<void>;
16
9
  export default plugin;
@@ -3,13 +3,6 @@ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
3
3
  import { CommandToolbarButton } from '@jupyterlab/ui-components';
4
4
  import { leftSidebarIcon, rightSidebarIcon } from './icons';
5
5
  const PLUGIN_ID = 'xtralab:top-bar';
6
- /**
7
- * Upstream commands from `@jupyterlab/application-extension` that collapse or
8
- * expand the side areas. We bind the buttons to these rather than
9
- * reimplementing the toggle, so behavior (and the `isToggled`/`isEnabled`
10
- * state the buttons reflect) stays in lockstep with the View menu entries
11
- * and their keyboard shortcuts.
12
- */
13
6
  const TOGGLE_LEFT_AREA = 'application:toggle-left-area';
14
7
  const TOGGLE_RIGHT_AREA = 'application:toggle-right-area';
15
8
  const BUTTONS = [
@@ -31,17 +24,10 @@ const BUTTONS = [
31
24
  }
32
25
  ];
33
26
  /**
34
- * Add two icon buttons to the top bar that toggle the left and right side
35
- * areas, mirroring the macOS-style sidebar buttons: a left-sidebar button at
36
- * the leading edge and a right-sidebar button at the far edge.
37
- *
38
- * Each is a `CommandToolbarButton` wrapping an existing
39
- * `application:toggle-{left,right}-area` command, so clicking it runs the
40
- * same toggle as the View menu and the button reflects the command's state —
41
- * pressed while the matching sidebar is open, and disabled when the area is
42
- * empty. Note xtralab ships nothing in the right area by default (the
43
- * property inspector, table of contents and debugger are all disabled), so
44
- * the right button stays disabled until a widget is moved or added there.
27
+ * Add macOS-style sidebar toggle buttons to the top bar: left at the leading
28
+ * edge, right at the far edge. Each wraps the upstream toggle command and
29
+ * reflects its state — pressed while the sidebar is open, disabled while the
30
+ * area is empty (as the right area is by default).
45
31
  */
46
32
  const plugin = {
47
33
  id: PLUGIN_ID,
@@ -52,39 +38,25 @@ const plugin = {
52
38
  activate: (app, labShell, translator) => {
53
39
  const { commands } = app;
54
40
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
55
- // The upstream toggle-{left,right}-area commands derive `isEnabled` from
56
- // `!labShell.isEmpty(side)` but don't fire `commandChanged` when widgets
57
- // move in or out of a side area. `CommandToolbarButton` only re-renders on
58
- // `commandChanged` for its own id, so the buttons need an explicit notify
59
- // whenever the shell layout shifts — `layoutModified` covers each side
60
- // handler's `_updated` (add/remove, expand/collapse), keeping both
61
- // `isEnabled` and `isToggled` in sync.
41
+ // The upstream toggle commands don't fire `commandChanged` on layout
42
+ // shifts, and `CommandToolbarButton` only re-renders on that signal.
62
43
  labShell.layoutModified.connect(() => {
63
44
  for (const spec of BUTTONS) {
64
45
  commands.notifyCommandChanged(spec.command);
65
46
  }
66
47
  });
67
- // Defer to `app.restored` so the upstream toggle commands are already in
68
- // the registry: `CommandToolbarButton` renders nothing until its command
69
- // exists and only re-renders on `commandChanged` of type `changed` /
70
- // `many-changed` (not `added`), so mounting earlier could leave a button
71
- // blank until the next unrelated command change.
48
+ // Defer to `app.restored`: `CommandToolbarButton` renders nothing until
49
+ // its command exists and doesn't re-render on the `added` change type.
72
50
  void app.restored.then(() => {
73
51
  for (const spec of BUTTONS) {
74
52
  const button = new CommandToolbarButton({
75
53
  commands,
76
54
  id: spec.command,
77
55
  icon: spec.icon,
78
- // Empty label keeps the button icon-only: the override is forwarded
79
- // verbatim and the component skips the label span when it is falsy.
80
56
  label: '',
81
57
  caption: spec.caption(trans),
82
- // Toggle on mousedown without focusing the button. Otherwise
83
- // ToolbarButtonComponent calls `event.target.focus()` on click,
84
- // leaving the stealth button showing a focus fill/outline (the
85
- // stray "border") until you click elsewhere — unwanted chrome for a
86
- // title-bar control. Keyboard Tab focus still works, so this stays
87
- // accessible.
58
+ // ToolbarButtonComponent otherwise focuses the button on click,
59
+ // leaving a stray focus fill on this title-bar control; Tab still works.
88
60
  noFocusOnClick: true
89
61
  });
90
62
  button.id = spec.id;
@@ -1,14 +1,9 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Contribute `xtralab:walkthrough`: build a persistent, read-only walkthrough
4
- * in the right side area instead of narrating only in the agent's chat.
5
- *
6
- * Each call appends a step (Markdown prose, an optional embedded visual, and an
7
- * optional code reference). When a step names a file, the editor follows along
8
- * (opening it full-width in the main area, no split) and its lines are
9
- * highlighted; the step also keeps a button so the user can jump back to it
10
- * later. The panel accumulates the whole tour beside the code, so the user can
11
- * read at their own pace rather than chasing the chat.
3
+ * Contributes `xtralab:walkthrough`: each call appends a step (Markdown prose,
4
+ * optional visual, optional code reference) to a persistent read-only panel in
5
+ * the right side area, so the tour accumulates beside the code instead of
6
+ * scrolling away in the agent's chat.
12
7
  */
13
8
  declare const plugin: JupyterFrontEndPlugin<void>;
14
9
  export default plugin;
@@ -6,15 +6,9 @@ import { tocIcon } from '@jupyterlab/ui-components';
6
6
  import { coerceData } from '../mimeData';
7
7
  import { WalkthroughPanel } from './panel';
8
8
  const PLUGIN_ID = 'xtralab:walkthrough';
9
- /**
10
- * Append a step to the read-only walkthrough panel.
11
- */
12
9
  const WALKTHROUGH_COMMAND = 'xtralab:walkthrough';
13
10
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
14
11
  const PANEL_ID = 'xtralab-walkthrough';
15
- /**
16
- * Pull a `{ mimeType, data }` media object out of a JSON command argument.
17
- */
18
12
  function readMedia(value) {
19
13
  if (value &&
20
14
  typeof value === 'object' &&
@@ -36,15 +30,10 @@ function toLine(value) {
36
30
  return Number.isFinite(n) && n >= 1 ? n : undefined;
37
31
  }
38
32
  /**
39
- * Contribute `xtralab:walkthrough`: build a persistent, read-only walkthrough
40
- * in the right side area instead of narrating only in the agent's chat.
41
- *
42
- * Each call appends a step (Markdown prose, an optional embedded visual, and an
43
- * optional code reference). When a step names a file, the editor follows along
44
- * (opening it full-width in the main area, no split) and its lines are
45
- * highlighted; the step also keeps a button so the user can jump back to it
46
- * later. The panel accumulates the whole tour beside the code, so the user can
47
- * read at their own pace rather than chasing the chat.
33
+ * Contributes `xtralab:walkthrough`: each call appends a step (Markdown prose,
34
+ * optional visual, optional code reference) to a persistent read-only panel in
35
+ * the right side area, so the tour accumulates beside the code instead of
36
+ * scrolling away in the agent's chat.
48
37
  */
49
38
  const plugin = {
50
39
  id: PLUGIN_ID,
@@ -63,9 +52,8 @@ const plugin = {
63
52
  panel.title.icon = tocIcon;
64
53
  panel.title.label = trans.__('Walkthrough');
65
54
  panel.title.caption = trans.__('Walkthrough');
66
- // Closable so it gets a close button if the user drags it out of the
67
- // side area into the main document area. Closing disposes it; the next
68
- // xtralab:walkthrough call recreates it back in the side area.
55
+ // Closable so a drag into the main area gets a close button; closing
56
+ // disposes, and the next call recreates the panel in the side area.
69
57
  panel.title.closable = true;
70
58
  labShell.add(panel, 'right', { rank: 1000, type: 'Walkthrough' });
71
59
  const created = panel;
@@ -148,10 +136,8 @@ const plugin = {
148
136
  args['reset'] !== true) {
149
137
  throw new Error('xtralab:walkthrough needs at least one of: title, body, media, path');
150
138
  }
151
- // Reveal the panel, then render the step into it.
152
139
  labShell.activateById(current.id);
153
140
  await current.addStep(step);
154
- // Let the editor follow the newest step.
155
141
  if (path && args['reveal'] !== false) {
156
142
  await commands.execute(HIGHLIGHT_LINES_COMMAND, {
157
143
  path,