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,11 +1,5 @@
1
1
  import { LabIcon, textEditorIcon } from '@jupyterlab/ui-components';
2
2
  import { BUILTIN_EDITOR_ICONS } from './icons';
3
- /**
4
- * Built-in editor candidates in preference order. The launcher shows the first
5
- * one whose command resolves on the server's `$PATH`, so Neovim wins over Vim
6
- * when both are installed. Users override, disable, or extend this list
7
- * through the `editors` setting; see {@link mergeEditors}.
8
- */
9
3
  const DEFAULTS = [
10
4
  {
11
5
  id: 'nvim',
@@ -27,11 +21,8 @@ const DEFAULTS = [
27
21
  }
28
22
  ];
29
23
  /**
30
- * The built-in editors projected into the JSON settings shape
31
- * ({@link IEditorSettings}), dropping the runtime-only {@link LabIcon}. Mirrors
32
- * {@link defaultAgentSettings}: {@link registerLauncherSchemaDefaults} injects
33
- * this as the `editors` setting's schema default so the Settings Editor shows
34
- * the shipped list.
24
+ * The built-in editors projected into the settings shape (no runtime LabIcon),
25
+ * injected as the `editors` schema default so the Settings Editor shows them.
35
26
  */
36
27
  export function defaultEditorSettings() {
37
28
  return DEFAULTS.map(editor => ({
@@ -54,13 +45,9 @@ function resolveEditorIcon(id, iconSvg) {
54
45
  return (_a = BUILTIN_EDITOR_ICONS[id]) !== null && _a !== void 0 ? _a : textEditorIcon;
55
46
  }
56
47
  /**
57
- * Merge xtralab's built-in editors with the user's `editors` settings. Built-in
58
- * entries keep their fields unless explicitly overridden; user-only entries are
59
- * appended. `enabled: false` filters an entry out of the result entirely (so
60
- * callers don't need to re-check the flag). The result is sorted by rank, which
61
- * is also the launcher's tile-preference order. Mirrors `mergeAgents`, including
62
- * turning `requireAvailable` off when the user overrides a built-in's `command`
63
- * so an aliased editor still shows.
48
+ * Merge the built-in editors with the user's `editors` settings; the result is
49
+ * sorted by rank, the tile-preference order. Mirrors `mergeAgents`, including
50
+ * turning `requireAvailable` off when a built-in's `command` is overridden.
64
51
  */
65
52
  export function mergeEditors(overrides) {
66
53
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
@@ -86,18 +73,11 @@ export function mergeEditors(overrides) {
86
73
  ? resolveEditorIcon(base.id, override.iconSvg)
87
74
  : base.icon,
88
75
  rank: (_d = override.rank) !== null && _d !== void 0 ? _d : base.rank,
89
- // See `mergeAgents`: once the user points a built-in editor at their own
90
- // command (e.g. a shell alias `which` can't resolve), stop requiring
91
- // availability so the tile still shows. The check stays on only while the
92
- // command is xtralab's default.
93
76
  requireAvailable: command === base.command
94
77
  ? ((_e = override.requireAvailable) !== null && _e !== void 0 ? _e : base.requireAvailable)
95
78
  : false
96
79
  });
97
80
  }
98
- // What remains in `overrideById` are ids that don't match a built-in — treat
99
- // them as new editors the user is adding. Skip disabled ones; the settings
100
- // schema validates the shape, so missing fields just fall back here.
101
81
  let nextRank = merged.reduce((max, editor) => Math.max(max, editor.rank), -1) + 1;
102
82
  for (const entry of overrideById.values()) {
103
83
  if (entry.enabled === false) {
@@ -119,15 +99,9 @@ export function mergeEditors(overrides) {
119
99
  return merged;
120
100
  }
121
101
  /**
122
- * Pick the single editor tile to show, given the merged editor list and the
123
- * set of commands known to be on `$PATH` (from the launcher's availability
124
- * probe). Returns the first candidate, by rank, that is either available or
125
- * opts out of the check (`requireAvailable: false`) — Neovim before Vim by
126
- * default — or `null` when none qualifies.
127
- *
128
- * When `available` is `null` (the endpoint couldn't be reached), a
129
- * `requireAvailable` editor is not shown; an entry with
130
- * `requireAvailable: false` is shown regardless.
102
+ * Pick the single tile: the first editor, by rank, that is available or opts
103
+ * out of the check, else `null`. When `available` is `null` (probe failed),
104
+ * only `requireAvailable: false` entries qualify.
131
105
  */
132
106
  export function resolveEditor(editors, available) {
133
107
  var _a;
@@ -1,15 +1,3 @@
1
1
  import { LabIcon } from '@jupyterlab/ui-components';
2
- /**
3
- * Built-in icons keyed by the agent's id, used to resolve the icon for an
4
- * agent declared without an inline `iconSvg` override (i.e. one of xtralab's
5
- * defaults). Custom agents added via settings can either supply their own
6
- * `iconSvg` or fall back to the generic terminal icon.
7
- */
8
2
  export declare const BUILTIN_AGENT_ICONS: Record<string, LabIcon>;
9
- /**
10
- * Built-in icons keyed by the editor's id, used to resolve the icon for a
11
- * built-in editor (Neovim, Vim) declared without an inline `iconSvg`. Custom
12
- * editors added via settings can supply their own `iconSvg` or fall back to
13
- * the generic text-editor icon.
14
- */
15
3
  export declare const BUILTIN_EDITOR_ICONS: Record<string, LabIcon>;
@@ -1,29 +1,10 @@
1
1
  import { LabIcon } from '@jupyterlab/ui-components';
2
2
  /**
3
- * Brand icons for the agent launcher cards. Most marks are taken verbatim
4
- * from `jupyter-ai-contrib/jupyter-ai-acp-client`
5
- * (`jupyter_ai_acp_client/static/*.svg`, BSD-3-Clause); Antigravity post-dates
6
- * that set, so its mark is the official glyph from `@lobehub/icons-static-svg`
7
- * (MIT) — we ship the brand-colored `antigravity-color` variant so Antigravity
8
- * reads as a multi-color Google mark. Small adjustments are applied
9
- * to play nicely with JupyterLab themes:
10
- * - Monochrome marks (Codex, Copilot, Goose) use `currentColor` so they
11
- * inherit the surrounding launcher card's text color rather than
12
- * hard-coding black, which disappears on the dark theme.
13
- * - The Goose mark upstream is rendered on a white rounded rect; we drop
14
- * the rect so the silhouette can sit on the launcher card directly.
15
- * - SVG ids that would collide if two copies of the same artwork ended up
16
- * on the page (gradient/mask/filter defs) are namespaced under
17
- * `xtralab-…`.
18
- *
19
- * Brand-colored icons (Antigravity, Claude, Kiro, Mistral Vibe, OpenCode) keep
20
- * their upstream colors; the dark/light contrast of the launcher card behind
21
- * them is the same as on `jupyter-ai-acp-client`.
22
- *
23
- * The Vim and Neovim marks belong to the launcher's "Open" editor tile rather
24
- * than the agent grid. They are the brand silhouettes from Simple Icons
25
- * (https://simpleicons.org, CC0), each filled with its brand green so the mark
26
- * reads on both the light and dark themes.
3
+ * Brand icons for the launcher cards: `jupyter-ai-contrib/jupyter-ai-acp-client`
4
+ * (BSD-3-Clause), except Antigravity (`@lobehub/icons-static-svg`, MIT),
5
+ * Pi (pi.dev, MIT) and Vim/Neovim (Simple Icons, CC0). Monochrome marks use
6
+ * `currentColor` to follow the active theme; svg ids are namespaced under
7
+ * `xtralab-…` so two copies of one mark on a page don't collide.
27
8
  */
28
9
  const antigravityIcon = new LabIcon({
29
10
  name: 'xtralab:agent-antigravity',
@@ -169,6 +150,13 @@ const opencodeIcon = new LabIcon({
169
150
  <path fill-rule="evenodd" clip-rule="evenodd" d="M384 416H128V96H384V416ZM320 160H192V352H320V160Z" fill="white"/>
170
151
  </svg>`
171
152
  });
153
+ const piIcon = new LabIcon({
154
+ name: 'xtralab:agent-pi',
155
+ svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" fill="currentColor">
156
+ <path fill-rule="evenodd" d="M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z"/>
157
+ <path d="M517.36 400H634.72V634.72H517.36Z"/>
158
+ </svg>`
159
+ });
172
160
  const neovimIcon = new LabIcon({
173
161
  name: 'xtralab:editor-neovim',
174
162
  svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#57A143">
@@ -181,12 +169,6 @@ const vimIcon = new LabIcon({
181
169
  <path d="M24 11.986h-.027l-4.318-4.318 4.303-4.414V1.461l-.649-.648h-8.198l-.66.605v1.045L12.015.027V0L12 .014 11.986 0v.027l-1.29 1.291-.538-.539H2.035l-.638.692v1.885l.616.616h.72v5.31L.027 11.987H0L.014 12 0 12.014h.027l2.706 2.706v6.467l.907.523h2.322l1.857-1.904 4.166 4.166V24l.015-.014.014.014v-.028l2.51-2.509h.485c.111 0 .211-.07.25-.179l.146-.426c.028-.084.012-.172-.037-.239l1.462-1.462-.612 1.962c-.043.141.036.289.177.332.025.008.052.012.078.012h1.824c.106-.001.201-.064.243-.163l.165-.394c.025-.065.024-.138-.004-.203-.027-.065-.08-.116-.146-.142-.029-.012-.062-.019-.097-.02h-.075l.84-2.644h1.232l-1.016 3.221c-.043.141.036.289.176.332.025.008.052.012.079.012h2.002c.11 0 .207-.066.248-.17l.164-.428c.051-.138-.021-.29-.158-.341-.029-.011-.06-.017-.091-.017h-.145l1.131-3.673c.027-.082.012-.173-.039-.24l-.375-.504-.003-.005c-.051-.064-.127-.102-.209-.102h-1.436c-.071 0-.141.03-.19.081l-.4.439h-.624l-.042-.046 4.445-4.445H24L23.986 12l.014-.014zM9.838 21.139l1.579-4.509h-.501l.297-.304h1.659l-1.563 4.555h.623l-.079.258H9.838zm3.695-7.516l.15.151-.269.922-.225.226h-.969l-.181-.181.311-.871.288-.247h.895zM5.59 20.829H3.877l-.262-.15V3.091H2.379l-.1-.1V1.815l.143-.154h7.371l.213.214v1.108l-.142.173H8.785v8.688l8.807-8.688h-2.086l-.175-.188V1.805l.121-.111h7.49l.132.133v1.07L12.979 13.25h-.373c-.015-.001-.028 0-.042.001l-.02.003c-.045.01-.086.03-.119.06l-.343.295-.004.003c-.033.031-.059.069-.073.111l-.296.83-6.119 6.276zm14.768-3.952l.474-.519h1.334l.309.415-1.265 4.107h.493l-.08.209H19.84l1.124-3.564h-2.015l-1.077 3.391h.424l-.073.174h-1.605l1.107-3.548h-2.096l-1.062 3.339h.436l-.072.209H13.27l1.514-4.46H14.198l.091-.271h1.65l.519.537h.906l.491-.554h1.061l.489.535h.953z"/>
182
170
  </svg>`
183
171
  });
184
- /**
185
- * Built-in icons keyed by the agent's id, used to resolve the icon for an
186
- * agent declared without an inline `iconSvg` override (i.e. one of xtralab's
187
- * defaults). Custom agents added via settings can either supply their own
188
- * `iconSvg` or fall back to the generic terminal icon.
189
- */
190
172
  export const BUILTIN_AGENT_ICONS = {
191
173
  antigravity: antigravityIcon,
192
174
  claude: claudeIcon,
@@ -195,14 +177,9 @@ export const BUILTIN_AGENT_ICONS = {
195
177
  goose: gooseIcon,
196
178
  kiro: kiroIcon,
197
179
  'mistral-vibe': mistralVibeIcon,
198
- opencode: opencodeIcon
180
+ opencode: opencodeIcon,
181
+ pi: piIcon
199
182
  };
200
- /**
201
- * Built-in icons keyed by the editor's id, used to resolve the icon for a
202
- * built-in editor (Neovim, Vim) declared without an inline `iconSvg`. Custom
203
- * editors added via settings can supply their own `iconSvg` or fall back to
204
- * the generic text-editor icon.
205
- */
206
183
  export const BUILTIN_EDITOR_ICONS = {
207
184
  nvim: neovimIcon,
208
185
  vim: vimIcon
@@ -15,26 +15,10 @@ import { registerLauncherSchemaDefaults } from './schemaDefaults';
15
15
  import { IAgentRegistry } from './tokens';
16
16
  const PLUGIN_ID = 'xtralab:launcher';
17
17
  /**
18
- * The xtralab launcher plugin. Replaces the stock JupyterLab launcher
19
- * (which is disabled via `package.json`'s `jupyterlab.disabledExtensions`)
20
- * with an agent-focused dashboard: an optional initial prompt, a row of
21
- * agent buttons (Claude, Codex, Antigravity, …), and a collapsible list of
22
- * changed files (clickable into the diff viewer) below them. Clicking an
23
- * agent opens a fresh terminal and pipes the agent's command into it; if
24
- * the prompt textarea is non-empty, the prompt is shell-quoted and
25
- * spliced into the launch line per the agent's `promptArgs` recipe.
26
- *
27
- * The agent list is the merge of xtralab's defaults with the user's
28
- * `xtralab:launcher` settings, then filtered by a server-side `which`
29
- * check so users only see agents that are actually installed. Agents with
30
- * `requireAvailable: false` skip the filter, as does any built-in whose
31
- * `command` the user has overridden (a user-chosen command — often a shell
32
- * alias the server can't resolve — is trusted and always shown).
33
- *
34
- * The plugin deliberately does NOT provide the `ILauncher` token: other
35
- * extensions register notebook/console/terminal cards on it as a side
36
- * effect, which would defeat the point of the agent-only launcher. If an
37
- * extension needs to surface itself, we'll add it here explicitly.
18
+ * The xtralab launcher plugin: replaces the stock JupyterLab launcher
19
+ * (disabled via `jupyterlab.disabledExtensions`) with an agent-focused
20
+ * dashboard. It deliberately does NOT provide `ILauncher` — other extensions
21
+ * would register their cards on it and defeat the agent-only design.
38
22
  */
39
23
  const plugin = {
40
24
  id: PLUGIN_ID,
@@ -52,20 +36,10 @@ const plugin = {
52
36
  activate: async (app, labShell, palette, settingRegistry, translator, agentSessions, editorRegistry) => {
53
37
  const { commands, shell } = app;
54
38
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
55
- // The shared agent registry this plugin provides on `IAgentRegistry`.
56
- // It holds the active agent list (updated whenever settings change), so
57
- // every freshly-created launcher widget — and any other plugin that
58
- // consumes the token, e.g. the terminals panel — renders the latest list.
59
39
  const registry = new AgentRegistry();
60
- // Track command registrations so a settings change can wipe them
61
- // before re-registering — without this the command palette would
62
- // accumulate stale entries when the agent list shrinks.
63
40
  let registered = null;
64
41
  const applyAgents = async (overrides) => {
65
42
  const agents = mergeAgents(overrides);
66
- // Probe the server's `$PATH` so we only surface agents that are actually
67
- // installed. Agents with `requireAvailable: false` opt out (their command
68
- // may be a shell alias that `which` can't see) and are kept regardless.
69
43
  const probe = Array.from(new Set(agents
70
44
  .filter(agent => agent.requireAvailable)
71
45
  .map(agent => agent.command)));
@@ -97,8 +71,6 @@ const plugin = {
97
71
  }
98
72
  catch (reason) {
99
73
  console.error('xtralab: failed to load launcher settings', reason);
100
- // Settings load failed — fall back to defaults so the launcher
101
- // still has cards instead of going silent.
102
74
  await applyAgents([]);
103
75
  }
104
76
  }
@@ -111,11 +83,8 @@ const plugin = {
111
83
  var _a;
112
84
  const id = Private.nextId();
113
85
  const onAgentLaunch = (item) => {
114
- // When an agent command returns a Widget that ends up in the main
115
- // area, slot it where this launcher used to sit so opening an
116
- // agent feels like the launcher transformed into the terminal.
117
- // Disposing the inner ReactWidget cascades to the MainAreaWidget
118
- // host via its `content.disposed` connection.
86
+ // Slot the widget where this launcher sits; disposing the inner
87
+ // ReactWidget cascades to its MainAreaWidget host via `content.disposed`.
119
88
  if (find(shell.widgets('main'), w => w === item)) {
120
89
  shell.add(item, 'main', { ref: id });
121
90
  launcher.dispose();
@@ -127,9 +96,6 @@ const plugin = {
127
96
  editor: (_a = editorRegistry === null || editorRegistry === void 0 ? void 0 : editorRegistry.current) !== null && _a !== void 0 ? _a : null,
128
97
  agentSessions,
129
98
  onAgentLaunch,
130
- // Empty repoPath/cwd matches the JupyterLab convention used by
131
- // the git panel and the stock launcher: let the server resolve
132
- // the working tree from its root directory.
133
99
  repoPath: '',
134
100
  cwd: '',
135
101
  trans
@@ -137,9 +103,7 @@ const plugin = {
137
103
  launcher.title.icon = launcherIcon;
138
104
  launcher.title.label = trans.__('Launcher');
139
105
  const main = new MainAreaWidget({ content: launcher });
140
- // Hide the close button when the launcher is the only thing in the
141
- // main area: closing it would leave the user staring at an empty
142
- // shell with no way back.
106
+ // Closing the only main-area widget would leave an empty shell.
143
107
  main.title.closable = !!Array.from(shell.widgets('main')).length;
144
108
  main.id = id;
145
109
  shell.add(main, 'main', {
@@ -171,9 +135,7 @@ const plugin = {
171
135
  labShell.layoutModified.connect(() => {
172
136
  maybeCreate();
173
137
  });
174
- // Layout has settled by the time `app.restored` resolves; if it's
175
- // empty (fresh start, no restored widgets) the connect above won't
176
- // fire on its own — kick it off here.
138
+ // If the restored layout is already empty the connect never fires.
177
139
  maybeCreate();
178
140
  });
179
141
  labShell.addButtonEnabled = true;
@@ -188,14 +150,9 @@ const plugin = {
188
150
  }
189
151
  };
190
152
  /**
191
- * Drop agents whose command isn't on `$PATH`, except entries that opt out via
192
- * `requireAvailable: false` (e.g. shell aliases the user wants surfaced
193
- * regardless). `available` is the resolved set from
194
- * {@link fetchAvailableCommands}.
195
- *
196
- * When `available` is `null` (the endpoint couldn't be reached) we fail open
197
- * and return the input unchanged — better to show an unreachable agent than
198
- * to hide the entire launcher because the server extension didn't load.
153
+ * Drop agents whose command isn't on `$PATH`, keeping `requireAvailable:
154
+ * false` entries. When `available` is `null` (probe failed), fail open and
155
+ * return the input unchanged.
199
156
  */
200
157
  function filterAgents(agents, available) {
201
158
  if (!available) {
@@ -207,7 +164,7 @@ var Private;
207
164
  (function (Private) {
208
165
  let counter = 0;
209
166
  /**
210
- * Returns the next unique launcher widget id.
167
+ * Generate a unique id for a launcher widget.
211
168
  */
212
169
  function nextId() {
213
170
  return `launcher-${counter++}`;
@@ -1,14 +1,8 @@
1
1
  import type { IAgent } from './agents';
2
2
  /**
3
- * Compose the literal command line typed into a fresh terminal for `agent`.
4
- * When `prompt` is empty (or whitespace-only), this is just `agent.command`.
5
- * When non-empty, the prompt is shell-quoted and spliced in using the
6
- * agent's `promptArgs` recipe — see `IAgent.promptArgs` for the encoding.
7
- *
8
- * Returns `agent.command` unchanged when the agent declares no prompt
9
- * support (`promptArgs === undefined`), so a stray prompt typed into the
10
- * launcher does not silently mutate the launch line for that agent. The
11
- * launcher UI separately prevents the click in that case so the dropped
12
- * prompt is never surprising to the user.
3
+ * Compose the literal command line typed into a fresh terminal for `agent`:
4
+ * the shell-quoted prompt is spliced in per the `promptArgs` recipe. Returns
5
+ * bare `agent.command` when the prompt is empty or the agent has no prompt
6
+ * support.
13
7
  */
14
8
  export declare function buildAgentInvocation(agent: IAgent, prompt: string): string;
@@ -1,16 +1,7 @@
1
1
  /**
2
- * Quote a string so it can be safely passed as a single argument on a
3
- * bash/zsh command line that is *typed into an interactive terminal* —
4
- * not the usual file-parsing context.
5
- *
6
- * The distinction matters: an actual newline byte (0x0A) sent into a PTY
7
- * is interpreted by the line discipline as Enter regardless of whether
8
- * it sits inside `'...'` quotes, so the shell never sees the closing
9
- * quote. We therefore use ANSI-C `$'...'` quoting and emit `\n` (and
10
- * `\r`) as their two-character escape sequences; the shell expands them
11
- * to real newlines after the line is submitted, when readline is no
12
- * longer involved. Backslashes and single quotes are escaped first so
13
- * the source text round-trips unchanged.
2
+ * Quote a string for a command line typed into an interactive terminal. A raw
3
+ * newline sent to a PTY acts as Enter even inside '…' quotes, so ANSI-C
4
+ * `$'…'` quoting emits `\n`/`\r` as escapes the shell expands after submit.
14
5
  */
15
6
  function shellQuote(value) {
16
7
  const escaped = value
@@ -21,16 +12,10 @@ function shellQuote(value) {
21
12
  return `$'${escaped}'`;
22
13
  }
23
14
  /**
24
- * Compose the literal command line typed into a fresh terminal for `agent`.
25
- * When `prompt` is empty (or whitespace-only), this is just `agent.command`.
26
- * When non-empty, the prompt is shell-quoted and spliced in using the
27
- * agent's `promptArgs` recipe — see `IAgent.promptArgs` for the encoding.
28
- *
29
- * Returns `agent.command` unchanged when the agent declares no prompt
30
- * support (`promptArgs === undefined`), so a stray prompt typed into the
31
- * launcher does not silently mutate the launch line for that agent. The
32
- * launcher UI separately prevents the click in that case so the dropped
33
- * prompt is never surprising to the user.
15
+ * Compose the literal command line typed into a fresh terminal for `agent`:
16
+ * the shell-quoted prompt is spliced in per the `promptArgs` recipe. Returns
17
+ * bare `agent.command` when the prompt is empty or the agent has no prompt
18
+ * support.
34
19
  */
35
20
  export function buildAgentInvocation(agent, prompt) {
36
21
  const trimmed = prompt.trim();
@@ -2,18 +2,20 @@ import { ISignal } from '@lumino/signaling';
2
2
  import type { IAgent } from './agents';
3
3
  import type { IAgentRegistry } from './tokens';
4
4
  /**
5
- * The concrete {@link IAgentRegistry} the launcher plugin provides on the
6
- * `IAgentRegistry` token. Holds the active agent list and re-emits `changed`
7
- * whenever the launcher recomputes it (on a settings change). The launcher
8
- * is the only writer, so the write side ({@link setAgents}) is kept off the
9
- * shared token.
5
+ * Concrete {@link IAgentRegistry} the launcher plugin provides. The launcher
6
+ * is the only writer, so {@link setAgents} is kept off the shared token.
10
7
  */
11
8
  export declare class AgentRegistry implements IAgentRegistry {
9
+ /**
10
+ * The current agents, filtered by availability and sorted by rank.
11
+ */
12
12
  get agents(): IAgent[];
13
+ /**
14
+ * Emitted whenever {@link agents} changes.
15
+ */
13
16
  get changed(): ISignal<IAgentRegistry, void>;
14
17
  /**
15
- * Replace the agent list and notify observers. Called by the launcher
16
- * after merging the user's settings and filtering by availability.
18
+ * Replace the agent list and notify observers.
17
19
  */
18
20
  setAgents(agents: IAgent[]): void;
19
21
  private _agents;
@@ -1,25 +1,27 @@
1
1
  import { Signal } from '@lumino/signaling';
2
2
  /**
3
- * The concrete {@link IAgentRegistry} the launcher plugin provides on the
4
- * `IAgentRegistry` token. Holds the active agent list and re-emits `changed`
5
- * whenever the launcher recomputes it (on a settings change). The launcher
6
- * is the only writer, so the write side ({@link setAgents}) is kept off the
7
- * shared token.
3
+ * Concrete {@link IAgentRegistry} the launcher plugin provides. The launcher
4
+ * is the only writer, so {@link setAgents} is kept off the shared token.
8
5
  */
9
6
  export class AgentRegistry {
10
7
  constructor() {
11
8
  this._agents = [];
12
9
  this._changed = new Signal(this);
13
10
  }
11
+ /**
12
+ * The current agents, filtered by availability and sorted by rank.
13
+ */
14
14
  get agents() {
15
15
  return this._agents;
16
16
  }
17
+ /**
18
+ * Emitted whenever {@link agents} changes.
19
+ */
17
20
  get changed() {
18
21
  return this._changed;
19
22
  }
20
23
  /**
21
- * Replace the agent list and notify observers. Called by the launcher
22
- * after merging the user's settings and filtering by availability.
24
+ * Replace the agent list and notify observers.
23
25
  */
24
26
  setAgents(agents) {
25
27
  this._agents = agents;
@@ -4,14 +4,7 @@ import type { ISettingRegistry } from '@jupyterlab/settingregistry';
4
4
  */
5
5
  export declare const LAUNCHER_PLUGIN_ID = "xtralab:launcher";
6
6
  /**
7
- * Register a `fetch` transform that injects xtralab's built-in agent and
8
- * editor lists as the schema defaults for the `xtralab:launcher` settings, so
9
- * the Settings Editor shows the shipped list.
10
- *
11
- * The schema declares `jupyter.lab.transform: true`, so the registry defers
12
- * loading the plugin until a transform is registered: callers must invoke this
13
- * before their first `settingRegistry.load(LAUNCHER_PLUGIN_ID)`. Idempotent, so
14
- * the two plugins that load these settings can both call it regardless of
15
- * activation order.
7
+ * Must run before the first `load(LAUNCHER_PLUGIN_ID)` (the schema sets
8
+ * `jupyter.lab.transform: true`); idempotent across callers.
16
9
  */
17
10
  export declare function registerLauncherSchemaDefaults(settingRegistry: ISettingRegistry): void;
@@ -5,20 +5,38 @@ import { defaultEditorSettings } from './editors';
5
5
  */
6
6
  export const LAUNCHER_PLUGIN_ID = 'xtralab:launcher';
7
7
  /**
8
- * Setting registries that already have the transform, so both callers skip
9
- * re-registering (the registry rejects a duplicate transformer).
8
+ * Registries that already have the transform (duplicates are rejected).
10
9
  */
11
10
  const registered = new WeakSet();
12
11
  /**
13
- * Register a `fetch` transform that injects xtralab's built-in agent and
14
- * editor lists as the schema defaults for the `xtralab:launcher` settings, so
15
- * the Settings Editor shows the shipped list.
16
- *
17
- * The schema declares `jupyter.lab.transform: true`, so the registry defers
18
- * loading the plugin until a transform is registered: callers must invoke this
19
- * before their first `settingRegistry.load(LAUNCHER_PLUGIN_ID)`. Idempotent, so
20
- * the two plugins that load these settings can both call it regardless of
21
- * activation order.
12
+ * Merge sparse user entries over the built-in defaults, keyed by `id`.
13
+ */
14
+ function mergeSettingsById(defaults, user) {
15
+ const entries = Array.isArray(user) ? user : [];
16
+ const overrideById = new Map(entries.map(entry => [entry.id, entry]));
17
+ const merged = defaults.map(base => {
18
+ const override = overrideById.get(base.id);
19
+ if (!override) {
20
+ return base;
21
+ }
22
+ overrideById.delete(base.id);
23
+ return { ...base, ...override };
24
+ });
25
+ merged.push(...overrideById.values());
26
+ // Item schema defaults are applied before this transform runs; without the
27
+ // refill, sparse entries render with unchecked boxes.
28
+ return merged.map(entry => {
29
+ var _a, _b;
30
+ return ({
31
+ ...entry,
32
+ enabled: (_a = entry.enabled) !== null && _a !== void 0 ? _a : true,
33
+ requireAvailable: (_b = entry.requireAvailable) !== null && _b !== void 0 ? _b : true
34
+ });
35
+ });
36
+ }
37
+ /**
38
+ * Must run before the first `load(LAUNCHER_PLUGIN_ID)` (the schema sets
39
+ * `jupyter.lab.transform: true`); idempotent across callers.
22
40
  */
23
41
  export function registerLauncherSchemaDefaults(settingRegistry) {
24
42
  if (registered.has(settingRegistry)) {
@@ -26,11 +44,17 @@ export function registerLauncherSchemaDefaults(settingRegistry) {
26
44
  }
27
45
  registered.add(settingRegistry);
28
46
  settingRegistry.transform(LAUNCHER_PLUGIN_ID, {
47
+ compose: plugin => {
48
+ const { user } = plugin.data;
49
+ const composite = { ...plugin.data.composite };
50
+ composite.agents = mergeSettingsById(defaultAgentSettings(), user.agents);
51
+ composite.editors = mergeSettingsById(defaultEditorSettings(), user.editors);
52
+ plugin.data = { composite, user };
53
+ return plugin;
54
+ },
29
55
  fetch: plugin => {
30
56
  const properties = plugin.schema.properties;
31
57
  if (properties) {
32
- // The settings shapes are plain JSON by construction, but their named
33
- // interfaces lack an index signature, so cast at this TS→JSON boundary.
34
58
  if (properties.agents) {
35
59
  properties.agents.default =
36
60
  defaultAgentSettings();
@@ -2,40 +2,28 @@ import { Token } from '@lumino/coreutils';
2
2
  import type { ISignal } from '@lumino/signaling';
3
3
  import type { IAgent } from './agents';
4
4
  /**
5
- * A read-only, observable view of the launcher's available agents.
6
- *
7
- * The launcher plugin owns the agent list — xtralab's defaults merged with
8
- * the user's `xtralab:launcher` settings, then filtered by a server-side
9
- * `which` check — and registers an `xtralab:start-agent:<id>` command for
10
- * each entry. This token shares that list, and (via {@link agentCommandId})
11
- * the command id that launches each agent, so other plugins can surface the
12
- * same agents without re-deriving the list or duplicating the icons. The
13
- * terminals panel uses it to build its "new terminal" dropdown.
5
+ * A read-only, observable view of the launcher's available agents — the
6
+ * merged, availability-filtered list the launcher renders — shared so other
7
+ * plugins (e.g. the terminals panel) can surface the same agents and icons.
14
8
  */
15
9
  export interface IAgentRegistry {
16
10
  /**
17
- * The current agents, already filtered by availability and sorted by
18
- * rank — the same array the launcher renders.
11
+ * The current agents, filtered by availability and sorted by rank.
19
12
  */
20
13
  readonly agents: IAgent[];
21
14
  /**
22
- * Emitted whenever {@link agents} changes (e.g. the user edits the
23
- * launcher settings). Consumers that cache the list should re-read it
24
- * here; consumers that read it on demand can ignore the signal.
15
+ * Emitted whenever {@link agents} changes.
25
16
  */
26
17
  readonly changed: ISignal<IAgentRegistry, void>;
27
18
  }
28
19
  /**
29
- * DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher` and
30
- * consumed — optionally, so the panel still works when the launcher is
31
- * disabled — by `xtralab:terminals`.
20
+ * DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher`;
21
+ * consumers depend on it optionally so they survive the launcher being
22
+ * disabled.
32
23
  */
33
24
  export declare const IAgentRegistry: Token<IAgentRegistry>;
34
25
  /**
35
- * The JupyterLab command id that launches a given agent in a new terminal.
36
- * Defined here — in the shared contract module rather than the launcher's
37
- * command-registration internals — so the launcher (which registers the
38
- * commands) and any consumer (which references them, e.g. in a menu) agree
39
- * on the id.
26
+ * The command id that launches a given agent in a new terminal, defined in
27
+ * the shared contract module so the launcher and consumers agree on it.
40
28
  */
41
29
  export declare function agentCommandId(agentId: string): string;
@@ -1,16 +1,13 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
  /**
3
- * DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher` and
4
- * consumed — optionally, so the panel still works when the launcher is
5
- * disabled — by `xtralab:terminals`.
3
+ * DI token for {@link IAgentRegistry}. Provided by `xtralab:launcher`;
4
+ * consumers depend on it optionally so they survive the launcher being
5
+ * disabled.
6
6
  */
7
7
  export const IAgentRegistry = new Token('xtralab:IAgentRegistry', 'A read-only, observable view of the launcher agents, shared so other plugins can offer the same agent list and icons.');
8
8
  /**
9
- * The JupyterLab command id that launches a given agent in a new terminal.
10
- * Defined here — in the shared contract module rather than the launcher's
11
- * command-registration internals — so the launcher (which registers the
12
- * commands) and any consumer (which references them, e.g. in a menu) agree
13
- * on the id.
9
+ * The command id that launches a given agent in a new terminal, defined in
10
+ * the shared contract module so the launcher and consumers agree on it.
14
11
  */
15
12
  export function agentCommandId(agentId) {
16
13
  return `xtralab:start-agent:${agentId}`;
@@ -1,15 +1,8 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Collapse the main menu bar into a compact menu button.
4
- *
5
- * The menu bar is hidden and a hamburger button in the top bar opens the same
6
- * menus as a vertical popup. The popup reuses the live `RankedMenu` instances
7
- * owned by the `MainMenu`, so menus added or removed at runtime are reflected
8
- * without duplication.
9
- *
10
- * Hiding covers the `jp-menu-panel` container too, which has its own
11
- * `min-height` and would otherwise leave an empty strip. The "Show Menu Bar"
12
- * toggle restores the classic bar.
3
+ * Collapse the main menu bar into a hamburger button whose popup reuses the
4
+ * live `RankedMenu` instances owned by `MainMenu`. Hiding covers the
5
+ * `jp-menu-panel` container too — its `min-height` would leave an empty strip.
13
6
  */
14
7
  declare const plugin: JupyterFrontEndPlugin<void>;
15
8
  export default plugin;