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
@@ -16,87 +16,31 @@ import { deserializeQueuedPrompts, PromptQueue, serializeQueuedPrompts } from '.
16
16
  import { AskAgentQueuePanel } from './queuePanel';
17
17
  import { ASK_AGENT_COMMAND, IAskAgent } from './tokens';
18
18
  const PLUGIN_ID = 'xtralab:ask-agent';
19
- /**
20
- * Command id that reveals the queued-prompts panel.
21
- */
22
19
  const QUEUE_PANEL_COMMAND = 'xtralab:ask-agent-queue';
23
- /**
24
- * Widget id of the queued-prompts side panel.
25
- */
26
20
  const QUEUE_PANEL_ID = 'xtralab-ask-agent-queue';
27
- /**
28
- * State-database key remembering the last agent picked in the popup.
29
- */
30
21
  const LAST_AGENT_STATE_KEY = 'xtralab:ask-agent:agent';
31
- /**
32
- * State-database key remembering the last target picked in the popup.
33
- */
34
22
  const LAST_TARGET_STATE_KEY = 'xtralab:ask-agent:target';
35
- /**
36
- * State-database key holding the queued prompts across page loads.
37
- */
38
23
  const QUEUE_STATE_KEY = 'xtralab:ask-agent:queue';
39
- /**
40
- * Debounce for mirroring the queue into the state database, so typing in
41
- * the panel's textareas does not issue a write per keystroke.
42
- */
43
24
  const QUEUE_PERSIST_MS = 500;
44
- /**
45
- * Stored target value for "start the agent in a new terminal".
46
- */
47
25
  const NEW_TARGET_VALUE = 'new';
48
- /**
49
- * Stored-target prefix for an existing session; the session name follows.
50
- */
51
26
  const SESSION_TARGET_PREFIX = 'session:';
52
27
  /**
53
- * Delay between a selection change and showing the pill, so the affordance
54
- * appears once the selection settles rather than flickering along a drag.
28
+ * Delay before showing the pill, so it does not flicker along a drag.
55
29
  */
56
30
  const SELECTION_SETTLE_MS = 250;
57
31
  /**
58
- * Keystroke that opens the popup in a file editor. `Accel I` would be the
59
- * familiar "inline chat" chord but CodeMirror's default keymap owns `Mod-i`
60
- * (selectParentSyntax) inside editors and prevents-default before Lumino
61
- * sees it; `Accel .` — the "quick fix" chord elsewhere — is free in the
62
- * editor, the browser and JupyterLab.
32
+ * `Accel I` would be the familiar "inline chat" chord but CodeMirror's
33
+ * default keymap owns `Mod-i` and prevents-default before Lumino sees it;
34
+ * `Accel .` is free in the editor, the browser and JupyterLab.
63
35
  */
64
36
  const ASK_AGENT_KEYS = 'Accel .';
65
- /**
66
- * Gap between the pill and the selection anchor, in pixels.
67
- */
68
37
  const PILL_GAP = 6;
69
- /**
70
- * Minimum distance kept between the pill and the viewport edges.
71
- */
72
38
  const PILL_VIEWPORT_MARGIN = 8;
73
39
  /**
74
- * Select code in a file editor (or pick diff lines) and prompt a coding
75
- * agent about it.
76
- *
77
- * The plugin watches the document selection: a non-empty selection inside a
78
- * CodeMirror file editor or notebook cell grows a small floating "Ask agent"
79
- * pill next to the selection. Clicking it (or running `xtralab:ask-agent`,
80
- * bound to Accel+. in editors and notebooks) opens a popup where the user
81
- * types an instruction, picks one of the launcher's agents and picks where
82
- * the prompt goes; submitting either starts that agent in a fresh terminal
83
- * via `xtralab:start-agent:<id>`, or pastes the prompt into an agent already
84
- * running in one of the open terminal sessions (via `IAgentTerminals`; the
85
- * agent CLIs queue prompts that arrive while they are busy). Sends to a
86
- * running agent stay in the background — focus never leaves the editor, and
87
- * a success toast offers to open the terminal. Either way the prompt embeds
88
- * the file path, cell index for notebooks, line range and selected snippet.
89
- *
90
- * The popup's Queue button (or Accel+Enter) defers the send instead: the
91
- * comment lands in a persistent queue, stamped with the popup's selected
92
- * destination — every queued prompt is independent and may aim at a
93
- * different agent or session. A right-sidebar panel reviews the queue
94
- * (edit, retarget, remove) and flushes it in one go, combining prompts that
95
- * share a destination into a single numbered message. The queue survives
96
- * page reloads.
97
- *
98
- * The same popup is provided on the `IAskAgent` token so the git diff
99
- * viewers can open it for a selected diff line range.
40
+ * Prompt a coding agent about selected code: a pill/popup over editor,
41
+ * notebook-cell and git-diff selections sends the instruction to a fresh
42
+ * agent terminal or into a running one, or defers it into a persistent
43
+ * queue reviewed in a right-sidebar panel and flushed as one batch.
100
44
  */
