xtralab 0.14.1 → 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.d.ts +7 -0
- package/lib/customPanel/index.js +89 -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 +51 -98
- package/lib/fileBrowser/contents.d.ts +14 -0
- package/lib/fileBrowser/contents.js +28 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
- package/lib/fileBrowser/dragAndDrop.js +110 -0
- package/lib/fileBrowser/fileBrowser.d.ts +21 -9
- package/lib/fileBrowser/fileBrowser.js +211 -405
- 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 +41 -10
- package/lib/fileBrowser/widget.d.ts +170 -53
- package/lib/fileBrowser/widget.js +242 -18
- 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 +2 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +25 -54
- 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 +14 -37
- 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/schemaDefaults.d.ts +2 -9
- package/lib/launcher/schemaDefaults.js +37 -13
- 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 -13
- package/lib/sidebar/index.js +73 -46
- 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 -33
- package/lib/terminals/index.js +24 -102
- 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 +72 -38
- package/lib/terminals/widget.js +173 -73
- 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/schema/launcher.json +2 -2
- package/schema/sidebar.json +47 -7
- 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 +119 -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 +70 -98
- package/src/fileBrowser/contents.ts +33 -0
- package/src/fileBrowser/dragAndDrop.ts +188 -0
- package/src/fileBrowser/fileBrowser.tsx +259 -440
- 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 +43 -10
- package/src/fileBrowser/widget.tsx +344 -64
- 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 +2 -9
- package/src/launcher/agents.ts +74 -92
- 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 +15 -37
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/schemaDefaults.ts +43 -13
- 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 +103 -52
- 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 +31 -102
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +229 -113
- 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 +25 -73
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +6 -0
- package/style/git.css +22 -184
- package/style/highlight.css +1 -3
- package/style/index.js +1 -0
- 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 +18 -123
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
|
@@ -5,11 +5,9 @@ import { addIcon, closeIcon, collapseAllIcon, CommandToolbarButton, copyIcon, do
|
|
|
5
5
|
import { toCanonicalPath, toServerPath } from './contents';
|
|
6
6
|
import { FILE_BROWSER_ID } from './widget';
|
|
7
7
|
/**
|
|
8
|
-
* Command
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* via `IFileBrowserFactory.tracker` — our widget is not a `FileBrowser`, so
|
|
12
|
-
* those handlers would never see it.
|
|
8
|
+
* Command ids for the xtralab browser. Namespaced `xtralab:` rather than
|
|
9
|
+
* reusing the `filebrowser:` ids: the core handlers resolve their target via
|
|
10
|
+
* `IFileBrowserFactory.tracker`, which never sees this widget.
|
|
13
11
|
*/
|
|
14
12
|
export var CommandIDs;
|
|
15
13
|
(function (CommandIDs) {
|
|
@@ -29,18 +27,13 @@ export var CommandIDs;
|
|
|
29
27
|
CommandIDs.revealInFileTree = 'xtralab:reveal-in-file-tree';
|
|
30
28
|
})(CommandIDs || (CommandIDs = {}));
|
|
31
29
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* core's populator does not try to fill ours with items derived from the
|
|
35
|
-
* default file browser's selection.
|
|
30
|
+
* "Open With" submenu id. Distinct from the core `jp-contextmenu-open-with`
|
|
31
|
+
* so the core's populator does not fill ours from its own selection.
|
|
36
32
|
*/
|
|
37
33
|
const OPEN_WITH_SUBMENU_ID = 'jp-contextmenu-xtralab-open-with';
|
|
38
34
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* items match a selector — and fall back to the first item in the tree's
|
|
42
|
-
* selection if the command was invoked without an active contextmenu event
|
|
43
|
-
* (e.g. from the command palette).
|
|
35
|
+
* Path of the right-clicked item, from the contextmenu event's target;
|
|
36
|
+
* falls back to the tree selection when invoked without one (the palette).
|
|
44
37
|
*/
|
|
45
38
|
function getTargetPath(app, browser) {
|
|
46
39
|
const node = app.contextMenuHitTest(n => n.dataset !== undefined && n.dataset.type === 'item');
|
|
@@ -50,25 +43,15 @@ function getTargetPath(app, browser) {
|
|
|
50
43
|
}
|
|
51
44
|
return browser.selectedPaths[0];
|
|
52
45
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Resolve the kind of the item that was right-clicked, based on the data
|
|
55
|
-
* attributes emitted by `@pierre/trees` rows. Returns `undefined` if no
|
|
56
|
-
* contextmenu event is active or the target is not a tree item.
|
|
57
|
-
*/
|
|
58
46
|
function getTargetKind(app) {
|
|
59
47
|
const node = app.contextMenuHitTest(n => n.dataset !== undefined && n.dataset.type === 'item');
|
|
60
48
|
const kind = node === null || node === void 0 ? void 0 : node.dataset.itemType;
|
|
61
49
|
return kind === 'file' || kind === 'folder' ? kind : undefined;
|
|
62
50
|
}
|
|
63
51
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* "Open" on any file in a multi-selection opens them all. If the
|
|
68
|
-
* right-clicked row is *not* in the selection, only that row is opened.
|
|
69
|
-
*
|
|
70
|
-
* Folders are always filtered out: there is no "current directory" in
|
|
71
|
-
* this tree, so opening a folder via "Open" is a no-op.
|
|
52
|
+
* Canonical paths "Open" acts on: the whole selection when the right-clicked
|
|
53
|
+
* row is part of it (matching the default browser), else just that row.
|
|
54
|
+
* Folders are filtered out — this tree has no "current directory".
|
|
72
55
|
*/
|
|
73
56
|
function getOpenPaths(app, browser) {
|
|
74
57
|
const node = app.contextMenuHitTest(n => n.dataset !== undefined && n.dataset.type === 'item');
|
|
@@ -83,18 +66,13 @@ function getOpenPaths(app, browser) {
|
|
|
83
66
|
}
|
|
84
67
|
return candidates.filter(path => !path.endsWith('/'));
|
|
85
68
|
}
|
|
86
|
-
/**
|
|
87
|
-
* True iff there is an actionable target for a command on the right-click.
|
|
88
|
-
*/
|
|
89
69
|
function hasTarget(app, browser) {
|
|
90
70
|
return getTargetPath(app, browser) !== undefined;
|
|
91
71
|
}
|
|
92
72
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* when the tree has no selection — that matches the contents API's
|
|
97
|
-
* convention for "the root directory".
|
|
73
|
+
* Working directory for create actions: a selected folder is its own cwd, a
|
|
74
|
+
* file falls back to its parent; empty string (the contents-API root) when
|
|
75
|
+
* nothing is selected.
|
|
98
76
|
*/
|
|
99
77
|
function getWorkingDirectory(browser) {
|
|
100
78
|
const first = browser.selectedPaths[0];
|
|
@@ -108,14 +86,10 @@ function getWorkingDirectory(browser) {
|
|
|
108
86
|
return PathExt.dirname(serverPath);
|
|
109
87
|
}
|
|
110
88
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* Valid only during a context-menu invocation: `contextMenuHitTest` reads the
|
|
117
|
-
* last context-menu event, which is not cleared when the menu closes, so
|
|
118
|
-
* {@link documentPathToReveal} gates this to avoid resolving a stale tab.
|
|
89
|
+
* Main-area widget whose tab is the target of the current context-menu
|
|
90
|
+
* event, or `null`. `contextMenuHitTest` reads the last context-menu event,
|
|
91
|
+
* which is not cleared when the menu closes, so callers must gate against
|
|
92
|
+
* resolving a stale tab.
|
|
119
93
|
*/
|
|
120
94
|
function contextMenuTabWidget(app) {
|
|
121
95
|
const node = app.contextMenuHitTest(n => n.dataset !== undefined && n.dataset.type === 'document-title');
|
|
@@ -131,10 +105,9 @@ function contextMenuTabWidget(app) {
|
|
|
131
105
|
return null;
|
|
132
106
|
}
|
|
133
107
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* `path` is already canonical. Returns `undefined` when nothing resolves.
|
|
108
|
+
* Canonical path of the document to reveal: the right-clicked tab when
|
|
109
|
+
* `fromContextMenu` is true (the file-tab menu), else the active main-area
|
|
110
|
+
* widget (the palette).
|
|
138
111
|
*/
|
|
139
112
|
function documentPathToReveal(app, docManager, fromContextMenu) {
|
|
140
113
|
var _a, _b, _c;
|
|
@@ -146,14 +119,10 @@ function documentPathToReveal(app, docManager, fromContextMenu) {
|
|
|
146
119
|
return path.length > 0 ? path : undefined;
|
|
147
120
|
}
|
|
148
121
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* `@jupyterlab/filebrowser-extension:open-with` pattern: the schema declares
|
|
152
|
-
* an empty submenu by id, and we look it up on the open context menu and
|
|
153
|
-
* fill it before the user can hover.
|
|
154
|
-
*
|
|
122
|
+
* Build the populator that fills the "Open With" submenu on every
|
|
123
|
+
* context-menu open, mirroring `filebrowser-extension:open-with`.
|
|
155
124
|
* `preferredWidgetFactories(path)` is path-only, so the populator stays
|
|
156
|
-
* synchronous and the submenu is ready before the user can hover
|
|
125
|
+
* synchronous and the submenu is ready before the user can hover.
|
|
157
126
|
*/
|
|
158
127
|
function makeOpenWithUpdater(app, browser) {
|
|
159
128
|
let items = [];
|
|
@@ -182,13 +151,9 @@ function makeOpenWithUpdater(app, browser) {
|
|
|
182
151
|
};
|
|
183
152
|
}
|
|
184
153
|
/**
|
|
185
|
-
* Register
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @returns A function that detaches the menu items and signal listener.
|
|
190
|
-
* The commands themselves are owned by the registry for the lifetime of
|
|
191
|
-
* the plugin.
|
|
154
|
+
* Register the xtralab commands, context-menu wiring, and the dynamic
|
|
155
|
+
* "Open With" populator. Returns a detach function; the commands themselves
|
|
156
|
+
* stay owned by the registry.
|
|
192
157
|
*/
|
|
193
158
|
export function registerCommands(opts) {
|
|
194
159
|
var _a;
|
|
@@ -403,27 +368,32 @@ export function registerCommands(opts) {
|
|
|
403
368
|
browser.toggleFileFilter();
|
|
404
369
|
}
|
|
405
370
|
});
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
// the tree focused opens a search session.
|
|
371
|
+
// The filter can also auto-show when typing with the tree focused, so
|
|
372
|
+
// track visibility changes rather than the command's own executions.
|
|
409
373
|
const onFilterVisibleChanged = () => {
|
|
410
374
|
commands.notifyCommandChanged(CommandIDs.toggleFileFilter);
|
|
411
375
|
};
|
|
412
376
|
browser.fileFilterVisibleChanged.connect(onFilterVisibleChanged);
|
|
413
|
-
// Public reveal seam
|
|
414
|
-
//
|
|
415
|
-
//
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
377
|
+
// Public reveal seam for other plugins; an empty path is the workspace-root
|
|
378
|
+
// gesture (the root has no tree row of its own). The tree is movable, so
|
|
379
|
+
// surface whichever sidebar widget holds it.
|
|
380
|
+
const activateTreeHost = () => {
|
|
381
|
+
for (const area of ['left', 'right']) {
|
|
382
|
+
for (const widget of app.shell.widgets(area)) {
|
|
383
|
+
if (widget.node.contains(browser.sectionNode)) {
|
|
384
|
+
app.shell.activateById(widget.id);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
app.shell.activateById(FILE_BROWSER_ID);
|
|
390
|
+
};
|
|
421
391
|
commands.addCommand(CommandIDs.revealPath, {
|
|
422
392
|
label: trans.__('Reveal in File Browser'),
|
|
423
393
|
execute: (args) => {
|
|
424
394
|
var _a;
|
|
425
395
|
const path = (_a = args.path) !== null && _a !== void 0 ? _a : '';
|
|
426
|
-
|
|
396
|
+
activateTreeHost();
|
|
427
397
|
if (path.length === 0) {
|
|
428
398
|
browser.scrollToRoot();
|
|
429
399
|
}
|
|
@@ -432,8 +402,6 @@ export function registerCommands(opts) {
|
|
|
432
402
|
}
|
|
433
403
|
}
|
|
434
404
|
});
|
|
435
|
-
// Reveal the resolved document in the tree browser via the reveal-path
|
|
436
|
-
// command, which surfaces the sidebar and scrolls to the target.
|
|
437
405
|
commands.addCommand(CommandIDs.revealInFileTree, {
|
|
438
406
|
label: trans.__('Show in File Tree'),
|
|
439
407
|
caption: trans.__('Show this file in the xtralab file browser'),
|
|
@@ -468,41 +436,29 @@ export function registerCommands(opts) {
|
|
|
468
436
|
commands.addCommand(CommandIDs.newLauncher, {
|
|
469
437
|
label: trans.__('New Launcher'),
|
|
470
438
|
caption: trans.__('Open a new launcher'),
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
// styling expects the raw `jp-icon3` paths from `addIcon` so it can
|
|
474
|
-
// recolor them against the brand background.
|
|
439
|
+
// Raw `addIcon`, no `menuItem` bindprops: the launcher-extension's blue
|
|
440
|
+
// toolbar-button styling recolors the raw `jp-icon3` paths.
|
|
475
441
|
icon: addIcon,
|
|
476
442
|
execute: () => {
|
|
477
443
|
const cwd = getWorkingDirectory(browser);
|
|
478
444
|
return commands.execute('launcher:create', { cwd });
|
|
479
445
|
}
|
|
480
446
|
});
|
|
481
|
-
// Re-evaluate enabled/visible state when the user changes the tree
|
|
482
|
-
// selection, in case a command's predicate depends on the selection.
|
|
483
447
|
browser.selectionChanged.connect(() => {
|
|
484
448
|
for (const id of Object.values(CommandIDs)) {
|
|
485
449
|
commands.notifyCommandChanged(id);
|
|
486
450
|
}
|
|
487
451
|
});
|
|
488
|
-
// This command's enabled state tracks the active widget, not the tree
|
|
489
|
-
// selection, so re-evaluate it on current-widget changes. `currentChanged`
|
|
490
|
-
// is optional on the shell interface.
|
|
491
452
|
const onCurrentChanged = () => {
|
|
492
453
|
commands.notifyCommandChanged(CommandIDs.revealInFileTree);
|
|
493
454
|
};
|
|
494
455
|
(_a = app.shell.currentChanged) === null || _a === void 0 ? void 0 : _a.connect(onCurrentChanged);
|
|
495
|
-
// Also surface it in the palette (acts on the active editor); the file-tab
|
|
496
|
-
// context-menu entry is declared in `schema/plugin.json`.
|
|
497
456
|
const paletteItem = palette === null || palette === void 0 ? void 0 : palette.addItem({
|
|
498
457
|
command: CommandIDs.revealInFileTree,
|
|
499
458
|
category: trans.__('File Browser')
|
|
500
459
|
});
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
// schema also declares an empty submenu placeholder with id
|
|
504
|
-
// `OPEN_WITH_SUBMENU_ID`; we fill it on every open from the document
|
|
505
|
-
// factories that can open the right-clicked file.
|
|
460
|
+
// Static context-menu items live in `schema/plugin.json` so users can
|
|
461
|
+
// override them; the schema declares the empty "Open With" placeholder.
|
|
506
462
|
const updateOpenWithMenu = makeOpenWithUpdater(app, browser);
|
|
507
463
|
app.contextMenu.opened.connect(updateOpenWithMenu);
|
|
508
464
|
return () => {
|
|
@@ -514,9 +470,8 @@ export function registerCommands(opts) {
|
|
|
514
470
|
};
|
|
515
471
|
}
|
|
516
472
|
/**
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* than commands.
|
|
473
|
+
* Toolbar item names, distinct from {@link CommandIDs}: the toolbar API
|
|
474
|
+
* uses opaque names rather than commands.
|
|
520
475
|
*/
|
|
521
476
|
export var ToolbarNames;
|
|
522
477
|
(function (ToolbarNames) {
|
|
@@ -527,10 +482,8 @@ export var ToolbarNames;
|
|
|
527
482
|
ToolbarNames.toggleFileFilter = 'toggle-file-filter';
|
|
528
483
|
})(ToolbarNames || (ToolbarNames = {}));
|
|
529
484
|
/**
|
|
530
|
-
* Populate the
|
|
531
|
-
*
|
|
532
|
-
* button, a refresh button, a collapse-all button, and the file filter
|
|
533
|
-
* toggle.
|
|
485
|
+
* Populate the toolbar with the buttons that mirror the default JupyterLab
|
|
486
|
+
* file browser.
|
|
534
487
|
*/
|
|
535
488
|
export function populateToolbar(opts) {
|
|
536
489
|
const { app, browser } = opts;
|
|
@@ -10,11 +10,25 @@ export declare const ROOT_LOAD_KEY = "";
|
|
|
10
10
|
* still be represented in the flat path list.
|
|
11
11
|
*/
|
|
12
12
|
export declare function toCanonicalPath(child: Contents.IModel): string;
|
|
13
|
+
/**
|
|
14
|
+
* Compute the canonical parent path for a canonical path. Returns
|
|
15
|
+
* {@link ROOT_LOAD_KEY} for top-level entries.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parentOf(canonicalPath: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* The last segment of a canonical path. Directories keep their trailing
|
|
20
|
+
* slash, so appending the result to a canonical directory path yields a
|
|
21
|
+
* canonical path again.
|
|
22
|
+
*/
|
|
23
|
+
export declare function canonicalBasename(canonicalPath: string): string;
|
|
13
24
|
/**
|
|
14
25
|
* Strip the trailing slash that `@pierre/trees` uses for canonical directory
|
|
15
26
|
* paths so the value can be sent to the Jupyter contents API.
|
|
16
27
|
*/
|
|
17
28
|
export declare function toServerPath(canonicalPath: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The immediate children of a directory, as canonical `@pierre/trees` paths.
|
|
31
|
+
*/
|
|
18
32
|
interface IListedDirectory {
|
|
19
33
|
/**
|
|
20
34
|
* Canonical paths for every immediate child of the requested directory.
|
|
@@ -12,6 +12,34 @@ export const ROOT_LOAD_KEY = '';
|
|
|
12
12
|
export function toCanonicalPath(child) {
|
|
13
13
|
return child.type === 'directory' ? `${child.path}${DIR_SUFFIX}` : child.path;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Compute the canonical parent path for a canonical path. Returns
|
|
17
|
+
* {@link ROOT_LOAD_KEY} for top-level entries.
|
|
18
|
+
*/
|
|
19
|
+
export function parentOf(canonicalPath) {
|
|
20
|
+
const trimmed = canonicalPath.endsWith(DIR_SUFFIX)
|
|
21
|
+
? canonicalPath.slice(0, -DIR_SUFFIX.length)
|
|
22
|
+
: canonicalPath;
|
|
23
|
+
const idx = trimmed.lastIndexOf(DIR_SUFFIX);
|
|
24
|
+
if (idx < 0) {
|
|
25
|
+
return ROOT_LOAD_KEY;
|
|
26
|
+
}
|
|
27
|
+
return `${trimmed.slice(0, idx)}${DIR_SUFFIX}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The last segment of a canonical path. Directories keep their trailing
|
|
31
|
+
* slash, so appending the result to a canonical directory path yields a
|
|
32
|
+
* canonical path again.
|
|
33
|
+
*/
|
|
34
|
+
export function canonicalBasename(canonicalPath) {
|
|
35
|
+
const isDir = canonicalPath.endsWith(DIR_SUFFIX);
|
|
36
|
+
const trimmed = isDir
|
|
37
|
+
? canonicalPath.slice(0, -DIR_SUFFIX.length)
|
|
38
|
+
: canonicalPath;
|
|
39
|
+
const idx = trimmed.lastIndexOf(DIR_SUFFIX);
|
|
40
|
+
const base = idx < 0 ? trimmed : trimmed.slice(idx + DIR_SUFFIX.length);
|
|
41
|
+
return isDir ? `${base}${DIR_SUFFIX}` : base;
|
|
42
|
+
}
|
|
15
43
|
/**
|
|
16
44
|
* Strip the trailing slash that `@pierre/trees` uses for canonical directory
|
|
17
45
|
* paths so the value can be sent to the Jupyter contents API.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { FileTree, FileTreeDragAndDropConfig, FileTreeDropContext, FileTreeDropResult } from '@pierre/trees';
|
|
3
|
+
/**
|
|
4
|
+
* One tree-model move a drop performs, as canonical paths.
|
|
5
|
+
*/
|
|
6
|
+
export interface IDropMove {
|
|
7
|
+
/**
|
|
8
|
+
* The canonical path of the dragged item.
|
|
9
|
+
*/
|
|
10
|
+
from: string;
|
|
11
|
+
/**
|
|
12
|
+
* The canonical destination path: its basename under the target directory.
|
|
13
|
+
*/
|
|
14
|
+
to: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Receives drop outcomes from the `@pierre/trees` drag-and-drop
|
|
18
|
+
* machinery. Implemented inside the file browser's contents-sync
|
|
19
|
+
* effect, which owns the load-state and contents-API bookkeeping.
|
|
20
|
+
*/
|
|
21
|
+
export interface ITreeDropHandler {
|
|
22
|
+
/**
|
|
23
|
+
* The tree applied the drop to its model: persist the move and
|
|
24
|
+
* re-key the load-state mirrors.
|
|
25
|
+
*/
|
|
26
|
+
completeDrop(result: FileTreeDropResult): void;
|
|
27
|
+
/**
|
|
28
|
+
* The tree refused the drop (typically a name collision) and left
|
|
29
|
+
* its model untouched.
|
|
30
|
+
*/
|
|
31
|
+
failedDrop(error: string, context: FileTreeDropContext): void;
|
|
32
|
+
/**
|
|
33
|
+
* A drop on the empty space below the last row: move the dragged
|
|
34
|
+
* paths to the workspace root.
|
|
35
|
+
*/
|
|
36
|
+
dropOnRoot(draggedPaths: readonly string[]): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The from→to moves a drop performs: each dragged path keeps its
|
|
40
|
+
* basename under the target directory. Paths already directly inside
|
|
41
|
+
* the target are skipped.
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeDropMoves(context: FileTreeDropContext): IDropMove[];
|
|
44
|
+
/**
|
|
45
|
+
* The moves for a drop of `paths` onto the workspace root.
|
|
46
|
+
*/
|
|
47
|
+
export declare function computeRootDropMoves(paths: readonly string[]): IDropMove[];
|
|
48
|
+
/**
|
|
49
|
+
* Build the `dragAndDrop` option for `useFileTree`. The tree captures
|
|
50
|
+
* its options once, so the callbacks delegate through `handlerRef`,
|
|
51
|
+
* filled by the contents-sync effect. `canDrop` hides the drop
|
|
52
|
+
* highlight for all-no-op drops, which the library would accept.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createTreeDragAndDropConfig(handlerRef: React.RefObject<ITreeDropHandler | null>): FileTreeDragAndDropConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Options for {@link useRootDropZone}.
|
|
57
|
+
*/
|
|
58
|
+
interface IRootDropZoneOptions {
|
|
59
|
+
/**
|
|
60
|
+
* The tree model, queried for the selection at drag start.
|
|
61
|
+
*/
|
|
62
|
+
model: FileTree;
|
|
63
|
+
/**
|
|
64
|
+
* The drop handler ref, filled by the contents-sync effect.
|
|
65
|
+
*/
|
|
66
|
+
handlerRef: React.RefObject<ITreeDropHandler | null>;
|
|
67
|
+
/**
|
|
68
|
+
* The light-DOM wrapper around the tree host; composed drag events
|
|
69
|
+
* bubble out of the shadow root to it.
|
|
70
|
+
*/
|
|
71
|
+
wrapperRef: React.RefObject<HTMLDivElement | null>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Accept drops on the empty space below the last row as moves to the
|
|
75
|
+
* workspace root — the tree resolves a drop target only while the cursor
|
|
76
|
+
* is over a row and silently discards such drops.
|
|
77
|
+
*/
|
|
78
|
+
export declare function useRootDropZone(options: IRootDropZoneOptions): void;
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ROOT_LOAD_KEY, canonicalBasename, parentOf } from './contents';
|
|
3
|
+
/**
|
|
4
|
+
* The from→to moves a drop performs: each dragged path keeps its
|
|
5
|
+
* basename under the target directory. Paths already directly inside
|
|
6
|
+
* the target are skipped.
|
|
7
|
+
*/
|
|
8
|
+
export function computeDropMoves(context) {
|
|
9
|
+
var _a;
|
|
10
|
+
const dir = (_a = context.target.directoryPath) !== null && _a !== void 0 ? _a : ROOT_LOAD_KEY;
|
|
11
|
+
return context.draggedPaths
|
|
12
|
+
.filter(path => parentOf(path) !== dir)
|
|
13
|
+
.map(path => ({ from: path, to: `${dir}${canonicalBasename(path)}` }));
|
|
14
|
+
}
|
|
15
|
+
const ROOT_DROP_TARGET = {
|
|
16
|
+
directoryPath: null,
|
|
17
|
+
flattenedSegmentPath: null,
|
|
18
|
+
hoveredPath: null,
|
|
19
|
+
kind: 'root'
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The moves for a drop of `paths` onto the workspace root.
|
|
23
|
+
*/
|
|
24
|
+
export function computeRootDropMoves(paths) {
|
|
25
|
+
return computeDropMoves({ draggedPaths: paths, target: ROOT_DROP_TARGET });
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the `dragAndDrop` option for `useFileTree`. The tree captures
|
|
29
|
+
* its options once, so the callbacks delegate through `handlerRef`,
|
|
30
|
+
* filled by the contents-sync effect. `canDrop` hides the drop
|
|
31
|
+
* highlight for all-no-op drops, which the library would accept.
|
|
32
|
+
*/
|
|
33
|
+
export function createTreeDragAndDropConfig(handlerRef) {
|
|
34
|
+
return {
|
|
35
|
+
canDrop: context => computeDropMoves(context).length > 0,
|
|
36
|
+
onDropComplete: result => {
|
|
37
|
+
var _a;
|
|
38
|
+
(_a = handlerRef.current) === null || _a === void 0 ? void 0 : _a.completeDrop(result);
|
|
39
|
+
},
|
|
40
|
+
onDropError: (error, context) => {
|
|
41
|
+
var _a;
|
|
42
|
+
(_a = handlerRef.current) === null || _a === void 0 ? void 0 : _a.failedDrop(error, context);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Accept drops on the empty space below the last row as moves to the
|
|
48
|
+
* workspace root — the tree resolves a drop target only while the cursor
|
|
49
|
+
* is over a row and silently discards such drops.
|
|
50
|
+
*/
|
|
51
|
+
export function useRootDropZone(options) {
|
|
52
|
+
const { model, handlerRef, wrapperRef } = options;
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
const wrapper = wrapperRef.current;
|
|
55
|
+
if (wrapper === null) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let draggedPaths = null;
|
|
59
|
+
const findRow = (event) => {
|
|
60
|
+
for (const target of event.composedPath()) {
|
|
61
|
+
if (target instanceof HTMLElement && target.dataset.type === 'item') {
|
|
62
|
+
return target;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
};
|
|
67
|
+
const handleDragStart = (event) => {
|
|
68
|
+
var _a;
|
|
69
|
+
draggedPaths = null;
|
|
70
|
+
// The tree cancels dragstart when it refuses a session, e.g.
|
|
71
|
+
// while the filter is open.
|
|
72
|
+
if (event.defaultPrevented) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const path = (_a = findRow(event)) === null || _a === void 0 ? void 0 : _a.dataset.itemPath;
|
|
76
|
+
if (path === undefined || path.length === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// The selection is the dragged set (the tree selects the pressed
|
|
80
|
+
// row); entries nested under a dragged folder travel with it.
|
|
81
|
+
const selection = model.getSelectedPaths();
|
|
82
|
+
const candidates = selection.includes(path) ? selection : [path];
|
|
83
|
+
const folders = candidates.filter(c => c.endsWith('/'));
|
|
84
|
+
draggedPaths = candidates.filter(c => !folders.some(folder => folder !== c && c.startsWith(folder)));
|
|
85
|
+
};
|
|
86
|
+
const handleDrop = (event) => {
|
|
87
|
+
var _a;
|
|
88
|
+
const paths = draggedPaths;
|
|
89
|
+
draggedPaths = null;
|
|
90
|
+
// Drops on a row were already resolved by the tree itself.
|
|
91
|
+
if (paths === null || findRow(event) !== null) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
(_a = handlerRef.current) === null || _a === void 0 ? void 0 : _a.dropOnRoot(paths);
|
|
96
|
+
};
|
|
97
|
+
const handleDragEnd = () => {
|
|
98
|
+
draggedPaths = null;
|
|
99
|
+
};
|
|
100
|
+
wrapper.addEventListener('dragstart', handleDragStart);
|
|
101
|
+
wrapper.addEventListener('drop', handleDrop);
|
|
102
|
+
window.addEventListener('dragend', handleDragEnd);
|
|
103
|
+
return () => {
|
|
104
|
+
wrapper.removeEventListener('dragstart', handleDragStart);
|
|
105
|
+
wrapper.removeEventListener('drop', handleDrop);
|
|
106
|
+
window.removeEventListener('dragend', handleDragEnd);
|
|
107
|
+
draggedPaths = null;
|
|
108
|
+
};
|
|
109
|
+
}, [model, handlerRef, wrapperRef]);
|
|
110
|
+
}
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
3
|
import { Contents } from '@jupyterlab/services';
|
|
4
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
4
5
|
import type { XtralabFileBrowser } from './widget';
|
|
6
|
+
/**
|
|
7
|
+
* Props for {@link FileBrowserComponent}.
|
|
8
|
+
*/
|
|
5
9
|
interface IFileBrowserProps {
|
|
10
|
+
/**
|
|
11
|
+
* The Jupyter contents manager backing the tree.
|
|
12
|
+
*/
|
|
6
13
|
contentsManager: Contents.IManager;
|
|
14
|
+
/**
|
|
15
|
+
* The document manager used to rename files on drag-and-drop moves.
|
|
16
|
+
*/
|
|
7
17
|
docManager: IDocumentManager;
|
|
18
|
+
/**
|
|
19
|
+
* Called with the server path of an activated file.
|
|
20
|
+
*/
|
|
8
21
|
onOpenFile?: (serverPath: string) => void;
|
|
9
22
|
/**
|
|
10
|
-
* The
|
|
11
|
-
|
|
23
|
+
* The application translator; defaults to `nullTranslator`.
|
|
24
|
+
*/
|
|
25
|
+
translator?: ITranslator;
|
|
26
|
+
/**
|
|
27
|
+
* The host widget; selection changes are pushed up for command handlers.
|
|
12
28
|
*/
|
|
13
29
|
widget?: XtralabFileBrowser;
|
|
14
30
|
}
|
|
15
31
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* the tree is populated lazily: the root is fetched on mount, and each
|
|
20
|
-
* directory is fetched the first time the user expands it. Expansion is
|
|
21
|
-
* detected by subscribing to the model and diffing against an in-memory load
|
|
22
|
-
* state map.
|
|
32
|
+
* A `@pierre/trees` file tree backed by the Jupyter contents API. The API
|
|
33
|
+
* returns one directory level per request, so directories load lazily on
|
|
34
|
+
* first expand, detected by diffing the model against a load-state map.
|
|
23
35
|
*/
|
|
24
36
|
export declare function FileBrowserComponent(props: IFileBrowserProps): React.ReactElement;
|
|
25
37
|
export {};
|