xtralab 0.13.0 → 0.14.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/README.md +1 -1
- package/lib/about/icons.d.ts +7 -0
- package/lib/about/icons.js +11 -0
- package/lib/about/index.d.ts +13 -0
- package/lib/about/index.js +81 -0
- package/lib/editorBreadcrumbs/widget.js +5 -0
- package/lib/fileBrowser/commands.d.ts +4 -2
- package/lib/fileBrowser/commands.js +27 -3
- package/lib/fileBrowser/fileBrowser.js +65 -0
- package/lib/fileBrowser/widget.d.ts +27 -0
- package/lib/fileBrowser/widget.js +18 -0
- package/lib/index.js +2 -0
- package/lib/omnibox/index.d.ts +5 -3
- package/lib/omnibox/index.js +17 -4
- package/lib/omnibox/model.d.ts +6 -0
- package/lib/omnibox/model.js +67 -12
- package/lib/omnibox/recents.d.ts +9 -2
- package/lib/omnibox/recents.js +40 -15
- package/lib/omnibox/widget.d.ts +6 -0
- package/lib/omnibox/widget.js +3 -2
- package/logo.svg +63 -0
- package/package.json +4 -2
- package/schema/about.json +24 -0
- package/src/about/icons.ts +13 -0
- package/src/about/index.tsx +125 -0
- package/src/editorBreadcrumbs/widget.ts +5 -0
- package/src/fileBrowser/commands.ts +32 -2
- package/src/fileBrowser/fileBrowser.tsx +64 -0
- package/src/fileBrowser/widget.tsx +47 -0
- package/src/index.ts +2 -0
- package/src/omnibox/index.ts +19 -4
- package/src/omnibox/model.ts +73 -11
- package/src/omnibox/recents.ts +53 -25
- package/src/omnibox/widget.tsx +9 -1
- package/src/svg.d.ts +4 -0
- package/style/about.css +66 -0
- package/style/chrome.css +8 -50
- package/style/index.css +1 -0
- package/style/index.js +1 -0
package/README.md
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
/**
|
|
3
|
+
* The project logo mark — `logo.svg` at the repository root, the same file
|
|
4
|
+
* the README and the docs site use. The labextension build inlines it as a
|
|
5
|
+
* raw string, so the dialog always shows the current artwork.
|
|
6
|
+
*/
|
|
7
|
+
export declare const aboutLogoIcon: LabIcon;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
import logoSvgstr from '../../logo.svg';
|
|
3
|
+
/**
|
|
4
|
+
* The project logo mark — `logo.svg` at the repository root, the same file
|
|
5
|
+
* the README and the docs site use. The labextension build inlines it as a
|
|
6
|
+
* raw string, so the dialog always shows the current artwork.
|
|
7
|
+
*/
|
|
8
|
+
export const aboutLogoIcon = new LabIcon({
|
|
9
|
+
name: 'xtralab:about-logo',
|
|
10
|
+
svgstr: logoSvgstr
|
|
11
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
|
+
/**
|
|
3
|
+
* An "About xtralab" dialog replacing the stock "About JupyterLab" one.
|
|
4
|
+
*
|
|
5
|
+
* The upstream `@jupyterlab/help-extension:about` plugin is disabled via
|
|
6
|
+
* `jupyterlab.disabledExtensions` (package.json), which removes both its
|
|
7
|
+
* `help:about` command and its Help menu contribution; this plugin registers
|
|
8
|
+
* the same command id and restores the menu entry through its own schema.
|
|
9
|
+
* The dialog leads with the xtralab version and keeps the underlying
|
|
10
|
+
* JupyterLab version visible underneath.
|
|
11
|
+
*/
|
|
12
|
+
declare const plugin: JupyterFrontEndPlugin<void>;
|
|
13
|
+
export default plugin;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils';
|
|
2
|
+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { aboutLogoIcon } from './icons';
|
|
6
|
+
const PLUGIN_ID = 'xtralab:about';
|
|
7
|
+
/**
|
|
8
|
+
* Upstream command id. Reusing it keeps the Help menu entry (contributed by
|
|
9
|
+
* `schema/about.json` at the same rank as upstream), the command palette
|
|
10
|
+
* muscle memory, and any external caller of `help:about` working.
|
|
11
|
+
*/
|
|
12
|
+
const CommandIDs = {
|
|
13
|
+
about: 'help:about'
|
|
14
|
+
};
|
|
15
|
+
const REPO_URL = 'https://github.com/jtpio/xtralab';
|
|
16
|
+
/**
|
|
17
|
+
* An "About xtralab" dialog replacing the stock "About JupyterLab" one.
|
|
18
|
+
*
|
|
19
|
+
* The upstream `@jupyterlab/help-extension:about` plugin is disabled via
|
|
20
|
+
* `jupyterlab.disabledExtensions` (package.json), which removes both its
|
|
21
|
+
* `help:about` command and its Help menu contribution; this plugin registers
|
|
22
|
+
* the same command id and restores the menu entry through its own schema.
|
|
23
|
+
* The dialog leads with the xtralab version and keeps the underlying
|
|
24
|
+
* JupyterLab version visible underneath.
|
|
25
|
+
*/
|
|
26
|
+
const plugin = {
|
|
27
|
+
id: PLUGIN_ID,
|
|
28
|
+
description: 'Show an About dialog with the xtralab and JupyterLab versions.',
|
|
29
|
+
autoStart: true,
|
|
30
|
+
requires: [ISettingRegistry],
|
|
31
|
+
optional: [ICommandPalette, ITranslator],
|
|
32
|
+
activate: (app, settingRegistry, palette, translator) => {
|
|
33
|
+
const { commands } = app;
|
|
34
|
+
const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
|
|
35
|
+
commands.addCommand(CommandIDs.about, {
|
|
36
|
+
label: trans.__('About %1', 'xtralab'),
|
|
37
|
+
describedBy: {
|
|
38
|
+
args: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
execute: async () => {
|
|
44
|
+
// The settings payload carries the version of the installed
|
|
45
|
+
// labextension (read server-side from its package.json), which is
|
|
46
|
+
// the release actually running — the page config only knows the
|
|
47
|
+
// JupyterLab version. 'N/A' is jupyterlab_server's not-found value.
|
|
48
|
+
const version = await settingRegistry
|
|
49
|
+
.load(PLUGIN_ID)
|
|
50
|
+
.then(settings => settings.version === 'N/A' ? '' : settings.version)
|
|
51
|
+
.catch(() => '');
|
|
52
|
+
const title = (React.createElement("span", { className: "jp-xtralab-About-header" },
|
|
53
|
+
React.createElement(aboutLogoIcon.react, { className: "jp-xtralab-About-logo", height: "56px", width: "56px" }),
|
|
54
|
+
React.createElement("div", { className: "jp-xtralab-About-header-info" },
|
|
55
|
+
React.createElement("span", { className: "jp-xtralab-About-wordmark" }, "xtralab"),
|
|
56
|
+
version ? (React.createElement("span", { className: "jp-xtralab-About-version" }, trans.__('Version %1', version))) : null)));
|
|
57
|
+
const body = (React.createElement("div", { className: "jp-xtralab-About-body" },
|
|
58
|
+
React.createElement("span", { className: "jp-xtralab-About-basedOn" }, trans.__('Based on JupyterLab %1', app.version)),
|
|
59
|
+
React.createElement("span", { className: "jp-xtralab-About-externalLinks" },
|
|
60
|
+
React.createElement("a", { href: REPO_URL, target: "_blank", rel: "noopener noreferrer" }, trans.__('xtralab on GitHub'))),
|
|
61
|
+
React.createElement("span", { className: "jp-xtralab-About-copyright" }, trans.__('© %1 Jeremy Tuloup', 2026))));
|
|
62
|
+
return showDialog({
|
|
63
|
+
title,
|
|
64
|
+
body,
|
|
65
|
+
buttons: [
|
|
66
|
+
Dialog.cancelButton({
|
|
67
|
+
label: trans.__('Close')
|
|
68
|
+
})
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (palette) {
|
|
74
|
+
palette.addItem({
|
|
75
|
+
command: CommandIDs.about,
|
|
76
|
+
category: trans.__('Help')
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
export default plugin;
|
|
@@ -2,6 +2,7 @@ import { PageConfig } from '@jupyterlab/coreutils';
|
|
|
2
2
|
import { homeIcon as rootIcon } from '@jupyterlab/ui-components';
|
|
3
3
|
import { Widget } from '@lumino/widgets';
|
|
4
4
|
const EDITOR_BREADCRUMBS_CLASS = 'jp-xtralab-EditorBreadcrumbs';
|
|
5
|
+
const TOOLBAR_SPACER_CLASS = 'jp-Toolbar-spacer';
|
|
5
6
|
const BREADCRUMBS_CONTAINER_CLASS = 'jp-BreadCrumbs-container';
|
|
6
7
|
const BREADCRUMBS_CONTENT_CLASS = 'jp-BreadCrumbs-content';
|
|
7
8
|
const ROOT_CLASS = 'jp-BreadCrumbs-home';
|
|
@@ -25,6 +26,10 @@ export class EditorBreadcrumbs extends Widget {
|
|
|
25
26
|
this._commands = options.commands;
|
|
26
27
|
this._trans = options.trans;
|
|
27
28
|
this.addClass(EDITOR_BREADCRUMBS_CLASS);
|
|
29
|
+
// The breadcrumbs stretch to fill the toolbar, so ReactiveToolbar's
|
|
30
|
+
// overflow math must count them as a spacer (2px) — measured at their
|
|
31
|
+
// stretched clientWidth they would evict every item into the popup.
|
|
32
|
+
this.addClass(TOOLBAR_SPACER_CLASS);
|
|
28
33
|
this.node.setAttribute('aria-label', this._trans.__('Editor file path'));
|
|
29
34
|
this._container = document.createElement('span');
|
|
30
35
|
this._container.className = BREADCRUMBS_CONTAINER_CLASS;
|
|
@@ -20,6 +20,7 @@ export declare namespace CommandIDs {
|
|
|
20
20
|
const download = "xtralab:download";
|
|
21
21
|
const refresh = "xtralab:refresh";
|
|
22
22
|
const collapseAll = "xtralab:collapse-all";
|
|
23
|
+
const toggleFileFilter = "xtralab:toggle-file-filter";
|
|
23
24
|
const createNewDirectory = "xtralab:create-new-directory";
|
|
24
25
|
const newLauncher = "xtralab:new-launcher";
|
|
25
26
|
const revealPath = "xtralab:reveal-path";
|
|
@@ -52,12 +53,13 @@ export declare namespace ToolbarNames {
|
|
|
52
53
|
const newDirectory = "new-directory";
|
|
53
54
|
const refresh = "refresh";
|
|
54
55
|
const collapseAll = "collapse-all";
|
|
56
|
+
const toggleFileFilter = "toggle-file-filter";
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
59
|
* Populate the file browser's toolbar with the buttons that mirror the
|
|
58
60
|
* default JupyterLab file browser: a "+" launcher button, a new-folder
|
|
59
|
-
* button,
|
|
60
|
-
*
|
|
61
|
+
* button, a refresh button, a collapse-all button, and the file filter
|
|
62
|
+
* toggle.
|
|
61
63
|
*/
|
|
62
64
|
export declare function populateToolbar(opts: {
|
|
63
65
|
app: JupyterFrontEnd;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Clipboard, Dialog, InputDialog, showDialog, showErrorMessage } from '@jupyterlab/apputils';
|
|
2
2
|
import { PageConfig, PathExt } from '@jupyterlab/coreutils';
|
|
3
3
|
import { nullTranslator } from '@jupyterlab/translation';
|
|
4
|
-
import { addIcon, closeIcon, collapseAllIcon, CommandToolbarButton, copyIcon, downloadIcon, editIcon, fileIcon, newFolderIcon, refreshIcon } from '@jupyterlab/ui-components';
|
|
4
|
+
import { addIcon, closeIcon, collapseAllIcon, CommandToolbarButton, copyIcon, downloadIcon, editIcon, fileIcon, filterIcon, newFolderIcon, refreshIcon } from '@jupyterlab/ui-components';
|
|
5
5
|
import { toCanonicalPath, toServerPath } from './contents';
|
|
6
6
|
import { FILE_BROWSER_ID } from './widget';
|
|
7
7
|
/**
|
|
@@ -22,6 +22,7 @@ export var CommandIDs;
|
|
|
22
22
|
CommandIDs.download = 'xtralab:download';
|
|
23
23
|
CommandIDs.refresh = 'xtralab:refresh';
|
|
24
24
|
CommandIDs.collapseAll = 'xtralab:collapse-all';
|
|
25
|
+
CommandIDs.toggleFileFilter = 'xtralab:toggle-file-filter';
|
|
25
26
|
CommandIDs.createNewDirectory = 'xtralab:create-new-directory';
|
|
26
27
|
CommandIDs.newLauncher = 'xtralab:new-launcher';
|
|
27
28
|
CommandIDs.revealPath = 'xtralab:reveal-path';
|
|
@@ -393,6 +394,22 @@ export function registerCommands(opts) {
|
|
|
393
394
|
browser.collapseAll();
|
|
394
395
|
}
|
|
395
396
|
});
|
|
397
|
+
commands.addCommand(CommandIDs.toggleFileFilter, {
|
|
398
|
+
label: trans.__('Toggle File Filter'),
|
|
399
|
+
caption: trans.__('Show or hide the file filter'),
|
|
400
|
+
icon: filterIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
401
|
+
isToggled: () => browser.fileFilterVisible,
|
|
402
|
+
execute: () => {
|
|
403
|
+
browser.toggleFileFilter();
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
// Refresh the toggle's state however the filter is shown or hidden —
|
|
407
|
+
// the toolbar button, or the auto-show that kicks in when typing with
|
|
408
|
+
// the tree focused opens a search session.
|
|
409
|
+
const onFilterVisibleChanged = () => {
|
|
410
|
+
commands.notifyCommandChanged(CommandIDs.toggleFileFilter);
|
|
411
|
+
};
|
|
412
|
+
browser.fileFilterVisibleChanged.connect(onFilterVisibleChanged);
|
|
396
413
|
// Public reveal seam. Other plugins (editor breadcrumbs, future
|
|
397
414
|
// "reveal in tree" actions) call this command rather than depending
|
|
398
415
|
// on the file browser widget directly. Activating the sidebar is part
|
|
@@ -492,6 +509,7 @@ export function registerCommands(opts) {
|
|
|
492
509
|
var _a;
|
|
493
510
|
app.contextMenu.opened.disconnect(updateOpenWithMenu);
|
|
494
511
|
(_a = app.shell.currentChanged) === null || _a === void 0 ? void 0 : _a.disconnect(onCurrentChanged);
|
|
512
|
+
browser.fileFilterVisibleChanged.disconnect(onFilterVisibleChanged);
|
|
495
513
|
paletteItem === null || paletteItem === void 0 ? void 0 : paletteItem.dispose();
|
|
496
514
|
};
|
|
497
515
|
}
|
|
@@ -506,12 +524,13 @@ export var ToolbarNames;
|
|
|
506
524
|
ToolbarNames.newDirectory = 'new-directory';
|
|
507
525
|
ToolbarNames.refresh = 'refresh';
|
|
508
526
|
ToolbarNames.collapseAll = 'collapse-all';
|
|
527
|
+
ToolbarNames.toggleFileFilter = 'toggle-file-filter';
|
|
509
528
|
})(ToolbarNames || (ToolbarNames = {}));
|
|
510
529
|
/**
|
|
511
530
|
* Populate the file browser's toolbar with the buttons that mirror the
|
|
512
531
|
* default JupyterLab file browser: a "+" launcher button, a new-folder
|
|
513
|
-
* button,
|
|
514
|
-
*
|
|
532
|
+
* button, a refresh button, a collapse-all button, and the file filter
|
|
533
|
+
* toggle.
|
|
515
534
|
*/
|
|
516
535
|
export function populateToolbar(opts) {
|
|
517
536
|
const { app, browser } = opts;
|
|
@@ -536,4 +555,9 @@ export function populateToolbar(opts) {
|
|
|
536
555
|
id: CommandIDs.collapseAll,
|
|
537
556
|
label: ''
|
|
538
557
|
}));
|
|
558
|
+
browser.toolbar.addItem(ToolbarNames.toggleFileFilter, new CommandToolbarButton({
|
|
559
|
+
commands,
|
|
560
|
+
id: CommandIDs.toggleFileFilter,
|
|
561
|
+
label: ''
|
|
562
|
+
}));
|
|
539
563
|
}
|
|
@@ -64,9 +64,20 @@ const GIT_REPO_PATH = '';
|
|
|
64
64
|
* resolution cost just for one string.
|
|
65
65
|
*/
|
|
66
66
|
const FILE_TREE_TAG = 'file-tree-container';
|
|
67
|
+
/**
|
|
68
|
+
* Injected into the tree's shadow root. The second rule hides the search
|
|
69
|
+
* box unless the host carries the visibility marker set by the filter
|
|
70
|
+
* bridge effect below — `@pierre/trees` always renders the box when
|
|
71
|
+
* `search` is enabled, and its stylesheet lives in the shadow root where
|
|
72
|
+
* outside CSS cannot reach.
|
|
73
|
+
*/
|
|
67
74
|
const FILE_TREE_UNSAFE_CSS = '[data-type="item"][data-item-selected="true"] ' +
|
|
68
75
|
'[data-item-section="spacing-item"] {' +
|
|
69
76
|
'border-left-color: transparent;' +
|
|
77
|
+
'}' +
|
|
78
|
+
':host(:not([data-xtralab-filter-visible])) ' +
|
|
79
|
+
'[data-file-tree-search-container] {' +
|
|
80
|
+
'display: none;' +
|
|
70
81
|
'}';
|
|
71
82
|
/**
|
|
72
83
|
* Renders a `@pierre/trees` file tree backed by the Jupyter contents API.
|
|
@@ -652,6 +663,17 @@ export function FileBrowserComponent(props) {
|
|
|
652
663
|
};
|
|
653
664
|
contentsManager.fileChanged.connect(onContentsFileChanged);
|
|
654
665
|
const unsubscribe = model.subscribe(() => {
|
|
666
|
+
// Search-driven expansion must not trigger fetches: the tree
|
|
667
|
+
// auto-expands every directory whose path matches the query, and
|
|
668
|
+
// treating those as user expansions would recursively fetch every
|
|
669
|
+
// matching subtree — a single common letter can walk the whole
|
|
670
|
+
// workspace, node_modules included. Closing the search restores
|
|
671
|
+
// the pre-search expansion state, and a directory clicked in the
|
|
672
|
+
// results is toggled after the session closes, so real expansions
|
|
673
|
+
// are still fetched the moment the session ends.
|
|
674
|
+
if (model.isSearchOpen()) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
655
677
|
knownDirs.forEach((state, canonicalPath) => {
|
|
656
678
|
if (state !== 'unloaded') {
|
|
657
679
|
return;
|
|
@@ -744,6 +766,49 @@ export function FileBrowserComponent(props) {
|
|
|
744
766
|
unsubscribe();
|
|
745
767
|
};
|
|
746
768
|
}, [model, widget]);
|
|
769
|
+
// Bridge the filter-box visibility between the widget and the tree. The
|
|
770
|
+
// widget owns the flag; applying it stamps the marker attribute the
|
|
771
|
+
// unsafeCSS rule keys on and opens or closes the model's search session
|
|
772
|
+
// (opening focuses the input, closing clears any active filter). The
|
|
773
|
+
// model subscription covers the reverse direction: typing a printable
|
|
774
|
+
// character while the tree has focus opens a session on the tree's own
|
|
775
|
+
// initiative, and the box must surface for that session to be usable.
|
|
776
|
+
React.useEffect(() => {
|
|
777
|
+
if (widget === undefined) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
const apply = (visible) => {
|
|
781
|
+
const host = model.getFileTreeContainer();
|
|
782
|
+
if (host !== undefined) {
|
|
783
|
+
if (visible) {
|
|
784
|
+
host.dataset.xtralabFilterVisible = 'true';
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
delete host.dataset.xtralabFilterVisible;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (visible && !model.isSearchOpen()) {
|
|
791
|
+
model.openSearch();
|
|
792
|
+
}
|
|
793
|
+
else if (!visible && model.isSearchOpen()) {
|
|
794
|
+
model.closeSearch();
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
apply(widget.fileFilterVisible);
|
|
798
|
+
const visibleSlot = (sender, visible) => {
|
|
799
|
+
apply(visible);
|
|
800
|
+
};
|
|
801
|
+
widget.fileFilterVisibleChanged.connect(visibleSlot);
|
|
802
|
+
const unsubscribe = model.subscribe(() => {
|
|
803
|
+
if (model.isSearchOpen() && !widget.fileFilterVisible) {
|
|
804
|
+
widget.setFileFilterVisible(true);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
return () => {
|
|
808
|
+
widget.fileFilterVisibleChanged.disconnect(visibleSlot);
|
|
809
|
+
unsubscribe();
|
|
810
|
+
};
|
|
811
|
+
}, [model, widget]);
|
|
747
812
|
const wrapperRef = React.useRef(null);
|
|
748
813
|
React.useEffect(() => {
|
|
749
814
|
const wrapper = wrapperRef.current;
|
|
@@ -71,6 +71,19 @@ export interface IXtralabFileBrowser {
|
|
|
71
71
|
* directories, calling `.collapse()` on each expanded one.
|
|
72
72
|
*/
|
|
73
73
|
readonly collapseAllRequested: ISignal<IXtralabFileBrowser, void>;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the filter box above the tree is shown. Mirrors the default
|
|
76
|
+
* file browser's `showFileFilter`: hidden on startup and flipped by the
|
|
77
|
+
* toolbar toggle. Also forced to `true` when the tree opens a search
|
|
78
|
+
* session on its own — typing a letter while the tree has focus.
|
|
79
|
+
*/
|
|
80
|
+
readonly fileFilterVisible: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Emits when {@link fileFilterVisible} changes. The React component
|
|
83
|
+
* listens to show or hide the filter box; the toggle command listens
|
|
84
|
+
* to refresh its toggled state.
|
|
85
|
+
*/
|
|
86
|
+
readonly fileFilterVisibleChanged: ISignal<IXtralabFileBrowser, boolean>;
|
|
74
87
|
/**
|
|
75
88
|
* Trigger a refresh of every loaded directory in the tree.
|
|
76
89
|
*/
|
|
@@ -98,6 +111,14 @@ export interface IXtralabFileBrowser {
|
|
|
98
111
|
* Ask the tree to collapse every currently expanded folder.
|
|
99
112
|
*/
|
|
100
113
|
collapseAll(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Show or hide the filter box above the tree.
|
|
116
|
+
*/
|
|
117
|
+
setFileFilterVisible(visible: boolean): void;
|
|
118
|
+
/**
|
|
119
|
+
* Toggle the filter box above the tree.
|
|
120
|
+
*/
|
|
121
|
+
toggleFileFilter(): void;
|
|
101
122
|
}
|
|
102
123
|
/**
|
|
103
124
|
* Lumino widget that hosts the React-based `@pierre/trees` file browser.
|
|
@@ -115,6 +136,8 @@ export declare class XtralabFileBrowser extends Widget implements IXtralabFileBr
|
|
|
115
136
|
get revealRequested(): ISignal<this, string>;
|
|
116
137
|
get rootRequested(): ISignal<this, void>;
|
|
117
138
|
get collapseAllRequested(): ISignal<this, void>;
|
|
139
|
+
get fileFilterVisible(): boolean;
|
|
140
|
+
get fileFilterVisibleChanged(): ISignal<this, boolean>;
|
|
118
141
|
/**
|
|
119
142
|
* Update the cached selection. Called from the React tree when the
|
|
120
143
|
* underlying `@pierre/trees` model emits a selection change.
|
|
@@ -125,6 +148,8 @@ export declare class XtralabFileBrowser extends Widget implements IXtralabFileBr
|
|
|
125
148
|
reveal(canonicalPath: string): void;
|
|
126
149
|
scrollToRoot(): void;
|
|
127
150
|
collapseAll(): void;
|
|
151
|
+
setFileFilterVisible(visible: boolean): void;
|
|
152
|
+
toggleFileFilter(): void;
|
|
128
153
|
private _contentsManager;
|
|
129
154
|
private _docManager;
|
|
130
155
|
private _onOpenFile;
|
|
@@ -135,6 +160,8 @@ export declare class XtralabFileBrowser extends Widget implements IXtralabFileBr
|
|
|
135
160
|
private _revealRequested;
|
|
136
161
|
private _rootRequested;
|
|
137
162
|
private _collapseAllRequested;
|
|
163
|
+
private _fileFilterVisible;
|
|
164
|
+
private _fileFilterVisibleChanged;
|
|
138
165
|
private _toolbar;
|
|
139
166
|
private _content;
|
|
140
167
|
}
|
|
@@ -29,6 +29,8 @@ export class XtralabFileBrowser extends Widget {
|
|
|
29
29
|
this._revealRequested = new Signal(this);
|
|
30
30
|
this._rootRequested = new Signal(this);
|
|
31
31
|
this._collapseAllRequested = new Signal(this);
|
|
32
|
+
this._fileFilterVisible = false;
|
|
33
|
+
this._fileFilterVisibleChanged = new Signal(this);
|
|
32
34
|
const layout = (this.layout = new PanelLayout());
|
|
33
35
|
this._contentsManager = options.contentsManager;
|
|
34
36
|
this._docManager = options.docManager;
|
|
@@ -77,6 +79,12 @@ export class XtralabFileBrowser extends Widget {
|
|
|
77
79
|
get collapseAllRequested() {
|
|
78
80
|
return this._collapseAllRequested;
|
|
79
81
|
}
|
|
82
|
+
get fileFilterVisible() {
|
|
83
|
+
return this._fileFilterVisible;
|
|
84
|
+
}
|
|
85
|
+
get fileFilterVisibleChanged() {
|
|
86
|
+
return this._fileFilterVisibleChanged;
|
|
87
|
+
}
|
|
80
88
|
/**
|
|
81
89
|
* Update the cached selection. Called from the React tree when the
|
|
82
90
|
* underlying `@pierre/trees` model emits a selection change.
|
|
@@ -103,6 +111,16 @@ export class XtralabFileBrowser extends Widget {
|
|
|
103
111
|
collapseAll() {
|
|
104
112
|
this._collapseAllRequested.emit();
|
|
105
113
|
}
|
|
114
|
+
setFileFilterVisible(visible) {
|
|
115
|
+
if (this._fileFilterVisible === visible) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this._fileFilterVisible = visible;
|
|
119
|
+
this._fileFilterVisibleChanged.emit(visible);
|
|
120
|
+
}
|
|
121
|
+
toggleFileFilter() {
|
|
122
|
+
this.setFileFilterVisible(!this._fileFilterVisible);
|
|
123
|
+
}
|
|
106
124
|
}
|
|
107
125
|
/**
|
|
108
126
|
* The React-rendered region of the file browser. Lives inside the host
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import aboutPlugin from './about';
|
|
1
2
|
import agentSessionsPlugin from './agentSessions';
|
|
2
3
|
import askAgentPlugin from './askAgent';
|
|
3
4
|
import commandBarPlugin from './commandBar';
|
|
@@ -27,6 +28,7 @@ import walkthroughPlugin from './walkthrough';
|
|
|
27
28
|
* they are spread in.
|
|
28
29
|
*/
|
|
29
30
|
const plugins = [
|
|
31
|
+
aboutPlugin,
|
|
30
32
|
agentSessionsPlugin,
|
|
31
33
|
askAgentPlugin,
|
|
32
34
|
commandBarPlugin,
|
package/lib/omnibox/index.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ import { IOmnibox } from './tokens';
|
|
|
3
3
|
/**
|
|
4
4
|
* The omnibox: a single launcher overlay that fuzzy-searches workspace files
|
|
5
5
|
* (via jupyterlab-quickopen's gitignore-aware endpoint) and JupyterLab
|
|
6
|
-
* commands
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* commands (the command palette's entries plus registry commands the palette
|
|
7
|
+
* doesn't list), and routes a typed prompt to one of the configured agents
|
|
8
|
+
* (running it in a fresh terminal through the launcher's
|
|
9
|
+
* `xtralab:start-agent:<id>` commands). It is opened by the top-bar command
|
|
10
|
+
* bar.
|
|
9
11
|
*
|
|
10
12
|
* Commands run and files opened through the overlay are remembered (persisted
|
|
11
13
|
* in the state database) and offered again at the top while the query is
|
package/lib/omnibox/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { ICommandPalette } from '@jupyterlab/apputils';
|
|
|
2
2
|
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
3
3
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
4
4
|
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
5
|
-
import { Widget } from '@lumino/widgets';
|
|
5
|
+
import { CommandPalette, Widget } from '@lumino/widgets';
|
|
6
6
|
import { IAgentRegistry } from '../launcher/tokens';
|
|
7
7
|
import { OmniboxRecents } from './recents';
|
|
8
8
|
import { IOmnibox, OMNIBOX_OPEN_COMMAND } from './tokens';
|
|
@@ -11,9 +11,11 @@ const PLUGIN_ID = 'xtralab:omnibox';
|
|
|
11
11
|
/**
|
|
12
12
|
* The omnibox: a single launcher overlay that fuzzy-searches workspace files
|
|
13
13
|
* (via jupyterlab-quickopen's gitignore-aware endpoint) and JupyterLab
|
|
14
|
-
* commands
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* commands (the command palette's entries plus registry commands the palette
|
|
15
|
+
* doesn't list), and routes a typed prompt to one of the configured agents
|
|
16
|
+
* (running it in a fresh terminal through the launcher's
|
|
17
|
+
* `xtralab:start-agent:<id>` commands). It is opened by the top-bar command
|
|
18
|
+
* bar.
|
|
17
19
|
*
|
|
18
20
|
* Commands run and files opened through the overlay are remembered (persisted
|
|
19
21
|
* in the state database) and offered again at the top while the query is
|
|
@@ -38,6 +40,16 @@ const plugin = {
|
|
|
38
40
|
const { commands, docRegistry } = app;
|
|
39
41
|
const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
|
|
40
42
|
const placeholder = trans.__('Search files and commands, or ask an agent…');
|
|
43
|
+
// The palette's items carry per-item args (e.g. one "Use Theme: …" per
|
|
44
|
+
// theme) that a raw command-registry scan can't see, but `ICommandPalette`
|
|
45
|
+
// exposes no item list and the widget itself is unreachable through the
|
|
46
|
+
// shell (`LabShell.add` defers it until layout restore, after which the
|
|
47
|
+
// modal setting re-parents it out of the left area). The wrapper's
|
|
48
|
+
// `_palette` field is private in TypeScript only, so read it guarded.
|
|
49
|
+
const paletteItems = () => {
|
|
50
|
+
const widget = palette === null || palette === void 0 ? void 0 : palette._palette;
|
|
51
|
+
return widget instanceof CommandPalette ? [...widget.items] : [];
|
|
52
|
+
};
|
|
41
53
|
const recents = new OmniboxRecents({ state });
|
|
42
54
|
// Restore the persisted recents only after the configured cap is known:
|
|
43
55
|
// `restore` trims to `maxItems`, so restoring at the constructor default
|
|
@@ -77,6 +89,7 @@ const plugin = {
|
|
|
77
89
|
close();
|
|
78
90
|
const widget = new OmniboxWidget({
|
|
79
91
|
commands,
|
|
92
|
+
paletteItems: paletteItems(),
|
|
80
93
|
docRegistry,
|
|
81
94
|
agents: agentRegistry ? agentRegistry.agents : [],
|
|
82
95
|
placeholder,
|
package/lib/omnibox/model.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
|
2
2
|
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
3
3
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
4
4
|
import type { CommandRegistry } from '@lumino/commands';
|
|
5
|
+
import type { CommandPalette } from '@lumino/widgets';
|
|
5
6
|
import type { IAgent } from '../launcher/agents';
|
|
6
7
|
import type { OmniboxRecents } from './recents';
|
|
7
8
|
/**
|
|
@@ -53,6 +54,11 @@ interface IOmniboxSections {
|
|
|
53
54
|
interface IComputeOptions {
|
|
54
55
|
query: string;
|
|
55
56
|
commands: CommandRegistry;
|
|
57
|
+
/**
|
|
58
|
+
* The command palette's items, whose labels are computed with each item's
|
|
59
|
+
* args — the only form in which entries like "Use Theme: …" exist.
|
|
60
|
+
*/
|
|
61
|
+
paletteItems: ReadonlyArray<CommandPalette.IItem>;
|
|
56
62
|
docRegistry: DocumentRegistry;
|
|
57
63
|
agents: IAgent[];
|
|
58
64
|
files: string[];
|