@jupyterlab/filebrowser-extension 4.0.0-alpha.8 → 4.0.0-beta.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/lib/index.js +266 -226
- package/lib/index.js.map +1 -1
- package/package.json +20 -21
- package/schema/browser.json +30 -37
- package/schema/widget.json +39 -6
- package/src/index.ts +1482 -0
- package/style/base.css +9 -0
- package/style/index.css +0 -1
- package/style/index.js +0 -1
package/lib/index.js
CHANGED
|
@@ -8,15 +8,20 @@ import { ILabShell, ILayoutRestorer, IRouter, ITreePathUpdater, JupyterFrontEnd,
|
|
|
8
8
|
import { Clipboard, createToolbarFactory, ICommandPalette, InputDialog, IToolbarWidgetRegistry, setToolbar, showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
|
|
9
9
|
import { PageConfig, PathExt } from '@jupyterlab/coreutils';
|
|
10
10
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
11
|
-
import { FileBrowser, FileUploadStatus, FilterFileBrowserModel, IFileBrowserFactory, Uploader } from '@jupyterlab/filebrowser';
|
|
11
|
+
import { FileBrowser, FileUploadStatus, FilterFileBrowserModel, IDefaultFileBrowser, IFileBrowserCommands, IFileBrowserFactory, Uploader } from '@jupyterlab/filebrowser';
|
|
12
12
|
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
13
13
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
14
14
|
import { IStatusBar } from '@jupyterlab/statusbar';
|
|
15
15
|
import { ITranslator } from '@jupyterlab/translation';
|
|
16
|
-
import { addIcon, closeIcon, copyIcon, cutIcon, downloadIcon, editIcon, fileIcon, folderIcon, linkIcon, markdownIcon, newFolderIcon, pasteIcon, refreshIcon, stopIcon, textEditorIcon } from '@jupyterlab/ui-components';
|
|
17
|
-
import { find, map
|
|
16
|
+
import { addIcon, closeIcon, copyIcon, cutIcon, downloadIcon, editIcon, fileIcon, FilenameSearcher, folderIcon, linkIcon, markdownIcon, newFolderIcon, pasteIcon, refreshIcon, stopIcon, textEditorIcon } from '@jupyterlab/ui-components';
|
|
17
|
+
import { find, map } from '@lumino/algorithm';
|
|
18
18
|
import { CommandRegistry } from '@lumino/commands';
|
|
19
19
|
const FILE_BROWSER_FACTORY = 'FileBrowser';
|
|
20
|
+
const FILE_BROWSER_PLUGIN_ID = '@jupyterlab/filebrowser-extension:browser';
|
|
21
|
+
/**
|
|
22
|
+
* The class name added to the filebrowser filterbox node.
|
|
23
|
+
*/
|
|
24
|
+
const FILTERBOX_CLASS = 'jp-FileBrowser-filterBox';
|
|
20
25
|
/**
|
|
21
26
|
* The command IDs used by the file browser plugin.
|
|
22
27
|
*/
|
|
@@ -24,8 +29,6 @@ var CommandIDs;
|
|
|
24
29
|
(function (CommandIDs) {
|
|
25
30
|
CommandIDs.copy = 'filebrowser:copy';
|
|
26
31
|
CommandIDs.copyDownloadLink = 'filebrowser:copy-download-link';
|
|
27
|
-
// For main browser only.
|
|
28
|
-
CommandIDs.createLauncher = 'filebrowser:create-main-launcher';
|
|
29
32
|
CommandIDs.cut = 'filebrowser:cut';
|
|
30
33
|
CommandIDs.del = 'filebrowser:delete';
|
|
31
34
|
CommandIDs.download = 'filebrowser:download';
|
|
@@ -54,8 +57,10 @@ var CommandIDs;
|
|
|
54
57
|
CommandIDs.toggleBrowser = 'filebrowser:toggle-main';
|
|
55
58
|
CommandIDs.toggleNavigateToCurrentDirectory = 'filebrowser:toggle-navigate-to-current-directory';
|
|
56
59
|
CommandIDs.toggleLastModified = 'filebrowser:toggle-last-modified';
|
|
60
|
+
CommandIDs.toggleFileSize = 'filebrowser:toggle-file-size';
|
|
57
61
|
CommandIDs.search = 'filebrowser:search';
|
|
58
62
|
CommandIDs.toggleHiddenFiles = 'filebrowser:toggle-hidden-files';
|
|
63
|
+
CommandIDs.toggleFileCheckboxes = 'filebrowser:toggle-file-checkboxes';
|
|
59
64
|
})(CommandIDs || (CommandIDs = {}));
|
|
60
65
|
/**
|
|
61
66
|
* The file browser namespace token.
|
|
@@ -65,18 +70,18 @@ const namespace = 'filebrowser';
|
|
|
65
70
|
* The default file browser extension.
|
|
66
71
|
*/
|
|
67
72
|
const browser = {
|
|
68
|
-
id:
|
|
69
|
-
requires: [IFileBrowserFactory, ITranslator],
|
|
73
|
+
id: FILE_BROWSER_PLUGIN_ID,
|
|
74
|
+
requires: [IDefaultFileBrowser, IFileBrowserFactory, ITranslator],
|
|
70
75
|
optional: [
|
|
71
76
|
ILayoutRestorer,
|
|
72
77
|
ISettingRegistry,
|
|
73
78
|
ITreePathUpdater,
|
|
74
79
|
ICommandPalette
|
|
75
80
|
],
|
|
81
|
+
provides: IFileBrowserCommands,
|
|
76
82
|
autoStart: true,
|
|
77
|
-
activate: async (app, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
|
|
78
|
-
const
|
|
79
|
-
const browser = factory.defaultBrowser;
|
|
83
|
+
activate: async (app, defaultFileBrowser, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
|
|
84
|
+
const browser = defaultFileBrowser;
|
|
80
85
|
// Let the application restorer track the primary file browser (that is
|
|
81
86
|
// automatically created) for restoration of application state (e.g. setting
|
|
82
87
|
// the file browser as the current side bar widget).
|
|
@@ -91,66 +96,42 @@ const browser = {
|
|
|
91
96
|
if (preferredPath) {
|
|
92
97
|
await browser.model.cd(preferredPath);
|
|
93
98
|
}
|
|
94
|
-
addCommands(app, factory, translator, settingRegistry, commandPalette);
|
|
95
|
-
|
|
96
|
-
const updateBrowserTitle = () => {
|
|
97
|
-
const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
|
|
98
|
-
if (binding) {
|
|
99
|
-
const ks = CommandRegistry.formatKeystroke(binding.keys.join(' '));
|
|
100
|
-
browser.title.caption = trans.__('File Browser (%1)', ks);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
browser.title.caption = trans.__('File Browser');
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
updateBrowserTitle();
|
|
107
|
-
app.commands.keyBindingChanged.connect(() => {
|
|
108
|
-
updateBrowserTitle();
|
|
109
|
-
});
|
|
110
|
-
void Promise.all([app.restored, browser.model.restored]).then(() => {
|
|
99
|
+
addCommands(app, browser, factory, translator, settingRegistry, commandPalette);
|
|
100
|
+
return void Promise.all([app.restored, browser.model.restored]).then(() => {
|
|
111
101
|
if (treePathUpdater) {
|
|
112
102
|
browser.model.pathChanged.connect((sender, args) => {
|
|
113
103
|
treePathUpdater(args.newValue);
|
|
114
104
|
});
|
|
115
105
|
}
|
|
116
|
-
let navigateToCurrentDirectory = false;
|
|
117
|
-
let showLastModifiedColumn = true;
|
|
118
|
-
let useFuzzyFilter = true;
|
|
119
|
-
let showHiddenFiles = false;
|
|
120
106
|
if (settingRegistry) {
|
|
121
|
-
void settingRegistry
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
useFuzzyFilter = settings.get('useFuzzyFilter')
|
|
144
|
-
.composite;
|
|
145
|
-
browser.useFuzzyFilter = useFuzzyFilter;
|
|
146
|
-
settings.changed.connect(settings => {
|
|
147
|
-
showHiddenFiles = settings.get('showHiddenFiles')
|
|
107
|
+
void settingRegistry.load(FILE_BROWSER_PLUGIN_ID).then(settings => {
|
|
108
|
+
/**
|
|
109
|
+
* File browser configuration.
|
|
110
|
+
*/
|
|
111
|
+
const fileBrowserConfig = {
|
|
112
|
+
navigateToCurrentDirectory: false,
|
|
113
|
+
showLastModifiedColumn: true,
|
|
114
|
+
showFileSizeColumn: false,
|
|
115
|
+
showHiddenFiles: false,
|
|
116
|
+
showFileCheckboxes: false
|
|
117
|
+
};
|
|
118
|
+
const fileBrowserModelConfig = {
|
|
119
|
+
filterDirectories: true
|
|
120
|
+
};
|
|
121
|
+
function onSettingsChanged(settings) {
|
|
122
|
+
let key;
|
|
123
|
+
for (key in fileBrowserConfig) {
|
|
124
|
+
const value = settings.get(key).composite;
|
|
125
|
+
fileBrowserConfig[key] = value;
|
|
126
|
+
browser[key] = value;
|
|
127
|
+
}
|
|
128
|
+
const value = settings.get('filterDirectories')
|
|
148
129
|
.composite;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
130
|
+
fileBrowserModelConfig.filterDirectories = value;
|
|
131
|
+
browser.model.filterDirectories = value;
|
|
132
|
+
}
|
|
133
|
+
settings.changed.connect(onSettingsChanged);
|
|
134
|
+
onSettingsChanged(settings);
|
|
154
135
|
});
|
|
155
136
|
}
|
|
156
137
|
});
|
|
@@ -163,14 +144,8 @@ const factory = {
|
|
|
163
144
|
id: '@jupyterlab/filebrowser-extension:factory',
|
|
164
145
|
provides: IFileBrowserFactory,
|
|
165
146
|
requires: [IDocumentManager, ITranslator],
|
|
166
|
-
optional: [
|
|
167
|
-
|
|
168
|
-
IRouter,
|
|
169
|
-
JupyterFrontEnd.ITreeResolver,
|
|
170
|
-
JupyterLab.IInfo
|
|
171
|
-
],
|
|
172
|
-
activate: async (app, docManager, translator, state, router, tree, info) => {
|
|
173
|
-
const { commands } = app;
|
|
147
|
+
optional: [IStateDB, JupyterLab.IInfo],
|
|
148
|
+
activate: async (app, docManager, translator, state, info) => {
|
|
174
149
|
const tracker = new WidgetTracker({ namespace });
|
|
175
150
|
const createFileBrowser = (id, options = {}) => {
|
|
176
151
|
var _a;
|
|
@@ -196,13 +171,26 @@ const factory = {
|
|
|
196
171
|
void tracker.add(widget);
|
|
197
172
|
return widget;
|
|
198
173
|
};
|
|
174
|
+
return { createFileBrowser, tracker };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* The default file browser factory provider.
|
|
179
|
+
*/
|
|
180
|
+
const defaultFileBrowser = {
|
|
181
|
+
id: '@jupyterlab/filebrowser-extension:default-file-browser',
|
|
182
|
+
provides: IDefaultFileBrowser,
|
|
183
|
+
requires: [IFileBrowserFactory],
|
|
184
|
+
optional: [IRouter, JupyterFrontEnd.ITreeResolver, ILabShell],
|
|
185
|
+
activate: async (app, fileBrowserFactory, router, tree, labShell) => {
|
|
186
|
+
const { commands } = app;
|
|
199
187
|
// Manually restore and load the default file browser.
|
|
200
|
-
const defaultBrowser = createFileBrowser('filebrowser', {
|
|
188
|
+
const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
|
|
201
189
|
auto: false,
|
|
202
190
|
restore: false
|
|
203
191
|
});
|
|
204
|
-
void Private.restoreBrowser(defaultBrowser, commands, router, tree);
|
|
205
|
-
return
|
|
192
|
+
void Private.restoreBrowser(defaultBrowser, commands, router, tree, app, labShell);
|
|
193
|
+
return defaultBrowser;
|
|
206
194
|
}
|
|
207
195
|
};
|
|
208
196
|
/**
|
|
@@ -237,7 +225,7 @@ const downloadPlugin = {
|
|
|
237
225
|
return;
|
|
238
226
|
}
|
|
239
227
|
return widget.model.manager.services.contents
|
|
240
|
-
.getDownloadUrl(widget.selectedItems().next().path)
|
|
228
|
+
.getDownloadUrl(widget.selectedItems().next().value.path)
|
|
241
229
|
.then(url => {
|
|
242
230
|
Clipboard.copyToSystem(url);
|
|
243
231
|
});
|
|
@@ -255,29 +243,73 @@ const browserWidget = {
|
|
|
255
243
|
id: '@jupyterlab/filebrowser-extension:widget',
|
|
256
244
|
requires: [
|
|
257
245
|
IDocumentManager,
|
|
246
|
+
IDefaultFileBrowser,
|
|
258
247
|
IFileBrowserFactory,
|
|
259
248
|
ISettingRegistry,
|
|
260
249
|
IToolbarWidgetRegistry,
|
|
261
250
|
ITranslator,
|
|
262
|
-
ILabShell
|
|
251
|
+
ILabShell,
|
|
252
|
+
IFileBrowserCommands
|
|
263
253
|
],
|
|
254
|
+
optional: [ICommandPalette],
|
|
264
255
|
autoStart: true,
|
|
265
|
-
activate: (app, docManager, factory,
|
|
256
|
+
activate: (app, docManager, browser, factory, settingRegistry, toolbarRegistry, translator, labShell,
|
|
257
|
+
// Wait until file browser commands are ready before activating file browser widget
|
|
258
|
+
fileBrowserCommands, commandPalette) => {
|
|
266
259
|
const { commands } = app;
|
|
267
|
-
const {
|
|
260
|
+
const { tracker } = factory;
|
|
268
261
|
const trans = translator.load('jupyterlab');
|
|
269
262
|
// Set attributes when adding the browser to the UI
|
|
270
263
|
browser.node.setAttribute('role', 'region');
|
|
271
264
|
browser.node.setAttribute('aria-label', trans.__('File Browser Section'));
|
|
272
265
|
browser.title.icon = folderIcon;
|
|
266
|
+
// Show the current file browser shortcut in its title.
|
|
267
|
+
const updateBrowserTitle = () => {
|
|
268
|
+
const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
|
|
269
|
+
if (binding) {
|
|
270
|
+
const ks = binding.keys.map(CommandRegistry.formatKeystroke).join(', ');
|
|
271
|
+
browser.title.caption = trans.__('File Browser (%1)', ks);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
browser.title.caption = trans.__('File Browser');
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
updateBrowserTitle();
|
|
278
|
+
app.commands.keyBindingChanged.connect(() => {
|
|
279
|
+
updateBrowserTitle();
|
|
280
|
+
});
|
|
273
281
|
// Toolbar
|
|
274
|
-
toolbarRegistry.
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
toolbarRegistry.addFactory(FILE_BROWSER_FACTORY, 'uploader', (browser) => new Uploader({ model: browser.model, translator }));
|
|
283
|
+
toolbarRegistry.addFactory(FILE_BROWSER_FACTORY, 'fileNameSearcher', (browser) => {
|
|
284
|
+
const searcher = FilenameSearcher({
|
|
285
|
+
updateFilter: (filterFn, query) => {
|
|
286
|
+
browser.model.setFilter(value => {
|
|
287
|
+
return filterFn(value.name.toLowerCase());
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
useFuzzyFilter: true,
|
|
291
|
+
placeholder: trans.__('Filter files by name'),
|
|
292
|
+
forceRefresh: true
|
|
293
|
+
});
|
|
294
|
+
searcher.addClass(FILTERBOX_CLASS);
|
|
295
|
+
return searcher;
|
|
296
|
+
});
|
|
297
|
+
setToolbar(browser, createToolbarFactory(toolbarRegistry, settingRegistry, FILE_BROWSER_FACTORY, browserWidget.id, translator));
|
|
298
|
+
labShell.add(browser, 'left', { rank: 100, type: 'File Browser' });
|
|
299
|
+
commands.addCommand(CommandIDs.toggleBrowser, {
|
|
300
|
+
label: trans.__('File Browser'),
|
|
301
|
+
execute: () => {
|
|
302
|
+
if (browser.isHidden) {
|
|
303
|
+
return commands.execute(CommandIDs.showBrowser, void 0);
|
|
304
|
+
}
|
|
305
|
+
return commands.execute(CommandIDs.hideBrowser, void 0);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
277
308
|
commands.addCommand(CommandIDs.showBrowser, {
|
|
309
|
+
label: trans.__('Open the file browser for the provided `path`.'),
|
|
278
310
|
execute: args => {
|
|
279
311
|
const path = args.path || '';
|
|
280
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
312
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
281
313
|
// Check for browser not found
|
|
282
314
|
if (!browserForPath) {
|
|
283
315
|
return;
|
|
@@ -290,20 +322,18 @@ const browserWidget = {
|
|
|
290
322
|
else {
|
|
291
323
|
const areas = ['left', 'right'];
|
|
292
324
|
for (const area of areas) {
|
|
293
|
-
const
|
|
294
|
-
let widget = it.next();
|
|
295
|
-
while (widget) {
|
|
325
|
+
for (const widget of labShell.widgets(area)) {
|
|
296
326
|
if (widget.contains(browserForPath)) {
|
|
297
327
|
labShell.activateById(widget.id);
|
|
298
328
|
return;
|
|
299
329
|
}
|
|
300
|
-
widget = it.next();
|
|
301
330
|
}
|
|
302
331
|
}
|
|
303
332
|
}
|
|
304
333
|
}
|
|
305
334
|
});
|
|
306
335
|
commands.addCommand(CommandIDs.hideBrowser, {
|
|
336
|
+
label: trans.__('Hide the file browser.'),
|
|
307
337
|
execute: () => {
|
|
308
338
|
const widget = tracker.currentWidget;
|
|
309
339
|
if (widget && !widget.isHidden) {
|
|
@@ -311,6 +341,25 @@ const browserWidget = {
|
|
|
311
341
|
}
|
|
312
342
|
}
|
|
313
343
|
});
|
|
344
|
+
commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
|
|
345
|
+
label: trans.__('Show Active File in File Browser'),
|
|
346
|
+
isToggled: () => browser.navigateToCurrentDirectory,
|
|
347
|
+
execute: () => {
|
|
348
|
+
const value = !browser.navigateToCurrentDirectory;
|
|
349
|
+
const key = 'navigateToCurrentDirectory';
|
|
350
|
+
return settingRegistry
|
|
351
|
+
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
352
|
+
.catch((reason) => {
|
|
353
|
+
console.error(`Failed to set navigateToCurrentDirectory setting`);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
if (commandPalette) {
|
|
358
|
+
commandPalette.addItem({
|
|
359
|
+
command: CommandIDs.toggleNavigateToCurrentDirectory,
|
|
360
|
+
category: trans.__('File Operations')
|
|
361
|
+
});
|
|
362
|
+
}
|
|
314
363
|
// If the layout is a fresh session without saved data and not in single document
|
|
315
364
|
// mode, open file browser.
|
|
316
365
|
void labShell.restored.then(layout => {
|
|
@@ -319,17 +368,6 @@ const browserWidget = {
|
|
|
319
368
|
}
|
|
320
369
|
});
|
|
321
370
|
void Promise.all([app.restored, browser.model.restored]).then(() => {
|
|
322
|
-
function maybeCreate() {
|
|
323
|
-
// Create a launcher if there are no open items.
|
|
324
|
-
if (labShell.isEmpty('main') &&
|
|
325
|
-
commands.hasCommand('launcher:create')) {
|
|
326
|
-
void Private.createLauncher(commands, browser);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
// When layout is modified, create a launcher if there are no open items.
|
|
330
|
-
labShell.layoutModified.connect(() => {
|
|
331
|
-
maybeCreate();
|
|
332
|
-
});
|
|
333
371
|
// Whether to automatically navigate to a document's current directory
|
|
334
372
|
labShell.currentChanged.connect(async (_, change) => {
|
|
335
373
|
if (browser.navigateToCurrentDirectory && change.newValue) {
|
|
@@ -338,7 +376,7 @@ const browserWidget = {
|
|
|
338
376
|
if (context) {
|
|
339
377
|
const { path } = context;
|
|
340
378
|
try {
|
|
341
|
-
await Private.navigateToPath(path, factory, translator);
|
|
379
|
+
await Private.navigateToPath(path, browser, factory, translator);
|
|
342
380
|
}
|
|
343
381
|
catch (reason) {
|
|
344
382
|
console.warn(`${CommandIDs.goToPath} failed to open: ${path}`, reason);
|
|
@@ -346,7 +384,6 @@ const browserWidget = {
|
|
|
346
384
|
}
|
|
347
385
|
}
|
|
348
386
|
});
|
|
349
|
-
maybeCreate();
|
|
350
387
|
});
|
|
351
388
|
}
|
|
352
389
|
};
|
|
@@ -373,17 +410,17 @@ const shareFile = {
|
|
|
373
410
|
execute: () => {
|
|
374
411
|
const widget = tracker.currentWidget;
|
|
375
412
|
const model = widget === null || widget === void 0 ? void 0 : widget.selectedItems().next();
|
|
376
|
-
if (
|
|
413
|
+
if (model === undefined || model.done) {
|
|
377
414
|
return;
|
|
378
415
|
}
|
|
379
416
|
Clipboard.copyToSystem(PageConfig.getUrl({
|
|
380
417
|
workspace: PageConfig.defaultWorkspace,
|
|
381
|
-
treePath: model.path,
|
|
418
|
+
treePath: model.value.path,
|
|
382
419
|
toShare: true
|
|
383
420
|
}));
|
|
384
421
|
},
|
|
385
422
|
isVisible: () => !!tracker.currentWidget &&
|
|
386
|
-
|
|
423
|
+
Array.from(tracker.currentWidget.selectedItems()).length === 1,
|
|
387
424
|
icon: linkIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
388
425
|
label: trans.__('Copy Shareable Link')
|
|
389
426
|
});
|
|
@@ -402,6 +439,7 @@ const openWithPlugin = {
|
|
|
402
439
|
activate: (app, factory) => {
|
|
403
440
|
const { docRegistry } = app;
|
|
404
441
|
const { tracker } = factory;
|
|
442
|
+
let items = [];
|
|
405
443
|
function updateOpenWithMenu(contextMenu) {
|
|
406
444
|
var _a, _b;
|
|
407
445
|
const openWith = (_b = (_a = contextMenu.menu.items.find(item => {
|
|
@@ -413,6 +451,9 @@ const openWithPlugin = {
|
|
|
413
451
|
return; // Bail early if the open with menu is not displayed
|
|
414
452
|
}
|
|
415
453
|
// clear the current menu items
|
|
454
|
+
items.forEach(item => item.dispose());
|
|
455
|
+
items.length = 0;
|
|
456
|
+
// Ensure that the menu is empty
|
|
416
457
|
openWith.clearItems();
|
|
417
458
|
// get the widget factories that could be used to open all of the items
|
|
418
459
|
// in the current filebrowser selection
|
|
@@ -422,12 +463,10 @@ const openWithPlugin = {
|
|
|
422
463
|
}))
|
|
423
464
|
: new Set();
|
|
424
465
|
// make new menu items from the widget factories
|
|
425
|
-
factories.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
});
|
|
430
|
-
});
|
|
466
|
+
items = [...factories].map(factory => openWith.addItem({
|
|
467
|
+
args: { factory: factory.name, label: factory.label || factory.name },
|
|
468
|
+
command: CommandIDs.open
|
|
469
|
+
}));
|
|
431
470
|
}
|
|
432
471
|
app.contextMenu.opened.connect(updateOpenWithMenu);
|
|
433
472
|
}
|
|
@@ -450,19 +489,38 @@ const openBrowserTabPlugin = {
|
|
|
450
489
|
const trans = translator.load('jupyterlab');
|
|
451
490
|
const { tracker } = factory;
|
|
452
491
|
commands.addCommand(CommandIDs.openBrowserTab, {
|
|
453
|
-
execute:
|
|
492
|
+
execute: args => {
|
|
454
493
|
const widget = tracker.currentWidget;
|
|
455
494
|
if (!widget) {
|
|
456
495
|
return;
|
|
457
496
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
497
|
+
const mode = args['mode'];
|
|
498
|
+
return Promise.all(Array.from(map(widget.selectedItems(), item => {
|
|
499
|
+
if (mode === 'single-document') {
|
|
500
|
+
const url = PageConfig.getUrl({
|
|
501
|
+
mode: 'single-document',
|
|
502
|
+
treePath: item.path
|
|
503
|
+
});
|
|
504
|
+
const opened = window.open();
|
|
505
|
+
if (opened) {
|
|
506
|
+
opened.opener = null;
|
|
507
|
+
opened.location.href = url;
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
throw new Error('Failed to open new browser tab.');
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
return commands.execute('docmanager:open-browser-tab', {
|
|
515
|
+
path: item.path
|
|
516
|
+
});
|
|
517
|
+
}
|
|
462
518
|
})));
|
|
463
519
|
},
|
|
464
520
|
icon: addIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
465
|
-
label:
|
|
521
|
+
label: args => args['mode'] === 'single-document'
|
|
522
|
+
? trans.__('Open in Simple Mode')
|
|
523
|
+
: trans.__('Open in New Browser Tab'),
|
|
466
524
|
mnemonic: 0
|
|
467
525
|
});
|
|
468
526
|
}
|
|
@@ -500,12 +558,11 @@ export const fileUploadStatus = {
|
|
|
500
558
|
const openUrlPlugin = {
|
|
501
559
|
id: '@jupyterlab/filebrowser-extension:open-url',
|
|
502
560
|
autoStart: true,
|
|
503
|
-
requires: [
|
|
561
|
+
requires: [IDefaultFileBrowser, ITranslator],
|
|
504
562
|
optional: [ICommandPalette],
|
|
505
|
-
activate: (app,
|
|
563
|
+
activate: (app, browser, translator, palette) => {
|
|
506
564
|
const { commands } = app;
|
|
507
565
|
const trans = translator.load('jupyterlab');
|
|
508
|
-
const { defaultBrowser: browser } = factory;
|
|
509
566
|
const command = CommandIDs.openUrl;
|
|
510
567
|
commands.addCommand(command, {
|
|
511
568
|
label: args => args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'),
|
|
@@ -564,10 +621,10 @@ const openUrlPlugin = {
|
|
|
564
621
|
/**
|
|
565
622
|
* Add the main file browser commands to the application's command registry.
|
|
566
623
|
*/
|
|
567
|
-
function addCommands(app, factory, translator, settingRegistry, commandPalette) {
|
|
624
|
+
function addCommands(app, browser, factory, translator, settingRegistry, commandPalette) {
|
|
568
625
|
const trans = translator.load('jupyterlab');
|
|
569
626
|
const { docRegistry: registry, commands } = app;
|
|
570
|
-
const {
|
|
627
|
+
const { tracker } = factory;
|
|
571
628
|
commands.addCommand(CommandIDs.del, {
|
|
572
629
|
execute: () => {
|
|
573
630
|
const widget = tracker.currentWidget;
|
|
@@ -611,14 +668,15 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
611
668
|
label: trans.__('Duplicate')
|
|
612
669
|
});
|
|
613
670
|
commands.addCommand(CommandIDs.goToPath, {
|
|
671
|
+
label: trans.__('Update the file browser to display the provided `path`.'),
|
|
614
672
|
execute: async (args) => {
|
|
615
673
|
var _a;
|
|
616
674
|
const path = args.path || '';
|
|
617
675
|
const showBrowser = !((_a = args === null || args === void 0 ? void 0 : args.dontShowBrowser) !== null && _a !== void 0 ? _a : false);
|
|
618
676
|
try {
|
|
619
|
-
const item = await Private.navigateToPath(path, factory, translator);
|
|
677
|
+
const item = await Private.navigateToPath(path, browser, factory, translator);
|
|
620
678
|
if (item.type !== 'directory' && showBrowser) {
|
|
621
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
679
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
622
680
|
if (browserForPath) {
|
|
623
681
|
browserForPath.clearSelectedItems();
|
|
624
682
|
const parts = path.split('/');
|
|
@@ -640,7 +698,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
640
698
|
commands.addCommand(CommandIDs.goUp, {
|
|
641
699
|
label: 'go up',
|
|
642
700
|
execute: async () => {
|
|
643
|
-
const browserForPath = Private.getBrowserForPath('', factory);
|
|
701
|
+
const browserForPath = Private.getBrowserForPath('', browser, factory);
|
|
644
702
|
if (!browserForPath) {
|
|
645
703
|
return;
|
|
646
704
|
}
|
|
@@ -684,7 +742,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
684
742
|
// The normal contents service errors on paths ending in slash
|
|
685
743
|
path = path.slice(0, path.length - 1);
|
|
686
744
|
}
|
|
687
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
745
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
688
746
|
const { services } = browserForPath.model.manager;
|
|
689
747
|
const item = await services.contents.get(path, {
|
|
690
748
|
content: false
|
|
@@ -724,7 +782,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
724
782
|
return;
|
|
725
783
|
}
|
|
726
784
|
const { contents } = widget.model.manager.services;
|
|
727
|
-
return Promise.all(
|
|
785
|
+
return Promise.all(Array.from(map(widget.selectedItems(), item => {
|
|
728
786
|
if (item.type === 'directory') {
|
|
729
787
|
const localPath = contents.localPath(item.path);
|
|
730
788
|
return widget.model.cd(`/${localPath}`);
|
|
@@ -749,7 +807,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
749
807
|
return folderIcon.bindprops({ stylesheet: 'menuItem' });
|
|
750
808
|
}
|
|
751
809
|
},
|
|
752
|
-
// FIXME-TRANS: Is this localizable?
|
|
753
810
|
label: args => (args['label'] || args['factory'] || trans.__('Open')),
|
|
754
811
|
mnemonic: 0
|
|
755
812
|
});
|
|
@@ -823,13 +880,13 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
823
880
|
return;
|
|
824
881
|
}
|
|
825
882
|
const item = widget.selectedItems().next();
|
|
826
|
-
if (
|
|
883
|
+
if (item.done) {
|
|
827
884
|
return;
|
|
828
885
|
}
|
|
829
|
-
Clipboard.copyToSystem(item.path);
|
|
886
|
+
Clipboard.copyToSystem(item.value.path);
|
|
830
887
|
},
|
|
831
888
|
isVisible: () => !!tracker.currentWidget &&
|
|
832
|
-
tracker.currentWidget.selectedItems().next
|
|
889
|
+
!tracker.currentWidget.selectedItems().next().done,
|
|
833
890
|
icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
834
891
|
label: trans.__('Copy Path')
|
|
835
892
|
});
|
|
@@ -843,39 +900,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
843
900
|
icon: stopIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
844
901
|
label: trans.__('Shut Down Kernel')
|
|
845
902
|
});
|
|
846
|
-
commands.addCommand(CommandIDs.toggleBrowser, {
|
|
847
|
-
label: trans.__('File Browser'),
|
|
848
|
-
execute: () => {
|
|
849
|
-
if (browser.isHidden) {
|
|
850
|
-
return commands.execute(CommandIDs.showBrowser, void 0);
|
|
851
|
-
}
|
|
852
|
-
return commands.execute(CommandIDs.hideBrowser, void 0);
|
|
853
|
-
}
|
|
854
|
-
});
|
|
855
|
-
commands.addCommand(CommandIDs.createLauncher, {
|
|
856
|
-
label: trans.__('New Launcher'),
|
|
857
|
-
icon: args => (args.toolbar ? addIcon : undefined),
|
|
858
|
-
execute: (args) => {
|
|
859
|
-
if (commands.hasCommand('launcher:create')) {
|
|
860
|
-
return Private.createLauncher(commands, browser, args);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
});
|
|
864
|
-
if (settingRegistry) {
|
|
865
|
-
commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
|
|
866
|
-
label: trans.__('Show Active File in File Browser'),
|
|
867
|
-
isToggled: () => browser.navigateToCurrentDirectory,
|
|
868
|
-
execute: () => {
|
|
869
|
-
const value = !browser.navigateToCurrentDirectory;
|
|
870
|
-
const key = 'navigateToCurrentDirectory';
|
|
871
|
-
return settingRegistry
|
|
872
|
-
.set('@jupyterlab/filebrowser-extension:browser', key, value)
|
|
873
|
-
.catch((reason) => {
|
|
874
|
-
console.error(`Failed to set navigateToCurrentDirectory setting`);
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
903
|
commands.addCommand(CommandIDs.toggleLastModified, {
|
|
880
904
|
label: trans.__('Show Last Modified Column'),
|
|
881
905
|
isToggled: () => browser.showLastModifiedColumn,
|
|
@@ -884,9 +908,24 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
884
908
|
const key = 'showLastModifiedColumn';
|
|
885
909
|
if (settingRegistry) {
|
|
886
910
|
return settingRegistry
|
|
887
|
-
.set(
|
|
911
|
+
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
888
912
|
.catch((reason) => {
|
|
889
|
-
console.error(`Failed to set
|
|
913
|
+
console.error(`Failed to set ${key} setting`);
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
commands.addCommand(CommandIDs.toggleFileSize, {
|
|
919
|
+
label: trans.__('Show File Size Column'),
|
|
920
|
+
isToggled: () => browser.showFileSizeColumn,
|
|
921
|
+
execute: () => {
|
|
922
|
+
const value = !browser.showFileSizeColumn;
|
|
923
|
+
const key = 'showFileSizeColumn';
|
|
924
|
+
if (settingRegistry) {
|
|
925
|
+
return settingRegistry
|
|
926
|
+
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
927
|
+
.catch((reason) => {
|
|
928
|
+
console.error(`Failed to set ${key} setting`);
|
|
890
929
|
});
|
|
891
930
|
}
|
|
892
931
|
}
|
|
@@ -900,51 +939,59 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
900
939
|
const key = 'showHiddenFiles';
|
|
901
940
|
if (settingRegistry) {
|
|
902
941
|
return settingRegistry
|
|
903
|
-
.set(
|
|
942
|
+
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
904
943
|
.catch((reason) => {
|
|
905
944
|
console.error(`Failed to set showHiddenFiles setting`);
|
|
906
945
|
});
|
|
907
946
|
}
|
|
908
947
|
}
|
|
909
948
|
});
|
|
949
|
+
commands.addCommand(CommandIDs.toggleFileCheckboxes, {
|
|
950
|
+
label: trans.__('Show File Checkboxes'),
|
|
951
|
+
isToggled: () => browser.showFileCheckboxes,
|
|
952
|
+
execute: () => {
|
|
953
|
+
const value = !browser.showFileCheckboxes;
|
|
954
|
+
const key = 'showFileCheckboxes';
|
|
955
|
+
if (settingRegistry) {
|
|
956
|
+
return settingRegistry
|
|
957
|
+
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
958
|
+
.catch((reason) => {
|
|
959
|
+
console.error(`Failed to set showFileCheckboxes setting`);
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
});
|
|
910
964
|
commands.addCommand(CommandIDs.search, {
|
|
911
965
|
label: trans.__('Search on File Names'),
|
|
912
966
|
execute: () => alert('search')
|
|
913
967
|
});
|
|
914
|
-
if (commandPalette) {
|
|
915
|
-
commandPalette.addItem({
|
|
916
|
-
command: CommandIDs.toggleNavigateToCurrentDirectory,
|
|
917
|
-
category: trans.__('File Operations')
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
968
|
}
|
|
969
|
+
/**
|
|
970
|
+
* Export the plugins as default.
|
|
971
|
+
*/
|
|
972
|
+
const plugins = [
|
|
973
|
+
factory,
|
|
974
|
+
defaultFileBrowser,
|
|
975
|
+
browser,
|
|
976
|
+
shareFile,
|
|
977
|
+
fileUploadStatus,
|
|
978
|
+
downloadPlugin,
|
|
979
|
+
browserWidget,
|
|
980
|
+
openWithPlugin,
|
|
981
|
+
openBrowserTabPlugin,
|
|
982
|
+
openUrlPlugin
|
|
983
|
+
];
|
|
984
|
+
export default plugins;
|
|
921
985
|
/**
|
|
922
986
|
* A namespace for private module data.
|
|
923
987
|
*/
|
|
924
988
|
var Private;
|
|
925
989
|
(function (Private) {
|
|
926
|
-
/**
|
|
927
|
-
* Create a launcher for a given filebrowser widget.
|
|
928
|
-
*/
|
|
929
|
-
function createLauncher(commands, browser, args) {
|
|
930
|
-
const { model } = browser;
|
|
931
|
-
return commands
|
|
932
|
-
.execute('launcher:create', Object.assign({ cwd: model.path }, args))
|
|
933
|
-
.then((launcher) => {
|
|
934
|
-
model.pathChanged.connect(() => {
|
|
935
|
-
if (launcher.content) {
|
|
936
|
-
launcher.content.cwd = model.path;
|
|
937
|
-
}
|
|
938
|
-
}, launcher);
|
|
939
|
-
return launcher;
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
Private.createLauncher = createLauncher;
|
|
943
990
|
/**
|
|
944
991
|
* Get browser object given file path.
|
|
945
992
|
*/
|
|
946
|
-
function getBrowserForPath(path, factory) {
|
|
947
|
-
const {
|
|
993
|
+
function getBrowserForPath(path, browser, factory) {
|
|
994
|
+
const { tracker } = factory;
|
|
948
995
|
const driveName = browser.model.manager.services.contents.driveName(path);
|
|
949
996
|
if (driveName) {
|
|
950
997
|
const browserForPath = tracker.find(_path => _path.model.driveName === driveName);
|
|
@@ -962,9 +1009,9 @@ var Private;
|
|
|
962
1009
|
/**
|
|
963
1010
|
* Navigate to a path or the path containing a file.
|
|
964
1011
|
*/
|
|
965
|
-
async function navigateToPath(path, factory, translator) {
|
|
1012
|
+
async function navigateToPath(path, browser, factory, translator) {
|
|
966
1013
|
const trans = translator.load('jupyterlab');
|
|
967
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
1014
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
968
1015
|
if (!browserForPath) {
|
|
969
1016
|
throw new Error(trans.__('No browser for path'));
|
|
970
1017
|
}
|
|
@@ -986,7 +1033,7 @@ var Private;
|
|
|
986
1033
|
/**
|
|
987
1034
|
* Restores file browser state and overrides state if tree resolver resolves.
|
|
988
1035
|
*/
|
|
989
|
-
async function restoreBrowser(browser, commands, router, tree) {
|
|
1036
|
+
async function restoreBrowser(browser, commands, router, tree, app, labShell) {
|
|
990
1037
|
const restoring = 'jp-mod-restoring';
|
|
991
1038
|
browser.addClass(restoring);
|
|
992
1039
|
if (!router) {
|
|
@@ -1019,27 +1066,13 @@ var Private;
|
|
|
1019
1066
|
await browser.model.refresh();
|
|
1020
1067
|
}
|
|
1021
1068
|
browser.removeClass(restoring);
|
|
1069
|
+
if (labShell === null || labShell === void 0 ? void 0 : labShell.isEmpty('main')) {
|
|
1070
|
+
void commands.execute('launcher:create');
|
|
1071
|
+
}
|
|
1022
1072
|
};
|
|
1023
1073
|
router.routed.connect(listener);
|
|
1024
1074
|
}
|
|
1025
1075
|
Private.restoreBrowser = restoreBrowser;
|
|
1026
|
-
})(Private || (Private = {}));
|
|
1027
|
-
/**
|
|
1028
|
-
* Export the plugins as default.
|
|
1029
|
-
*/
|
|
1030
|
-
const plugins = [
|
|
1031
|
-
factory,
|
|
1032
|
-
browser,
|
|
1033
|
-
shareFile,
|
|
1034
|
-
fileUploadStatus,
|
|
1035
|
-
downloadPlugin,
|
|
1036
|
-
browserWidget,
|
|
1037
|
-
openWithPlugin,
|
|
1038
|
-
openBrowserTabPlugin,
|
|
1039
|
-
openUrlPlugin
|
|
1040
|
-
];
|
|
1041
|
-
export default plugins;
|
|
1042
|
-
(function (Private) {
|
|
1043
1076
|
let OpenWith;
|
|
1044
1077
|
(function (OpenWith) {
|
|
1045
1078
|
/**
|
|
@@ -1061,26 +1094,33 @@ export default plugins;
|
|
|
1061
1094
|
}
|
|
1062
1095
|
OpenWith.getFactories = getFactories;
|
|
1063
1096
|
/**
|
|
1064
|
-
* Return the intersection of multiple
|
|
1097
|
+
* Return the intersection of multiple iterables.
|
|
1065
1098
|
*
|
|
1066
|
-
* @param
|
|
1067
|
-
* @returns Set of common elements to all
|
|
1099
|
+
* @param iterables Iterator of iterables
|
|
1100
|
+
* @returns Set of common elements to all iterables
|
|
1068
1101
|
*/
|
|
1069
|
-
function intersection(
|
|
1070
|
-
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1102
|
+
function intersection(iterables) {
|
|
1103
|
+
let accumulator = undefined;
|
|
1104
|
+
for (const current of iterables) {
|
|
1105
|
+
// Initialize accumulator.
|
|
1106
|
+
if (accumulator === undefined) {
|
|
1107
|
+
accumulator = new Set(current);
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
// Return early if empty.
|
|
1111
|
+
if (accumulator.size === 0) {
|
|
1112
|
+
return accumulator;
|
|
1113
|
+
}
|
|
1114
|
+
// Keep the intersection of accumulator and current.
|
|
1115
|
+
let intersection = new Set();
|
|
1116
|
+
for (const value of current) {
|
|
1117
|
+
if (accumulator.has(value)) {
|
|
1118
|
+
intersection.add(value);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
accumulator = intersection;
|
|
1075
1122
|
}
|
|
1076
|
-
|
|
1077
|
-
const isect = new Set(first);
|
|
1078
|
-
// reduce over the remaining elements of iter
|
|
1079
|
-
return reduce(iter, (isect, subarr) => {
|
|
1080
|
-
// filter out all elements not present in both isect and subarr,
|
|
1081
|
-
// accumulate result in new set
|
|
1082
|
-
return new Set(subarr.filter(x => isect.has(x)));
|
|
1083
|
-
}, isect);
|
|
1123
|
+
return accumulator !== null && accumulator !== void 0 ? accumulator : new Set();
|
|
1084
1124
|
}
|
|
1085
1125
|
OpenWith.intersection = intersection;
|
|
1086
1126
|
})(OpenWith = Private.OpenWith || (Private.OpenWith = {}));
|