xtralab 0.15.0 → 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 (228) 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.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 +28 -19
  36. package/lib/fileBrowser/commands.js +39 -98
  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 +68 -216
  42. package/lib/fileBrowser/gitStatus.d.ts +4 -9
  43. package/lib/fileBrowser/gitStatus.js +7 -17
  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 +126 -48
  50. package/lib/fileBrowser/widget.js +89 -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 +40 -5
  57. package/lib/git/commands.js +16 -10
  58. package/lib/git/diffModel.js +4 -0
  59. package/lib/git/diffSurface.d.ts +41 -55
  60. package/lib/git/diffSurface.js +42 -117
  61. package/lib/git/diffWidget.d.ts +81 -2
  62. package/lib/git/diffWidget.js +62 -12
  63. package/lib/git/imageDiff.d.ts +5 -6
  64. package/lib/git/imageDiff.js +18 -48
  65. package/lib/git/index.d.ts +0 -3
  66. package/lib/git/index.js +3 -8
  67. package/lib/git/notebookDiff.d.ts +110 -33
  68. package/lib/git/notebookDiff.js +52 -195
  69. package/lib/git/tokens.d.ts +73 -31
  70. package/lib/git/tokens.js +2 -3
  71. package/lib/highlight/index.d.ts +4 -10
  72. package/lib/highlight/index.js +10 -41
  73. package/lib/index.d.ts +0 -9
  74. package/lib/index.js +0 -9
  75. package/lib/launcher/agents.d.ts +55 -51
  76. package/lib/launcher/agents.js +15 -57
  77. package/lib/launcher/availability.d.ts +3 -9
  78. package/lib/launcher/availability.js +3 -9
  79. package/lib/launcher/commands.d.ts +7 -32
  80. package/lib/launcher/commands.js +14 -45
  81. package/lib/launcher/dashboard.d.ts +19 -24
  82. package/lib/launcher/dashboard.js +21 -67
  83. package/lib/launcher/editorRegistry.d.ts +10 -22
  84. package/lib/launcher/editorRegistry.js +14 -18
  85. package/lib/launcher/editors.d.ts +39 -45
  86. package/lib/launcher/editors.js +8 -34
  87. package/lib/launcher/icons.d.ts +0 -12
  88. package/lib/launcher/icons.js +5 -39
  89. package/lib/launcher/index.js +12 -55
  90. package/lib/launcher/invocation.d.ts +4 -10
  91. package/lib/launcher/invocation.js +7 -22
  92. package/lib/launcher/registry.d.ts +9 -7
  93. package/lib/launcher/registry.js +9 -7
  94. package/lib/launcher/tokens.d.ts +10 -22
  95. package/lib/launcher/tokens.js +5 -8
  96. package/lib/menuBar/index.d.ts +3 -10
  97. package/lib/menuBar/index.js +11 -28
  98. package/lib/menus/index.d.ts +3 -9
  99. package/lib/menus/index.js +14 -30
  100. package/lib/omnibox/files.d.ts +2 -4
  101. package/lib/omnibox/files.js +5 -11
  102. package/lib/omnibox/index.d.ts +4 -14
  103. package/lib/omnibox/index.js +9 -27
  104. package/lib/omnibox/model.d.ts +38 -10
  105. package/lib/omnibox/model.js +15 -33
  106. package/lib/omnibox/recents.d.ts +15 -17
  107. package/lib/omnibox/recents.js +12 -17
  108. package/lib/omnibox/tokens.d.ts +5 -10
  109. package/lib/omnibox/tokens.js +2 -6
  110. package/lib/omnibox/widget.d.ts +11 -7
  111. package/lib/omnibox/widget.js +8 -16
  112. package/lib/searchReplace/index.d.ts +4 -20
  113. package/lib/searchReplace/index.js +7 -27
  114. package/lib/showOutput/index.d.ts +3 -12
  115. package/lib/showOutput/index.js +6 -22
  116. package/lib/sidebar/index.d.ts +4 -7
  117. package/lib/sidebar/index.js +5 -12
  118. package/lib/terminalNotifications/index.d.ts +4 -10
  119. package/lib/terminalNotifications/index.js +6 -20
  120. package/lib/terminals/agentTerminals.d.ts +12 -5
  121. package/lib/terminals/agentTerminals.js +25 -44
  122. package/lib/terminals/detection.d.ts +4 -8
  123. package/lib/terminals/detection.js +4 -8
  124. package/lib/terminals/index.d.ts +0 -35
  125. package/lib/terminals/index.js +12 -101
  126. package/lib/terminals/model.d.ts +64 -139
  127. package/lib/terminals/model.js +80 -220
  128. package/lib/terminals/tokens.d.ts +11 -22
  129. package/lib/terminals/tokens.js +2 -2
  130. package/lib/terminals/widget.d.ts +43 -13
  131. package/lib/terminals/widget.js +33 -18
  132. package/lib/topBar/icons.d.ts +3 -8
  133. package/lib/topBar/icons.js +3 -8
  134. package/lib/topBar/index.d.ts +4 -11
  135. package/lib/topBar/index.js +10 -38
  136. package/lib/walkthrough/index.d.ts +4 -9
  137. package/lib/walkthrough/index.js +6 -20
  138. package/lib/walkthrough/panel.d.ts +40 -9
  139. package/lib/walkthrough/panel.js +4 -10
  140. package/package.json +2 -2
  141. package/src/about/icons.ts +0 -5
  142. package/src/about/index.tsx +6 -17
  143. package/src/agentSessions.ts +9 -29
  144. package/src/askAgent/editorSelection.ts +22 -29
  145. package/src/askAgent/icons.ts +2 -3
  146. package/src/askAgent/index.ts +54 -158
  147. package/src/askAgent/popup.tsx +23 -45
  148. package/src/askAgent/prompt.ts +6 -19
  149. package/src/askAgent/queue.ts +13 -24
  150. package/src/askAgent/queuePanel.tsx +21 -46
  151. package/src/askAgent/targetPicker.tsx +5 -15
  152. package/src/askAgent/tokens.ts +32 -46
  153. package/src/commandBar/index.ts +24 -39
  154. package/src/customPanel/index.ts +15 -0
  155. package/src/editorBreadcrumbs/index.ts +10 -6
  156. package/src/editorBreadcrumbs/widget.ts +23 -17
  157. package/src/editorIndent/index.ts +17 -23
  158. package/src/fileBrowser/commands.ts +57 -98
  159. package/src/fileBrowser/contents.ts +3 -0
  160. package/src/fileBrowser/dragAndDrop.ts +17 -7
  161. package/src/fileBrowser/fileBrowser.tsx +84 -223
  162. package/src/fileBrowser/gitStatus.ts +7 -17
  163. package/src/fileBrowser/gitignore.ts +4 -25
  164. package/src/fileBrowser/icons.ts +25 -80
  165. package/src/fileBrowser/index.ts +2 -4
  166. package/src/fileBrowser/widget.tsx +149 -50
  167. package/src/fileTypeIcons/index.ts +0 -10
  168. package/src/git/api.ts +10 -36
  169. package/src/git/askRequest.ts +6 -20
  170. package/src/git/commands.ts +70 -10
  171. package/src/git/diffModel.ts +25 -0
  172. package/src/git/diffSurface.tsx +71 -159
  173. package/src/git/diffWidget.tsx +104 -14
  174. package/src/git/imageDiff.tsx +45 -51
  175. package/src/git/index.ts +3 -8
  176. package/src/git/notebookDiff.tsx +211 -238
  177. package/src/git/tokens.ts +73 -31
  178. package/src/highlight/index.ts +10 -41
  179. package/src/index.ts +0 -9
  180. package/src/launcher/agents.ts +64 -95
  181. package/src/launcher/availability.ts +3 -9
  182. package/src/launcher/commands.ts +14 -51
  183. package/src/launcher/dashboard.tsx +49 -88
  184. package/src/launcher/editorRegistry.ts +21 -29
  185. package/src/launcher/editors.ts +39 -58
  186. package/src/launcher/icons.ts +5 -39
  187. package/src/launcher/index.ts +12 -55
  188. package/src/launcher/invocation.ts +7 -22
  189. package/src/launcher/registry.ts +9 -7
  190. package/src/launcher/tokens.ts +10 -22
  191. package/src/menuBar/index.ts +11 -28
  192. package/src/menus/index.ts +14 -30
  193. package/src/omnibox/files.ts +10 -14
  194. package/src/omnibox/index.ts +9 -27
  195. package/src/omnibox/model.ts +50 -38
  196. package/src/omnibox/recents.ts +17 -22
  197. package/src/omnibox/tokens.ts +5 -10
  198. package/src/omnibox/widget.tsx +14 -19
  199. package/src/searchReplace/index.ts +7 -27
  200. package/src/showOutput/index.ts +6 -22
  201. package/src/sidebar/index.ts +20 -12
  202. package/src/terminalNotifications/index.ts +42 -25
  203. package/src/terminals/agentTerminals.ts +25 -49
  204. package/src/terminals/detection.ts +4 -8
  205. package/src/terminals/index.ts +12 -101
  206. package/src/terminals/model.ts +130 -246
  207. package/src/terminals/tokens.ts +11 -22
  208. package/src/terminals/widget.tsx +48 -31
  209. package/src/topBar/icons.ts +3 -8
  210. package/src/topBar/index.ts +21 -44
  211. package/src/walkthrough/index.ts +6 -20
  212. package/src/walkthrough/panel.ts +40 -10
  213. package/style/about.css +2 -7
  214. package/style/askAgent.css +8 -40
  215. package/style/base.css +9 -45
  216. package/style/chrome.css +108 -308
  217. package/style/commandBar.css +4 -20
  218. package/style/customPanel.css +1 -4
  219. package/style/git.css +22 -184
  220. package/style/highlight.css +1 -3
  221. package/style/launcher.css +6 -70
  222. package/style/omnibox.css +2 -14
  223. package/style/showOutput.css +0 -6
  224. package/style/sidebar.css +29 -9
  225. package/style/tabs.css +5 -13
  226. package/style/terminals.css +6 -53
  227. package/style/topBar.css +2 -22
  228. package/style/walkthrough.css +0 -8
