xtralab 0.15.0 → 0.15.2

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 (237) hide show
  1. package/README.md +65 -30
  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.js +15 -0
  29. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  30. package/lib/editorBreadcrumbs/index.js +10 -6
  31. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  32. package/lib/editorBreadcrumbs/widget.js +11 -17
  33. package/lib/editorIndent/index.d.ts +4 -12
  34. package/lib/editorIndent/index.js +8 -23
  35. package/lib/fileBrowser/commands.d.ts +29 -19
  36. package/lib/fileBrowser/commands.js +83 -97
  37. package/lib/fileBrowser/contents.d.ts +3 -0
  38. package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
  39. package/lib/fileBrowser/dragAndDrop.js +2 -7
  40. package/lib/fileBrowser/fileBrowser.d.ts +19 -9
  41. package/lib/fileBrowser/fileBrowser.js +70 -222
  42. package/lib/fileBrowser/gitStatus.d.ts +16 -10
  43. package/lib/fileBrowser/gitStatus.js +31 -30
  44. package/lib/fileBrowser/gitignore.d.ts +4 -17
  45. package/lib/fileBrowser/gitignore.js +4 -25
  46. package/lib/fileBrowser/icons.d.ts +7 -17
  47. package/lib/fileBrowser/icons.js +25 -80
  48. package/lib/fileBrowser/index.js +2 -4
  49. package/lib/fileBrowser/widget.d.ts +141 -48
  50. package/lib/fileBrowser/widget.js +102 -4
  51. package/lib/fileTypeIcons/index.js +0 -10
  52. package/lib/git/api.d.ts +5 -13
  53. package/lib/git/api.js +10 -36
  54. package/lib/git/askRequest.d.ts +4 -8
  55. package/lib/git/askRequest.js +6 -20
  56. package/lib/git/commands.d.ts +45 -5
  57. package/lib/git/commands.js +20 -13
  58. package/lib/git/diffModel.js +4 -0
  59. package/lib/git/diffPreferences.d.ts +90 -0
  60. package/lib/git/diffPreferences.js +107 -0
  61. package/lib/git/diffProvider.js +10 -2
  62. package/lib/git/diffSurface.d.ts +64 -63
  63. package/lib/git/diffSurface.js +72 -213
  64. package/lib/git/diffTheme.d.ts +7 -0
  65. package/lib/git/diffTheme.js +23 -0
  66. package/lib/git/diffWidget.d.ts +70 -14
  67. package/lib/git/diffWidget.js +100 -94
  68. package/lib/git/imageDiff.d.ts +14 -6
  69. package/lib/git/imageDiff.js +19 -75
  70. package/lib/git/index.d.ts +0 -3
  71. package/lib/git/index.js +44 -10
  72. package/lib/git/notebookDiff.d.ts +110 -33
  73. package/lib/git/notebookDiff.js +55 -197
  74. package/lib/git/tokens.d.ts +73 -31
  75. package/lib/git/tokens.js +2 -3
  76. package/lib/highlight/index.d.ts +4 -10
  77. package/lib/highlight/index.js +10 -41
  78. package/lib/index.d.ts +0 -9
  79. package/lib/index.js +0 -9
  80. package/lib/launcher/agents.d.ts +55 -51
  81. package/lib/launcher/agents.js +15 -57
  82. package/lib/launcher/availability.d.ts +3 -9
  83. package/lib/launcher/availability.js +3 -9
  84. package/lib/launcher/commands.d.ts +7 -32
  85. package/lib/launcher/commands.js +14 -45
  86. package/lib/launcher/dashboard.d.ts +19 -24
  87. package/lib/launcher/dashboard.js +21 -67
  88. package/lib/launcher/editorRegistry.d.ts +10 -22
  89. package/lib/launcher/editorRegistry.js +14 -18
  90. package/lib/launcher/editors.d.ts +39 -45
  91. package/lib/launcher/editors.js +8 -34
  92. package/lib/launcher/icons.d.ts +0 -12
  93. package/lib/launcher/icons.js +5 -39
  94. package/lib/launcher/index.js +12 -55
  95. package/lib/launcher/invocation.d.ts +4 -10
  96. package/lib/launcher/invocation.js +7 -22
  97. package/lib/launcher/registry.d.ts +9 -7
  98. package/lib/launcher/registry.js +9 -7
  99. package/lib/launcher/tokens.d.ts +10 -22
  100. package/lib/launcher/tokens.js +5 -8
  101. package/lib/menuBar/index.d.ts +3 -10
  102. package/lib/menuBar/index.js +11 -28
  103. package/lib/menus/index.d.ts +3 -9
  104. package/lib/menus/index.js +14 -30
  105. package/lib/omnibox/files.d.ts +2 -4
  106. package/lib/omnibox/files.js +5 -11
  107. package/lib/omnibox/index.d.ts +4 -14
  108. package/lib/omnibox/index.js +9 -27
  109. package/lib/omnibox/model.d.ts +38 -10
  110. package/lib/omnibox/model.js +15 -33
  111. package/lib/omnibox/recents.d.ts +15 -17
  112. package/lib/omnibox/recents.js +12 -17
  113. package/lib/omnibox/tokens.d.ts +5 -10
  114. package/lib/omnibox/tokens.js +2 -6
  115. package/lib/omnibox/widget.d.ts +11 -7
  116. package/lib/omnibox/widget.js +8 -16
  117. package/lib/searchReplace/index.d.ts +4 -20
  118. package/lib/searchReplace/index.js +7 -27
  119. package/lib/showOutput/index.d.ts +3 -12
  120. package/lib/showOutput/index.js +6 -22
  121. package/lib/sidebar/index.d.ts +4 -7
  122. package/lib/sidebar/index.js +5 -12
  123. package/lib/terminalNotifications/index.d.ts +4 -10
  124. package/lib/terminalNotifications/index.js +6 -20
  125. package/lib/terminals/agentTerminals.d.ts +12 -5
  126. package/lib/terminals/agentTerminals.js +25 -44
  127. package/lib/terminals/detection.d.ts +4 -8
  128. package/lib/terminals/detection.js +4 -8
  129. package/lib/terminals/index.d.ts +0 -35
  130. package/lib/terminals/index.js +12 -101
  131. package/lib/terminals/model.d.ts +64 -139
  132. package/lib/terminals/model.js +80 -220
  133. package/lib/terminals/tokens.d.ts +11 -22
  134. package/lib/terminals/tokens.js +2 -2
  135. package/lib/terminals/widget.d.ts +43 -13
  136. package/lib/terminals/widget.js +33 -18
  137. package/lib/topBar/icons.d.ts +3 -8
  138. package/lib/topBar/icons.js +3 -8
  139. package/lib/topBar/index.d.ts +4 -11
  140. package/lib/topBar/index.js +10 -38
  141. package/lib/walkthrough/index.d.ts +4 -9
  142. package/lib/walkthrough/index.js +6 -20
  143. package/lib/walkthrough/panel.d.ts +40 -9
  144. package/lib/walkthrough/panel.js +4 -10
  145. package/package.json +5 -3
  146. package/schema/git-diff-preferences.json +42 -0
  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 +15 -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 +119 -96
  165. package/src/fileBrowser/contents.ts +3 -0
  166. package/src/fileBrowser/dragAndDrop.ts +17 -7
  167. package/src/fileBrowser/fileBrowser.tsx +86 -230
  168. package/src/fileBrowser/gitStatus.ts +36 -33
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +2 -4
  172. package/src/fileBrowser/widget.tsx +171 -50
  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 +83 -11
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffPreferences.ts +192 -0
  179. package/src/git/diffProvider.tsx +11 -2
  180. package/src/git/diffSurface.tsx +165 -286
  181. package/src/git/diffTheme.ts +24 -0
  182. package/src/git/diffWidget.tsx +194 -134
  183. package/src/git/imageDiff.tsx +56 -84
  184. package/src/git/index.ts +54 -10
  185. package/src/git/notebookDiff.tsx +214 -240
  186. package/src/git/tokens.ts +73 -31
  187. package/src/highlight/index.ts +10 -41
  188. package/src/index.ts +0 -9
  189. package/src/launcher/agents.ts +64 -95
  190. package/src/launcher/availability.ts +3 -9
  191. package/src/launcher/commands.ts +14 -51
  192. package/src/launcher/dashboard.tsx +49 -88
  193. package/src/launcher/editorRegistry.ts +21 -29
  194. package/src/launcher/editors.ts +39 -58
  195. package/src/launcher/icons.ts +5 -39
  196. package/src/launcher/index.ts +12 -55
  197. package/src/launcher/invocation.ts +7 -22
  198. package/src/launcher/registry.ts +9 -7
  199. package/src/launcher/tokens.ts +10 -22
  200. package/src/menuBar/index.ts +11 -28
  201. package/src/menus/index.ts +14 -30
  202. package/src/omnibox/files.ts +10 -14
  203. package/src/omnibox/index.ts +9 -27
  204. package/src/omnibox/model.ts +50 -38
  205. package/src/omnibox/recents.ts +17 -22
  206. package/src/omnibox/tokens.ts +5 -10
  207. package/src/omnibox/widget.tsx +14 -19
  208. package/src/searchReplace/index.ts +7 -27
  209. package/src/showOutput/index.ts +6 -22
  210. package/src/sidebar/index.ts +20 -12
  211. package/src/terminalNotifications/index.ts +42 -25
  212. package/src/terminals/agentTerminals.ts +25 -49
  213. package/src/terminals/detection.ts +4 -8
  214. package/src/terminals/index.ts +12 -101
  215. package/src/terminals/model.ts +130 -246
  216. package/src/terminals/tokens.ts +11 -22
  217. package/src/terminals/widget.tsx +48 -31
  218. package/src/topBar/icons.ts +3 -8
  219. package/src/topBar/index.ts +21 -44
  220. package/src/walkthrough/index.ts +6 -20
  221. package/src/walkthrough/panel.ts +40 -10
  222. package/style/about.css +2 -7
  223. package/style/askAgent.css +8 -40
  224. package/style/base.css +9 -45
  225. package/style/chrome.css +108 -308
  226. package/style/commandBar.css +4 -20
  227. package/style/customPanel.css +1 -4
  228. package/style/git.css +26 -186
  229. package/style/highlight.css +1 -3
  230. package/style/launcher.css +6 -70
  231. package/style/omnibox.css +2 -14
  232. package/style/showOutput.css +0 -6
  233. package/style/sidebar.css +29 -9
  234. package/style/tabs.css +5 -13
  235. package/style/terminals.css +6 -53
  236. package/style/topBar.css +2 -22
  237. package/style/walkthrough.css +0 -8
