@plmbr/notebook-intelligence 5.0.0
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/LICENSE +674 -0
- package/README.md +412 -0
- package/lib/api.d.ts +288 -0
- package/lib/api.js +927 -0
- package/lib/cell-output-bundle.d.ts +25 -0
- package/lib/cell-output-bundle.js +129 -0
- package/lib/cell-output-toolbar.d.ts +26 -0
- package/lib/cell-output-toolbar.js +188 -0
- package/lib/chat-progress-feedback.d.ts +3 -0
- package/lib/chat-progress-feedback.js +27 -0
- package/lib/chat-sidebar.d.ts +92 -0
- package/lib/chat-sidebar.js +3452 -0
- package/lib/command-ids.d.ts +39 -0
- package/lib/command-ids.js +44 -0
- package/lib/components/ask-user-question.d.ts +2 -0
- package/lib/components/ask-user-question.js +85 -0
- package/lib/components/checkbox.d.ts +2 -0
- package/lib/components/checkbox.js +30 -0
- package/lib/components/claude-mcp-panel.d.ts +2 -0
- package/lib/components/claude-mcp-panel.js +275 -0
- package/lib/components/claude-mcp-paste.d.ts +7 -0
- package/lib/components/claude-mcp-paste.js +104 -0
- package/lib/components/claude-session-picker.d.ts +8 -0
- package/lib/components/claude-session-picker.js +127 -0
- package/lib/components/form-dialog.d.ts +25 -0
- package/lib/components/form-dialog.js +35 -0
- package/lib/components/launcher-picker.d.ts +6 -0
- package/lib/components/launcher-picker.js +135 -0
- package/lib/components/mcp-util.d.ts +2 -0
- package/lib/components/mcp-util.js +37 -0
- package/lib/components/notebook-generation-popover.d.ts +7 -0
- package/lib/components/notebook-generation-popover.js +60 -0
- package/lib/components/pill.d.ts +2 -0
- package/lib/components/pill.js +5 -0
- package/lib/components/plugins-panel.d.ts +3 -0
- package/lib/components/plugins-panel.js +466 -0
- package/lib/components/settings-panel.d.ts +11 -0
- package/lib/components/settings-panel.js +742 -0
- package/lib/components/skills-panel.d.ts +2 -0
- package/lib/components/skills-panel.js +1264 -0
- package/lib/handler.d.ts +8 -0
- package/lib/handler.js +36 -0
- package/lib/icons.d.ts +45 -0
- package/lib/icons.js +54 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +2079 -0
- package/lib/markdown-renderer.d.ts +10 -0
- package/lib/markdown-renderer.js +64 -0
- package/lib/notebook-generation-toolbar.d.ts +16 -0
- package/lib/notebook-generation-toolbar.js +197 -0
- package/lib/notebook-generation.d.ts +8 -0
- package/lib/notebook-generation.js +12 -0
- package/lib/open-file-refresh-watcher-env.d.ts +4 -0
- package/lib/open-file-refresh-watcher-env.js +33 -0
- package/lib/open-file-refresh-watcher.d.ts +97 -0
- package/lib/open-file-refresh-watcher.js +190 -0
- package/lib/shell-utils.d.ts +6 -0
- package/lib/shell-utils.js +9 -0
- package/lib/task-target-notebook.d.ts +2 -0
- package/lib/task-target-notebook.js +28 -0
- package/lib/terminal-drag-format.d.ts +9 -0
- package/lib/terminal-drag-format.js +23 -0
- package/lib/terminal-drag.d.ts +12 -0
- package/lib/terminal-drag.js +268 -0
- package/lib/tokens.d.ts +149 -0
- package/lib/tokens.js +88 -0
- package/lib/tour/tour-anchors.d.ts +18 -0
- package/lib/tour/tour-anchors.js +18 -0
- package/lib/tour/tour-config.d.ts +66 -0
- package/lib/tour/tour-config.js +99 -0
- package/lib/tour/tour-defaults.json +58 -0
- package/lib/tour/tour-events.d.ts +19 -0
- package/lib/tour/tour-events.js +30 -0
- package/lib/tour/tour-overlay.d.ts +6 -0
- package/lib/tour/tour-overlay.js +350 -0
- package/lib/tour/tour-state.d.ts +20 -0
- package/lib/tour/tour-state.js +81 -0
- package/lib/tour/tour-steps.d.ts +33 -0
- package/lib/tour/tour-steps.js +216 -0
- package/lib/utils.d.ts +53 -0
- package/lib/utils.js +385 -0
- package/package.json +258 -0
- package/schema/plugin.json +42 -0
- package/src/api.ts +1424 -0
- package/src/cell-output-bundle.ts +176 -0
- package/src/cell-output-toolbar.ts +232 -0
- package/src/chat-progress-feedback.ts +35 -0
- package/src/chat-sidebar.tsx +5147 -0
- package/src/command-ids.ts +67 -0
- package/src/components/ask-user-question.tsx +151 -0
- package/src/components/checkbox.tsx +62 -0
- package/src/components/claude-mcp-panel.tsx +543 -0
- package/src/components/claude-mcp-paste.ts +132 -0
- package/src/components/claude-session-picker.tsx +214 -0
- package/src/components/form-dialog.tsx +75 -0
- package/src/components/launcher-picker.tsx +237 -0
- package/src/components/mcp-util.ts +53 -0
- package/src/components/notebook-generation-popover.tsx +127 -0
- package/src/components/pill.tsx +15 -0
- package/src/components/plugins-panel.tsx +774 -0
- package/src/components/settings-panel.tsx +1631 -0
- package/src/components/skills-panel.tsx +2084 -0
- package/src/handler.ts +51 -0
- package/src/icons.ts +71 -0
- package/src/index.ts +2583 -0
- package/src/markdown-renderer.tsx +153 -0
- package/src/notebook-generation-toolbar.tsx +281 -0
- package/src/notebook-generation.ts +23 -0
- package/src/open-file-refresh-watcher-env.ts +52 -0
- package/src/open-file-refresh-watcher.ts +260 -0
- package/src/shell-utils.ts +10 -0
- package/src/svg.d.ts +4 -0
- package/src/task-target-notebook.ts +37 -0
- package/src/terminal-drag-format.ts +29 -0
- package/src/terminal-drag.ts +382 -0
- package/src/tokens.ts +171 -0
- package/src/tour/tour-anchors.ts +21 -0
- package/src/tour/tour-config.ts +160 -0
- package/src/tour/tour-events.ts +34 -0
- package/src/tour/tour-overlay.tsx +474 -0
- package/src/tour/tour-state.ts +87 -0
- package/src/tour/tour-steps.ts +281 -0
- package/src/utils.ts +455 -0
- package/style/base.css +3238 -0
- package/style/icons/cell-toolbar-bug.svg +5 -0
- package/style/icons/cell-toolbar-chat.svg +5 -0
- package/style/icons/cell-toolbar-sparkle.svg +5 -0
- package/style/icons/claude.svg +1 -0
- package/style/icons/copilot-warning.svg +1 -0
- package/style/icons/copilot.svg +1 -0
- package/style/icons/copy.svg +1 -0
- package/style/icons/openai.svg +1 -0
- package/style/icons/opencode.svg +1 -0
- package/style/icons/sparkles-warning.svg +5 -0
- package/style/icons/sparkles.svg +1 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
|
|
3
|
+
import { Notification } from '@jupyterlab/apputils';
|
|
4
|
+
import { Widget } from '@lumino/widgets';
|
|
5
|
+
|
|
6
|
+
import { NBIAPI } from './api';
|
|
7
|
+
import { DragMode, formatForMode, invertMode } from './terminal-drag-format';
|
|
8
|
+
|
|
9
|
+
export type { DragMode } from './terminal-drag-format';
|
|
10
|
+
export { formatForMode, invertMode } from './terminal-drag-format';
|
|
11
|
+
|
|
12
|
+
// Structural types over `IMainAreaWidgetLike` and
|
|
13
|
+
// `ITerminalTracker`: @jupyterlab/terminal nests its own copies of
|
|
14
|
+
// @jupyterlab/apputils and @lumino/widgets, so the nominal types don't
|
|
15
|
+
// unify with our top-level ones. Capturing only what we touch keeps the
|
|
16
|
+
// module decoupled from that version skew and unit-testable without a
|
|
17
|
+
// real JupyterLab application context.
|
|
18
|
+
interface ITerminalWidgetLike {
|
|
19
|
+
paste(text: string): void;
|
|
20
|
+
activate?(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface IDisposedSignalLike {
|
|
24
|
+
connect(slot: () => void): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ITerminalToolbarLike {
|
|
28
|
+
addItem(name: string, widget: Widget): boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface IMainAreaWidgetLike {
|
|
32
|
+
node: HTMLElement;
|
|
33
|
+
content: ITerminalWidgetLike;
|
|
34
|
+
toolbar: ITerminalToolbarLike;
|
|
35
|
+
disposed: IDisposedSignalLike;
|
|
36
|
+
isDisposed?: boolean;
|
|
37
|
+
activate?(): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ITerminalTrackerLike {
|
|
41
|
+
widgetAdded: {
|
|
42
|
+
connect(slot: (sender: unknown, widget: IMainAreaWidgetLike) => void): void;
|
|
43
|
+
};
|
|
44
|
+
forEach(fn: (widget: IMainAreaWidgetLike) => void): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// File-browser drag dispatches a Lumino lm-drop event carrying paths
|
|
48
|
+
// under this MIME (see node_modules/@jupyterlab/filebrowser/lib/listing.js).
|
|
49
|
+
const FILE_BROWSER_MIME = 'application/x-jupyter-icontents';
|
|
50
|
+
|
|
51
|
+
const DRAG_OVER_CLASS = 'nbi-terminal-drag-over';
|
|
52
|
+
const TOOLBAR_BUTTON_CLASS = 'nbi-terminal-drag-mode-button';
|
|
53
|
+
|
|
54
|
+
interface ITerminalDragState {
|
|
55
|
+
mode: DragMode;
|
|
56
|
+
dragDepth: number;
|
|
57
|
+
cleanup: () => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const widgetState = new WeakMap<IMainAreaWidgetLike, ITerminalDragState>();
|
|
61
|
+
|
|
62
|
+
export interface ITerminalDragOptions {
|
|
63
|
+
// Untyped on the public surface because @jupyterlab/terminal nests its
|
|
64
|
+
// own copy of every Lumino/JL type; coercing inside keeps callers from
|
|
65
|
+
// needing to know about the duplication.
|
|
66
|
+
tracker: unknown;
|
|
67
|
+
/**
|
|
68
|
+
* Re-evaluated on each event so flipping the admin policy at runtime
|
|
69
|
+
* (e.g. force-off via env at next reload) takes effect on listeners
|
|
70
|
+
* that are already wired without needing to tear them down.
|
|
71
|
+
*/
|
|
72
|
+
isEnabled: () => boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function attachTerminalDragDrop(options: ITerminalDragOptions): void {
|
|
76
|
+
const tracker = options.tracker as ITerminalTrackerLike;
|
|
77
|
+
const { isEnabled } = options;
|
|
78
|
+
|
|
79
|
+
const wire = (widget: IMainAreaWidgetLike) => {
|
|
80
|
+
if (widgetState.has(widget)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
setupTerminal(widget, isEnabled);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
tracker.forEach(wire);
|
|
87
|
+
tracker.widgetAdded.connect((_, widget) => wire(widget));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setupTerminal(
|
|
91
|
+
widget: IMainAreaWidgetLike,
|
|
92
|
+
isEnabled: () => boolean
|
|
93
|
+
): void {
|
|
94
|
+
const host = widget.node;
|
|
95
|
+
|
|
96
|
+
const state: ITerminalDragState = {
|
|
97
|
+
mode: 'mention',
|
|
98
|
+
dragDepth: 0,
|
|
99
|
+
cleanup: () => {}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const inject = (paths: string[], shiftHeld: boolean) => {
|
|
103
|
+
if (paths.length === 0) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Async upload paths can finish after the terminal is closed; calling
|
|
107
|
+
// paste on a disposed Lumino Widget throws.
|
|
108
|
+
if (widget.isDisposed) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const effectiveMode = invertMode(state.mode, shiftHeld);
|
|
112
|
+
widget.content.paste(`${formatForMode(paths, effectiveMode)} `);
|
|
113
|
+
// Activate the outer MainAreaWidget so the terminal also gets raised
|
|
114
|
+
// if it's a background tab in a split. Otherwise the next keystroke
|
|
115
|
+
// goes to the file-browser (Enter would "open the selected file") or
|
|
116
|
+
// to whichever surface held focus before the drag.
|
|
117
|
+
widget.activate?.();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleDragEnter = (event: DragEvent) => {
|
|
121
|
+
if (!isEnabled() || !event.dataTransfer) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!event.dataTransfer.types.includes('Files')) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
state.dragDepth += 1;
|
|
128
|
+
host.classList.add(DRAG_OVER_CLASS);
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
event.stopImmediatePropagation();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleDragOver = (event: DragEvent) => {
|
|
134
|
+
if (!isEnabled() || !event.dataTransfer) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!event.dataTransfer.types.includes('Files')) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
event.stopImmediatePropagation();
|
|
142
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const handleDragLeave = (event: DragEvent) => {
|
|
146
|
+
if (!isEnabled()) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
state.dragDepth = Math.max(0, state.dragDepth - 1);
|
|
150
|
+
if (state.dragDepth === 0) {
|
|
151
|
+
host.classList.remove(DRAG_OVER_CLASS);
|
|
152
|
+
}
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
event.stopImmediatePropagation();
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const handleDrop = (event: DragEvent) => {
|
|
158
|
+
if (!isEnabled()) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
state.dragDepth = 0;
|
|
162
|
+
host.classList.remove(DRAG_OVER_CLASS);
|
|
163
|
+
if (!event.dataTransfer) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const files = Array.from(event.dataTransfer.files);
|
|
167
|
+
if (files.length === 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
event.stopImmediatePropagation();
|
|
172
|
+
const shiftHeld = event.shiftKey;
|
|
173
|
+
void uploadAndInject(files, shiftHeld, inject);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Lumino dispatches lm-* events on the deepest DOM element under the
|
|
177
|
+
// cursor. Listening on `host` would in theory catch the bubble, but
|
|
178
|
+
// intermediate widgets (e.g. xterm's viewport) can call
|
|
179
|
+
// stopPropagation in a target-phase handler before we see it. Listening
|
|
180
|
+
// at the document level with a containment check is the most reliable
|
|
181
|
+
// way to observe a drop that's geometrically inside this terminal.
|
|
182
|
+
const isInsideHost = (event: Event): boolean => {
|
|
183
|
+
const target = event.target;
|
|
184
|
+
return target instanceof Node && host.contains(target);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const handleLuminoDragEnter = (event: Event) => {
|
|
188
|
+
if (!isEnabled() || !hasFileBrowserPaths(event) || !isInsideHost(event)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
state.dragDepth += 1;
|
|
192
|
+
host.classList.add(DRAG_OVER_CLASS);
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
event.stopPropagation();
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const handleLuminoDragOver = (event: Event) => {
|
|
198
|
+
if (!isEnabled() || !hasFileBrowserPaths(event) || !isInsideHost(event)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
event.stopPropagation();
|
|
203
|
+
// Echo the source's proposedAction back as dropAction. The file
|
|
204
|
+
// browser starts its Drag with supportedActions: 'move', so a
|
|
205
|
+
// hardcoded 'copy' falls through validateAction to 'none' and
|
|
206
|
+
// Lumino skips lm-drop on pointerup. Mirroring proposedAction keeps
|
|
207
|
+
// us inside whatever the source supports.
|
|
208
|
+
const dragEvent = event as unknown as {
|
|
209
|
+
proposedAction?: string;
|
|
210
|
+
dropAction: string;
|
|
211
|
+
};
|
|
212
|
+
dragEvent.dropAction = dragEvent.proposedAction || 'move';
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const handleLuminoDragLeave = (event: Event) => {
|
|
216
|
+
if (!isEnabled() || !isInsideHost(event)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
state.dragDepth = Math.max(0, state.dragDepth - 1);
|
|
220
|
+
if (state.dragDepth === 0) {
|
|
221
|
+
host.classList.remove(DRAG_OVER_CLASS);
|
|
222
|
+
}
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
event.stopPropagation();
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleLuminoDrop = (event: Event) => {
|
|
228
|
+
if (!isEnabled() || !hasFileBrowserPaths(event) || !isInsideHost(event)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const dragEvent = event as unknown as {
|
|
232
|
+
mimeData: { getData: (key: string) => unknown };
|
|
233
|
+
shiftKey: boolean;
|
|
234
|
+
proposedAction?: string;
|
|
235
|
+
dropAction: string;
|
|
236
|
+
};
|
|
237
|
+
const paths = dragEvent.mimeData.getData(FILE_BROWSER_MIME);
|
|
238
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
event.stopPropagation();
|
|
243
|
+
dragEvent.dropAction = dragEvent.proposedAction || 'move';
|
|
244
|
+
state.dragDepth = 0;
|
|
245
|
+
host.classList.remove(DRAG_OVER_CLASS);
|
|
246
|
+
inject(
|
|
247
|
+
paths.filter((p): p is string => typeof p === 'string'),
|
|
248
|
+
dragEvent.shiftKey
|
|
249
|
+
);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
host.addEventListener('dragenter', handleDragEnter, true);
|
|
253
|
+
host.addEventListener('dragover', handleDragOver, true);
|
|
254
|
+
host.addEventListener('dragleave', handleDragLeave, true);
|
|
255
|
+
host.addEventListener('drop', handleDrop, true);
|
|
256
|
+
// lm-* listeners go on the document (capture phase) so we observe
|
|
257
|
+
// them before any intermediate widget can stopPropagation. The
|
|
258
|
+
// containment check filters to events whose target is inside this
|
|
259
|
+
// terminal's host node.
|
|
260
|
+
document.addEventListener('lm-dragenter', handleLuminoDragEnter, true);
|
|
261
|
+
document.addEventListener('lm-dragover', handleLuminoDragOver, true);
|
|
262
|
+
document.addEventListener('lm-dragleave', handleLuminoDragLeave, true);
|
|
263
|
+
document.addEventListener('lm-drop', handleLuminoDrop, true);
|
|
264
|
+
|
|
265
|
+
const button = new TerminalDragModeButton('mention', () => {
|
|
266
|
+
state.mode = state.mode === 'mention' ? 'raw' : 'mention';
|
|
267
|
+
button.setMode(state.mode);
|
|
268
|
+
});
|
|
269
|
+
widget.toolbar.addItem('nbi-terminal-drag-mode', button);
|
|
270
|
+
|
|
271
|
+
state.cleanup = () => {
|
|
272
|
+
host.removeEventListener('dragenter', handleDragEnter, true);
|
|
273
|
+
host.removeEventListener('dragover', handleDragOver, true);
|
|
274
|
+
host.removeEventListener('dragleave', handleDragLeave, true);
|
|
275
|
+
host.removeEventListener('drop', handleDrop, true);
|
|
276
|
+
document.removeEventListener('lm-dragenter', handleLuminoDragEnter, true);
|
|
277
|
+
document.removeEventListener('lm-dragover', handleLuminoDragOver, true);
|
|
278
|
+
document.removeEventListener('lm-dragleave', handleLuminoDragLeave, true);
|
|
279
|
+
document.removeEventListener('lm-drop', handleLuminoDrop, true);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
widget.disposed.connect(() => {
|
|
283
|
+
state.cleanup();
|
|
284
|
+
widgetState.delete(widget);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
widgetState.set(widget, state);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function hasFileBrowserPaths(event: Event): boolean {
|
|
291
|
+
const mimeData = (
|
|
292
|
+
event as unknown as { mimeData?: { hasData?: (key: string) => boolean } }
|
|
293
|
+
).mimeData;
|
|
294
|
+
return mimeData?.hasData?.(FILE_BROWSER_MIME) === true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function uploadAndInject(
|
|
298
|
+
files: File[],
|
|
299
|
+
shiftHeld: boolean,
|
|
300
|
+
inject: (paths: string[], shiftHeld: boolean) => void
|
|
301
|
+
): Promise<void> {
|
|
302
|
+
const results = await Promise.allSettled(
|
|
303
|
+
files.map(f => NBIAPI.uploadFile(f))
|
|
304
|
+
);
|
|
305
|
+
const paths: string[] = [];
|
|
306
|
+
const failures: { name: string; reason: string }[] = [];
|
|
307
|
+
results.forEach((result, index) => {
|
|
308
|
+
const file = files[index];
|
|
309
|
+
if (result.status === 'fulfilled') {
|
|
310
|
+
paths.push(result.value.serverPath);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
failures.push({
|
|
314
|
+
name: file.name,
|
|
315
|
+
reason: describeUploadError(result.reason)
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
if (failures.length > 0) {
|
|
319
|
+
// Inline in the toast so the user sees both what failed and why
|
|
320
|
+
// (e.g. 413 from the size cap). Truncated to 3 entries; rest collapsed
|
|
321
|
+
// into a "+ N more" footer to fit JL's 140-char notification limit.
|
|
322
|
+
const head = failures
|
|
323
|
+
.slice(0, 3)
|
|
324
|
+
.map(f => `${f.name}: ${f.reason}`)
|
|
325
|
+
.join('; ');
|
|
326
|
+
const tail = failures.length > 3 ? ` (+${failures.length - 3} more)` : '';
|
|
327
|
+
Notification.error(`Terminal drop upload failed for ${head}${tail}`);
|
|
328
|
+
}
|
|
329
|
+
inject(paths, shiftHeld);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function describeUploadError(reason: unknown): string {
|
|
333
|
+
if (reason && typeof reason === 'object') {
|
|
334
|
+
const r = reason as { message?: unknown; response?: { status?: number } };
|
|
335
|
+
if (typeof r.message === 'string' && r.message.trim().length > 0) {
|
|
336
|
+
return r.message;
|
|
337
|
+
}
|
|
338
|
+
if (r.response && typeof r.response.status === 'number') {
|
|
339
|
+
return `HTTP ${r.response.status}`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return String(reason);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
class TerminalDragModeButton extends Widget {
|
|
346
|
+
private _button: HTMLButtonElement;
|
|
347
|
+
private _onToggle: () => void;
|
|
348
|
+
|
|
349
|
+
constructor(initialMode: DragMode, onToggle: () => void) {
|
|
350
|
+
super();
|
|
351
|
+
this.addClass('jp-Toolbar-item');
|
|
352
|
+
this.addClass(TOOLBAR_BUTTON_CLASS);
|
|
353
|
+
this._onToggle = onToggle;
|
|
354
|
+
|
|
355
|
+
this._button = document.createElement('button');
|
|
356
|
+
this._button.type = 'button';
|
|
357
|
+
this._button.classList.add('jp-ToolbarButtonComponent');
|
|
358
|
+
this._button.classList.add(`${TOOLBAR_BUTTON_CLASS}-toggle`);
|
|
359
|
+
this._button.addEventListener('click', () => this._onToggle());
|
|
360
|
+
this.node.appendChild(this._button);
|
|
361
|
+
|
|
362
|
+
this.setMode(initialMode);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
setMode(mode: DragMode): void {
|
|
366
|
+
const isMention = mode === 'mention';
|
|
367
|
+
this._button.textContent = isMention ? '@' : '/';
|
|
368
|
+
this._button.setAttribute('aria-pressed', isMention ? 'false' : 'true');
|
|
369
|
+
// aria-label carries the full mode + Shift-modifier explanation;
|
|
370
|
+
// title is a short hover-tip so screen readers don't double-announce
|
|
371
|
+
// the same string from both attributes.
|
|
372
|
+
this._button.setAttribute(
|
|
373
|
+
'aria-label',
|
|
374
|
+
isMention
|
|
375
|
+
? 'Terminal drop inserts @-mention paths. Click to switch to raw path mode. Hold Shift while dropping to invert for one drop.'
|
|
376
|
+
: 'Terminal drop inserts raw, shell-escaped absolute paths. Click to switch to @-mention mode. Hold Shift while dropping to invert for one drop.'
|
|
377
|
+
);
|
|
378
|
+
this._button.title = isMention
|
|
379
|
+
? 'Drop mode: @-mention'
|
|
380
|
+
: 'Drop mode: raw path';
|
|
381
|
+
}
|
|
382
|
+
}
|
package/src/tokens.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
|
|
3
|
+
import { Widget } from '@lumino/widgets';
|
|
4
|
+
import { CodeEditor } from '@jupyterlab/codeeditor';
|
|
5
|
+
import { Token } from '@lumino/coreutils';
|
|
6
|
+
|
|
7
|
+
export interface IActiveDocumentInfo {
|
|
8
|
+
activeWidget: Widget | null;
|
|
9
|
+
language: string;
|
|
10
|
+
filename: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
activeCellIndex: number;
|
|
13
|
+
selection?: CodeEditor.IRange;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IChatCompletionResponseEmitter {
|
|
17
|
+
emit: (response: any) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum RequestDataType {
|
|
21
|
+
ChatRequest = 'chat-request',
|
|
22
|
+
ChatUserInput = 'chat-user-input',
|
|
23
|
+
ClearChatHistory = 'clear-chat-history',
|
|
24
|
+
RunUICommandResponse = 'run-ui-command-response',
|
|
25
|
+
GenerateCode = 'generate-code',
|
|
26
|
+
CancelChatRequest = 'cancel-chat-request',
|
|
27
|
+
InlineCompletionRequest = 'inline-completion-request',
|
|
28
|
+
CancelInlineCompletionRequest = 'cancel-inline-completion-request'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export enum BackendMessageType {
|
|
32
|
+
StreamMessage = 'stream-message',
|
|
33
|
+
StreamEnd = 'stream-end',
|
|
34
|
+
RunUICommand = 'run-ui-command',
|
|
35
|
+
GitHubCopilotLoginStatusChange = 'github-copilot-login-status-change',
|
|
36
|
+
MCPServerStatusChange = 'mcp-server-status-change',
|
|
37
|
+
ClaudeCodeStatusChange = 'claude-code-status-change',
|
|
38
|
+
ClaudeCodeHeartbeat = 'claude-code-heartbeat',
|
|
39
|
+
SkillsReloaded = 'skills-reloaded'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export enum ResponseStreamDataType {
|
|
43
|
+
LLMRaw = 'llm-raw',
|
|
44
|
+
Markdown = 'markdown',
|
|
45
|
+
MarkdownPart = 'markdown-part',
|
|
46
|
+
Image = 'image',
|
|
47
|
+
HTMLFrame = 'html-frame',
|
|
48
|
+
Button = 'button',
|
|
49
|
+
Anchor = 'anchor',
|
|
50
|
+
Progress = 'progress',
|
|
51
|
+
Confirmation = 'confirmation',
|
|
52
|
+
AskUserQuestion = 'ask-user-question'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export enum ContextType {
|
|
56
|
+
Custom = 'custom',
|
|
57
|
+
CurrentFile = 'current-file',
|
|
58
|
+
OutputContext = 'output-context'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IOutputContextItem {
|
|
62
|
+
cellSource: string;
|
|
63
|
+
mimeBundles: { mimeType: string; data: string; sizeTokens: number }[];
|
|
64
|
+
isError: boolean;
|
|
65
|
+
truncated: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export enum MCPServerStatus {
|
|
69
|
+
NotConnected = 'not-connected',
|
|
70
|
+
Connecting = 'connecting',
|
|
71
|
+
Disconnecting = 'disconnecting',
|
|
72
|
+
FailedToConnect = 'failed-to-connect',
|
|
73
|
+
Connected = 'connected',
|
|
74
|
+
UpdatingToolList = 'updating-tool-list',
|
|
75
|
+
UpdatedToolList = 'updated-tool-list',
|
|
76
|
+
UpdatingPromptList = 'updating-prompt-list',
|
|
77
|
+
UpdatedPromptList = 'updated-prompt-list'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface IContextItem {
|
|
81
|
+
type: ContextType;
|
|
82
|
+
content: string;
|
|
83
|
+
currentCellContents: ICellContents;
|
|
84
|
+
filePath?: string;
|
|
85
|
+
cellIndex?: number;
|
|
86
|
+
startLine?: number;
|
|
87
|
+
endLine?: number;
|
|
88
|
+
isImage?: boolean;
|
|
89
|
+
mimeType?: string;
|
|
90
|
+
outputContext?: IOutputContextItem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ICellContents {
|
|
94
|
+
input: string;
|
|
95
|
+
output: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface IChatParticipant {
|
|
99
|
+
id: string;
|
|
100
|
+
name: string;
|
|
101
|
+
description: string;
|
|
102
|
+
iconPath: string;
|
|
103
|
+
commands: string[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface IToolSelections {
|
|
107
|
+
builtinToolsets?: string[];
|
|
108
|
+
mcpServers?: {
|
|
109
|
+
[key: string]: string[];
|
|
110
|
+
};
|
|
111
|
+
extensions?: {
|
|
112
|
+
[key: string]: string[];
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export enum BuiltinToolsetType {
|
|
117
|
+
NotebookEdit = 'nbi-notebook-edit',
|
|
118
|
+
NotebookExecute = 'nbi-notebook-execute',
|
|
119
|
+
PythonFileEdit = 'nbi-python-file-edit',
|
|
120
|
+
FileEdit = 'nbi-file-edit',
|
|
121
|
+
FileRead = 'nbi-file-read',
|
|
122
|
+
CommandExecute = 'nbi-command-execute'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const GITHUB_COPILOT_PROVIDER_ID = 'github-copilot';
|
|
126
|
+
export const CLAUDE_CODE_CHAT_PARTICIPANT_ID = 'claude-code';
|
|
127
|
+
|
|
128
|
+
export enum AssistantMode {
|
|
129
|
+
Default = 'default',
|
|
130
|
+
Claude = 'claude'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export enum TelemetryEventType {
|
|
134
|
+
InlineCompletionRequest = 'inline-completion-request',
|
|
135
|
+
ExplainThisRequest = 'explain-this-request',
|
|
136
|
+
FixThisCodeRequest = 'fix-this-code-request',
|
|
137
|
+
ExplainThisOutputRequest = 'explain-this-output-request',
|
|
138
|
+
TroubleshootThisOutputRequest = 'troubleshoot-this-output-request',
|
|
139
|
+
OutputFollowUpRequest = 'output-follow-up-request',
|
|
140
|
+
GenerateCodeRequest = 'generate-code-request',
|
|
141
|
+
ChatRequest = 'chat-request',
|
|
142
|
+
InlineChatRequest = 'inline-chat-request',
|
|
143
|
+
ChatResponse = 'chat-response',
|
|
144
|
+
InlineChatResponse = 'inline-chat-response',
|
|
145
|
+
InlineCompletionResponse = 'inline-completion-response',
|
|
146
|
+
Feedback = 'feedback'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ITelemetryEvent {
|
|
150
|
+
type: TelemetryEventType;
|
|
151
|
+
data?: any;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface ITelemetryListener {
|
|
155
|
+
get name(): string;
|
|
156
|
+
onTelemetryEvent: (event: ITelemetryEvent) => void;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface ITelemetryEmitter {
|
|
160
|
+
emitTelemetryEvent(event: ITelemetryEvent): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const INotebookIntelligence = new Token<INotebookIntelligence>(
|
|
164
|
+
'@plmbr/notebook-intelligence:INotebookIntelligence',
|
|
165
|
+
'AI coding assistant for JupyterLab.'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
export interface INotebookIntelligence {
|
|
169
|
+
registerTelemetryListener: (listener: ITelemetryListener) => void;
|
|
170
|
+
unregisterTelemetryListener: (listener: ITelemetryListener) => void;
|
|
171
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Anchor-id constants shared between the tour step definitions and the
|
|
5
|
+
* chat-sidebar JSX. Centralising these keeps the two halves coupled at
|
|
6
|
+
* the TypeScript level rather than by string identity — a typo on
|
|
7
|
+
* either side now surfaces at compile time instead of silently
|
|
8
|
+
* skipping a step at runtime.
|
|
9
|
+
*/
|
|
10
|
+
export const TOUR_ANCHOR = {
|
|
11
|
+
newChat: 'new-chat',
|
|
12
|
+
settingsGear: 'settings-gear',
|
|
13
|
+
claudeHistory: 'claude-history',
|
|
14
|
+
slashCommands: 'slash-commands',
|
|
15
|
+
addContext: 'add-context',
|
|
16
|
+
uploadFile: 'upload-file',
|
|
17
|
+
promptInput: 'prompt-input',
|
|
18
|
+
chatMode: 'chat-mode'
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
export type TourAnchorId = (typeof TOUR_ANCHOR)[keyof typeof TOUR_ANCHOR];
|