@@ -9,21 +9,13 @@ import type { IAgent } from '../launcher/agents';
9
9
  import { AgentChoices, ISessionTarget, TargetChips } from './targetPicker';
10
10
  import type { AskAgentTarget, IAskAgentContext } from './tokens';
11
11
 
12
- /**
13
- * Gap between the popup and its anchor rectangle, in pixels.
14
- */
15
12
  const ANCHOR_GAP = 6;
16
13
 
17
- /**
18
- * Minimum distance kept between the popup and the viewport edges.
19
- */
20
14
  const VIEWPORT_MARGIN = 8;
21
15
 
22
16
  /**
23
- * Short human-readable descriptor of the prompted range, shown in the popup
24
- * header and the queue panel: file name, notebook cell (when applicable),
25
- * line range and, for ranges that are not current file content (such as the
26
- * old side of a diff), a clarifying tag.
17
+ * Short descriptor of the prompted range for the popup header and the queue
18
+ * panel: file name, notebook cell, line range and any clarifying tag.
27
19
  */
28
20
  export function contextSummary(
29
21
  context: IAskAgentContext,
@@ -66,7 +58,6 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
66
58
  const known = agents.find(agent => agent.id === initialAgentId);
67
59
  return (known ?? agents[0])?.id ?? '';
68
60
  });
