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
@@ -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',
@@ -12,25 +12,13 @@ import { IOmnibox, OMNIBOX_OPEN_COMMAND } from '../omnibox/tokens';
12
12
 
13
13
  const PLUGIN_ID = 'xtralab:command-bar';
14
14
 
15
- /**
16
- * Factory name of JupyterLab's settings-driven top bar toolbar (`#jp-top-bar`,
17
- * built by `@jupyterlab/application-extension:top-bar`).
18
- */
19
15
  const TOPBAR_FACTORY = 'TopBar';
20
16
 
21
- /**
22
- * Name of the pill item within that toolbar. Its placement is declared under
23
- * `jupyter.lab.toolbars` in this plugin's settings schema
24
- * (schema/command-bar.json): the core toolbar ships a spacer at rank 50, so
25
- * the pill's higher rank lands it at the trailing end of the toolbar, next to
26
- * the right-sidebar toggle button.
27
- */
28
17
  const ITEM_NAME = 'omnibox';
29
18
 
30
19
  /**
31
- * A search-bar-styled launcher pill. It holds no text input of its own:
32
- * clicking it (or pressing Enter/Space while it is focused) runs `onActivate`,
33
- * which opens the omnibox.
20
+ * A search-bar-styled launcher pill with no input of its own: activating it
21
+ * runs `onActivate`, which opens the omnibox.
34
22
  */
35
23
  class CommandBar extends Widget {
36
24
  constructor(options: CommandBar.IOptions) {
@@ -45,19 +33,26 @@ class CommandBar extends Widget {
45
33
  this._onActivate = options.onActivate;
46
34
  }
47
35
 
36
+ /**
37
+ * Handle the DOM events for the command bar.
38
+ */
48
39
  handleEvent(event: Event): void {
49
40
  if (event.type === 'click') {
50
41
  this._onActivate();
51
42
  }
52
43
  }
53
44
 
45
+ /**
46
+ * A message handler invoked on an `'after-attach'` message.
47
+ */
54
48
  protected onAfterAttach(): void {
55
- // The click bubbles from the inner pill button up to the root node
56
- // listened on here; keyboard Enter/Space on the focused button raises the
57
- // same synthetic click.
49
+ // Keyboard Enter/Space on the focused button raises the same synthetic click.
58
50
  this.node.addEventListener('click', this);
59
51
  }
60
52
 
53
+ /**
54
+ * A message handler invoked on a `'before-detach'` message.
55
+ */
61
56
  protected onBeforeDetach(): void {
62
57
  this.node.removeEventListener('click', this);
63
58
  }
@@ -66,6 +61,9 @@ class CommandBar extends Widget {
66
61
  }
67
62
 
68
63
  namespace CommandBar {
64
+ /**
65
+ * The options used to create a `CommandBar`.
66
+ */
69
67
  export interface IOptions {
70
68
  /**
71
69
  * Placeholder-style text shown inside the pill.
@@ -85,15 +83,16 @@ namespace CommandBar {
85
83
  onActivate: () => void;
86
84
  }
87
85
 
86
+ /**
87
+ * Create the DOM node for a command bar.
88
+ */
88
89
  export function createNode(
89
90
  label: string,
90
91
  caption: string,
91
92
  shortcut?: string
92
93
  ): HTMLElement {
93
- // The root is a plain wrapper: as a toolbar item it receives the core
94
- // `.jp-Toolbar > .jp-Toolbar-item` sizing (full height, centered flex),
95
- // which would otherwise stretch the pill itself; the button inside keeps
96
- // its own compact pill height.
94
+ // Plain wrapper: the core `.jp-Toolbar-item` sizing would otherwise
95
+ // stretch the pill itself.
97
96
  const wrapper = document.createElement('div');
98
97
 
99
98
  const button = document.createElement('button');
@@ -118,7 +117,6 @@ namespace CommandBar {
118
117
  const hint = document.createElement('span');
119
118
  hint.className = 'jp-xtralab-CommandBar-shortcut';
120
119
  hint.textContent = shortcut;
121
- // Decorative: the button's aria-label already names the action.
122
120
  hint.setAttribute('aria-hidden', 'true');
123
121
  button.appendChild(hint);
124
122
  }
@@ -129,19 +127,10 @@ namespace CommandBar {
129
127
  }
130
128
 
131
129
  /**
132
- * Contribute a search-bar-styled command bar to JupyterLab's top bar toolbar.
133
- * Clicking it opens the omnibox — a launcher overlay that fuzzy-searches files
134
- * and commands and routes a typed prompt to an agent.
135
- *
136
- * The pill is a regular settings-driven toolbar item: this plugin registers a
137
- * widget factory for it on the `IToolbarWidgetRegistry` and declares its rank
138
- * in schema/command-bar.json, so users can move or disable it from the Top
139
- * Bar settings like any other toolbar item.
140
- *
141
- * The factory is only registered when the omnibox is available — the
142
- * `IOmnibox` token is provided by `xtralab:omnibox` — so the pill never
143
- * appears with nothing to open (without a factory, the toolbar item resolves
144
- * to an empty command button that renders nothing).
130
+ * Contribute a search-bar-styled pill to the top bar toolbar that opens the
131
+ * omnibox. It is a settings-driven toolbar item (factory here, rank in
132
+ * schema/command-bar.json) registered only when `IOmnibox` is provided —
133
+ * without a factory the item resolves to an empty button that renders nothing.
145
134
  */
146
135
  const plugin: JupyterFrontEndPlugin<void> = {
147
136
  id: PLUGIN_ID,
@@ -163,10 +152,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
163
152
  const trans = (translator ?? nullTranslator).load('jupyterlab');
164
153
 
165
154
  toolbarRegistry.addFactory(TOPBAR_FACTORY, ITEM_NAME, () => {
166
- // Show the omnibox's keyboard shortcut as a hint in the pill, derived
167
- // from the live binding (registered by xtralab:omnibox, which activates
168
- // first as the IOmnibox provider) so it stays correct and uses the
169
- // platform's modifier symbols. Empty when no binding is registered.
170
155
  const binding = app.commands.keyBindings.find(
171
156
  keyBinding => keyBinding.command === OMNIBOX_OPEN_COMMAND
172
157
  );
@@ -31,23 +31,38 @@ class CustomPanel extends SidePanel implements IMovableSectionDestination {
31
31
  this.addClass('jp-xtralab-CustomPanel');
32
32
  }
33
33
 
34
+ /**
35
+ * The accordion panel hosting the sections.
36
+ */
34
37
  get accordionPanel(): AccordionPanel {
35
38
  return this.content as AccordionPanel;
36
39
  }
37
40
 
41
+ /**
42
+ * The section widgets currently hosted by the panel.
43
+ */
38
44
  get sections(): ReadonlyArray<Widget> {
39
45
  return this.content.widgets;
40
46
  }
41
47
 
48
+ /**
49
+ * A signal emitted with the new count when the number of sections changes.
50
+ */
42
51
  get sectionCountChanged(): ISignal<this, number> {
43
52
  return this._sectionCountChanged;
44
53
  }
45
54
 
55
+ /**
56
+ * Add a section widget to the panel.
57
+ */
46
58
  addSection(widget: Widget): void {
47
59
  this.addWidget(widget);
48
60
  this._sectionCountChanged.emit(this.content.widgets.length);
49
61
  }
50
62
 
63
+ /**
64
+ * Remove a section widget from the panel, if it is hosted here.
65
+ */
51
66
  removeSectionWidget(widget: Widget): void {
52
67
  if (widget.parent !== this.content) {
53
68
  return;