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,21 +1,16 @@
1
1
  /**
2
- * Identity of an entry: two uses only match when kind, id and args all do,
3
- * so runs of one command with different args stay distinct entries.
2
+ * Identity of an entry: two uses only match when kind, id and args all do, so
3
+ * runs of one command with different args stay distinct.
4
4
  */
5
5
  function entryKey(entry) {
6
6
  var _a;
7
7
  return `${entry.kind}\0${entry.id}\0${JSON.stringify((_a = entry.args) !== null && _a !== void 0 ? _a : null)}`;
8
8
  }
9
- /**
10
- * State database key the recents list is persisted under.
11
- */
12
9
  const STATE_KEY = 'xtralab:omnibox:recents';
13
10
  /**
14
- * A most-recently-used list of omnibox results, generic over the result kind
15
- * so commands and files (and future kinds) share one store. Entries are kept
16
- * most-recent-first, capped at `maxItems` per kind, and persisted through the
17
- * state database so they survive reloads. Without a state database the list
18
- * still works for the lifetime of the page.
11
+ * A most-recently-used list of omnibox results, most-recent-first, capped at
12
+ * `maxItems` per kind and persisted through the state database. Without a
13
+ * state database it still works for the lifetime of the page.
19
14
  */
20
15
  export class OmniboxRecents {
21
16
  constructor(options = {}) {
@@ -25,12 +20,14 @@ export class OmniboxRecents {
25
20
  this._state = (_a = options.state) !== null && _a !== void 0 ? _a : null;
26
21
  }
27
22
  /**
28
- * Maximum number of entries remembered per kind. Lowering it trims the list
29
- * immediately; `0` disables recents entirely.
23
+ * Maximum entries kept per kind; lowering trims immediately, `0` disables recents.
30
24
  */
31
25
  get maxItems() {
32
26
  return this._maxItems;
33
27
  }
28
+ /**
29
+ * Set the maximum entries kept per kind.
30
+ */
34
31
  set maxItems(value) {
35
32
  const normalized = Math.max(0, Math.floor(value));
36
33
  if (normalized === this._maxItems) {
@@ -44,8 +41,7 @@ export class OmniboxRecents {
44
41
  }
45
42
  }
46
43
  /**
47
- * The recorded entries, most recent first — all of them, or only those of
48
- * one kind.
44
+ * The recorded entries, most recent first, optionally filtered to one kind.
49
45
  */
50
46
  entries(kind) {
51
47
  return kind
@@ -53,8 +49,7 @@ export class OmniboxRecents {
53
49
  : [...this._entries];
54
50
  }
55
51
  /**
56
- * Record a use of `id` (with the args it ran with, if any), moving it to
57
- * the front of its kind's list.
52
+ * Record a use, moving the entry to the front of its kind's list.
58
53
  */
59
54
  touch(kind, id, args) {
60
55
  const entry = args === undefined ? { kind, id } : { kind, id, args };
@@ -65,7 +60,7 @@ export class OmniboxRecents {
65
60
  }
66
61
  /**
67
62
  * Load the persisted list. Entries recorded before the load completes stay
68
- * at the front, ahead of the restored history.
63
+ * ahead of the restored history.
69
64
  */
70
65
  async restore() {
71
66
  if (!this._state) {
@@ -1,9 +1,8 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
  /**
3
- * A launcher overlay that fuzzy-searches across multiple sources — workspace
4
- * files, JupyterLab commands — and routes a typed prompt to one of the
5
- * configured agents. Provided by `xtralab:omnibox` and consumed (optionally)
6
- * by the top-bar command bar, which opens it on click.
3
+ * A launcher overlay that fuzzy-searches workspace files and commands and
4
+ * routes a typed prompt to a configured agent. Provided by `xtralab:omnibox`;
5
+ * opened by the top-bar command bar.
7
6
  */
8
7
  export interface IOmnibox {
9
8
  /**
@@ -15,13 +14,9 @@ export interface IOmnibox {
15
14
  */
16
15
  close(): void;
17
16
  }
18
- /**
19
- * DI token for {@link IOmnibox}. Provided by `xtralab:omnibox`; consumed by
20
- * `xtralab:command-bar` so the top-bar pill opens the omnibox.
21
- */
22
17
  export declare const IOmnibox: Token<IOmnibox>;
23
18
  /**
24
- * Command id that opens the omnibox (registered by `xtralab:omnibox`). Shared
25
- * here so the command bar can look up its keybinding and show a shortcut hint.
19
+ * Command id that opens the omnibox, shared so the command bar can look up
20
+ * its keybinding for the shortcut hint.
26
21
  */
27
22
  export declare const OMNIBOX_OPEN_COMMAND = "xtralab:omnibox:open";
@@ -1,11 +1,7 @@
1
1
  import { Token } from '@lumino/coreutils';
2
- /**
3
- * DI token for {@link IOmnibox}. Provided by `xtralab:omnibox`; consumed by
4
- * `xtralab:command-bar` so the top-bar pill opens the omnibox.
5
- */
6
2
  export const IOmnibox = new Token('xtralab:IOmnibox', 'Opens the omnibox overlay that searches files and commands and prompts agents.');
7
3
  /**
8
- * Command id that opens the omnibox (registered by `xtralab:omnibox`). Shared
9
- * here so the command bar can look up its keybinding and show a shortcut hint.
4
+ * Command id that opens the omnibox, shared so the command bar can look up
5
+ * its keybinding for the shortcut hint.
10
6
  */
11
7
  export const OMNIBOX_OPEN_COMMAND = 'xtralab:omnibox:open';
@@ -6,30 +6,34 @@ import type { CommandPalette } from '@lumino/widgets';
6
6
  import type { IAgent } from '../launcher/agents';
7
7
  import type { OmniboxRecents } from './recents';
8
8
  /**
9
- * The omnibox overlay widget. Hosts {@link OmniboxComponent} in a React root
10
- * attached to `document.body`. It carries `jp-ThemedContainer` so JupyterLab's
11
- * theme CSS variables resolve outside the shell, and its own `jp-xtralab-Omnibox`
12
- * class for the overlay/panel styling (style/omnibox.css).
9
+ * The omnibox overlay widget, attached to `document.body`; `jp-ThemedContainer`
10
+ * makes JupyterLab's theme CSS variables resolve outside the shell.
13
11
  */
14
12
  export declare class OmniboxWidget extends ReactWidget {
15
13
  constructor(options: OmniboxWidget.IOptions);
14
+ /**
15
+ * Render the omnibox overlay content.
16
+ */
16
17
  render(): JSX.Element;
17
18
  private _options;
18
19
  }
19
- /**
20
- * A namespace for `OmniboxWidget` statics.
21
- */
22
20
  export declare namespace OmniboxWidget {
23
21
  /**
24
22
  * Construction options for {@link OmniboxWidget}.
25
23
  */
26
24
  interface IOptions {
25
+ /**
26
+ * The command registry used to look up and execute commands.
27
+ */
27
28
  commands: CommandRegistry;
28
29
  /**
29
30
  * Snapshot of the command palette's items, read once when the overlay
30
31
  * opens; empty when the palette isn't available.
31
32
  */
32
33
  paletteItems: ReadonlyArray<CommandPalette.IItem>;
34
+ /**
35
+ * The document registry used to pick file icons.
36
+ */
33
37
  docRegistry: DocumentRegistry;
34
38
  /**
35
39
  * Snapshot of the available agents, read once when the overlay opens.
@@ -2,19 +2,13 @@ import { ReactWidget } from '@jupyterlab/ui-components';
2
2
  import * as React from 'react';
3
3
  import { computeSections } from './model';
4
4
  import { loadWorkspaceFiles } from './files';
5
- /**
6
- * DOM id of the results listbox, referenced by the input's `aria-controls`.
7
- */
8
5
  const LIST_ID = 'jp-xtralab-Omnibox-list';
9
- /**
10
- * Stable DOM id for the result row at `index`, for `aria-activedescendant`.
11
- */
12
6
  function optionId(index) {
13
7
  return `jp-xtralab-Omnibox-option-${index}`;
14
8
  }
15
9
  /**
16
10
  * Render `text` with the fuzzy-matched characters at `indices` wrapped in a
17
- * highlight span, leaving the rest as plain text.
11
+ * highlight span.
18
12
  */
19
13
  function renderHighlight(text, indices) {
20
14
  if (indices.length === 0) {
@@ -77,19 +71,16 @@ function OmniboxComponent(props) {
77
71
  ...sections.agents
78
72
  ], [sections]);
79
73
  const hasResults = flat.length > 0;
80
- // Reset the highlighted row whenever the result set changes, and keep the
81
- // index in range when results shrink.
82
74
  React.useEffect(() => {
83
75
  setActive(current => (current < flat.length ? current : 0));
84
76
  }, [flat]);
85
- // Keep the highlighted row scrolled into view as it moves.
86
77
  React.useEffect(() => {
87
78
  var _a, _b;
88
79
  (_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('.jp-mod-active')) === null || _b === void 0 ? void 0 : _b.scrollIntoView({ block: 'nearest' });
89
80
  }, [active, flat]);
90
81
  const dismiss = React.useCallback(() => {
91
- // Defer so unmounting this React root (via onClose) never happens
92
- // synchronously inside the event handler that triggered it.
82
+ // Defer so unmounting this React root never happens synchronously inside
83
+ // the event handler that triggered it.
93
84
  window.setTimeout(onClose, 0);
94
85
  }, [onClose]);
95
86
  const run = React.useCallback((item) => {
@@ -162,10 +153,8 @@ function OmniboxComponent(props) {
162
153
  : trans.__('Search files and commands, or type a prompt and pick an agent. Prefix with > for commands or / for files.')))))));
163
154
  }
164
155
  /**
165
- * The omnibox overlay widget. Hosts {@link OmniboxComponent} in a React root
166
- * attached to `document.body`. It carries `jp-ThemedContainer` so JupyterLab's
167
- * theme CSS variables resolve outside the shell, and its own `jp-xtralab-Omnibox`
168
- * class for the overlay/panel styling (style/omnibox.css).
156
+ * The omnibox overlay widget, attached to `document.body`; `jp-ThemedContainer`
157
+ * makes JupyterLab's theme CSS variables resolve outside the shell.
169
158
  */
170
159
  export class OmniboxWidget extends ReactWidget {
171
160
  constructor(options) {
@@ -175,6 +164,9 @@ export class OmniboxWidget extends ReactWidget {
175
164
  this.addClass('jp-xtralab-Omnibox');
176
165
  this.addClass('jp-ThemedContainer');
177
166
  }
167
+ /**
168
+ * Render the omnibox overlay content.
169
+ */
178
170
  render() {
179
171
  return React.createElement(OmniboxComponent, { ...this._options });
180
172
  }
@@ -1,25 +1,9 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Bind Accel+Shift+F (Cmd+Shift+F on macOS, Ctrl+Shift+F elsewhere) to the
4
- * project-wide Search and Replace panel, matching the equivalent shortcut in
5
- * editors like VS Code.
6
- *
7
- * Two pieces are needed because that chord is already taken: JupyterLab core
8
- * binds Accel+Shift+F to `filebrowser:toggle-main`.
9
- *
10
- * 1. The chord is freed declaratively. `SettingRegistry.reconcileShortcuts`
11
- * keys collisions and disables on (keys, selector) — not on command — so the
12
- * `@jupyterlab/shortcuts-extension:shortcuts` override in
13
- * `default_setting_overrides.d/00-xtralab.json` disables the file-browser
14
- * binding. The file browser stays reachable from its sidebar tab and the
15
- * View menu; only the keyboard shortcut moves.
16
- *
17
- * 2. The new binding is added imperatively below rather than as another shipped
18
- * default. A `disabled` default and a re-binding default land on the same
19
- * (keys, selector) slot, and the disable suppresses every default on that
20
- * slot — including the re-binding. `commands.addKeyBinding` runs outside the
21
- * reconcile pass, so it is unaffected and wins cleanly once the file-browser
22
- * binding is gone.
3
+ * Bind Accel+Shift+F to the Search and Replace panel (VS Code parity). Core
4
+ * binds the chord to `filebrowser:toggle-main`; a `default_setting_overrides.d`
5
+ * override disables it. The re-binding is imperative: `reconcileShortcuts` keys
6
+ * on (keys, selector), so the disable would suppress a shipped default too.
23
7
  */
24
8
  declare const plugin: JupyterFrontEndPlugin<void>;
25
9
  export default plugin;
@@ -4,33 +4,15 @@ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
4
4
  const PLUGIN_ID = 'xtralab:search-replace';
5
5
  const COMMAND_ID = 'xtralab:activate-search-replace';
6
6
  /**
7
- * Widget id of the panel contributed by `jupyterlab-search-replace`. The
8
- * extension adds it to the left sidebar but registers no command of its own,
9
- * so there is nothing to bind a shortcut to out of the box — this plugin adds
10
- * the missing command.
7
+ * Widget id of the `jupyterlab-search-replace` left-sidebar panel; the
8
+ * extension registers no command of its own to bind a shortcut to.
11
9
  */
12
10
  const SEARCH_REPLACE_WIDGET_ID = 'jp-search-replace';
13
11
  /**
14
- * Bind Accel+Shift+F (Cmd+Shift+F on macOS, Ctrl+Shift+F elsewhere) to the
15
- * project-wide Search and Replace panel, matching the equivalent shortcut in
16
- * editors like VS Code.
17
- *
18
- * Two pieces are needed because that chord is already taken: JupyterLab core
19
- * binds Accel+Shift+F to `filebrowser:toggle-main`.
20
- *
21
- * 1. The chord is freed declaratively. `SettingRegistry.reconcileShortcuts`
22
- * keys collisions and disables on (keys, selector) — not on command — so the
23
- * `@jupyterlab/shortcuts-extension:shortcuts` override in
24
- * `default_setting_overrides.d/00-xtralab.json` disables the file-browser
25
- * binding. The file browser stays reachable from its sidebar tab and the
26
- * View menu; only the keyboard shortcut moves.
27
- *
28
- * 2. The new binding is added imperatively below rather than as another shipped
29
- * default. A `disabled` default and a re-binding default land on the same
30
- * (keys, selector) slot, and the disable suppresses every default on that
31
- * slot — including the re-binding. `commands.addKeyBinding` runs outside the
32
- * reconcile pass, so it is unaffected and wins cleanly once the file-browser
33
- * binding is gone.
12
+ * Bind Accel+Shift+F to the Search and Replace panel (VS Code parity). Core
13
+ * binds the chord to `filebrowser:toggle-main`; a `default_setting_overrides.d`
14
+ * override disables it. The re-binding is imperative: `reconcileShortcuts` keys
15
+ * on (keys, selector), so the disable would suppress a shipped default too.
34
16
  */
35
17
  const plugin = {
36
18
  id: PLUGIN_ID,
@@ -45,9 +27,7 @@ const plugin = {
45
27
  label: trans.__('Search and Replace'),
46
28
  caption: trans.__('Show the Search and Replace panel'),
47
29
  execute: () => {
48
- // `activateById` expands the left sidebar when collapsed, selects the
49
- // tab, and the widget focuses its search input from `onAfterShow`, so
50
- // the user can start typing a query immediately. It no-ops if
30
+ // The widget focuses its search input from `onAfterShow`; no-ops if
51
31
  // `jupyterlab-search-replace` is unavailable.
52
32
  labShell.activateById(SEARCH_REPLACE_WIDGET_ID);
53
33
  }
@@ -1,17 +1,8 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Contribute `xtralab:show`: render a single MIME bundle into a main-area
4
- * panel using the application's render-mime registry.
5
- *
6
- * This is the kernel-free counterpart to running a notebook. A coding agent
7
- * generates the content itself (a Vega-Lite spec, a Markdown explainer, an
8
- * HTML fragment, an SVG, a base64 image) and hands it to this command, which
9
- * renders it with the same renderers JupyterLab uses for cell output and docks
10
- * the result beside the code. Nothing is written to disk and no kernel is
11
- * required, so it works even where the kernel has no plotting libraries.
12
- *
13
- * Repeated calls with the same `id` reuse one panel, so a walkthrough can
14
- * refresh the view in place instead of piling up tabs.
3
+ * Contributes `xtralab:show`: render a single MIME bundle into a panel with
4
+ * the same renderers JupyterLab uses for cell output — no kernel, no file on
5
+ * disk. Repeated calls with the same `id` reuse one panel.
15
6
  */
16
7
  declare const plugin: JupyterFrontEndPlugin<void>;
17
8
  export default plugin;
@@ -5,23 +5,11 @@ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
5
5
  import { Panel } from '@lumino/widgets';
6
6
  import { coerceData } from '../mimeData';
7
7
  const PLUGIN_ID = 'xtralab:show-output';
8
- /**
9
- * Render arbitrary rich content into a panel, with no notebook and no kernel.
10
- */
11
8
  const SHOW_COMMAND = 'xtralab:show';
12
9
  /**
13
- * Contribute `xtralab:show`: render a single MIME bundle into a main-area
14
- * panel using the application's render-mime registry.
15
- *
16
- * This is the kernel-free counterpart to running a notebook. A coding agent
17
- * generates the content itself (a Vega-Lite spec, a Markdown explainer, an
18
- * HTML fragment, an SVG, a base64 image) and hands it to this command, which
19
- * renders it with the same renderers JupyterLab uses for cell output and docks
20
- * the result beside the code. Nothing is written to disk and no kernel is
21
- * required, so it works even where the kernel has no plotting libraries.
22
- *
23
- * Repeated calls with the same `id` reuse one panel, so a walkthrough can
24
- * refresh the view in place instead of piling up tabs.
10
+ * Contributes `xtralab:show`: render a single MIME bundle into a panel with
11
+ * the same renderers JupyterLab uses for cell output — no kernel, no file on
12
+ * disk. Repeated calls with the same `id` reuse one panel.
25
13
  */
26
14
  const plugin = {
27
15
  id: PLUGIN_ID,
@@ -32,7 +20,6 @@ const plugin = {
32
20
  activate: (app, rendermime, labShell, palette, translator) => {
33
21
  const { commands } = app;
34
22
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
35
- // One reusable panel per `id`, so repeated shows refresh in place.
36
23
  const panels = new Map();
37
24
  commands.addCommand(SHOW_COMMAND, {
38
25
  label: trans.__('Show Rich Output'),
@@ -89,16 +76,14 @@ const plugin = {
89
76
  }
90
77
  const model = rendermime.createModel({ data: bundle, trusted: true });
91
78
  const renderer = rendermime.createRenderer(chosen);
92
- // A scrollable Panel, not a MainAreaWidget: MainAreaWidget does not lay
93
- // out its content in the side area, and the panel needs to scroll when
94
- // the content is taller than it (style/showOutput.css).
79
+ // Not a MainAreaWidget: it does not lay out its content in the side
80
+ // area; the panel scrolls via style/showOutput.css.
95
81
  const widget = new Panel();
96
82
  widget.addClass('jp-xtralab-ShowPanel');
97
83
  widget.id = `xtralab-show-${id}`;
98
84
  widget.title.label = label;
99
85
  widget.title.closable = true;
100
86
  widget.addWidget(renderer);
101
- // Replace any existing panel with this id so shows refresh in place.
102
87
  const existing = panels.get(id);
103
88
  if (existing && !existing.isDisposed) {
104
89
  existing.dispose();
@@ -110,8 +95,7 @@ const plugin = {
110
95
  }
111
96
  });
112
97
  // Attach before rendering so renderers that need layout (Vega) size
113
- // correctly. Default to the side area so the editor keeps full width;
114
- // only split the document area when `area` is "main".
98
+ // correctly.
115
99
  if (area === 'main') {
116
100
  labShell.add(widget, 'main', {
117
101
  mode,
@@ -1,18 +1,9 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Lets the user toggle individual left-sidebar tabs from
4
- * View > Appearance > Left Sidebar. Removing a widget from a `LabShell`
5
- * side area is done by setting `widget.parent = null`: the StackedPanel
6
- * emits `widgetRemoved`, which the SideBarHandler responds to by removing
7
- * the matching tab. The widget instance is preserved, so we can re-add it
8
- * later via `labShell.add` with its original rank.
9
- *
10
- * The menu placement is contributed declaratively in
11
- * `schema/sidebar.json`, so this plugin only registers commands and
12
- * applies state — it does not touch `IMainMenu` directly.
13
- *
14
- * The preference is persisted to `xtralab:sidebar` settings so the choice
15
- * survives reloads.
3
+ * Toggles individual sidebar tabs from View > Appearance > Sidebars. Hiding
4
+ * detaches the widget while keeping the instance for a later re-add; which
5
+ * side it came from is only tracked in-memory, so after a reload a re-shown
6
+ * tab lands on the left. Menu placement is declared in schema/sidebar.json.
16
7
  */
17
8
  declare const plugin: JupyterFrontEndPlugin<void>;
18
9
  export default plugin;
@@ -3,6 +3,27 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry';
3
3
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
4
4
  const PLUGIN_ID = 'xtralab:sidebar';
5
5
  const TARGETS = [
6
+ {
7
+ id: 'xtralab-running-terminals',
8
+ rank: 1,
9
+ settingKey: 'showTerminals',
10
+ command: 'xtralab:toggle-terminals',
11
+ label: trans => trans.__('Terminals')
12
+ },
13
+ {
14
+ id: 'xtralab:file-browser',
15
+ rank: 2,
16
+ settingKey: 'showFileBrowser',
17
+ command: 'xtralab:toggle-file-browser',
18
+ label: trans => trans.__('File Browser')
19
+ },
20
+ {
21
+ id: 'jp-git-sessions',
22
+ rank: 200,
23
+ settingKey: 'showGitPanel',
24
+ command: 'xtralab:toggle-git-panel',
25
+ label: trans => trans.__('Git')
26
+ },
6
27
  {
7
28
  id: 'filebrowser',
8
29
  rank: 100,
@@ -16,55 +37,56 @@ const TARGETS = [
16
37
  settingKey: 'showRunningSessions',
17
38
  command: 'xtralab:toggle-running-sessions',
18
39
  label: trans => trans.__('Running Terminals and Kernels')
40
+ },
41
+ {
42
+ id: 'jp-search-replace',
43
+ rank: 900,
44
+ settingKey: 'showSearchReplace',
45
+ command: 'xtralab:toggle-search-replace',
46
+ label: trans => trans.__('Search and Replace')
19
47
  }
20
48
  ];
21
49
  /**
22
- * Lets the user toggle individual left-sidebar tabs from
23
- * View > Appearance > Left Sidebar. Removing a widget from a `LabShell`
24
- * side area is done by setting `widget.parent = null`: the StackedPanel
25
- * emits `widgetRemoved`, which the SideBarHandler responds to by removing
26
- * the matching tab. The widget instance is preserved, so we can re-add it
27
- * later via `labShell.add` with its original rank.
28
- *
29
- * The menu placement is contributed declaratively in
30
- * `schema/sidebar.json`, so this plugin only registers commands and
31
- * applies state — it does not touch `IMainMenu` directly.
32
- *
33
- * The preference is persisted to `xtralab:sidebar` settings so the choice
34
- * survives reloads.
50
+ * Toggles individual sidebar tabs from View > Appearance > Sidebars. Hiding
51
+ * detaches the widget while keeping the instance for a later re-add; which
52
+ * side it came from is only tracked in-memory, so after a reload a re-shown
53
+ * tab lands on the left. Menu placement is declared in schema/sidebar.json.
35
54
  */
36
55
  const plugin = {
37
56
  id: PLUGIN_ID,
38
- description: 'Toggle visibility of selected sidebar tabs (default file browser, running sessions) from the View menu.',
57
+ description: 'Toggle visibility of individual sidebar tabs from the View menu.',
39
58
  autoStart: true,
40
59
  requires: [ILabShell],
41
60
  optional: [ISettingRegistry, ITranslator],
42
61
  activate: async (app, labShell, settingRegistry, translator) => {
43
62
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
44
63
  const { commands } = app;
45
- // Cache widget instances so we can re-add them after `widget.parent =
46
- // null` has detached them from the sidebar. The widget is no longer
47
- // reachable through `labShell.widgets('left')` once removed, so a
48
- // missing cache entry would leave us unable to bring the tab back.
64
+ // Detached widgets are unreachable via `labShell.widgets(...)`; keep
65
+ // instances here so hidden tabs can be re-added.
49
66
  const widgetCache = new Map();
50
- const findInSidebar = (id) => {
51
- for (const w of labShell.widgets('left')) {
52
- if (w.id === id) {
53
- return w;
67
+ const hiddenFrom = new Map();
68
+ const findInSidebars = (id) => {
69
+ for (const area of ['left', 'right']) {
70
+ for (const widget of labShell.widgets(area)) {
71
+ if (widget.id === id) {
72
+ return { widget, area };
73
+ }
54
74
  }
55
75
  }
56
76
  return null;
57
77
  };
78
+ const locate = (id) => { var _a, _b; return (_b = (_a = findInSidebars(id)) === null || _a === void 0 ? void 0 : _a.area) !== null && _b !== void 0 ? _b : null; };
58
79
  const captureWidget = (id) => {
80
+ var _a;
59
81
  const cached = widgetCache.get(id);
60
82
  if (cached && !cached.isDisposed) {
61
83
  return cached;
62
84
  }
63
- const found = findInSidebar(id);
85
+ const found = findInSidebars(id);
64
86
  if (found) {
65
- widgetCache.set(id, found);
87
+ widgetCache.set(id, found.widget);
66
88
  }
67
- return found;
89
+ return (_a = found === null || found === void 0 ? void 0 : found.widget) !== null && _a !== void 0 ? _a : null;
68
90
  };
69
91
  let settings = null;
70
92
  const readPreference = (target) => {
@@ -75,22 +97,27 @@ const plugin = {
75
97
  return typeof value === 'boolean' ? value : true;
76
98
  };
77
99
  const apply = (target) => {
100
+ var _a;
78
101
  const widget = captureWidget(target.id);
79
102
  if (!widget) {
80
- // The upstream widget hasn't been added yet (or the extension is
81
- // disabled). Nothing to do — the next time `apply` runs and finds
82
- // it, the preference will take effect.
83
103
  return;
84
104
  }
85
- const present = findInSidebar(target.id) !== null;
105
+ const area = locate(target.id);
106
+ if (!area && widget.parent !== null) {
107
+ // Attached outside the sidebars — leave it alone.
108
+ return;
109
+ }
86
110
  const wantPresent = readPreference(target);
87
- if (wantPresent && !present) {
88
- labShell.add(widget, 'left', { rank: target.rank });
111
+ if (wantPresent && !area) {
112
+ labShell.add(widget, (_a = hiddenFrom.get(target.id)) !== null && _a !== void 0 ? _a : 'left', {
113
+ rank: target.rank
114
+ });
115
+ hiddenFrom.delete(target.id);
89
116
  }
90
- else if (!wantPresent && present) {
91
- // Setting `parent = null` triggers SideBarHandler's
92
- // `_onWidgetRemoved`, which strips the tab while leaving the
93
- // widget instance alive for a later re-add.
117
+ else if (!wantPresent && area) {
118
+ hiddenFrom.set(target.id, area);
119
+ // Detaching makes the SideBarHandler remove the tab while the
120
+ // widget instance stays alive for a later re-add.
94
121
  widget.parent = null;
95
122
  }
96
123
  };
@@ -103,23 +130,26 @@ const plugin = {
103
130
  commands.addCommand(target.command, {
104
131
  label: target.label(trans),
105
132
  isToggled: () => readPreference(target),
133
+ isVisible: () => {
134
+ // List only tabs this plugin can manage: in a sidebar, or
135
+ // hidden by this plugin (`parent === null`).
136
+ const widget = captureWidget(target.id);
137
+ return (widget !== null &&
138
+ (widget.parent === null || locate(target.id) !== null));
139
+ },
106
140
  execute: async () => {
107
141
  const next = !readPreference(target);
108
142
  if (settings) {
109
143
  try {
110
144
  await settings.set(target.settingKey, next);
111
- // `settings.changed` will fire and drive `apply` + the
112
- // command-state refresh. Returning here keeps the toggle's
113
- // single source of truth in the settings registry.
145
+ // `settings.changed` drives `apply` and the state refresh.
114
146
  return;
115
147
  }
116
148
  catch (reason) {
117
149
  console.error(`xtralab: failed to persist ${target.settingKey}`, reason);
118
150
  }
119
151
  }
120
- // No settings registry available (or the write failed): fall
121
- // back to applying the change in-memory so the toggle still
122
- // works for the current session.
152
+ // No settings (or the write failed): apply in-memory for this session.
123
153
  apply(target);
124
154
  commands.notifyCommandChanged(target.command);
125
155
  }
@@ -140,11 +170,8 @@ const plugin = {
140
170
  }
141
171
  }
142
172
  void app.restored.then(() => {
143
- // Capture references first so a hidden-on-startup widget is still
144
- // recoverable later. `apply` would also do the lookup, but only on
145
- // the targets that need to stay visible — by capturing every target
146
- // here we keep the cache populated for both directions of the
147
- // toggle.
173
+ // Capture every widget before applying, so tabs hidden on startup
174
+ // stay recoverable.
148
175
  for (const target of TARGETS) {
149
176
  captureWidget(target.id);
150
177
  }
@@ -1,15 +1,9 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Turns the notifications coding agents already emit (OSC 9, OSC 777, the bell)
4
- * into desktop notifications. JupyterLab's xterm renders these sequences but
5
- * never forwards them to the OS, so this plugin hooks each terminal and bridges
6
- * them through `window.xtralab.notify` (desktop) or the web Notifications API.
7
- *
8
- * A notification is suppressed while its terminal is the focused, active tab,
9
- * and throttled per terminal. The desktop shell advertises
10
- * `TERM_PROGRAM=iTerm.app` so agents emit OSC 9 in the first place, and the
11
- * session name is forwarded so clicking a notification focuses the terminal
12
- * that fired it.
3
+ * Turns the OSC 9 / OSC 777 / bell sequences agents emit into desktop
4
+ * notifications via `window.xtralab.notify` (desktop) or the web Notifications
5
+ * API — JupyterLab's xterm renders them but never forwards them to the OS.
6
+ * The desktop shell advertises `TERM_PROGRAM=iTerm.app` so agents emit OSC 9.
13
7
  */
14
8
  declare const plugin: JupyterFrontEndPlugin<void>;
15
9
  export default plugin;