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.
- package/README.md +10 -0
- package/lib/about/icons.d.ts +0 -5
- package/lib/about/icons.js +0 -5
- package/lib/about/index.d.ts +4 -8
- package/lib/about/index.js +6 -17
- package/lib/agentSessions.d.ts +9 -29
- package/lib/agentSessions.js +2 -7
- package/lib/askAgent/editorSelection.d.ts +20 -26
- package/lib/askAgent/editorSelection.js +11 -26
- package/lib/askAgent/icons.d.ts +2 -3
- package/lib/askAgent/icons.js +2 -3
- package/lib/askAgent/index.d.ts +4 -26
- package/lib/askAgent/index.js +54 -158
- package/lib/askAgent/popup.d.ts +12 -16
- package/lib/askAgent/popup.js +18 -37
- package/lib/askAgent/prompt.d.ts +3 -5
- package/lib/askAgent/prompt.js +6 -19
- package/lib/askAgent/queue.d.ts +11 -21
- package/lib/askAgent/queue.js +9 -19
- package/lib/askAgent/queuePanel.d.ts +13 -18
- package/lib/askAgent/queuePanel.js +15 -37
- package/lib/askAgent/targetPicker.d.ts +2 -9
- package/lib/askAgent/targetPicker.js +6 -11
- package/lib/askAgent/tokens.d.ts +32 -46
- package/lib/askAgent/tokens.js +4 -6
- package/lib/commandBar/index.d.ts +4 -13
- package/lib/commandBar/index.js +21 -39
- package/lib/customPanel/index.js +15 -0
- package/lib/editorBreadcrumbs/index.d.ts +3 -6
- package/lib/editorBreadcrumbs/index.js +10 -6
- package/lib/editorBreadcrumbs/widget.d.ts +17 -4
- package/lib/editorBreadcrumbs/widget.js +11 -17
- package/lib/editorIndent/index.d.ts +4 -12
- package/lib/editorIndent/index.js +8 -23
- package/lib/fileBrowser/commands.d.ts +28 -19
- package/lib/fileBrowser/commands.js +39 -98
- package/lib/fileBrowser/contents.d.ts +3 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
- package/lib/fileBrowser/dragAndDrop.js +2 -7
- package/lib/fileBrowser/fileBrowser.d.ts +19 -9
- package/lib/fileBrowser/fileBrowser.js +68 -216
- package/lib/fileBrowser/gitStatus.d.ts +4 -9
- package/lib/fileBrowser/gitStatus.js +7 -17
- package/lib/fileBrowser/gitignore.d.ts +4 -17
- package/lib/fileBrowser/gitignore.js +4 -25
- package/lib/fileBrowser/icons.d.ts +7 -17
- package/lib/fileBrowser/icons.js +25 -80
- package/lib/fileBrowser/index.js +2 -4
- package/lib/fileBrowser/widget.d.ts +126 -48
- package/lib/fileBrowser/widget.js +89 -4
- package/lib/fileTypeIcons/index.js +0 -10
- package/lib/git/api.d.ts +5 -13
- package/lib/git/api.js +10 -36
- package/lib/git/askRequest.d.ts +4 -8
- package/lib/git/askRequest.js +6 -20
- package/lib/git/commands.d.ts +40 -5
- package/lib/git/commands.js +16 -10
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffSurface.d.ts +41 -55
- package/lib/git/diffSurface.js +42 -117
- package/lib/git/diffWidget.d.ts +81 -2
- package/lib/git/diffWidget.js +62 -12
- package/lib/git/imageDiff.d.ts +5 -6
- package/lib/git/imageDiff.js +18 -48
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +3 -8
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +52 -195
- package/lib/git/tokens.d.ts +73 -31
- package/lib/git/tokens.js +2 -3
- package/lib/highlight/index.d.ts +4 -10
- package/lib/highlight/index.js +10 -41
- package/lib/index.d.ts +0 -9
- package/lib/index.js +0 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +15 -57
- package/lib/launcher/availability.d.ts +3 -9
- package/lib/launcher/availability.js +3 -9
- package/lib/launcher/commands.d.ts +7 -32
- package/lib/launcher/commands.js +14 -45
- package/lib/launcher/dashboard.d.ts +19 -24
- package/lib/launcher/dashboard.js +21 -67
- package/lib/launcher/editorRegistry.d.ts +10 -22
- package/lib/launcher/editorRegistry.js +14 -18
- package/lib/launcher/editors.d.ts +39 -45
- package/lib/launcher/editors.js +8 -34
- package/lib/launcher/icons.d.ts +0 -12
- package/lib/launcher/icons.js +5 -39
- package/lib/launcher/index.js +12 -55
- package/lib/launcher/invocation.d.ts +4 -10
- package/lib/launcher/invocation.js +7 -22
- package/lib/launcher/registry.d.ts +9 -7
- package/lib/launcher/registry.js +9 -7
- package/lib/launcher/tokens.d.ts +10 -22
- package/lib/launcher/tokens.js +5 -8
- package/lib/menuBar/index.d.ts +3 -10
- package/lib/menuBar/index.js +11 -28
- package/lib/menus/index.d.ts +3 -9
- package/lib/menus/index.js +14 -30
- package/lib/omnibox/files.d.ts +2 -4
- package/lib/omnibox/files.js +5 -11
- package/lib/omnibox/index.d.ts +4 -14
- package/lib/omnibox/index.js +9 -27
- package/lib/omnibox/model.d.ts +38 -10
- package/lib/omnibox/model.js +15 -33
- package/lib/omnibox/recents.d.ts +15 -17
- package/lib/omnibox/recents.js +12 -17
- package/lib/omnibox/tokens.d.ts +5 -10
- package/lib/omnibox/tokens.js +2 -6
- package/lib/omnibox/widget.d.ts +11 -7
- package/lib/omnibox/widget.js +8 -16
- package/lib/searchReplace/index.d.ts +4 -20
- package/lib/searchReplace/index.js +7 -27
- package/lib/showOutput/index.d.ts +3 -12
- package/lib/showOutput/index.js +6 -22
- package/lib/sidebar/index.d.ts +4 -7
- package/lib/sidebar/index.js +5 -12
- package/lib/terminalNotifications/index.d.ts +4 -10
- package/lib/terminalNotifications/index.js +6 -20
- package/lib/terminals/agentTerminals.d.ts +12 -5
- package/lib/terminals/agentTerminals.js +25 -44
- package/lib/terminals/detection.d.ts +4 -8
- package/lib/terminals/detection.js +4 -8
- package/lib/terminals/index.d.ts +0 -35
- package/lib/terminals/index.js +12 -101
- package/lib/terminals/model.d.ts +64 -139
- package/lib/terminals/model.js +80 -220
- package/lib/terminals/tokens.d.ts +11 -22
- package/lib/terminals/tokens.js +2 -2
- package/lib/terminals/widget.d.ts +43 -13
- package/lib/terminals/widget.js +33 -18
- package/lib/topBar/icons.d.ts +3 -8
- package/lib/topBar/icons.js +3 -8
- package/lib/topBar/index.d.ts +4 -11
- package/lib/topBar/index.js +10 -38
- package/lib/walkthrough/index.d.ts +4 -9
- package/lib/walkthrough/index.js +6 -20
- package/lib/walkthrough/panel.d.ts +40 -9
- package/lib/walkthrough/panel.js +4 -10
- package/package.json +2 -2
- package/src/about/icons.ts +0 -5
- package/src/about/index.tsx +6 -17
- package/src/agentSessions.ts +9 -29
- package/src/askAgent/editorSelection.ts +22 -29
- package/src/askAgent/icons.ts +2 -3
- package/src/askAgent/index.ts +54 -158
- package/src/askAgent/popup.tsx +23 -45
- package/src/askAgent/prompt.ts +6 -19
- package/src/askAgent/queue.ts +13 -24
- package/src/askAgent/queuePanel.tsx +21 -46
- package/src/askAgent/targetPicker.tsx +5 -15
- package/src/askAgent/tokens.ts +32 -46
- package/src/commandBar/index.ts +24 -39
- package/src/customPanel/index.ts +15 -0
- package/src/editorBreadcrumbs/index.ts +10 -6
- package/src/editorBreadcrumbs/widget.ts +23 -17
- package/src/editorIndent/index.ts +17 -23
- package/src/fileBrowser/commands.ts +57 -98
- package/src/fileBrowser/contents.ts +3 -0
- package/src/fileBrowser/dragAndDrop.ts +17 -7
- package/src/fileBrowser/fileBrowser.tsx +84 -223
- package/src/fileBrowser/gitStatus.ts +7 -17
- package/src/fileBrowser/gitignore.ts +4 -25
- package/src/fileBrowser/icons.ts +25 -80
- package/src/fileBrowser/index.ts +2 -4
- package/src/fileBrowser/widget.tsx +149 -50
- package/src/fileTypeIcons/index.ts +0 -10
- package/src/git/api.ts +10 -36
- package/src/git/askRequest.ts +6 -20
- package/src/git/commands.ts +70 -10
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffSurface.tsx +71 -159
- package/src/git/diffWidget.tsx +104 -14
- package/src/git/imageDiff.tsx +45 -51
- package/src/git/index.ts +3 -8
- package/src/git/notebookDiff.tsx +211 -238
- package/src/git/tokens.ts +73 -31
- package/src/highlight/index.ts +10 -41
- package/src/index.ts +0 -9
- package/src/launcher/agents.ts +64 -95
- package/src/launcher/availability.ts +3 -9
- package/src/launcher/commands.ts +14 -51
- package/src/launcher/dashboard.tsx +49 -88
- package/src/launcher/editorRegistry.ts +21 -29
- package/src/launcher/editors.ts +39 -58
- package/src/launcher/icons.ts +5 -39
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/tokens.ts +10 -22
- package/src/menuBar/index.ts +11 -28
- package/src/menus/index.ts +14 -30
- package/src/omnibox/files.ts +10 -14
- package/src/omnibox/index.ts +9 -27
- package/src/omnibox/model.ts +50 -38
- package/src/omnibox/recents.ts +17 -22
- package/src/omnibox/tokens.ts +5 -10
- package/src/omnibox/widget.tsx +14 -19
- package/src/searchReplace/index.ts +7 -27
- package/src/showOutput/index.ts +6 -22
- package/src/sidebar/index.ts +20 -12
- package/src/terminalNotifications/index.ts +42 -25
- package/src/terminals/agentTerminals.ts +25 -49
- package/src/terminals/detection.ts +4 -8
- package/src/terminals/index.ts +12 -101
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +48 -31
- package/src/topBar/icons.ts +3 -8
- package/src/topBar/index.ts +21 -44
- package/src/walkthrough/index.ts +6 -20
- package/src/walkthrough/panel.ts +40 -10
- package/style/about.css +2 -7
- package/style/askAgent.css +8 -40
- package/style/base.css +9 -45
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +1 -4
- package/style/git.css +22 -184
- package/style/highlight.css +1 -3
- package/style/launcher.css +6 -70
- package/style/omnibox.css +2 -14
- package/style/showOutput.css +0 -6
- package/style/sidebar.css +29 -9
- package/style/tabs.css +5 -13
- package/style/terminals.css +6 -53
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
package/src/askAgent/popup.tsx
CHANGED
|
@@ -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
|
|
24
|
-
*
|
|
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
|
|
81
|
-
//
|
|
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
|
-
//
|
|
115
|
-
//
|
|
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
|
|
127
|
-
//
|
|
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
|
-
//
|
|
135
|
-
//
|
|
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
|
-
//
|
|
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
|
|
238
|
-
//
|
|
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
|
|
328
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
396
|
-
*
|
|
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
|
|
402
|
-
*
|
|
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
|
}
|
package/src/askAgent/prompt.ts
CHANGED
|
@@ -14,10 +14,8 @@ export function serverPath(context: IAskAgentContext): string {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Cap on the
|
|
18
|
-
*
|
|
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
|
|
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:
|
|
106
|
-
*
|
|
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,
|
package/src/askAgent/queue.ts
CHANGED
|
@@ -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
|
|
9
|
-
*
|
|
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
|
|
29
|
-
*
|
|
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
|
-
*
|
|
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)
|
|
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
|
|
164
|
-
*
|
|
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
|
|
229
|
-
*
|
|
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
|
-
*
|
|
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,
|
|
29
|
-
*
|
|
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
|
-
//
|
|
90
|
-
//
|
|
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
|
|
106
|
-
//
|
|
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:
|
|
206
|
-
|
|
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
|
-
*
|
|
330
|
-
*
|
|
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
|
|
351
|
-
*
|
|
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
|
|
397
|
-
*
|
|
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 (
|
|
409
|
-
*
|
|
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
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
|
71
|
-
*
|
|
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
|
>
|
package/src/askAgent/tokens.ts
CHANGED
|
@@ -1,39 +1,34 @@
|
|
|
1
1
|
import { Token } from '@lumino/coreutils';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
|
19
|
-
*
|
|
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.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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
|
|
45
|
-
* known
|
|
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
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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
|
|
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
|
|
72
|
-
*
|
|
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
|
|
79
|
-
*
|
|
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
|
|
88
|
-
*
|
|
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
|
|
107
|
-
*
|
|
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
|
|
115
|
-
*
|
|
116
|
-
*
|
|
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}
|
|
133
|
-
*
|
|
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',
|