101
45
  const plugin = {
102
46
  id: PLUGIN_ID,
@@ -115,16 +59,15 @@ const plugin = {
115
59
  activate: (app, agentRegistry, palette, translator, agentTerminals, labShell, restorer, state) => {
116
60
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
117
61
  /**
118
- * Best-effort write to the state database; a failure only loses the
119
- * convenience of restoring this value on the next page load.
62
+ * Best-effort state-database write; a failure only loses a restored default.
120
63
  */
121
64
  const saveState = (key, value) => {
122
65
  state === null || state === void 0 ? void 0 : state.save(key, value).catch(error => {
123
66
  console.error('xtralab: failed to persist ask-agent state', error);
124
67
  });
125
68
  };
126
- // The last-used picks, restored asynchronously from the state database
127
- // and kept in memory so the popup can read them synchronously.
69
+ // Last-used picks, restored asynchronously but read synchronously by
70
+ // the popup.
128
71
  let lastAgentId = null;
129
72
  let lastTarget = null;
130
73
  if (state !== null) {
@@ -152,20 +95,14 @@ const plugin = {
152
95
  lastTarget = value;
153
96
  saveState(LAST_TARGET_STATE_KEY, value);
154
97
  };
155
- /**
156
- * Agents that can receive an initial prompt on their command line.
157
- */
158
98
  const promptAgents = () => {
159
99
  var _a;
160
100
  return ((_a = agentRegistry === null || agentRegistry === void 0 ? void 0 : agentRegistry.agents) !== null && _a !== void 0 ? _a : []).filter(agent => agent.promptArgs !== undefined);
161
101
  };
162
102
  /**
163
- * Running agent terminals offered as prompt targets, each resolved to
164
- * its agent's icon the same way the terminals panel badges its rows
165
- * (matching either the configured command or the canonical id, falling
166
- * back to the plain terminal icon when the agent has since been removed
167
- * from the settings). Unlike {@link promptAgents} this does not require
168
- * `promptArgs`: pasting into a running TUI needs no command-line recipe.
103
+ * Running agent terminals offered as prompt targets, badged with the
104
+ * matching agent's icon like the terminals panel (configured command or
105
+ * canonical id). Pasting into a running TUI needs no `promptArgs`.
169
106
  */
170
107
  const sessionTargets = () => {
171
108
  var _a;
@@ -194,24 +131,18 @@ const plugin = {
194
131
  pill.style.display = 'none';
195
132
  }
196
133
  };
197
- /**
198
- * Open the named terminal's tab (the success toast's call to action).
199
- */
200
134
  const openTerminal = (name) => {
201
135
  app.commands.execute('terminal:open', { name }).catch(reason => {
202
136
  console.error('xtralab: failed to open the terminal', reason);
203
137
  });
204
138
  };
205
139
  /**
206
- * Paste `prompt` into the running agent in session `name`. Delivery is
207
- * background — focus stays where it is — so `successMessage` is toasted
208
- * with an "Open" action instead. Failures are toasted too; the promise
209
- * still rejects so callers can chain their own cleanup to success only.
140
+ * Paste `prompt` into the running agent in session `name`, in the
141
+ * background. Failures are toasted but still reject, so callers can
142
+ * chain their own cleanup to success only.
210
143
  */
211
144
  const deliverToSession = async (name, prompt, successMessage) => {
212
145
  if (agentTerminals === null) {
213
- // Session targets are only offered when the terminals plugin is
214
- // present; fail loudly rather than resolving as a delivered send.
215
146
  throw new Error('xtralab: agent terminals are unavailable');
216
147
  }
217
148
  try {
@@ -234,8 +165,7 @@ const plugin = {
234
165
  };
235
166
  /**
236
167
  * Start `agentId` in a fresh terminal with `prompt` on its command
237
- * line. Failures are toasted; the promise still rejects so callers can
238
- * chain their own cleanup to success only.
168
+ * line. Failures are toasted but still reject.
239
169
  */
240
170
  const startAgentTerminal = async (agentId, prompt, cwd) => {
241
171
  const args = { prompt };
@@ -251,9 +181,8 @@ const plugin = {
251
181
  throw error;
252
182
  }
253
183
  };
254
- // The queue feature needs a side area to dock the review panel into, so
255
- // everything below stays off (and the popup hides its Queue button)
256
- // without the full Lab shell.
184
+ // The queue needs a side area for its review panel, so it stays off
185
+ // (and the popup hides its Queue button) without the full Lab shell.
257
186
  let queue = null;
258
187
  let queuePrompt = null;
259
188
  if (labShell !== null) {
@@ -261,8 +190,8 @@ const plugin = {
261
190
  queue = promptQueue;
262
191
  let panel = null;
263
192
  let flushing = false;
264
- // Mirror every queue change into the state database, debounced so
265
- // typing in the panel's textareas does not write on each keystroke.
193
+ // Debounced so typing in the panel's textareas does not write on
194
+ // each keystroke.
266
195
  const persistQueue = new Debouncer(() => {
267
196
  saveState(QUEUE_STATE_KEY, serializeQueuedPrompts(promptQueue.items));
268
197
  }, QUEUE_PERSIST_MS);
@@ -270,12 +199,9 @@ const plugin = {
270
199
  void persistQueue.invoke();
271
200
  });
272
201
  /**
273
- * The prompt (and working directory) for a batch sent to a new
274
- * terminal. When every item agrees on a repository root the terminal
275
- * starts there and the item paths stay relative to it; a mixed batch
276
- * starts at the server root instead, with every locator rewritten to
277
- * a server-relative path so none of them depends on the terminal's
278
- * cwd.
202
+ * The prompt (and cwd) for a batch sent to a new terminal: a shared
203
+ * repository root becomes the cwd; a mixed batch starts at the server
204
+ * root with every locator rewritten server-relative instead.
279
205
  */
280
206
  const newTerminalBatch = (items) => {
281
207
  const cwds = new Set(items.map(item => { var _a; return (_a = item.context.cwd) !== null && _a !== void 0 ? _a : ''; }));
@@ -297,13 +223,10 @@ const plugin = {
297
223
  return { prompt: buildBatchPrompt(normalized) };
298
224
  };
299
225
  /**
300
- * Flush the queue: prompts are grouped by destination — every prompt
301
- * is independent and may target a different agent or session — and
302
- * each group goes out as one numbered message. A group's prompts
303
- * leave the queue only when its delivery succeeds, so one dead
304
- * session never loses the comments aimed elsewhere. One flush runs
305
- * at a time — deliveries span server round trips, and a second click
306
- * meanwhile would re-send every still-queued group.
226
+ * Flush the queue: prompts are grouped by destination and each group
227
+ * goes out as one numbered message, leaving the queue only when its
228
+ * delivery succeeds. One flush at a time — a second click mid-flight
229
+ * would re-send every still-queued group.
307
230
  */
308
231
  const sendQueue = () => {
309
232
  var _a, _b, _c;
@@ -313,8 +236,8 @@ const plugin = {
313
236
  const groups = new Map();
314
237
  for (const item of promptQueue.items) {
315
238
  if (item.target === null) {
316
- // The panel disables sending while a target is missing; skip
317
- // defensively if a send races a target loss.
239
+ // The panel disables sending without a target; skip if a send
240
+ // races a target loss.
318
241
  continue;
319
242
  }
320
243
  const key = item.target.kind === 'session'
@@ -328,9 +251,8 @@ const plugin = {
328
251
  for (const { target, items } of groups.values()) {
329
252
  /**
330
253
  * Remove the delivered prompts — except any edited or retargeted
331
- * while the send was in flight: an edit makes a new object, so
332
- * identity is the "unchanged" check, and the edited prompt stays
333
- * queued for the next flush instead of being dropped unsent.
254
+ * in flight: an edit makes a new object, so identity is the
255
+ * "unchanged" check and the edited prompt stays queued.
334
256
  */
335
257
  const delivered = () => {
336
258
  promptQueue.removeMany(items
@@ -359,16 +281,12 @@ const plugin = {
359
281
  }
360
282
  flushing = true;
361
283
  panel === null || panel === void 0 ? void 0 : panel.update();
362
- // Every delivery caught its own failure above, so this resolves
363
- // once all groups have settled either way.
284
+ // Every delivery caught its own failure, so this settles either way.
364
285
  void Promise.all(deliveries).then(() => {
365
286
  flushing = false;
366
287
  panel === null || panel === void 0 ? void 0 : panel.update();
367
288
  });
368
289
  };
369
- /**
370
- * Empty the queue, with an undo toast (typed comments are work).
371
- */
372
290
  const clearQueue = () => {
373
291
  const removed = promptQueue.items;
374
292
  if (removed.length === 0) {
@@ -380,8 +298,8 @@ const plugin = {
380
298
  actions: [
381
299
  {
382
300
  label: trans.__('Undo'),
383
- // Prompts queued after the clear are newer; keep them,
384
- // after the restored ones.
301
+ // Prompts queued after the clear are newer; keep them after
302
+ // the restored ones.
385
303
  callback: () => promptQueue.reset([...removed, ...promptQueue.items])
386
304
  }
387
305
  ]
@@ -403,12 +321,9 @@ const plugin = {
403
321
  created.title.icon = askAgentIcon;
404
322
  created.title.label = trans.__('Prompt Queue');
405
323
  created.title.caption = trans.__('Queued ask-agent prompts');
406
- // Closable so it gets a close button if the user drags it out of
407
- // the side area; closing disposes it, and it is recreated on
408
- // demand (same arrangement as the walkthrough panel).
324
+ // Closable so it gets a close button when dragged out of the side
325
+ // area; closing disposes it and it is recreated on demand.
409
326
  created.title.closable = true;
410
- // The panel re-renders on queue changes itself; the chips also
411
- // mirror the live agent list and terminal sessions.
412
327
  agentRegistry === null || agentRegistry === void 0 ? void 0 : agentRegistry.changed.connect(created.update, created);
413
328
  agentTerminals === null || agentTerminals === void 0 ? void 0 : agentTerminals.changed.connect(created.update, created);
414
329
  labShell.add(created, 'right', { rank: 900 });
@@ -428,8 +343,6 @@ const plugin = {
428
343
  queuePrompt = (request, target, instruction) => {
429
344
  var _a;
430
345
  const wasEmpty = promptQueue.items.length === 0;
431
- // Queueing is a target choice like sending: remember it for the
432
- // next popup's preselection.
433
346
  if (target.kind === 'session') {
434
347
  rememberTarget(SESSION_TARGET_PREFIX + target.name);
435
348
  }
@@ -438,18 +351,14 @@ const plugin = {
438
351
  rememberTarget(NEW_TARGET_VALUE);
439
352
  }
440
353
  promptQueue.add(request.context, instruction, target);
441
- // Queueing is background like a session send: hand focus straight
442
- // back to the widget the ask came from.
443
354
  (_a = app.shell.currentWidget) === null || _a === void 0 ? void 0 : _a.activate();
444
355
  const queuePanel = ensurePanel();
445
356
  if (queuePanel.isVisible) {
446
- // The list visibly grows; no extra feedback needed.
447
357
  return;
448
358
  }
449
359
  if (wasEmpty) {
450
- // First prompt of a batch: reveal the panel once, so the user
451
- // sees where queued prompts accumulate. Later additions respect
452
- // a deliberately closed sidebar and toast instead.
360
+ // Reveal the panel once for the first prompt; later additions
361
+ // respect a deliberately closed sidebar and toast instead.
453
362
  labShell.activateById(queuePanel.id);
454
363
  return;
455
364
  }
@@ -464,10 +373,8 @@ const plugin = {
464
373
  icon: askAgentIcon,
465
374
  execute: revealPanel
466
375
  });
467
- // Restore the queue persisted by a previous page load. Anything
468
- // queued before the fetch resolves is newer and stays, after the
469
- // restored prompts. A restored queue should be discoverable without
470
- // queueing anything new, so its sidebar tab is recreated (collapsed).
376
+ // Prompts queued before the fetch resolves are newer and stay, after
377
+ // the restored ones; the tab is recreated so the restore is discoverable.
471
378
  if (state !== null) {
472
379
  void state
473
380
  .fetch(QUEUE_STATE_KEY)
@@ -488,11 +395,8 @@ const plugin = {
488
395
  closePopup();
489
396
  const agents = promptAgents();
490
397
  const targets = sessionTargets();
491
- // Preselect where the prompt goes. Queueing into a running agent is
492
- // the common follow-up loop, so a live session wins by default — the
493
- // last-used one when it is still running, otherwise the first — unless
494
- // the user explicitly chose a new terminal last time (and one can
495
- // still be started).
398
+ // Preselect: a live session wins (last-used if still running, else the
399
+ // first) unless a new terminal was last chosen and can still start.
496
400
  const storedTarget = lastTarget;
497
401
  let initialTargetName = targets.length > 0 ? targets[0].name : null;
498
402
  if (storedTarget === NEW_TARGET_VALUE && agents.length > 0) {
@@ -534,9 +438,8 @@ const plugin = {
534
438
  const prompt = buildPrompt(request.context, instruction);
535
439
  if (target.kind === 'session') {
536
440
  rememberTarget(SESSION_TARGET_PREFIX + target.name);
537
- // The send is background, so hand focus straight back to the
538
- // widget the ask came from (the editor or diff under the popup);
539
- // disposing the popup alone would drop it on `document.body`.
441
+ // The send is background: hand focus back to the widget the ask
442
+ // came from — disposing the popup alone drops it on `document.body`.
540
443
  (_a = app.shell.currentWidget) === null || _a === void 0 ? void 0 : _a.activate();
541
444
  const label = (_c = (_b = targets.find(entry => entry.name === target.name)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : target.name;
542
445
  deliverToSession(target.name, prompt, trans.__('Prompt sent to %1', label)).catch(() => {
@@ -595,9 +498,8 @@ const plugin = {
595
498
  const button = ensurePill();
596
499
  pillRequest = request;
597
500
  button.style.display = 'flex';
598
- // Measure after display so the rect is real, then sit the pill just
599
- // above the selection head (below it when that would leave the
600
- // viewport).
501
+ // Measure after display so the rect is real; the pill sits above the
502
+ // selection head, below it when that would leave the viewport.
601
503
  const rect = button.getBoundingClientRect();
602
504
  let left = anchor.left;
603
505
  let top = anchor.top - rect.height - PILL_GAP;
@@ -625,9 +527,6 @@ const plugin = {
625
527
  hidePill();
626
528
  return;
627
529
  }
628
- // The popup can send somewhere as long as an agent takes a prompt on
629
- // its command line (new terminal) or one is already running (existing
630
- // session) — only with neither is there nothing to offer.
631
530
  if (promptAgents().length === 0 &&
632
531
  ((_a = agentTerminals === null || agentTerminals === void 0 ? void 0 : agentTerminals.sessions()) !== null && _a !== void 0 ? _a : []).length === 0) {
633
532
  hidePill();
@@ -640,9 +539,8 @@ const plugin = {
640
539
  }
641
540
  showPill(request);
642
541
  };
643
- // Each invocation resets the timer, so evaluation runs once the
644
- // selection has settled. Mid-drag the selection is still moving; the
645
- // pointerup listener invokes it again once the drag ends.
542
+ // Mid-drag the selection is still moving; the pointerup listener
543
+ // re-invokes once the drag ends.
646
544
  const settled = new Debouncer(() => {
647
545
  if (!pointerIsDown) {
648
546
  evaluateSelection();
@@ -650,8 +548,8 @@ const plugin = {
650
548
  }, SELECTION_SETTLE_MS);
651
549
  document.addEventListener('selectionchange', () => {
652
550
  if (popup !== null) {
653
- // Typing in the popup's textarea moves the document selection; the
654
- // pill must not resurface underneath the open popup.
551
+ // Typing in the popup moves the document selection; the pill must
552
+ // not resurface underneath it.
655
553
  return;
656
554
  }
657
555
  hidePill();
@@ -682,9 +580,8 @@ const plugin = {
682
580
  return;
683
581
  }
684
582
  if (popup !== null) {
685
- // Focus is outside the popup — its own key handler consumes Escape
686
- // when focus is inside — so close it here and hand focus back to
687
- // the widget the ask came from.
583
+ // The popup's own handler consumes Escape while focus is inside it;
584
+ // here focus is elsewhere, so close and restore focus.
688
585
  closePopup();
689
586
  (_a = app.shell.currentWidget) === null || _a === void 0 ? void 0 : _a.activate();
690
587
  return;
@@ -705,7 +602,6 @@ const plugin = {
705
602
  open(request);
706
603
  }
707
604
  });
708
- // One binding per editing surface: file editors and notebooks.
709
605
  for (const selector of ['.jp-FileEditor', '.jp-Notebook']) {
710
606
  app.commands.addKeyBinding({
711
607
  command: ASK_AGENT_COMMAND,
@@ -4,20 +4,19 @@ import type { IAgent } from '../launcher/agents';
4
4
  import { ISessionTarget } from './targetPicker';
5
5
  import type { AskAgentTarget, IAskAgentContext } from './tokens';
6
6
  /**
7
- * Short human-readable descriptor of the prompted range, shown in the popup
8
- * header and the queue panel: file name, notebook cell (when applicable),
9
- * line range and, for ranges that are not current file content (such as the
10
- * old side of a diff), a clarifying tag.
7
+ * Short descriptor of the prompted range for the popup header and the queue
8
+ * panel: file name, notebook cell, line range and any clarifying tag.
11
9
  */
12
10
  export declare function contextSummary(context: IAskAgentContext, trans: TranslationBundle): string;
13
11
  /**
14
- * The floating ask-agent prompt box. Hosts {@link AskAgentPopupComponent} in
15
- * a React root attached to `document.body`; `jp-ThemedContainer` makes
16
- * JupyterLab's theme variables resolve outside the shell (the omnibox uses
17
- * the same arrangement).
12
+ * The floating ask-agent prompt box, attached to `document.body`;
13
+ * `jp-ThemedContainer` makes theme variables resolve outside the shell.
18
14
  */
19
15
  export declare class AskAgentPopup extends ReactWidget {
20
16
  constructor(options: AskAgentPopup.IOptions);
17
+ /**
18
+ * Render the popup content.
19
+ */
21
20
  render(): JSX.Element;
22
21
  private _options;
23
22
  }
@@ -55,8 +54,7 @@ export declare namespace AskAgentPopup {
55
54
  */
56
55
  initialTargetName: string | null;
57
56
  /**
58
- * Number of prompts already queued, shown on the queue button. Only
59
- * meaningful together with {@link onQueue}.
57
+ * Number of prompts already queued, shown on the queue button.
60
58
  */
61
59
  queueCount?: number;
62
60
  /**
@@ -68,15 +66,13 @@ export declare namespace AskAgentPopup {
68
66
  */
69
67
  onSubmit: (target: AskAgentTarget, instruction: string) => void;
70
68
  /**
71
- * Add the instruction to the prompt queue instead of sending it, keyed
72
- * to the same chosen target (the plugin closes the popup). The queue
73
- * button is hidden when omitted.
69
+ * Queue the instruction for the chosen target instead of sending it.
70
+ * The queue button is hidden when omitted.
74
71
  */
75
72
  onQueue?: (target: AskAgentTarget, instruction: string) => void;
76
73
  /**
77
- * Dismiss the popup (the plugin disposes the widget). `restoreFocus` is
78
- * true when the dismissal placed no focus of its own (Escape), asking
79
- * the plugin to hand focus back to the widget the ask came from.
74
+ * Dismiss the popup. `restoreFocus` is true when the dismissal placed
75
+ * no focus of its own (Escape).
80
76
  */
81
77
  onCancel: (restoreFocus: boolean) => void;
82
78
  }
@@ -3,19 +3,11 @@ import { ReactWidget } from '@jupyterlab/ui-components';
3
3
  import { CommandRegistry } from '@lumino/commands';
4
4
  import * as React from 'react';
5
5
  import { AgentChoices, TargetChips } from './targetPicker';
6
- /**
7
- * Gap between the popup and its anchor rectangle, in pixels.
8
- */
9
6
  const ANCHOR_GAP = 6;
10
- /**
11
- * Minimum distance kept between the popup and the viewport edges.
12
- */
13
7
  const VIEWPORT_MARGIN = 8;
14
8
  /**
15
- * Short human-readable descriptor of the prompted range, shown in the popup
16
- * header and the queue panel: file name, notebook cell (when applicable),
17
- * line range and, for ranges that are not current file content (such as the
18
- * old side of a diff), a clarifying tag.
9
+ * Short descriptor of the prompted range for the popup header and the queue
10
+ * panel: file name, notebook cell, line range and any clarifying tag.
19
11
  */
20
12
  export function contextSummary(context, trans) {
21
13
  var _a;
@@ -43,14 +35,12 @@ function AskAgentPopupComponent(props) {
43
35
  const known = agents.find(agent => agent.id === initialAgentId);
44
36
  return (_b = (_a = (known !== null && known !== void 0 ? known : agents[0])) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
45
37
  });
46
- // The running session the prompt goes to, or `null` for a new terminal.
47
38
  const [targetName, setTargetName] = React.useState(initialTargetName);
48
39
  const [position, setPosition] = React.useState(null);
49
40
  const popupRef = React.useRef(null);
50
41
  const textareaRef = React.useRef(null);
51
- // Place the popup once its size is known: below the anchor, flipped above
52
- // when there is no room, clamped into the viewport. Hidden until placed so
53
- // the measurement never flashes at the wrong position.
42
+ // Place the popup once its size is known; hidden until placed so the
43
+ // measurement never flashes at the wrong position.
54
44
  React.useLayoutEffect(() => {
55
45
  const node = popupRef.current;
56
46
  if (node === null) {
@@ -76,10 +66,8 @@ function AskAgentPopupComponent(props) {
76
66
  top = Math.max(VIEWPORT_MARGIN, Math.min(top, viewportHeight - rect.height - VIEWPORT_MARGIN));
77
67
  setPosition({ left, top });
78
68
  }, [anchor]);
79
- // Focus the input only once the popup is placed: while it is still
80
- // unpositioned it sits `visibility: hidden`, and hidden elements silently
81
- // refuse focus. The empty state renders no textarea; focus the dialog
82
- // root itself so it is announced and Escape works without a pointer.
69
+ // Unpositioned means `visibility: hidden`, and hidden elements refuse focus.
70
+ // With no textarea (empty state) the dialog root takes focus for Escape.
83
71
  React.useEffect(() => {
84
72
  var _a, _b;
85
73
  if (position !== null) {
@@ -87,14 +75,12 @@ function AskAgentPopupComponent(props) {
87
75
  }
88
76
  }, [position]);
89
77
  const dismiss = React.useCallback((restoreFocus) => {
90
- // Defer so unmounting this React root never happens synchronously
91
- // inside the event handler that triggered it (same pattern as the
92
- // omnibox).
78
+ // Defer so this React root never unmounts synchronously inside its
79
+ // own event handler (same pattern as the omnibox).
93
80
  window.setTimeout(() => onCancel(restoreFocus), 0);
94
81
  }, [onCancel]);
95
- // Close when the user clicks anywhere outside the popup. Capture phase so
96
- // a click into widgets that swallow events still dismisses it. The click
97
- // places focus itself, so none is restored.
82
+ // Capture phase so a click into event-swallowing widgets still dismisses;
83
+ // the click places focus itself, so none is restored.
98
84
  React.useEffect(() => {
99
85
  const onPointerDown = (event) => {
100
86
  const node = popupRef.current;
@@ -113,8 +99,6 @@ function AskAgentPopupComponent(props) {
113
99
  const selectedSession = targets.find(target => target.name === targetName);
114
100
  const canSubmit = instruction.trim().length > 0 &&
115
101
  (selectedSession !== undefined || selectedAgent !== undefined);
116
- // The destination both actions use: send delivers to it now, queue stamps
117
- // it on the queued prompt (still editable in the panel later).
118
102
  const resolveTarget = React.useCallback(() => {
119
103
  const session = targets.find(entry => entry.name === targetName);
120
104
  if (session !== undefined) {
@@ -132,8 +116,7 @@ function AskAgentPopupComponent(props) {
132
116
  if (trimmed.length === 0 || target === null) {
133
117
  return;
134
118
  }
135
- // Defer so unmounting this React root never happens synchronously
136
- // inside the event handler that triggered it.
119
+ // Same deferred pattern as dismiss.
137
120
  window.setTimeout(() => {
138
121
  onSubmit(target, trimmed);
139
122
  }, 0);
@@ -153,8 +136,6 @@ function AskAgentPopupComponent(props) {
153
136
  if (event.key === 'Escape') {
154
137
  event.preventDefault();
155
138
  event.stopPropagation();
156
- // A keyboard dismissal places no focus of its own; ask the plugin to
157
- // hand it back to the widget the ask came from.
158
139
  dismiss(true);
159
140
  }
160
141
  };
@@ -177,9 +158,8 @@ function AskAgentPopupComponent(props) {
177
158
  const summary = contextSummary(context, trans);
178
159
  const placeholder = trans.__('Describe the change…');
179
160
  return (React.createElement("div", { ref: popupRef, className: "jp-xtralab-AskAgent-popup", role: "dialog", "aria-label": trans.__('Ask an agent about %1', summary),
180
- // Focusable so clicks on non-interactive popup content keep focus
181
- // inside the dialog (Escape keeps working) and so the empty state
182
- // can be focused at all.
161
+ // Focusable so clicks on non-interactive content keep Escape working
162
+ // and the empty state can take focus at all.
183
163
  tabIndex: -1, style: {
184
164
  left: (_a = position === null || position === void 0 ? void 0 : position.left) !== null && _a !== void 0 ? _a : 0,
185
165
  top: (_b = position === null || position === void 0 ? void 0 : position.top) !== null && _b !== void 0 ? _b : 0,
@@ -202,10 +182,8 @@ function AskAgentPopupComponent(props) {
202
182
  : trans.__('Ask agent'))))))));
203
183
  }
204
184
  /**
205
- * The floating ask-agent prompt box. Hosts {@link AskAgentPopupComponent} in
206
- * a React root attached to `document.body`; `jp-ThemedContainer` makes
207
- * JupyterLab's theme variables resolve outside the shell (the omnibox uses
208
- * the same arrangement).
185
+ * The floating ask-agent prompt box, attached to `document.body`;
186
+ * `jp-ThemedContainer` makes theme variables resolve outside the shell.
209
187
  */
210
188
  export class AskAgentPopup extends ReactWidget {
211
189
  constructor(options) {
@@ -215,6 +193,9 @@ export class AskAgentPopup extends ReactWidget {
215
193
  this.addClass('jp-xtralab-AskAgent');
216
194
  this.addClass('jp-ThemedContainer');
217
195
  }
196
+ /**
197
+ * Render the popup content.
198
+ */
218
199
  render() {
219
200
  return React.createElement(AskAgentPopupComponent, { ...this._options });
220
201
  }
@@ -6,11 +6,9 @@ import type { IAskAgentContext } from './tokens';
6
6
  */
7
7
  export declare function serverPath(context: IAskAgentContext): string;
8
8
  /**
9
- * Compose the prompt handed to the agent CLI: a one-line locator, the
10
- * selected snippet in a code fence, then the user's instruction.
11
- *
12
- * The scaffold is written in English on purpose — it is consumed by the
13
- * agent, not shown in the UI, and the agent CLIs are English-first.
9
+ * Compose the prompt handed to the agent CLI: locator, fenced snippet,
10
+ * instruction. The scaffold is English on purpose — consumed by the agent,
11
+ * not shown in the UI.
14
12
  */
15
13
  export declare function buildPrompt(context: IAskAgentContext, instruction: string): string;
16
14
  /**