@jupyterlab/filebrowser-extension 4.0.0-alpha.9 → 4.0.0-beta.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/lib/index.js +215 -185
- package/lib/index.js.map +1 -1
- package/package.json +20 -21
- package/schema/browser.json +15 -39
- 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,16 +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, IFileBrowserCommands, 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
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';
|
|
21
25
|
/**
|
|
22
26
|
* The command IDs used by the file browser plugin.
|
|
23
27
|
*/
|
|
@@ -25,8 +29,6 @@ var CommandIDs;
|
|
|
25
29
|
(function (CommandIDs) {
|
|
26
30
|
CommandIDs.copy = 'filebrowser:copy';
|
|
27
31
|
CommandIDs.copyDownloadLink = 'filebrowser:copy-download-link';
|
|
28
|
-
// For main browser only.
|
|
29
|
-
CommandIDs.createLauncher = 'filebrowser:create-main-launcher';
|
|
30
32
|
CommandIDs.cut = 'filebrowser:cut';
|
|
31
33
|
CommandIDs.del = 'filebrowser:delete';
|
|
32
34
|
CommandIDs.download = 'filebrowser:download';
|
|
@@ -55,6 +57,7 @@ var CommandIDs;
|
|
|
55
57
|
CommandIDs.toggleBrowser = 'filebrowser:toggle-main';
|
|
56
58
|
CommandIDs.toggleNavigateToCurrentDirectory = 'filebrowser:toggle-navigate-to-current-directory';
|
|
57
59
|
CommandIDs.toggleLastModified = 'filebrowser:toggle-last-modified';
|
|
60
|
+
CommandIDs.toggleFileSize = 'filebrowser:toggle-file-size';
|
|
58
61
|
CommandIDs.search = 'filebrowser:search';
|
|
59
62
|
CommandIDs.toggleHiddenFiles = 'filebrowser:toggle-hidden-files';
|
|
60
63
|
CommandIDs.toggleFileCheckboxes = 'filebrowser:toggle-file-checkboxes';
|
|
@@ -68,7 +71,7 @@ const namespace = 'filebrowser';
|
|
|
68
71
|
*/
|
|
69
72
|
const browser = {
|
|
70
73
|
id: FILE_BROWSER_PLUGIN_ID,
|
|
71
|
-
requires: [IFileBrowserFactory, ITranslator],
|
|
74
|
+
requires: [IDefaultFileBrowser, IFileBrowserFactory, ITranslator],
|
|
72
75
|
optional: [
|
|
73
76
|
ILayoutRestorer,
|
|
74
77
|
ISettingRegistry,
|
|
@@ -77,9 +80,8 @@ const browser = {
|
|
|
77
80
|
],
|
|
78
81
|
provides: IFileBrowserCommands,
|
|
79
82
|
autoStart: true,
|
|
80
|
-
activate: async (app, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
|
|
81
|
-
const
|
|
82
|
-
const browser = factory.defaultBrowser;
|
|
83
|
+
activate: async (app, defaultFileBrowser, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
|
|
84
|
+
const browser = defaultFileBrowser;
|
|
83
85
|
// Let the application restorer track the primary file browser (that is
|
|
84
86
|
// automatically created) for restoration of application state (e.g. setting
|
|
85
87
|
// the file browser as the current side bar widget).
|
|
@@ -94,22 +96,7 @@ const browser = {
|
|
|
94
96
|
if (preferredPath) {
|
|
95
97
|
await browser.model.cd(preferredPath);
|
|
96
98
|
}
|
|
97
|
-
addCommands(app, factory, translator, settingRegistry, commandPalette);
|
|
98
|
-
// Show the current file browser shortcut in its title.
|
|
99
|
-
const updateBrowserTitle = () => {
|
|
100
|
-
const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
|
|
101
|
-
if (binding) {
|
|
102
|
-
const ks = CommandRegistry.formatKeystroke(binding.keys.join(' '));
|
|
103
|
-
browser.title.caption = trans.__('File Browser (%1)', ks);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
browser.title.caption = trans.__('File Browser');
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
updateBrowserTitle();
|
|
110
|
-
app.commands.keyBindingChanged.connect(() => {
|
|
111
|
-
updateBrowserTitle();
|
|
112
|
-
});
|
|
99
|
+
addCommands(app, browser, factory, translator, settingRegistry, commandPalette);
|
|
113
100
|
return void Promise.all([app.restored, browser.model.restored]).then(() => {
|
|
114
101
|
if (treePathUpdater) {
|
|
115
102
|
browser.model.pathChanged.connect((sender, args) => {
|
|
@@ -124,7 +111,7 @@ const browser = {
|
|
|
124
111
|
const fileBrowserConfig = {
|
|
125
112
|
navigateToCurrentDirectory: false,
|
|
126
113
|
showLastModifiedColumn: true,
|
|
127
|
-
|
|
114
|
+
showFileSizeColumn: false,
|
|
128
115
|
showHiddenFiles: false,
|
|
129
116
|
showFileCheckboxes: false
|
|
130
117
|
};
|
|
@@ -157,14 +144,8 @@ const factory = {
|
|
|
157
144
|
id: '@jupyterlab/filebrowser-extension:factory',
|
|
158
145
|
provides: IFileBrowserFactory,
|
|
159
146
|
requires: [IDocumentManager, ITranslator],
|
|
160
|
-
optional: [
|
|
161
|
-
|
|
162
|
-
IRouter,
|
|
163
|
-
JupyterFrontEnd.ITreeResolver,
|
|
164
|
-
JupyterLab.IInfo
|
|
165
|
-
],
|
|
166
|
-
activate: async (app, docManager, translator, state, router, tree, info) => {
|
|
167
|
-
const { commands } = app;
|
|
147
|
+
optional: [IStateDB, JupyterLab.IInfo],
|
|
148
|
+
activate: async (app, docManager, translator, state, info) => {
|
|
168
149
|
const tracker = new WidgetTracker({ namespace });
|
|
169
150
|
const createFileBrowser = (id, options = {}) => {
|
|
170
151
|
var _a;
|
|
@@ -190,13 +171,26 @@ const factory = {
|
|
|
190
171
|
void tracker.add(widget);
|
|
191
172
|
return widget;
|
|
192
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;
|
|
193
187
|
// Manually restore and load the default file browser.
|
|
194
|
-
const defaultBrowser = createFileBrowser('filebrowser', {
|
|
188
|
+
const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
|
|
195
189
|
auto: false,
|
|
196
190
|
restore: false
|
|
197
191
|
});
|
|
198
|
-
void Private.restoreBrowser(defaultBrowser, commands, router, tree);
|
|
199
|
-
return
|
|
192
|
+
void Private.restoreBrowser(defaultBrowser, commands, router, tree, app, labShell);
|
|
193
|
+
return defaultBrowser;
|
|
200
194
|
}
|
|
201
195
|
};
|
|
202
196
|
/**
|
|
@@ -231,7 +225,7 @@ const downloadPlugin = {
|
|
|
231
225
|
return;
|
|
232
226
|
}
|
|
233
227
|
return widget.model.manager.services.contents
|
|
234
|
-
.getDownloadUrl(widget.selectedItems().next().path)
|
|
228
|
+
.getDownloadUrl(widget.selectedItems().next().value.path)
|
|
235
229
|
.then(url => {
|
|
236
230
|
Clipboard.copyToSystem(url);
|
|
237
231
|
});
|
|
@@ -249,6 +243,7 @@ const browserWidget = {
|
|
|
249
243
|
id: '@jupyterlab/filebrowser-extension:widget',
|
|
250
244
|
requires: [
|
|
251
245
|
IDocumentManager,
|
|
246
|
+
IDefaultFileBrowser,
|
|
252
247
|
IFileBrowserFactory,
|
|
253
248
|
ISettingRegistry,
|
|
254
249
|
IToolbarWidgetRegistry,
|
|
@@ -256,23 +251,65 @@ const browserWidget = {
|
|
|
256
251
|
ILabShell,
|
|
257
252
|
IFileBrowserCommands
|
|
258
253
|
],
|
|
254
|
+
optional: [ICommandPalette],
|
|
259
255
|
autoStart: true,
|
|
260
|
-
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) => {
|
|
261
259
|
const { commands } = app;
|
|
262
|
-
const {
|
|
260
|
+
const { tracker } = factory;
|
|
263
261
|
const trans = translator.load('jupyterlab');
|
|
264
262
|
// Set attributes when adding the browser to the UI
|
|
265
263
|
browser.node.setAttribute('role', 'region');
|
|
266
264
|
browser.node.setAttribute('aria-label', trans.__('File Browser Section'));
|
|
267
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
|
+
});
|
|
268
281
|
// Toolbar
|
|
269
|
-
toolbarRegistry.
|
|
270
|
-
|
|
271
|
-
|
|
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
|
+
});
|
|
272
308
|
commands.addCommand(CommandIDs.showBrowser, {
|
|
309
|
+
label: trans.__('Open the file browser for the provided `path`.'),
|
|
273
310
|
execute: args => {
|
|
274
311
|
const path = args.path || '';
|
|
275
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
312
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
276
313
|
// Check for browser not found
|
|
277
314
|
if (!browserForPath) {
|
|
278
315
|
return;
|
|
@@ -285,20 +322,18 @@ const browserWidget = {
|
|
|
285
322
|
else {
|
|
286
323
|
const areas = ['left', 'right'];
|
|
287
324
|
for (const area of areas) {
|
|
288
|
-
const
|
|
289
|
-
let widget = it.next();
|
|
290
|
-
while (widget) {
|
|
325
|
+
for (const widget of labShell.widgets(area)) {
|
|
291
326
|
if (widget.contains(browserForPath)) {
|
|
292
327
|
labShell.activateById(widget.id);
|
|
293
328
|
return;
|
|
294
329
|
}
|
|
295
|
-
widget = it.next();
|
|
296
330
|
}
|
|
297
331
|
}
|
|
298
332
|
}
|
|
299
333
|
}
|
|
300
334
|
});
|
|
301
335
|
commands.addCommand(CommandIDs.hideBrowser, {
|
|
336
|
+
label: trans.__('Hide the file browser.'),
|
|
302
337
|
execute: () => {
|
|
303
338
|
const widget = tracker.currentWidget;
|
|
304
339
|
if (widget && !widget.isHidden) {
|
|
@@ -306,6 +341,25 @@ const browserWidget = {
|
|
|
306
341
|
}
|
|
307
342
|
}
|
|
308
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
|
+
}
|
|
309
363
|
// If the layout is a fresh session without saved data and not in single document
|
|
310
364
|
// mode, open file browser.
|
|
311
365
|
void labShell.restored.then(layout => {
|
|
@@ -314,17 +368,6 @@ const browserWidget = {
|
|
|
314
368
|
}
|
|
315
369
|
});
|
|
316
370
|
void Promise.all([app.restored, browser.model.restored]).then(() => {
|
|
317
|
-
function maybeCreate() {
|
|
318
|
-
// Create a launcher if there are no open items.
|
|
319
|
-
if (labShell.isEmpty('main') &&
|
|
320
|
-
commands.hasCommand('launcher:create')) {
|
|
321
|
-
void Private.createLauncher(commands, browser);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
// When layout is modified, create a launcher if there are no open items.
|
|
325
|
-
labShell.layoutModified.connect(() => {
|
|
326
|
-
maybeCreate();
|
|
327
|
-
});
|
|
328
371
|
// Whether to automatically navigate to a document's current directory
|
|
329
372
|
labShell.currentChanged.connect(async (_, change) => {
|
|
330
373
|
if (browser.navigateToCurrentDirectory && change.newValue) {
|
|
@@ -333,7 +376,7 @@ const browserWidget = {
|
|
|
333
376
|
if (context) {
|
|
334
377
|
const { path } = context;
|
|
335
378
|
try {
|
|
336
|
-
await Private.navigateToPath(path, factory, translator);
|
|
379
|
+
await Private.navigateToPath(path, browser, factory, translator);
|
|
337
380
|
}
|
|
338
381
|
catch (reason) {
|
|
339
382
|
console.warn(`${CommandIDs.goToPath} failed to open: ${path}`, reason);
|
|
@@ -341,7 +384,6 @@ const browserWidget = {
|
|
|
341
384
|
}
|
|
342
385
|
}
|
|
343
386
|
});
|
|
344
|
-
maybeCreate();
|
|
345
387
|
});
|
|
346
388
|
}
|
|
347
389
|
};
|
|
@@ -368,17 +410,17 @@ const shareFile = {
|
|
|
368
410
|
execute: () => {
|
|
369
411
|
const widget = tracker.currentWidget;
|
|
370
412
|
const model = widget === null || widget === void 0 ? void 0 : widget.selectedItems().next();
|
|
371
|
-
if (
|
|
413
|
+
if (model === undefined || model.done) {
|
|
372
414
|
return;
|
|
373
415
|
}
|
|
374
416
|
Clipboard.copyToSystem(PageConfig.getUrl({
|
|
375
417
|
workspace: PageConfig.defaultWorkspace,
|
|
376
|
-
treePath: model.path,
|
|
418
|
+
treePath: model.value.path,
|
|
377
419
|
toShare: true
|
|
378
420
|
}));
|
|
379
421
|
},
|
|
380
422
|
isVisible: () => !!tracker.currentWidget &&
|
|
381
|
-
|
|
423
|
+
Array.from(tracker.currentWidget.selectedItems()).length === 1,
|
|
382
424
|
icon: linkIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
383
425
|
label: trans.__('Copy Shareable Link')
|
|
384
426
|
});
|
|
@@ -397,6 +439,7 @@ const openWithPlugin = {
|
|
|
397
439
|
activate: (app, factory) => {
|
|
398
440
|
const { docRegistry } = app;
|
|
399
441
|
const { tracker } = factory;
|
|
442
|
+
let items = [];
|
|
400
443
|
function updateOpenWithMenu(contextMenu) {
|
|
401
444
|
var _a, _b;
|
|
402
445
|
const openWith = (_b = (_a = contextMenu.menu.items.find(item => {
|
|
@@ -408,6 +451,9 @@ const openWithPlugin = {
|
|
|
408
451
|
return; // Bail early if the open with menu is not displayed
|
|
409
452
|
}
|
|
410
453
|
// clear the current menu items
|
|
454
|
+
items.forEach(item => item.dispose());
|
|
455
|
+
items.length = 0;
|
|
456
|
+
// Ensure that the menu is empty
|
|
411
457
|
openWith.clearItems();
|
|
412
458
|
// get the widget factories that could be used to open all of the items
|
|
413
459
|
// in the current filebrowser selection
|
|
@@ -417,12 +463,10 @@ const openWithPlugin = {
|
|
|
417
463
|
}))
|
|
418
464
|
: new Set();
|
|
419
465
|
// make new menu items from the widget factories
|
|
420
|
-
factories.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
});
|
|
425
|
-
});
|
|
466
|
+
items = [...factories].map(factory => openWith.addItem({
|
|
467
|
+
args: { factory: factory.name, label: factory.label || factory.name },
|
|
468
|
+
command: CommandIDs.open
|
|
469
|
+
}));
|
|
426
470
|
}
|
|
427
471
|
app.contextMenu.opened.connect(updateOpenWithMenu);
|
|
428
472
|
}
|
|
@@ -445,19 +489,38 @@ const openBrowserTabPlugin = {
|
|
|
445
489
|
const trans = translator.load('jupyterlab');
|
|
446
490
|
const { tracker } = factory;
|
|
447
491
|
commands.addCommand(CommandIDs.openBrowserTab, {
|
|
448
|
-
execute:
|
|
492
|
+
execute: args => {
|
|
449
493
|
const widget = tracker.currentWidget;
|
|
450
494
|
if (!widget) {
|
|
451
495
|
return;
|
|
452
496
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
+
}
|
|
457
518
|
})));
|
|
458
519
|
},
|
|
459
520
|
icon: addIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
460
|
-
label:
|
|
521
|
+
label: args => args['mode'] === 'single-document'
|
|
522
|
+
? trans.__('Open in Simple Mode')
|
|
523
|
+
: trans.__('Open in New Browser Tab'),
|
|
461
524
|
mnemonic: 0
|
|
462
525
|
});
|
|
463
526
|
}
|
|
@@ -495,12 +558,11 @@ export const fileUploadStatus = {
|
|
|
495
558
|
const openUrlPlugin = {
|
|
496
559
|
id: '@jupyterlab/filebrowser-extension:open-url',
|
|
497
560
|
autoStart: true,
|
|
498
|
-
requires: [
|
|
561
|
+
requires: [IDefaultFileBrowser, ITranslator],
|
|
499
562
|
optional: [ICommandPalette],
|
|
500
|
-
activate: (app,
|
|
563
|
+
activate: (app, browser, translator, palette) => {
|
|
501
564
|
const { commands } = app;
|
|
502
565
|
const trans = translator.load('jupyterlab');
|
|
503
|
-
const { defaultBrowser: browser } = factory;
|
|
504
566
|
const command = CommandIDs.openUrl;
|
|
505
567
|
commands.addCommand(command, {
|
|
506
568
|
label: args => args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'),
|
|
@@ -559,10 +621,10 @@ const openUrlPlugin = {
|
|
|
559
621
|
/**
|
|
560
622
|
* Add the main file browser commands to the application's command registry.
|
|
561
623
|
*/
|
|
562
|
-
function addCommands(app, factory, translator, settingRegistry, commandPalette) {
|
|
624
|
+
function addCommands(app, browser, factory, translator, settingRegistry, commandPalette) {
|
|
563
625
|
const trans = translator.load('jupyterlab');
|
|
564
626
|
const { docRegistry: registry, commands } = app;
|
|
565
|
-
const {
|
|
627
|
+
const { tracker } = factory;
|
|
566
628
|
commands.addCommand(CommandIDs.del, {
|
|
567
629
|
execute: () => {
|
|
568
630
|
const widget = tracker.currentWidget;
|
|
@@ -606,14 +668,15 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
606
668
|
label: trans.__('Duplicate')
|
|
607
669
|
});
|
|
608
670
|
commands.addCommand(CommandIDs.goToPath, {
|
|
671
|
+
label: trans.__('Update the file browser to display the provided `path`.'),
|
|
609
672
|
execute: async (args) => {
|
|
610
673
|
var _a;
|
|
611
674
|
const path = args.path || '';
|
|
612
675
|
const showBrowser = !((_a = args === null || args === void 0 ? void 0 : args.dontShowBrowser) !== null && _a !== void 0 ? _a : false);
|
|
613
676
|
try {
|
|
614
|
-
const item = await Private.navigateToPath(path, factory, translator);
|
|
677
|
+
const item = await Private.navigateToPath(path, browser, factory, translator);
|
|
615
678
|
if (item.type !== 'directory' && showBrowser) {
|
|
616
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
679
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
617
680
|
if (browserForPath) {
|
|
618
681
|
browserForPath.clearSelectedItems();
|
|
619
682
|
const parts = path.split('/');
|
|
@@ -635,7 +698,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
635
698
|
commands.addCommand(CommandIDs.goUp, {
|
|
636
699
|
label: 'go up',
|
|
637
700
|
execute: async () => {
|
|
638
|
-
const browserForPath = Private.getBrowserForPath('', factory);
|
|
701
|
+
const browserForPath = Private.getBrowserForPath('', browser, factory);
|
|
639
702
|
if (!browserForPath) {
|
|
640
703
|
return;
|
|
641
704
|
}
|
|
@@ -679,7 +742,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
679
742
|
// The normal contents service errors on paths ending in slash
|
|
680
743
|
path = path.slice(0, path.length - 1);
|
|
681
744
|
}
|
|
682
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
745
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
683
746
|
const { services } = browserForPath.model.manager;
|
|
684
747
|
const item = await services.contents.get(path, {
|
|
685
748
|
content: false
|
|
@@ -719,7 +782,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
719
782
|
return;
|
|
720
783
|
}
|
|
721
784
|
const { contents } = widget.model.manager.services;
|
|
722
|
-
return Promise.all(
|
|
785
|
+
return Promise.all(Array.from(map(widget.selectedItems(), item => {
|
|
723
786
|
if (item.type === 'directory') {
|
|
724
787
|
const localPath = contents.localPath(item.path);
|
|
725
788
|
return widget.model.cd(`/${localPath}`);
|
|
@@ -744,7 +807,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
744
807
|
return folderIcon.bindprops({ stylesheet: 'menuItem' });
|
|
745
808
|
}
|
|
746
809
|
},
|
|
747
|
-
// FIXME-TRANS: Is this localizable?
|
|
748
810
|
label: args => (args['label'] || args['factory'] || trans.__('Open')),
|
|
749
811
|
mnemonic: 0
|
|
750
812
|
});
|
|
@@ -818,13 +880,13 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
818
880
|
return;
|
|
819
881
|
}
|
|
820
882
|
const item = widget.selectedItems().next();
|
|
821
|
-
if (
|
|
883
|
+
if (item.done) {
|
|
822
884
|
return;
|
|
823
885
|
}
|
|
824
|
-
Clipboard.copyToSystem(item.path);
|
|
886
|
+
Clipboard.copyToSystem(item.value.path);
|
|
825
887
|
},
|
|
826
888
|
isVisible: () => !!tracker.currentWidget &&
|
|
827
|
-
tracker.currentWidget.selectedItems().next
|
|
889
|
+
!tracker.currentWidget.selectedItems().next().done,
|
|
828
890
|
icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
829
891
|
label: trans.__('Copy Path')
|
|
830
892
|
});
|
|
@@ -838,50 +900,32 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
838
900
|
icon: stopIcon.bindprops({ stylesheet: 'menuItem' }),
|
|
839
901
|
label: trans.__('Shut Down Kernel')
|
|
840
902
|
});
|
|
841
|
-
commands.addCommand(CommandIDs.
|
|
842
|
-
label: trans.__('
|
|
903
|
+
commands.addCommand(CommandIDs.toggleLastModified, {
|
|
904
|
+
label: trans.__('Show Last Modified Column'),
|
|
905
|
+
isToggled: () => browser.showLastModifiedColumn,
|
|
843
906
|
execute: () => {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
return commands.execute(CommandIDs.hideBrowser, void 0);
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
commands.addCommand(CommandIDs.createLauncher, {
|
|
851
|
-
label: trans.__('New Launcher'),
|
|
852
|
-
icon: args => (args.toolbar ? addIcon : undefined),
|
|
853
|
-
execute: (args) => {
|
|
854
|
-
if (commands.hasCommand('launcher:create')) {
|
|
855
|
-
return Private.createLauncher(commands, browser, args);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
if (settingRegistry) {
|
|
860
|
-
commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
|
|
861
|
-
label: trans.__('Show Active File in File Browser'),
|
|
862
|
-
isToggled: () => browser.navigateToCurrentDirectory,
|
|
863
|
-
execute: () => {
|
|
864
|
-
const value = !browser.navigateToCurrentDirectory;
|
|
865
|
-
const key = 'navigateToCurrentDirectory';
|
|
907
|
+
const value = !browser.showLastModifiedColumn;
|
|
908
|
+
const key = 'showLastModifiedColumn';
|
|
909
|
+
if (settingRegistry) {
|
|
866
910
|
return settingRegistry
|
|
867
911
|
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
868
912
|
.catch((reason) => {
|
|
869
|
-
console.error(`Failed to set
|
|
913
|
+
console.error(`Failed to set ${key} setting`);
|
|
870
914
|
});
|
|
871
915
|
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
commands.addCommand(CommandIDs.
|
|
875
|
-
label: trans.__('Show
|
|
876
|
-
isToggled: () => browser.
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
commands.addCommand(CommandIDs.toggleFileSize, {
|
|
919
|
+
label: trans.__('Show File Size Column'),
|
|
920
|
+
isToggled: () => browser.showFileSizeColumn,
|
|
877
921
|
execute: () => {
|
|
878
|
-
const value = !browser.
|
|
879
|
-
const key = '
|
|
922
|
+
const value = !browser.showFileSizeColumn;
|
|
923
|
+
const key = 'showFileSizeColumn';
|
|
880
924
|
if (settingRegistry) {
|
|
881
925
|
return settingRegistry
|
|
882
926
|
.set(FILE_BROWSER_PLUGIN_ID, key, value)
|
|
883
927
|
.catch((reason) => {
|
|
884
|
-
console.error(`Failed to set
|
|
928
|
+
console.error(`Failed to set ${key} setting`);
|
|
885
929
|
});
|
|
886
930
|
}
|
|
887
931
|
}
|
|
@@ -921,40 +965,33 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
|
|
|
921
965
|
label: trans.__('Search on File Names'),
|
|
922
966
|
execute: () => alert('search')
|
|
923
967
|
});
|
|
924
|
-
if (commandPalette) {
|
|
925
|
-
commandPalette.addItem({
|
|
926
|
-
command: CommandIDs.toggleNavigateToCurrentDirectory,
|
|
927
|
-
category: trans.__('File Operations')
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
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;
|
|
931
985
|
/**
|
|
932
986
|
* A namespace for private module data.
|
|
933
987
|
*/
|
|
934
988
|
var Private;
|
|
935
989
|
(function (Private) {
|
|
936
|
-
/**
|
|
937
|
-
* Create a launcher for a given filebrowser widget.
|
|
938
|
-
*/
|
|
939
|
-
function createLauncher(commands, browser, args) {
|
|
940
|
-
const { model } = browser;
|
|
941
|
-
return commands
|
|
942
|
-
.execute('launcher:create', Object.assign({ cwd: model.path }, args))
|
|
943
|
-
.then((launcher) => {
|
|
944
|
-
model.pathChanged.connect(() => {
|
|
945
|
-
if (launcher.content) {
|
|
946
|
-
launcher.content.cwd = model.path;
|
|
947
|
-
}
|
|
948
|
-
}, launcher);
|
|
949
|
-
return launcher;
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
Private.createLauncher = createLauncher;
|
|
953
990
|
/**
|
|
954
991
|
* Get browser object given file path.
|
|
955
992
|
*/
|
|
956
|
-
function getBrowserForPath(path, factory) {
|
|
957
|
-
const {
|
|
993
|
+
function getBrowserForPath(path, browser, factory) {
|
|
994
|
+
const { tracker } = factory;
|
|
958
995
|
const driveName = browser.model.manager.services.contents.driveName(path);
|
|
959
996
|
if (driveName) {
|
|
960
997
|
const browserForPath = tracker.find(_path => _path.model.driveName === driveName);
|
|
@@ -972,9 +1009,9 @@ var Private;
|
|
|
972
1009
|
/**
|
|
973
1010
|
* Navigate to a path or the path containing a file.
|
|
974
1011
|
*/
|
|
975
|
-
async function navigateToPath(path, factory, translator) {
|
|
1012
|
+
async function navigateToPath(path, browser, factory, translator) {
|
|
976
1013
|
const trans = translator.load('jupyterlab');
|
|
977
|
-
const browserForPath = Private.getBrowserForPath(path, factory);
|
|
1014
|
+
const browserForPath = Private.getBrowserForPath(path, browser, factory);
|
|
978
1015
|
if (!browserForPath) {
|
|
979
1016
|
throw new Error(trans.__('No browser for path'));
|
|
980
1017
|
}
|
|
@@ -996,7 +1033,7 @@ var Private;
|
|
|
996
1033
|
/**
|
|
997
1034
|
* Restores file browser state and overrides state if tree resolver resolves.
|
|
998
1035
|
*/
|
|
999
|
-
async function restoreBrowser(browser, commands, router, tree) {
|
|
1036
|
+
async function restoreBrowser(browser, commands, router, tree, app, labShell) {
|
|
1000
1037
|
const restoring = 'jp-mod-restoring';
|
|
1001
1038
|
browser.addClass(restoring);
|
|
1002
1039
|
if (!router) {
|
|
@@ -1029,27 +1066,13 @@ var Private;
|
|
|
1029
1066
|
await browser.model.refresh();
|
|
1030
1067
|
}
|
|
1031
1068
|
browser.removeClass(restoring);
|
|
1069
|
+
if (labShell === null || labShell === void 0 ? void 0 : labShell.isEmpty('main')) {
|
|
1070
|
+
void commands.execute('launcher:create');
|
|
1071
|
+
}
|
|
1032
1072
|
};
|
|
1033
1073
|
router.routed.connect(listener);
|
|
1034
1074
|
}
|
|
1035
1075
|
Private.restoreBrowser = restoreBrowser;
|
|
1036
|
-
})(Private || (Private = {}));
|
|
1037
|
-
/**
|
|
1038
|
-
* Export the plugins as default.
|
|
1039
|
-
*/
|
|
1040
|
-
const plugins = [
|
|
1041
|
-
factory,
|
|
1042
|
-
browser,
|
|
1043
|
-
shareFile,
|
|
1044
|
-
fileUploadStatus,
|
|
1045
|
-
downloadPlugin,
|
|
1046
|
-
browserWidget,
|
|
1047
|
-
openWithPlugin,
|
|
1048
|
-
openBrowserTabPlugin,
|
|
1049
|
-
openUrlPlugin
|
|
1050
|
-
];
|
|
1051
|
-
export default plugins;
|
|
1052
|
-
(function (Private) {
|
|
1053
1076
|
let OpenWith;
|
|
1054
1077
|
(function (OpenWith) {
|
|
1055
1078
|
/**
|
|
@@ -1071,26 +1094,33 @@ export default plugins;
|
|
|
1071
1094
|
}
|
|
1072
1095
|
OpenWith.getFactories = getFactories;
|
|
1073
1096
|
/**
|
|
1074
|
-
* Return the intersection of multiple
|
|
1097
|
+
* Return the intersection of multiple iterables.
|
|
1075
1098
|
*
|
|
1076
|
-
* @param
|
|
1077
|
-
* @returns Set of common elements to all
|
|
1099
|
+
* @param iterables Iterator of iterables
|
|
1100
|
+
* @returns Set of common elements to all iterables
|
|
1078
1101
|
*/
|
|
1079
|
-
function intersection(
|
|
1080
|
-
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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;
|
|
1085
1122
|
}
|
|
1086
|
-
|
|
1087
|
-
const isect = new Set(first);
|
|
1088
|
-
// reduce over the remaining elements of iter
|
|
1089
|
-
return reduce(iter, (isect, subarr) => {
|
|
1090
|
-
// filter out all elements not present in both isect and subarr,
|
|
1091
|
-
// accumulate result in new set
|
|
1092
|
-
return new Set(subarr.filter(x => isect.has(x)));
|
|
1093
|
-
}, isect);
|
|
1123
|
+
return accumulator !== null && accumulator !== void 0 ? accumulator : new Set();
|
|
1094
1124
|
}
|
|
1095
1125
|
OpenWith.intersection = intersection;
|
|
1096
1126
|
})(OpenWith = Private.OpenWith || (Private.OpenWith = {}));
|