@@ -10,15 +10,9 @@ import type { Menu, MenuBar, Widget } from '@lumino/widgets';
10
10
  const PLUGIN_ID = 'xtralab:menus';
11
11
 
12
12
  /**
13
- * Hide the "Run" and "Kernel" top-level menus while no kernel-using widget
14
- * is open in the main area. The agent-first launcher does not use kernels,
15
- * so showing those menus on a fresh workspace surfaces commands the user
16
- * cannot meaningfully invoke; remove them until a notebook or console is
17
- * opened, then restore them at their original ranks.
18
- *
19
- * Detection is duck-typed on `sessionContext`: NotebookPanel and ConsolePanel
20
- * both expose it, which avoids pulling in `@jupyterlab/notebook` and
21
- * `@jupyterlab/console` just to identify their widgets.
13
+ * Hide the Run and Kernel menus while no kernel-using widget is open in the
14
+ * main area, then restore them at their original ranks — the agent-first
15
+ * launcher does not use kernels, so a fresh workspace should not surface them.
22
16
  */
23
17
  const plugin: JupyterFrontEndPlugin<void> = {
24
18
  id: PLUGIN_ID,
@@ -31,17 +25,14 @@ const plugin: JupyterFrontEndPlugin<void> = {
31
25
  mainMenu: IMainMenu,
32
26
  labShell: ILabShell
33
27
  ): void => {
34
- // The interface types (IRunMenu, IKernelMenu) extend IRankedMenu, but
35
- // the concrete instances are RankedMenu/Menu — cast so we can hand them
36
- // to MenuBar.removeMenu and IMainMenu.addMenu, which expect a `Menu`.
28
+ // The interface types extend IRankedMenu but the concrete instances are
29
+ // RankedMenu/Menu; cast for the MenuBar methods, which expect a `Menu`.
37
30
  const menuBar = mainMenu as unknown as MenuBar;
38
31
  const runMenu = mainMenu.runMenu as unknown as Menu;
39
32
  const kernelMenu = mainMenu.kernelMenu as unknown as Menu;
40
33
 
41
- // Capture the schema-assigned ranks so the menus reappear in their
42
- // original slots between View (rank 3) and Tabs (rank 500). Falling
43
- // back to the hard-coded defaults from `MainMenu` keeps things sane
44
- // if the schema ever omits the rank.
34
+ // Schema-assigned ranks so the menus reappear in their original slots;
35
+ // fall back to MainMenu's hard-coded defaults if the schema omits them.
45
36
  const runRank = mainMenu.runMenu.rank ?? 4;
46
37
  const kernelRank = mainMenu.kernelMenu.rank ?? 5;
47
38
 
@@ -51,11 +42,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
51
42
  const update = (): void => {
52
43
  const show = hasKernelWidget(labShell);
53
44
  if (show) {
54
- // Don't go through `mainMenu.addMenu`: it picks the insertion index
55
- // from a private `_items` array that `removeMenu` doesn't update,
56
- // so re-adding lands the menu at the wrong position. Insert
57
- // directly on the MenuBar by walking current menus and finding
58
- // the first sibling with a higher rank.
45
+ // Not `mainMenu.addMenu`: its insertion index comes from a private
46
+ // `_items` array that `removeMenu` doesn't update, misplacing re-adds.
59
47
  if (!isPresent(runMenu)) {
60
48
  insertByRank(menuBar, runMenu, runRank);
61
49
  }
@@ -80,11 +68,9 @@ const plugin: JupyterFrontEndPlugin<void> = {
80
68
  };
81
69
 
82
70
  /**
83
- * True iff any widget in the main area exposes a `sessionContext` — the
84
- * shared marker for NotebookPanel, ConsolePanel, and kernel-attached file
85
- * editors. We don't import their concrete types because that would drag
86
- * `@jupyterlab/notebook` and `@jupyterlab/console` into xtralab's bundle
87
- * just to do an `instanceof` check.
71
+ * True iff any main-area widget exposes a `sessionContext` (NotebookPanel,
72
+ * ConsolePanel, kernel-attached editors) — duck-typed to keep
73
+ * `@jupyterlab/notebook` and `@jupyterlab/console` out of the bundle.
88
74
  */
89
75
  function hasKernelWidget(labShell: ILabShell): boolean {
90
76
  for (const widget of labShell.widgets('main')) {
@@ -103,10 +89,8 @@ function hasSessionContext(widget: Widget): boolean {
103
89
  }
104
90
 
105
91
  /**
106
- * Insert `menu` into `menuBar` at the position dictated by `rank`, using
107
- * the existing menus' ranks to find the slot. Menus without a numeric
108
- * rank are treated as +Infinity so they sink to the end (matching the
109
- * convention in `MenuFactory.createMenus`).
92
+ * Insert `menu` at the slot dictated by `rank`; rank-less menus count as
93
+ * +Infinity and sink to the end, matching `MenuFactory.createMenus`.
110
94
  */
111
95
  function insertByRank(menuBar: MenuBar, menu: Menu, rank: number): void {
112
96
  const menus = menuBar.menus;
@@ -2,23 +2,21 @@ import { URLExt } from '@jupyterlab/coreutils';
2
2
  import { ServerConnection } from '@jupyterlab/services';
3
3
 
4
4
  /**
5
- * Shape of the `jupyterlab-quickopen` `api/files` response: a map from each
6
- * directory (relative to the server root, `""` for the root itself) to the
7
- * bare filenames it contains.
5
+ * Shape of the `jupyterlab-quickopen` `api/files` response: directory
6
+ * (relative to the server root, `""` for the root) to bare filenames.
8
7
  */
9
8
  interface IQuickOpenContents {
9
+ /**
10
+ * Filenames grouped by their containing directory.
11
+ */
10
12
  contents: { [dir: string]: string[] };
11
13
  }
12
14
 
13
15
  /**
14
16
  * Fetch and flatten the workspace file list from `jupyterlab-quickopen`'s
15
- * server endpoint (`GET {base}/jupyterlab-quickopen/api/files`).
16
- *
17
- * We reuse quickopen's endpoint rather than walking the contents API so the
18
- * listing honors the same server-side `.gitignore` filtering quickopen does
19
- * (xtralab ships `respectGitignore: true`) in a single request. The endpoint
20
- * only exists when quickopen's server extension is installed and the server
21
- * reads a local filesystem; callers treat a rejection as "no file results".
17
+ * server endpoint, which applies server-side `.gitignore` filtering in one
18
+ * request. Rejects when the server extension is absent; callers treat that as
19
+ * "no file results".
22
20
  */
23
21
  async function fetchWorkspaceFiles(respectGitignore = true): Promise<string[]> {
24
22
  const settings = ServerConnection.makeSettings();
@@ -54,10 +52,8 @@ const CACHE_TTL_MS = 15_000;
54
52
  let cache: { at: number; files: Promise<string[]> } | null = null;
55
53
 
56
54
  /**
57
- * The workspace file list, cached briefly so reopening the omnibox in quick
58
- * succession reuses one scan. A failed fetch (e.g. quickopen's server
59
- * extension is absent) resolves to an empty list so file search degrades to
60
- * "no results" instead of surfacing an error.
55
+ * The workspace file list, cached briefly so reopening the omnibox reuses one
56
+ * scan. A failed fetch resolves to an empty list.
61
57
  */
62
58
  export function loadWorkspaceFiles(): Promise<string[]> {
63
59
  const now = Date.now();
@@ -17,20 +17,10 @@ import { OmniboxWidget } from './widget';
17
17
  const PLUGIN_ID = 'xtralab:omnibox';
18
18
 
19
19
  /**
20
- * The omnibox: a single launcher overlay that fuzzy-searches workspace files
21
- * (via jupyterlab-quickopen's gitignore-aware endpoint) and JupyterLab
22
- * commands (the command palette's entries plus registry commands the palette
23
- * doesn't list), and routes a typed prompt to one of the configured agents
24
- * (running it in a fresh terminal through the launcher's
25
- * `xtralab:start-agent:<id>` commands). It is opened by the top-bar command
26
- * bar.
27
- *
28
- * Commands run and files opened through the overlay are remembered (persisted
29
- * in the state database) and offered again at the top while the query is
30
- * empty; the `maxNumberRecents` setting caps how many of each are kept.
31
- *
32
- * The agent rows come from the launcher's `IAgentRegistry`; when the launcher
33
- * is disabled the omnibox still searches files and commands.
20
+ * The omnibox: a launcher overlay that fuzzy-searches workspace files and
21
+ * commands and routes a typed prompt to a configured agent in a fresh
22
+ * terminal. Recently used commands and files persist in the state database;
23
+ * without the launcher it still searches files and commands.
34
24
  */
35
25
  const plugin: JupyterFrontEndPlugin<IOmnibox> = {
36
26
  id: PLUGIN_ID,
@@ -57,12 +47,8 @@ const plugin: JupyterFrontEndPlugin<IOmnibox> = {
57
47
  const trans = (translator ?? nullTranslator).load('jupyterlab');
58
48
  const placeholder = trans.__('Search files and commands, or ask an agent…');
59
49
 
60
- // The palette's items carry per-item args (e.g. one "Use Theme: …" per
61
- // theme) that a raw command-registry scan can't see, but `ICommandPalette`
62
- // exposes no item list and the widget itself is unreachable through the
63
- // shell (`LabShell.add` defers it until layout restore, after which the
64
- // modal setting re-parents it out of the left area). The wrapper's
65
- // `_palette` field is private in TypeScript only, so read it guarded.
50
+ // `ICommandPalette` exposes no item list and the palette widget is
51
+ // unreachable through the shell; duck-read the wrapper's TS-only-private `_palette`.
66
52
  const paletteItems = (): ReadonlyArray<CommandPalette.IItem> => {
67
53
  const widget = (palette as unknown as { _palette?: unknown } | null)
68
54
  ?._palette;
@@ -71,10 +57,8 @@ const plugin: JupyterFrontEndPlugin<IOmnibox> = {
71
57
 
72
58
  const recents = new OmniboxRecents({ state });
73
59
 
74
- // Restore the persisted recents only after the configured cap is known:
75
- // `restore` trims to `maxItems`, so restoring at the constructor default
76
- // would permanently drop entries beyond it whenever the user configured a
77
- // larger cap and the state fetch won the race against the settings load.
60
+ // Restore only after the configured cap is known: `restore` trims to
61
+ // `maxItems`, so restoring at the default could permanently drop entries.
78
62
  if (settingRegistry) {
79
63
  void settingRegistry
80
64
  .load(PLUGIN_ID)
@@ -110,7 +94,7 @@ const plugin: JupyterFrontEndPlugin<IOmnibox> = {
110
94
 
111
95
  const open = (query?: string): void => {
112
96
  // Reopen fresh each time so the input resets and the agent snapshot is
113
- // current; the file list is cached separately (see files.ts).
97
+ // current; the file list stays cached (files.ts).
114
98
  close();
115
99
  const widget = new OmniboxWidget({
116
100
  commands,
@@ -136,7 +120,6 @@ const plugin: JupyterFrontEndPlugin<IOmnibox> = {
136
120
  label: trans.__('Search…'),
137
121
  caption: trans.__('Search files and commands, or ask an agent'),
138
122
  execute: args => {
139
- // Toggle: a second press (e.g. of the keyboard shortcut) closes it.
140
123
  if (current && !current.isDisposed) {
141
124
  close();
142
125
  return;
@@ -146,7 +129,6 @@ const plugin: JupyterFrontEndPlugin<IOmnibox> = {
146
129
  }
147
130
  });
148
131
 
149
- // Open the omnibox with Cmd/Ctrl+K, the common "command center" chord.
150
132
  commands.addKeyBinding({
151
133
  command: OMNIBOX_OPEN_COMMAND,
152
134
  keys: ['Accel K'],
@@ -11,15 +11,15 @@ import { agentCommandId } from '../launcher/tokens';
11
11
 
12
12
  import type { OmniboxRecents, RecentKind } from './recents';
13
13
 
14
- /**
15
- * The source a result row came from, used to group rows under a header.
16
- */
17
14
  type OmniboxItemKind = 'command' | 'file' | 'agent';
18
15
 
19
16
  /**
20
17
  * A single result row, with the action it runs when chosen.
21
18
  */
22
19
  export interface IOmniboxItem {
20
+ /**
21
+ * The source category of the row: command, file, or agent.
22
+ */
23
23
  kind: OmniboxItemKind;
24
24
  /**
25
25
  * Stable React key.
@@ -55,49 +55,73 @@ interface IOmniboxSections {
55
55
  * Recently used commands and files, shown while the term is empty.
56
56
  */
57
57
  recent: IOmniboxItem[];
58
+ /**
59
+ * Commands matching the term.
60
+ */
58
61
  commands: IOmniboxItem[];
62
+ /**
63
+ * Workspace files matching the term.
64
+ */
59
65
  files: IOmniboxItem[];
66
+ /**
67
+ * Per-agent "Ask" rows carrying the term as a prompt.
68
+ */
60
69
  agents: IOmniboxItem[];
61
70
  }
62
71
 
72
+ /**
73
+ * Inputs for {@link computeSections}.
74
+ */
63
75
  interface IComputeOptions {
76
+ /**
77
+ * Raw query text from the input.
78
+ */
64
79
  query: string;
80
+ /**
81
+ * The command registry used to look up and execute commands.
82
+ */
65
83
  commands: CommandRegistry;
66
84
  /**
67
- * The command palette's items, whose labels are computed with each item's
68
- * args — the only form in which entries like "Use Theme: …" exist.
85
+ * Palette items, whose labels are computed with each item's args — the only
86
+ * form in which entries like "Use Theme: …" exist.
69
87
  */
70
88
  paletteItems: ReadonlyArray<CommandPalette.IItem>;
89
+ /**
90
+ * The document registry used to pick file icons.
91
+ */
71
92
  docRegistry: DocumentRegistry;
93
+ /**
94
+ * The available agents; prompt-capable ones get "Ask" rows.
95
+ */
72
96
  agents: IAgent[];
97
+ /**
98
+ * Workspace-relative file paths to match against.
99
+ */
73
100
  files: string[];
74
101
  /**
75
102
  * Recently-used tracker; `null` disables the recent rows and recording.
76
103
  */
77
104
  recents: OmniboxRecents | null;
105
+ /**
106
+ * Translation bundle for row labels.
107
+ */
78
108
  trans: TranslationBundle;
79
109
  }
80
110
 
81
111
  const COMMAND_LIMIT = 7;
82
112
  const FILE_LIMIT = 10;
83
113
 
84
- /**
85
- * Prefixes that narrow the search to a single source: a leading `>` to
86
- * commands, a leading `/` to files (relative paths never start with `/`, so it
87
- * is unambiguous). Without a prefix, every source is searched.
88
- */
89
114
  const COMMAND_PREFIX = '>';
115
+ // Unambiguous: workspace-relative paths never start with '/'.
90
116
  const FILE_PREFIX = '/';
91
117
 
92
118
  type Mode = 'all' | 'commands' | 'files';
93
119
 
94
120
  /**
95
121
  * Build the grouped result set for a query. A leading `>` searches only
96
- * commands and a leading `/` only files; otherwise commands and files are
97
- * fuzzy-matched and every prompt-capable agent is offered an "Ask" row
98
- * carrying the query as its prompt. An empty term yields the recently used
99
- * rows for the active mode instead (and the widget shows a hint when there
100
- * are none).
122
+ * commands, `/` only files; otherwise every prompt-capable agent also gets an
123
+ * "Ask" row carrying the query as its prompt. An empty term yields the
124
+ * recently used rows for the active mode.
101
125
  */
102
126
  export function computeSections(options: IComputeOptions): IOmniboxSections {
103
127
  const { query, commands, agents, trans } = options;
@@ -126,14 +150,12 @@ export function computeSections(options: IComputeOptions): IOmniboxSections {
126
150
  recent: [],
127
151
  commands: mode === 'files' ? [] : matchCommands(options, term),
128
152
  files: mode === 'commands' ? [] : matchFiles(options, term),
129
- // Agents only in the unprefixed view; the prompt is the full typed query.
130
153
  agents: mode === 'all' ? buildAgentItems(commands, agents, term, trans) : []
131
154
  };
132
155
  }
133
156
 
134
157
  /**
135
- * Run a command and record the use on success, so failed commands never enter
136
- * the recents list.
158
+ * Run a command and record the use on success, so failures never enter the recents.
137
159
  */
138
160
  function executeCommand(
139
161
  commands: CommandRegistry,
@@ -151,9 +173,6 @@ function executeCommand(
151
173
  });
152
174
  }
153
175
 
154
- /**
155
- * Open a file and record the use on success.
156
- */
157
176
  function openFile(
158
177
  commands: CommandRegistry,
159
178
  recents: OmniboxRecents | null,
@@ -171,9 +190,8 @@ function openFile(
171
190
 
172
191
  /**
173
192
  * Read a command's label, visibility and caption, or `null` when the command
174
- * is missing, hidden, label-less, or its accessors throw (a command's
175
- * accessors can assume a context — e.g. an active notebook — the omnibox
176
- * doesn't provide).
193
+ * is missing, hidden, label-less, or its accessors throw (they can assume a
194
+ * context the omnibox doesn't provide).
177
195
  */
178
196
  function commandDisplay(
179
197
  commands: CommandRegistry,
@@ -196,10 +214,9 @@ function commandDisplay(
196
214
 
197
215
  /**
198
216
  * The recently used rows for the empty term: both kinds interleaved by
199
- * recency in the unprefixed view, or only the mode's kind under a bare `>` or
200
- * `/`. Commands that no longer resolve are dropped, as are files missing from
201
- * the loaded workspace listing (while the listing is still loading — or
202
- * unavailable — file entries are shown as recorded).
217
+ * recency, or only the mode's kind under a bare `>` or `/`. Unresolvable
218
+ * commands are dropped, as are files absent from the workspace listing once
219
+ * it loads.
203
220
  */
204
221
  function buildRecentItems(
205
222
  options: IComputeOptions,
@@ -250,11 +267,10 @@ function buildRecentItems(
250
267
  }
251
268
 
252
269
  /**
253
- * Match the query against the palette's items first — their labels carry each
254
- * item's args (one "Use Theme: …" per theme, one row per font-size key), which
255
- * a raw registry scan can never produce — then against registry commands the
256
- * palette doesn't present. Palette-covered ids are skipped in the registry
257
- * pass so a command never also surfaces under its argless label.
270
+ * Match palette items first — their labels carry per-item args (one "Use
271
+ * Theme: …" per theme), which a registry scan can never produce — then
272
+ * registry commands the palette doesn't present, skipping palette-covered ids
273
+ * so a command never also surfaces under its argless label.
258
274
  */
259
275
  function matchCommands(options: IComputeOptions, term: string): IOmniboxItem[] {
260
276
  const { commands, paletteItems, recents } = options;
@@ -269,8 +285,6 @@ function matchCommands(options: IComputeOptions, term: string): IOmniboxItem[] {
269
285
  let visible = true;
270
286
  let caption = '';
271
287
  try {
272
- // An item's accessors can throw if the command assumes a context (e.g.
273
- // an active notebook) the omnibox doesn't provide; skip those.
274
288
  label = item.label;
275
289
  visible = item.isVisible;
276
290
  caption = item.caption;
@@ -311,8 +325,6 @@ function matchCommands(options: IComputeOptions, term: string): IOmniboxItem[] {
311
325
  let visible = true;
312
326
  let caption = '';
313
327
  try {
314
- // A command's accessors can throw if they assume a context (e.g. an
315
- // active notebook) the omnibox doesn't provide; skip those.
316
328
  label = commands.label(id);
317
329
  visible = commands.isVisible(id);
318
330
  caption = commands.caption(id);
@@ -385,7 +397,7 @@ function buildAgentItems(
385
397
  const items: IOmniboxItem[] = [];
386
398
  for (const agent of agents) {
387
399
  // Agents without `promptArgs` can't take an inline prompt (the launcher
388
- // would drop it), so they don't belong in an "Ask …" row.
400
+ // would drop it).
389
401
  if (agent.promptArgs === undefined) {
390
402
  continue;
391
403
  }
@@ -1,46 +1,41 @@
1
1
  import type { IStateDB } from '@jupyterlab/statedb';
2
2
  import type { ReadonlyPartialJSONObject } from '@lumino/coreutils';
3
3
 
4
- /**
5
- * Kinds of omnibox results whose uses are recorded.
6
- */
7
4
  export type RecentKind = 'command' | 'file';
8
5
 
9
6
  /**
10
7
  * A single recorded use: a command that was run or a file that was opened.
11
8
  */
12
9
  interface IRecentEntry {
10
+ /**
11
+ * Whether the entry records a command run or a file opened.
12
+ */
13
13
  kind: RecentKind;
14
14
  /**
15
15
  * The command id, or the workspace-relative file path.
16
16
  */
17
17
  id: string;
18
18
  /**
19
- * Arguments the command ran with, for palette-style entries whose label
20
- * and behavior depend on them (e.g. one "Use Theme: …" per theme).
19
+ * Arguments the command ran with, for palette-style entries whose label and
20
+ * behavior depend on them.
21
21
  */
22
22
  args?: ReadonlyPartialJSONObject;
23
23
  }
24
24
 
25
25
  /**
26
- * Identity of an entry: two uses only match when kind, id and args all do,
27
- * so runs of one command with different args stay distinct entries.
26
+ * Identity of an entry: two uses only match when kind, id and args all do, so
27
+ * runs of one command with different args stay distinct.
28
28
  */
29
29
  function entryKey(entry: IRecentEntry): string {
30
30
  return `${entry.kind}\0${entry.id}\0${JSON.stringify(entry.args ?? null)}`;
31
31
  }
32
32
 
33
- /**
34
- * State database key the recents list is persisted under.
35
- */
36
33
  const STATE_KEY = 'xtralab:omnibox:recents';
37
34
 
38
35
  /**
39
- * A most-recently-used list of omnibox results, generic over the result kind
40
- * so commands and files (and future kinds) share one store. Entries are kept
41
- * most-recent-first, capped at `maxItems` per kind, and persisted through the
42
- * state database so they survive reloads. Without a state database the list
43
- * still works for the lifetime of the page.
36
+ * A most-recently-used list of omnibox results, most-recent-first, capped at
37
+ * `maxItems` per kind and persisted through the state database. Without a
38
+ * state database it still works for the lifetime of the page.
44
39
  */
45
40
  export class OmniboxRecents {
46
41
  constructor(options: OmniboxRecents.IOptions = {}) {
@@ -48,12 +43,14 @@ export class OmniboxRecents {
48
43
  }
49
44
 
50
45
  /**
51
- * Maximum number of entries remembered per kind. Lowering it trims the list
52
- * immediately; `0` disables recents entirely.
46
+ * Maximum entries kept per kind; lowering trims immediately, `0` disables recents.
53
47
  */
54
48
  get maxItems(): number {
55
49
  return this._maxItems;
56
50
  }
51
+ /**
52
+ * Set the maximum entries kept per kind.
53
+ */
57
54
  set maxItems(value: number) {
58
55
  const normalized = Math.max(0, Math.floor(value));
59
56
  if (normalized === this._maxItems) {
@@ -68,8 +65,7 @@ export class OmniboxRecents {
68
65
  }
69
66
 
70
67
  /**
71
- * The recorded entries, most recent first — all of them, or only those of
72
- * one kind.
68
+ * The recorded entries, most recent first, optionally filtered to one kind.
73
69
  */
74
70
  entries(kind?: RecentKind): IRecentEntry[] {
75
71
  return kind
@@ -78,8 +74,7 @@ export class OmniboxRecents {
78
74
  }
79
75
 
80
76
  /**
81
- * Record a use of `id` (with the args it ran with, if any), moving it to
82
- * the front of its kind's list.
77
+ * Record a use, moving the entry to the front of its kind's list.
83
78
  */
84
79
  touch(kind: RecentKind, id: string, args?: ReadonlyPartialJSONObject): void {
85
80
  const entry: IRecentEntry =
@@ -92,7 +87,7 @@ export class OmniboxRecents {
92
87
 
93
88
  /**
94
89
  * Load the persisted list. Entries recorded before the load completes stay
95
- * at the front, ahead of the restored history.
90
+ * ahead of the restored history.
96
91
  */
97
92
  async restore(): Promise<void> {
98
93
  if (!this._state) {
@@ -1,10 +1,9 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
 
3
3
  /**
4
- * A launcher overlay that fuzzy-searches across multiple sources — workspace
5
- * files, JupyterLab commands — and routes a typed prompt to one of the
6
- * configured agents. Provided by `xtralab:omnibox` and consumed (optionally)
7
- * by the top-bar command bar, which opens it on click.
4
+ * A launcher overlay that fuzzy-searches workspace files and commands and
5
+ * routes a typed prompt to a configured agent. Provided by `xtralab:omnibox`;
6
+ * opened by the top-bar command bar.
8
7
  */
9
8
  export interface IOmnibox {
10
9
  /**
@@ -17,17 +16,13 @@ export interface IOmnibox {
17
16
  close(): void;
18
17
  }
19
18
 
20
- /**
21
- * DI token for {@link IOmnibox}. Provided by `xtralab:omnibox`; consumed by
22
- * `xtralab:command-bar` so the top-bar pill opens the omnibox.
23
- */
24
19
  export const IOmnibox = new Token<IOmnibox>(
25
20
  'xtralab:IOmnibox',
26
21
  'Opens the omnibox overlay that searches files and commands and prompts agents.'
27
22
  );
28
23
 
29
24
  /**
30
- * Command id that opens the omnibox (registered by `xtralab:omnibox`). Shared
31
- * here so the command bar can look up its keybinding and show a shortcut hint.
25
+ * Command id that opens the omnibox, shared so the command bar can look up
26
+ * its keybinding for the shortcut hint.
32
27
  */
33
28
  export const OMNIBOX_OPEN_COMMAND = 'xtralab:omnibox:open';
@@ -11,21 +11,15 @@ import { computeSections, IOmniboxItem } from './model';
11
11
  import { loadWorkspaceFiles } from './files';
12
12
  import type { OmniboxRecents } from './recents';
13
13
 
14
- /**
15
- * DOM id of the results listbox, referenced by the input's `aria-controls`.
16
- */
17
14
  const LIST_ID = 'jp-xtralab-Omnibox-list';
18
15
 
19
- /**
20
- * Stable DOM id for the result row at `index`, for `aria-activedescendant`.
21
- */
22
16
  function optionId(index: number): string {
23
17
  return `jp-xtralab-Omnibox-option-${index}`;
24
18
  }
25
19
 
26
20
  /**
27
21
  * Render `text` with the fuzzy-matched characters at `indices` wrapped in a
28
- * highlight span, leaving the rest as plain text.
22
+ * highlight span.
29
23
  */
30
24
  function renderHighlight(
31
25
  text: string,
@@ -125,13 +119,10 @@ function OmniboxComponent(props: OmniboxWidget.IOptions): JSX.Element {
125
119
  );
126
120
  const hasResults = flat.length > 0;
127
121
 
128
- // Reset the highlighted row whenever the result set changes, and keep the
129
- // index in range when results shrink.
130
122
  React.useEffect(() => {
131
123
  setActive(current => (current < flat.length ? current : 0));
132
124
  }, [flat]);
133
125
 
134
- // Keep the highlighted row scrolled into view as it moves.
135
126
  React.useEffect(() => {
136
127
  listRef.current
137
128
  ?.querySelector('.jp-mod-active')
@@ -139,8 +130,8 @@ function OmniboxComponent(props: OmniboxWidget.IOptions): JSX.Element {
139
130
  }, [active, flat]);
140
131
 
141
132
  const dismiss = React.useCallback(() => {
142
- // Defer so unmounting this React root (via onClose) never happens
143
- // synchronously inside the event handler that triggered it.
133
+ // Defer so unmounting this React root never happens synchronously inside
134
+ // the event handler that triggered it.
144
135
  window.setTimeout(onClose, 0);
145
136
  }, [onClose]);
146
137
 
@@ -307,10 +298,8 @@ function OmniboxComponent(props: OmniboxWidget.IOptions): JSX.Element {
307
298
  }
308
299
 
309
300
  /**
310
- * The omnibox overlay widget. Hosts {@link OmniboxComponent} in a React root
311
- * attached to `document.body`. It carries `jp-ThemedContainer` so JupyterLab's
312
- * theme CSS variables resolve outside the shell, and its own `jp-xtralab-Omnibox`
313
- * class for the overlay/panel styling (style/omnibox.css).
301
+ * The omnibox overlay widget, attached to `document.body`; `jp-ThemedContainer`
302
+ * makes JupyterLab's theme CSS variables resolve outside the shell.
314
303
  */
315
304
  export class OmniboxWidget extends ReactWidget {
316
305
  constructor(options: OmniboxWidget.IOptions) {
@@ -321,6 +310,9 @@ export class OmniboxWidget extends ReactWidget {
321
310
  this.addClass('jp-ThemedContainer');
322
311
  }
323
312
 
313
+ /**
314
+ * Render the omnibox overlay content.
315
+ */
324
316
  render(): JSX.Element {
325
317
  return <OmniboxComponent {...this._options} />;
326
318
  }
@@ -328,20 +320,23 @@ export class OmniboxWidget extends ReactWidget {
328
320
  private _options: OmniboxWidget.IOptions;
329
321
  }
330
322
 
331
- /**
332
- * A namespace for `OmniboxWidget` statics.
333
- */
334
323
  export namespace OmniboxWidget {
335
324
  /**
336
325
  * Construction options for {@link OmniboxWidget}.
337
326
  */
338
327
  export interface IOptions {
328
+ /**
329
+ * The command registry used to look up and execute commands.
330
+ */
339
331
  commands: CommandRegistry;
340
332
  /**
341
333
  * Snapshot of the command palette's items, read once when the overlay
342
334
  * opens; empty when the palette isn't available.
343
335
  */
344
336
  paletteItems: ReadonlyArray<CommandPalette.IItem>;
337
+ /**
338
+ * The document registry used to pick file icons.
339
+ */
345
340
  docRegistry: DocumentRegistry;
346
341
  /**
347
342
  * Snapshot of the available agents, read once when the overlay opens.