69
- // The running session the prompt goes to, or `null` for a new terminal.
70
61
  const [targetName, setTargetName] = React.useState<string | null>(
71
62
  initialTargetName
72
63
  );
@@ -77,9 +68,8 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
77
68
  const popupRef = React.useRef<HTMLDivElement>(null);
78
69
  const textareaRef = React.useRef<HTMLTextAreaElement>(null);
79
70
 
80
- // Place the popup once its size is known: below the anchor, flipped above
81
- // when there is no room, clamped into the viewport. Hidden until placed so
82
- // the measurement never flashes at the wrong position.
71
+ // Place the popup once its size is known; hidden until placed so the
72
+ // measurement never flashes at the wrong position.
83
73
  React.useLayoutEffect(() => {
84
74
  const node = popupRef.current;
85
75
  if (node === null) {
@@ -111,10 +101,8 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
111
101
  setPosition({ left, top });
112
102
  }, [anchor]);
113
103
 
114
- // Focus the input only once the popup is placed: while it is still
115
- // unpositioned it sits `visibility: hidden`, and hidden elements silently
116
- // refuse focus. The empty state renders no textarea; focus the dialog
117
- // root itself so it is announced and Escape works without a pointer.
104
+ // Unpositioned means `visibility: hidden`, and hidden elements refuse focus.
105
+ // With no textarea (empty state) the dialog root takes focus for Escape.
118
106
  React.useEffect(() => {
119
107
  if (position !== null) {
120
108
  (textareaRef.current ?? popupRef.current)?.focus();
@@ -123,17 +111,15 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
123
111
 
124
112
  const dismiss = React.useCallback(
125
113
  (restoreFocus: boolean) => {
126
- // Defer so unmounting this React root never happens synchronously
127
- // inside the event handler that triggered it (same pattern as the
128
- // omnibox).
114
+ // Defer so this React root never unmounts synchronously inside its
115
+ // own event handler (same pattern as the omnibox).
129
116
  window.setTimeout(() => onCancel(restoreFocus), 0);
130
117
  },
131
118
  [onCancel]
132
119
  );
133
120
 
134
- // Close when the user clicks anywhere outside the popup. Capture phase so
135
- // a click into widgets that swallow events still dismisses it. The click
136
- // places focus itself, so none is restored.
121
+ // Capture phase so a click into event-swallowing widgets still dismisses;
122
+ // the click places focus itself, so none is restored.
137
123
  React.useEffect(() => {
138
124
  const onPointerDown = (event: PointerEvent): void => {
139
125
  const node = popupRef.current;
@@ -157,8 +143,6 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
157
143
  instruction.trim().length > 0 &&
158
144
  (selectedSession !== undefined || selectedAgent !== undefined);
159
145
 
160
- // The destination both actions use: send delivers to it now, queue stamps
161
- // it on the queued prompt (still editable in the panel later).
162
146
  const resolveTarget = React.useCallback((): AskAgentTarget | null => {
163
147
  const session = targets.find(entry => entry.name === targetName);
164
148
  if (session !== undefined) {
@@ -177,8 +161,7 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
177
161
  if (trimmed.length === 0 || target === null) {
178
162
  return;
179
163
  }
180
- // Defer so unmounting this React root never happens synchronously
181
- // inside the event handler that triggered it.
164
+ // Same deferred pattern as dismiss.
182
165
  window.setTimeout(() => {
183
166
  onSubmit(target, trimmed);
184
167
  }, 0);
@@ -200,8 +183,6 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
200
183
  if (event.key === 'Escape') {
201
184
  event.preventDefault();
202
185
  event.stopPropagation();
203
- // A keyboard dismissal places no focus of its own; ask the plugin to
204
- // hand it back to the widget the ask came from.
205
186
  dismiss(true);
206
187
  }
207
188
  };
@@ -234,9 +215,8 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
234
215
  className="jp-xtralab-AskAgent-popup"
235
216
  role="dialog"
236
217
  aria-label={trans.__('Ask an agent about %1', summary)}
237
- // Focusable so clicks on non-interactive popup content keep focus
238
- // inside the dialog (Escape keeps working) and so the empty state
239
- // can be focused at all.
218
+ // Focusable so clicks on non-interactive content keep Escape working
219
+ // and the empty state can take focus at all.
240
220
  tabIndex={-1}
241
221
  style={{
242
222
  left: position?.left ?? 0,
@@ -324,10 +304,8 @@ function AskAgentPopupComponent(props: AskAgentPopup.IOptions): JSX.Element {
324
304
  }
325
305
 
326
306
  /**
327
- * The floating ask-agent prompt box. Hosts {@link AskAgentPopupComponent} in
328
- * a React root attached to `document.body`; `jp-ThemedContainer` makes
329
- * JupyterLab's theme variables resolve outside the shell (the omnibox uses
330
- * the same arrangement).
307
+ * The floating ask-agent prompt box, attached to `document.body`;
308
+ * `jp-ThemedContainer` makes theme variables resolve outside the shell.
331
309
  */
332
310
  export class AskAgentPopup extends ReactWidget {
333
311
  constructor(options: AskAgentPopup.IOptions) {
@@ -338,6 +316,9 @@ export class AskAgentPopup extends ReactWidget {
338
316
  this.addClass('jp-ThemedContainer');
339
317
  }
340
318
 
319
+ /**
320
+ * Render the popup content.
321
+ */
341
322
  render(): JSX.Element {
342
323
  return <AskAgentPopupComponent {...this._options} />;
343
324
  }
@@ -379,8 +360,7 @@ export namespace AskAgentPopup {
379
360
  */
380
361
  initialTargetName: string | null;
381
362
  /**
382
- * Number of prompts already queued, shown on the queue button. Only
383
- * meaningful together with {@link onQueue}.
363
+ * Number of prompts already queued, shown on the queue button.
384
364
  */
385
365
  queueCount?: number;
386
366
  /**
@@ -392,15 +372,13 @@ export namespace AskAgentPopup {
392
372
  */
393
373
  onSubmit: (target: AskAgentTarget, instruction: string) => void;
394
374
  /**
395
- * Add the instruction to the prompt queue instead of sending it, keyed
396
- * to the same chosen target (the plugin closes the popup). The queue
397
- * button is hidden when omitted.
375
+ * Queue the instruction for the chosen target instead of sending it.
376
+ * The queue button is hidden when omitted.
398
377
  */
399
378
  onQueue?: (target: AskAgentTarget, instruction: string) => void;
400
379
  /**
401
- * Dismiss the popup (the plugin disposes the widget). `restoreFocus` is
402
- * true when the dismissal placed no focus of its own (Escape), asking
403
- * the plugin to hand focus back to the widget the ask came from.
380
+ * Dismiss the popup. `restoreFocus` is true when the dismissal placed
381
+ * no focus of its own (Escape).
404
382
  */
405
383
  onCancel: (restoreFocus: boolean) => void;
406
384
  }
@@ -14,10 +14,8 @@ export function serverPath(context: IAskAgentContext): string {
14
14
  }
15
15
 
16
16
  /**
17
- * Cap on the snippet embedded in the prompt, in characters. The path and
18
- * line range are the authoritative pointers — agents open the file
19
- * themselves — so a very long selection is trimmed instead of being typed
20
- * into the terminal wholesale.
17
+ * Cap on the embedded snippet; the path and line range are the
18
+ * authoritative pointers, so a very long selection is trimmed.
21
19
  */
22
20
  const MAX_SNIPPET_CHARS = 2000;
23
21
 
@@ -33,10 +31,6 @@ function fenceFor(text: string): string {
33
31
  return '`'.repeat(Math.max(3, longest + 1));
34
32
  }
35
33
 
36
- /**
37
- * Trim `text` to {@link MAX_SNIPPET_CHARS}, cutting at a line boundary.
38
- * Returns the (possibly shortened) snippet and whether anything was dropped.
39
- */
40
34
  function clampSnippet(text: string): { snippet: string; truncated: boolean } {
41
35
  if (text.length <= MAX_SNIPPET_CHARS) {
42
36
  return { snippet: text, truncated: false };
@@ -58,7 +52,7 @@ function locatorFor(context: IAskAgentContext): string {
58
52
  const where: string[] = [`\`${context.path}\``];
59
53
  if (context.cell !== undefined) {
60
54
  // Both orderings so the agent can count cells or index the nbformat
61
- // `cells` array, whichever its notebook tooling prefers.
55
+ // `cells` array.
62
56
  where.push(
63
57
  `${context.cell.type} cell ${context.cell.index + 1} ` +
64
58
  `(0-based index ${context.cell.index})`
@@ -78,11 +72,6 @@ function locatorFor(context: IAskAgentContext): string {
78
72
  return where.join(', ');
79
73
  }
80
74
 
81
- /**
82
- * The blocks describing one comment: `headline`, the selected snippet in a
83
- * code fence (when there is one), then the user's instruction. Blocks are
84
- * joined with blank lines by the callers.
85
- */
86
75
  function commentParts(
87
76
  context: IAskAgentContext,
88
77
  instruction: string,
@@ -102,11 +91,9 @@ function commentParts(
102
91
  }
103
92
 
104
93
  /**
105
- * Compose the prompt handed to the agent CLI: a one-line locator, the
106
- * selected snippet in a code fence, then the user's instruction.
107
- *
108
- * The scaffold is written in English on purpose — it is consumed by the
109
- * agent, not shown in the UI, and the agent CLIs are English-first.
94
+ * Compose the prompt handed to the agent CLI: locator, fenced snippet,
95
+ * instruction. The scaffold is English on purpose — consumed by the agent,
96
+ * not shown in the UI.
110
97
  */
111
98
  export function buildPrompt(
112
99
  context: IAskAgentContext,
@@ -5,8 +5,8 @@ import type { AskAgentTarget, IAskAgentContext } from './tokens';
5
5
 
6
6
  /**
7
7
  * One queued prompt: a code selection, the user's instruction about it, and
8
- * where it should go. Every prompt is independent — a queue can mix targets
9
- * (different agents, new terminals, different running sessions) freely.
8
+ * where it should go. Every prompt is independent — a queue mixes targets
9
+ * freely.
10
10
  */
11
11
  export interface IQueuedPrompt {
12
12
  /**
@@ -25,9 +25,8 @@ export interface IQueuedPrompt {
25
25
  instruction: string;
26
26
 
27
27
  /**
28
- * The destination picked for this prompt (captured from the popup when it
29
- * was queued, editable in the panel). `null` when a persisted target no
30
- * longer parses — the panel then asks for a new pick before sending.
28
+ * The destination picked in the popup, editable in the panel. `null` when
29
+ * a persisted target no longer parses — the panel then asks for a new pick.
31
30
  */
32
31
  target: AskAgentTarget | null;
33
32
  }
@@ -39,15 +38,9 @@ function nextId(): string {
39
38
  }
40
39
 
41
40
  /**
42
- * The accumulated ask-agent prompts awaiting a batch send.
43
- *
44
- * Queueing decouples writing review comments from deciding where they go:
45
- * the popup appends entries here instead of sending, the side panel lists
46
- * and edits them, and one batch send flushes the lot, delivering each
47
- * destination's prompts as one numbered message. The queue itself is a
48
- * plain in-memory model; the plugin mirrors it into the JupyterLab state
49
- * database (via {@link serializeQueuedPrompts}) so a page reload does not
50
- * silently drop typed comments.
41
+ * The accumulated ask-agent prompts awaiting a batch send. A plain
42
+ * in-memory model; the plugin mirrors it into the state database so a page
43
+ * reload does not silently drop typed comments.
51
44
  */
52
45
  export class PromptQueue {
53
46
  constructor(items: IQueuedPrompt[] = []) {
@@ -125,8 +118,7 @@ export class PromptQueue {
125
118
  }
126
119
 
127
120
  /**
128
- * Remove several queued prompts at once (one signal emission) — used when
129
- * a batch send succeeds for one target while other targets' prompts stay.
121
+ * Remove several queued prompts at once (one signal emission).
130
122
  */
131
123
  removeMany(ids: readonly string[]): void {
132
124
  const drop = new Set(ids);
@@ -160,9 +152,8 @@ export class PromptQueue {
160
152
  }
161
153
 
162
154
  /**
163
- * Validate one persisted context. Only fields that still match the
164
- * `IAskAgentContext` contract are kept, so schema drift (or manual
165
- * state-database edits) degrades an entry instead of poisoning the queue.
155
+ * Validate one persisted context; schema drift degrades an entry instead
156
+ * of poisoning the queue.
166
157
  */
167
158
  function sanitizeContext(value: unknown): IAskAgentContext | null {
168
159
  if (value === null || typeof value !== 'object') {
@@ -225,9 +216,8 @@ function sanitizeTarget(value: unknown): AskAgentTarget | null {
225
216
  }
226
217
 
227
218
  /**
228
- * Rebuild queued prompts from a value read back from the state database.
229
- * Entries that no longer parse are dropped silently — the queue is a
230
- * convenience buffer, not a document.
219
+ * Rebuild queued prompts from the state database; entries that no longer
220
+ * parse are dropped silently.
231
221
  */
232
222
  export function deserializeQueuedPrompts(value: unknown): IQueuedPrompt[] {
233
223
  if (!Array.isArray(value)) {
@@ -255,8 +245,7 @@ export function deserializeQueuedPrompts(value: unknown): IQueuedPrompt[] {
255
245
 
256
246
  /**
257
247
  * The queue as a JSON value for the state database. Ids are session-scoped
258
- * handles, so they are not persisted; {@link deserializeQueuedPrompts}
259
- * assigns fresh ones on load.
248
+ * and not persisted; fresh ones are assigned on load.
260
249
  */
261
250
  export function serializeQueuedPrompts(
262
251
  items: readonly IQueuedPrompt[]
@@ -19,25 +19,16 @@ import type { IQueuedPrompt, PromptQueue } from './queue';
19
19
  import type { ISessionTarget } from './targetPicker';
20
20
  import type { AskAgentTarget } from './tokens';
21
21
 
22
- /**
23
- * Command the context buttons dispatch; keeps the panel decoupled from the plugin.
24
- */
25
22
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
26
23
 
27
24
  /**
28
- * Textarea height that fits `text` without scrolling, within sane bounds:
29
- * tall enough to look editable, short enough that one long comment cannot
30
- * crowd out the rest of the queue.
25
+ * Textarea height that fits `text` without scrolling, bounded so one long
26
+ * comment cannot crowd out the rest of the queue.
31
27
  */
32
28
  function rowsFor(text: string): number {
33
29
  return Math.min(8, Math.max(2, text.split('\n').length));
34
30
  }
35
31
 
36
- /**
37
- * A target as a `<select>` option value: `session:<name>` for a running
38
- * agent terminal, `new:<agentId>` for a fresh terminal, `''` for a missing
39
- * target (the placeholder option).
40
- */
41
32
  function encodeTarget(target: AskAgentTarget | null): string {
42
33
  if (target === null) {
43
34
  return '';
@@ -57,10 +48,6 @@ function decodeTarget(value: string): AskAgentTarget | null {
57
48
  return null;
58
49
  }
59
50
 
60
- /**
61
- * Whether `target` can be sent to right now: its session is still running,
62
- * or its agent still accepts a command-line prompt.
63
- */
64
51
  function targetIsLive(
65
52
  target: AskAgentTarget | null,
66
53
  agents: readonly IAgent[],
@@ -86,8 +73,8 @@ function QueuePanelComponent(props: AskAgentQueuePanel.IOptions): JSX.Element {
86
73
  onClear
87
74
  } = props;
88
75
 
89
- // Snapshots for this render; the widget re-renders on every queue, agent
90
- // registry and terminal change, so these stay current.
76
+ // The widget re-renders on every queue, agent registry and terminal
77
+ // change, so these snapshots stay current.
91
78
  const items = queue.items;
92
79
  const agents = readAgents();
93
80
  const targets = readTargets();
@@ -102,10 +89,8 @@ function QueuePanelComponent(props: AskAgentQueuePanel.IOptions): JSX.Element {
102
89
  const jumpTo = (item: IQueuedPrompt): void => {
103
90
  const { context } = item;
104
91
  const path = serverPath(context);
105
- // The highlighter needs line numbers that index the working file:
106
- // notebook lines are cell-relative (or raw-JSON offsets), and diff
107
- // selections may come from another revision — just open the document
108
- // in those cases.
92
+ // The highlighter needs working-file line numbers: notebook lines are
93
+ // cell-relative and diff lines may index another revision — just open.
109
94
  const jump =
110
95
  context.cell === undefined &&
111
96
  context.startLine !== undefined &&
@@ -145,9 +130,6 @@ function QueuePanelComponent(props: AskAgentQueuePanel.IOptions): JSX.Element {
145
130
  const missing = item.instruction.trim().length === 0;
146
131
  const target = item.target;
147
132
  const live = targetIsLive(target, agents, targets);
148
- // Say what sending will do for this prompt — paste into a running
149
- // terminal, or start a fresh one — and badge it with the agent's
150
- // icon, so a mixed queue can be scanned without opening dropdowns.
151
133
  let icon: LabIcon | null = null;
152
134
  let targetTitle = trans.__(
153
135
  'The destination picked for this prompt is gone — choose another'
@@ -202,10 +184,8 @@ function QueuePanelComponent(props: AskAgentQueuePanel.IOptions): JSX.Element {
202
184
  {item.context.text}
203
185
  </pre>
204
186
  )}
205
- {/* Uncontrolled on purpose: the queue re-renders through a
206
- posted Lumino update, and a controlled value fed back that
207
- late reverts keystrokes and jumps the caret. The `key` on
208
- the surrounding <li> still remounts it per queue entry. */}
187
+ {/* Uncontrolled on purpose: re-renders arrive as posted Lumino
188
+ updates, late enough to revert keystrokes if controlled. */}
209
189
  <textarea
210
190
  className="jp-xtralab-AskAgentQueue-instruction"
211
191
  rows={rowsFor(item.instruction)}
@@ -324,15 +304,10 @@ function QueuePanelComponent(props: AskAgentQueuePanel.IOptions): JSX.Element {
324
304
  }
325
305
 
326
306
  /**
327
- * The right-sidebar review panel for queued ask-agent prompts.
328
- *
329
- * Lists every queued comment — jump-to-code context line, snippet preview,
330
- * editable instruction, its own destination picker, remove button — plus a
331
- * send button that flushes the whole queue in one go: prompts sharing a
332
- * destination are combined into one numbered message, and each destination
333
- * gets its own delivery. The widget re-renders on queue changes itself; the
334
- * plugin additionally ties it to the agent registry and terminal signals so
335
- * the destination choices stay current.
307
+ * The right-sidebar review panel for queued ask-agent prompts: an editable
308
+ * list with per-prompt destinations, plus a send button that flushes the
309
+ * whole queue, combining prompts that share a destination into one
310
+ * numbered message.
336
311
  */
337
312
  export class AskAgentQueuePanel extends ReactWidget {
338
313
  constructor(options: AskAgentQueuePanel.IOptions) {
@@ -342,15 +317,16 @@ export class AskAgentQueuePanel extends ReactWidget {
342
317
  options.queue.changed.connect(this._onQueueChanged, this);
343
318
  }
344
319
 
320
+ /**
321
+ * Render the panel content.
322
+ */
345
323
  render(): JSX.Element {
346
324
  return <QueuePanelComponent {...this._options} />;
347
325
  }
348
326
 
349
327
  /**
350
- * Dispose on close (e.g. the close button when the panel has been dragged
351
- * to the main area) rather than leaving a detached, reusable widget
352
- * behind. The plugin recreates the panel in the side area on demand; the
353
- * queue itself lives on in the model.
328
+ * Dispose on close rather than leaving a detached widget behind; the
329
+ * plugin recreates the panel on demand and the queue lives on in the model.
354
330
  */
355
331
  protected onCloseRequest(msg: Message): void {
356
332
  super.onCloseRequest(msg);
@@ -393,9 +369,8 @@ export namespace AskAgentQueuePanel {
393
369
  targets: () => ISessionTarget[];
394
370
 
395
371
  /**
396
- * Live reader of whether a batch send is in flight (called on every
397
- * render). While true the send and clear buttons are disabled, so one
398
- * flush cannot be double-delivered.
372
+ * Live reader of whether a batch send is in flight; while true the send
373
+ * and clear buttons are disabled.
399
374
  */
400
375
  sending: () => boolean;
401
376
 
@@ -405,8 +380,8 @@ export namespace AskAgentQueuePanel {
405
380
  trans: TranslationBundle;
406
381
 
407
382
  /**
408
- * Send every queued prompt to its own destination (the panel only calls
409
- * this while all instructions and destinations are valid).
383
+ * Send every queued prompt to its own destination (called only while
384
+ * all instructions and destinations are valid).
410
385
  */
411
386
  onSend: () => void;
412
387
 
@@ -32,11 +32,9 @@ export interface ISessionTarget {
32
32
  }
33
33
 
34
34
  /**
35
- * Keyboard support shared by the two radiogroups below, following the ARIA
36
- * radio-group pattern: the group is one tab stop (only the checked radio is
37
- * tabbable) and the arrow keys move the selection to the neighbouring
38
- * radio, wrapping around. Selection follows focus, so the handler clicks
39
- * the neighbour and the re-render moves the roving tabindex along.
35
+ * Keyboard support for the radiogroups, per the ARIA radio pattern: one tab
36
+ * stop, arrow keys move and select — the handler clicks the neighbour and
37
+ * the re-render moves the roving tabindex along.
40
38
  */
41
39
  function onRadioGroupKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
42
40
  let delta: number;
@@ -67,18 +65,11 @@ function onRadioGroupKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
67
65
 
68
66
  /**
69
67
  * The chip row picking where a prompt goes: "New terminal" plus one chip
70
- * per running agent session. Renders nothing while no agent session is
71
- * running (a new terminal is then the only possibility and the row would
72
- * be noise).
68
+ * per running agent session. Renders nothing while no session is running
69
+ * (a new terminal is then the only possibility).
73
70
  */
74
71
  export function TargetChips(props: {
75
- /**
76
- * Agents that could start in a new terminal; gates the "New terminal" chip.
77
- */
78
72
  agents: IAgent[];
79
- /**
80
- * The running agent sessions.
81
- */
82
73
  targets: ISessionTarget[];
83
74
  /**
84
75
  * The selected session name, or `null` for a new terminal.
@@ -194,7 +185,6 @@ export function AgentChoices(props: {
194
185
  'jp-xtralab-AskAgent-agentButton' +
195
186
  (agent.id === agentId ? ' jp-mod-selected' : '')
196
187
  }
197
- // Keep focus where the user is typing while picking an agent.
198
188
  onMouseDown={event => event.preventDefault()}
199
189
  onClick={() => onSelect(agent.id)}
200
190
  >
@@ -1,39 +1,34 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
 
3
3
  /**
4
- * The command id that opens the ask-agent popup for the current text-editor
5
- * selection. Registered by `xtralab:ask-agent`; exposed here so menus and
6
- * other plugins can reference it without importing the plugin internals.
4
+ * Command id opening the ask-agent popup for the current editor selection;
5
+ * exposed so other plugins can reference it without the plugin internals.
7
6
  */
8
7
  export const ASK_AGENT_COMMAND = 'xtralab:ask-agent';
9
8
 
10
9
  /**
11
- * A code location (and optionally the selected source text) that a prompt
12
- * should point an agent at. The structured fields are composed into the
13
- * final prompt by the ask-agent plugin so every entry point — editor
14
- * selection, diff line selection — produces consistently worded prompts.
10
+ * A code location (and optionally the selected text) a prompt should point
11
+ * an agent at; the plugin composes the fields into consistently worded
12
+ * prompts for every entry point.
15
13
  */
16
14
  export interface IAskAgentContext {
17
15
  /**
18
- * Path of the file the selection belongs to, relative to {@link cwd} (or
19
- * to the server root when no cwd is given) so the launched agent can open
20
- * it directly.
16
+ * Path of the file, relative to {@link cwd} (or to the server root when
17
+ * no cwd is given).
21
18
  */
22
19
  path: string;
23
20
 
24
21
  /**
25
- * Server-relative directory the agent's terminal starts in. Omit to start
26
- * in the server root. Diff selections pass the git repository root here so
27
- * the agent can run git commands right away.
22
+ * Server-relative directory the agent's terminal starts in. Diff
23
+ * selections pass the git repository root so the agent can run git
24
+ * commands right away.
28
25
  */
29
26
  cwd?: string;
30
27
 
31
28
  /**
32
- * The notebook cell the selection lives in, when {@link path} is a
33
- * notebook. `index` is the cell's 0-based position in the nbformat
34
- * `cells` array; `type` is the nbformat cell type (`code`, `markdown`,
35
- * `raw`). When set, {@link startLine}/{@link endLine} count within the
36
- * cell's source rather than within a file.
29
+ * The notebook cell the selection lives in. `index` is the 0-based
30
+ * nbformat position, `type` the nbformat cell type; when set,
31
+ * {@link startLine}/{@link endLine} count within the cell's source.
37
32
  */
38
33
  cell?: {
39
34
  index: number;
@@ -41,8 +36,8 @@ export interface IAskAgentContext {
41
36
  };
42
37
 
43
38
  /**
44
- * First selected line, 1-indexed and inclusive. Omit when no line range is
45
- * known (the prompt then only references the file).
39
+ * First selected line, 1-indexed and inclusive. Omit when no line range
40
+ * is known.
46
41
  */
47
42
  startLine?: number;
48
43
 
@@ -53,40 +48,34 @@ export interface IAskAgentContext {
53
48
  endLine?: number;
54
49
 
55
50
  /**
56
- * Whether {@link startLine}/{@link endLine} index the file's current
57
- * working-tree content. `false` for diff selections taken from another
58
- * revision (the old side, or a challenger that is not the working tree),
59
- * whose numbers must not be used to highlight the working file. Omitted
60
- * means `true`.
51
+ * Whether the lines index the file's current working-tree content.
52
+ * `false` for diff selections taken from another revision, whose numbers
53
+ * must not highlight the working file. Omitted means `true`.
61
54
  */
62
55
  linesInWorkingFile?: boolean;
63
56
 
64
57
  /**
65
- * The selected source text. May be empty when only a location is known;
66
- * the prompt then relies on the path and line range alone.
58
+ * The selected source text; may be empty when only a location is known.
67
59
  */
68
60
  text: string;
69
61
 
70
62
  /**
71
- * Extra agent-facing locator appended after the line range in the prompt,
72
- * e.g. `the old side (INDEX) of the git diff`. Written in English because
73
- * it is consumed by the agent, not shown in the UI.
63
+ * Extra agent-facing locator appended after the line range, e.g. `the old
64
+ * side (INDEX) of the git diff`. English — consumed by the agent, not the UI.
74
65
  */
75
66
  location?: string;
76
67
 
77
68
  /**
78
- * Short user-facing tag shown in the popup header next to the file name,
79
- * e.g. "old version". Translated, unlike {@link location}. Omit when the
80
- * range reads naturally as the file's current content.
69
+ * Short user-facing tag shown in the popup header, e.g. "old version".
70
+ * Translated, unlike {@link location}.
81
71
  */
82
72
  note?: string;
83
73
  }
84
74
 
85
75
  /**
86
76
  * Where a submitted prompt goes: a fresh terminal started with the chosen
87
- * agent's command, or an existing terminal session whose running agent
88
- * receives the prompt in its input box (queued by the agent itself when it
89
- * is busy).
77
+ * agent's command, or an existing session whose running agent receives the
78
+ * prompt in its input box (queued by the agent itself when busy).
90
79
  */
91
80
  export type AskAgentTarget =
92
81
  | { kind: 'new'; agentId: string }
@@ -103,18 +92,16 @@ export interface IAskAgentRequest {
103
92
  context: IAskAgentContext;
104
93
 
105
94
  /**
106
- * Viewport rectangle the popup anchors to (typically the selection end or
107
- * the button that was clicked). `null` positions the popup near the top
108
- * center of the viewport.
95
+ * Viewport rectangle the popup anchors to; `null` positions it near the
96
+ * top center of the viewport.
109
97
  */
110
98
  anchor: DOMRect | null;
111
99
  }
112
100
 
113
101
  /**
114
- * The ask-agent popup: a small floating prompt box that sends the given
115
- * code selection, plus the user's typed instruction, to one of the
116
- * configured coding agents — in a fresh terminal, or into an agent already
117
- * running in an existing one.
102
+ * The ask-agent popup: a floating prompt box that sends the given code
103
+ * selection plus the user's instruction to a coding agent, in a fresh or
104
+ * running terminal.
118
105
  */
119
106
  export interface IAskAgent {
120
107
  /**
@@ -129,9 +116,8 @@ export interface IAskAgent {
129
116
  }
130
117
 
131
118
  /**
132
- * DI token for {@link IAskAgent}. Provided by `xtralab:ask-agent` and
133
- * consumed — optionally, so diffs still render when the plugin is disabled —
134
- * by the git diff plugins to offer "ask an agent about these lines".
119
+ * DI token for {@link IAskAgent}; consumed optionally by the git diff
120
+ * plugins so diffs still render when the plugin is disabled.
135
121
  */
136
122
  export const IAskAgent = new Token<IAskAgent>(
137
123
  'xtralab:IAskAgent',