@jupyterlab/apputils-extension
Advanced tools
Comparing version 0.17.2 to 0.18.0
317
lib/index.js
@@ -6,2 +6,10 @@ "use strict"; | ||
|----------------------------------------------------------------------------*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -45,5 +53,4 @@ const application_1 = require("@jupyterlab/application"); | ||
(function (Patterns) { | ||
Patterns.cloneState = /[?&]clone([=&]|$)/; | ||
Patterns.loadState = /^\/workspaces\/([^?\/]+)/; | ||
Patterns.resetOnLoad = /(\?reset|\&reset)($|&)/; | ||
Patterns.workspace = new RegExp(`^${coreutils_1.PageConfig.getOption('workspacesUrl')}([^?\/]+)`); | ||
})(Patterns || (Patterns = {})); | ||
@@ -124,3 +131,3 @@ /** | ||
const commands = app.commands; | ||
const url = app.info.urls.themes; | ||
const url = coreutils_1.URLExt.join(app.info.urls.base, app.info.urls.themes); | ||
const key = themes.id; | ||
@@ -132,2 +139,9 @@ const manager = new apputils_1.ThemeManager({ key, host, settings, splash, url }); | ||
let currentTheme; | ||
// Set data attributes on the application shell for the current theme. | ||
manager.themeChanged.connect((sender, args) => { | ||
currentTheme = args.newValue; | ||
app.shell.dataset.themeLight = String(manager.isLight(currentTheme)); | ||
app.shell.dataset.themeName = currentTheme; | ||
commands.notifyCommandChanged(CommandIDs.changeTheme); | ||
}); | ||
commands.addCommand(CommandIDs.changeTheme, { | ||
@@ -144,5 +158,3 @@ label: args => { | ||
} | ||
currentTheme = theme; | ||
manager.setTheme(theme); | ||
commands.notifyCommandChanged(CommandIDs.changeTheme); | ||
} | ||
@@ -193,16 +205,22 @@ }); | ||
requires: [application_1.IRouter], | ||
activate: (app, router) => { | ||
const candidate = Private.getWorkspace(router) || ''; | ||
activate: (app, router) => __awaiter(this, void 0, void 0, function* () { | ||
const resolver = new apputils_1.WindowResolver(); | ||
return resolver | ||
.resolve(candidate) | ||
.catch(reason => { | ||
console.warn('Window resolution failed:', reason); | ||
return Private.redirect(router); | ||
}) | ||
.then(() => { | ||
coreutils_1.PageConfig.setOption('workspace', resolver.name); | ||
return resolver; | ||
}); | ||
} | ||
const match = router.current.path.match(Patterns.workspace); | ||
const workspace = (match && decodeURIComponent(match[1])) || ''; | ||
const candidate = workspace | ||
? coreutils_1.URLExt.join(coreutils_1.PageConfig.getOption('baseUrl'), coreutils_1.PageConfig.getOption('workspacesUrl'), workspace) | ||
: app.info.defaultWorkspace; | ||
try { | ||
yield resolver.resolve(candidate); | ||
} | ||
catch (error) { | ||
console.warn('Window resolution failed:', error); | ||
// Return a promise that never resolves. | ||
return new Promise(() => { | ||
Private.redirect(router); | ||
}); | ||
} | ||
coreutils_1.PageConfig.setOption('workspace', resolver.name); | ||
return resolver; | ||
}) | ||
}; | ||
@@ -245,3 +263,3 @@ /** | ||
commands.addCommand(CommandIDs.recoverState, { | ||
execute: () => { | ||
execute: ({ global }) => __awaiter(this, void 0, void 0, function* () { | ||
const immediate = true; | ||
@@ -251,6 +269,19 @@ const silent = true; | ||
// will not be triggered as it causes a save state. | ||
return state | ||
.clear(silent) | ||
.then(() => commands.execute(CommandIDs.saveState, { immediate })); | ||
} | ||
yield state.clear(silent); | ||
// If the user explictly chooses to recover state, all of local storage | ||
// should be cleared. | ||
if (global) { | ||
try { | ||
window.localStorage.clear(); | ||
console.log('Cleared local storage'); | ||
} | ||
catch (error) { | ||
console.warn('Clearing local storage failed.', error); | ||
// To give the user time to see the console warning before redirect, | ||
// do not set the `immediate` flag. | ||
return commands.execute(CommandIDs.saveState); | ||
} | ||
} | ||
return commands.execute(CommandIDs.saveState, { immediate }); | ||
}) | ||
}); | ||
@@ -261,10 +292,6 @@ // Conflate all outstanding requests to the save state command that happen | ||
commands.addCommand(CommandIDs.saveState, { | ||
label: () => `Save Workspace (${Private.getWorkspace(router)})`, | ||
isEnabled: () => !!Private.getWorkspace(router), | ||
execute: args => { | ||
const workspace = Private.getWorkspace(router); | ||
if (!workspace) { | ||
return; | ||
} | ||
const timeout = args.immediate ? 0 : WORKSPACE_SAVE_DEBOUNCE_INTERVAL; | ||
label: () => `Save Workspace (${app.info.workspace})`, | ||
execute: ({ immediate }) => { | ||
const { workspace } = app.info; | ||
const timeout = immediate ? 0 : WORKSPACE_SAVE_DEBOUNCE_INTERVAL; | ||
const id = workspace; | ||
@@ -279,3 +306,3 @@ const metadata = { id }; | ||
} | ||
debouncer = window.setTimeout(() => { | ||
debouncer = window.setTimeout(() => __awaiter(this, void 0, void 0, function* () { | ||
// Prevent a race condition between the timeout and saving. | ||
@@ -285,14 +312,12 @@ if (!conflated) { | ||
} | ||
state | ||
.toJSON() | ||
.then(data => workspaces.save(id, { data, metadata })) | ||
.then(() => { | ||
const data = yield state.toJSON(); | ||
try { | ||
yield workspaces.save(id, { data, metadata }); | ||
conflated.resolve(undefined); | ||
conflated = null; | ||
}) | ||
.catch(reason => { | ||
conflated.reject(reason); | ||
conflated = null; | ||
}); | ||
}, timeout); | ||
} | ||
catch (error) { | ||
conflated.reject(error); | ||
} | ||
conflated = null; | ||
}), timeout); | ||
return conflated.promise; | ||
@@ -305,3 +330,3 @@ } | ||
commands.addCommand(CommandIDs.loadState, { | ||
execute: (args) => { | ||
execute: (args) => __awaiter(this, void 0, void 0, function* () { | ||
// Since the command can be executed an arbitrary number of times, make | ||
@@ -313,95 +338,68 @@ // sure it is safe to call multiple times. | ||
const { hash, path, search } = args; | ||
const workspace = Private.getWorkspace(router); | ||
const { defaultWorkspace, workspace } = app.info; | ||
const query = coreutils_1.URLExt.queryStringToObject(search || ''); | ||
const clone = query['clone']; | ||
const source = typeof clone === 'string' ? clone : workspace; | ||
let promise; | ||
// If the default /lab workspace is being cloned, copy it out of local | ||
// storage instead of making a round trip to the server because it | ||
// does not exist on the server. | ||
if (source === clone && source === '') { | ||
const prefix = `${source}:${info.namespace}:`; | ||
const mask = (key) => key.replace(prefix, ''); | ||
const contents = coreutils_1.StateDB.toJSON(prefix, mask); | ||
resolved = true; | ||
transform.resolve({ type: 'overwrite', contents }); | ||
promise = Promise.resolve(); | ||
const clone = typeof query['clone'] === 'string' | ||
? query['clone'] === '' | ||
? defaultWorkspace | ||
: coreutils_1.URLExt.join(coreutils_1.PageConfig.getOption('baseUrl'), coreutils_1.PageConfig.getOption('workspacesUrl'), query['clone']) | ||
: null; | ||
const source = clone || workspace; | ||
try { | ||
const saved = yield workspaces.fetch(source); | ||
// If this command is called after a reset, the state database | ||
// will already be resolved. | ||
if (!resolved) { | ||
resolved = true; | ||
transform.resolve({ type: 'overwrite', contents: saved.data }); | ||
} | ||
} | ||
// If there is no promise, fetch the source and overwrite the database. | ||
promise = | ||
promise || | ||
workspaces | ||
.fetch(source) | ||
.then(saved => { | ||
// If this command is called after a reset, the state database | ||
// will already be resolved. | ||
if (!resolved) { | ||
resolved = true; | ||
transform.resolve({ type: 'overwrite', contents: saved.data }); | ||
} | ||
}) | ||
.catch(reason => { | ||
console.warn(`Fetching workspace (${workspace}) failed:`, reason); | ||
// If the workspace does not exist, cancel the data transformation | ||
// and save a workspace with the current user state data. | ||
if (!resolved) { | ||
resolved = true; | ||
transform.resolve({ type: 'cancel', contents: null }); | ||
} | ||
}) | ||
.then(() => { | ||
// Any time the local state database changes, save the workspace. | ||
if (workspace) { | ||
state.changed.connect(listener, state); | ||
} | ||
}); | ||
return promise | ||
.catch(reason => { | ||
console.warn(`${CommandIDs.loadState} failed:`, reason); | ||
}) | ||
.then(() => { | ||
const immediate = true; | ||
if (source === clone) { | ||
// Maintain the query string parameters but remove `clone`. | ||
delete query['clone']; | ||
const url = path + coreutils_1.URLExt.objectToQueryString(query) + hash; | ||
const cloned = commands | ||
.execute(CommandIDs.saveState, { immediate }) | ||
.then(() => router.stop); | ||
// After the state has been cloned, navigate to the URL. | ||
cloned.then(() => { | ||
router.navigate(url, { silent: true }); | ||
}); | ||
return cloned; | ||
catch (error) { | ||
console.warn(`Fetching workspace (${workspace}) failed:`, error); | ||
// If the workspace does not exist, cancel the data transformation | ||
// and save a workspace with the current user state data. | ||
if (!resolved) { | ||
resolved = true; | ||
transform.resolve({ type: 'cancel', contents: null }); | ||
} | ||
// After the state database has finished loading, save it. | ||
return commands.execute(CommandIDs.saveState, { immediate }); | ||
}); | ||
} | ||
} | ||
// Any time the local state database changes, save the workspace. | ||
if (workspace) { | ||
state.changed.connect(listener, state); | ||
} | ||
const immediate = true; | ||
if (source === clone) { | ||
// Maintain the query string parameters but remove `clone`. | ||
delete query['clone']; | ||
const url = path + coreutils_1.URLExt.objectToQueryString(query) + hash; | ||
const cloned = commands | ||
.execute(CommandIDs.saveState, { immediate }) | ||
.then(() => router.stop); | ||
// After the state has been cloned, navigate to the URL. | ||
cloned.then(() => { | ||
console.log(`HERE: ${url}`); | ||
router.navigate(url, { silent: true }); | ||
}); | ||
return cloned; | ||
} | ||
// After the state database has finished loading, save it. | ||
return commands.execute(CommandIDs.saveState, { immediate }); | ||
}) | ||
}); | ||
// Both the load state and clone state patterns should trigger the load | ||
// state command if the URL matches one of them, but cloning a workspace | ||
// outranks loading it because it is an explicit user action. | ||
router.register({ | ||
command: CommandIDs.loadState, | ||
pattern: Patterns.cloneState, | ||
rank: 20 // Set loading rank at a higher priority than the default 100. | ||
pattern: /.?/, | ||
rank: 20 // Very high priority: 20/100. | ||
}); | ||
router.register({ | ||
command: CommandIDs.loadState, | ||
pattern: Patterns.loadState, | ||
rank: 30 // Set loading rank at a higher priority than the default 100. | ||
}); | ||
commands.addCommand(CommandIDs.reset, { | ||
label: 'Reset Application State', | ||
execute: () => { | ||
commands | ||
.execute(CommandIDs.recoverState) | ||
.then(() => { | ||
router.reload(); | ||
}) | ||
.catch(() => { | ||
router.reload(); | ||
}); | ||
} | ||
execute: () => __awaiter(this, void 0, void 0, function* () { | ||
const global = true; | ||
try { | ||
yield commands.execute(CommandIDs.recoverState, { global }); | ||
} | ||
catch (error) { | ||
/* Ignore failures and redirect. */ | ||
} | ||
router.reload(); | ||
}) | ||
}); | ||
@@ -454,12 +452,9 @@ commands.addCommand(CommandIDs.resetOnLoad, { | ||
}); | ||
const fallthrough = () => { | ||
// If the state database is still unresolved after the first URL has been | ||
// routed, leave it intact. | ||
if (!resolved) { | ||
resolved = true; | ||
transform.resolve({ type: 'cancel', contents: null }); | ||
} | ||
router.routed.disconnect(fallthrough, state); | ||
}; | ||
router.routed.connect(fallthrough, state); | ||
// Clean up state database when the window unloads. | ||
window.addEventListener('beforeunload', () => { | ||
const silent = true; | ||
state.clear(silent).catch(() => { | ||
/* no-op */ | ||
}); | ||
}); | ||
return state; | ||
@@ -487,10 +482,2 @@ } | ||
/** | ||
* Returns the workspace name from the URL, if it exists. | ||
*/ | ||
function getWorkspace(router) { | ||
const match = router.current.path.match(Patterns.loadState); | ||
return (match && decodeURIComponent(match[1])) || ''; | ||
} | ||
Private.getWorkspace = getWorkspace; | ||
/** | ||
* Create a splash element. | ||
@@ -563,23 +550,23 @@ */ | ||
function redirect(router, warn = false) { | ||
const form = redirect_1.createRedirectForm(warn); | ||
const dialog = new apputils_1.Dialog({ | ||
title: 'Please use a different workspace.', | ||
body: form, | ||
focusNodeSelector: 'input', | ||
buttons: [apputils_1.Dialog.okButton({ label: 'Switch Workspace' })] | ||
}); | ||
return dialog.launch().then(result => { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const form = redirect_1.createRedirectForm(warn); | ||
const dialog = new apputils_1.Dialog({ | ||
title: 'Please use a different workspace.', | ||
body: form, | ||
focusNodeSelector: 'input', | ||
buttons: [apputils_1.Dialog.okButton({ label: 'Switch Workspace' })] | ||
}); | ||
const result = yield dialog.launch(); | ||
dialog.dispose(); | ||
if (result.value) { | ||
const url = `workspaces/${result.value}`; | ||
// Navigate to a new workspace URL and abandon this session altogether. | ||
router.navigate(url, { hard: true, silent: true }); | ||
// This promise will never resolve because the application navigates | ||
// away to a new location. It only exists to satisfy the return type | ||
// of the `redirect` function. | ||
return new Promise(() => { | ||
/* no-op */ | ||
}); | ||
if (!result.value) { | ||
return redirect(router, true); | ||
} | ||
return redirect(router, true); | ||
// Navigate to a new workspace URL and abandon this session altogether. | ||
const workspaces = coreutils_1.PageConfig.getOption('workspacesUrl'); | ||
const url = coreutils_1.URLExt.join(workspaces, result.value); | ||
router.navigate(url, { hard: true, silent: true }); | ||
// This promise will never resolve because the application navigates | ||
// away to a new location. It only exists to satisfy the return type | ||
// of the `redirect` function. | ||
return new Promise(() => undefined); | ||
}); | ||
@@ -586,0 +573,0 @@ } |
@@ -26,2 +26,5 @@ "use strict"; | ||
this._palette = palette; | ||
this._palette.title.iconClass = 'jp-PaletteIcon jp-SideBar-tabIcon'; | ||
this._palette.title.label = ''; | ||
this._palette.title.caption = 'Command Palette'; | ||
} | ||
@@ -70,3 +73,3 @@ /** | ||
palette.inputNode.placeholder = 'SEARCH'; | ||
shell.addToLeftArea(palette); | ||
shell.addToLeftArea(palette, { rank: 300 }); | ||
return new Palette(palette); | ||
@@ -73,0 +76,0 @@ } |
{ | ||
"name": "@jupyterlab/apputils-extension", | ||
"version": "0.17.2", | ||
"version": "0.18.0", | ||
"description": "JupyterLab - Application Utilities Extension", | ||
@@ -35,7 +35,7 @@ "homepage": "https://github.com/jupyterlab/jupyterlab", | ||
"dependencies": { | ||
"@jupyterlab/application": "^0.17.2", | ||
"@jupyterlab/apputils": "^0.17.2", | ||
"@jupyterlab/coreutils": "^2.0.2", | ||
"@jupyterlab/mainmenu": "^0.6.2", | ||
"@jupyterlab/services": "^3.0.3", | ||
"@jupyterlab/application": "^0.18.0", | ||
"@jupyterlab/apputils": "^0.18.0", | ||
"@jupyterlab/coreutils": "^2.1.0", | ||
"@jupyterlab/mainmenu": "^0.7.0", | ||
"@jupyterlab/services": "^3.1.0", | ||
"@phosphor/commands": "^1.5.0", | ||
@@ -42,0 +42,0 @@ "@phosphor/coreutils": "^1.3.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
87062
1057
+ Added@jupyterlab/application@0.18.6(transitive)
+ Added@jupyterlab/apputils@0.18.4(transitive)
+ Added@jupyterlab/codeeditor@0.18.4(transitive)
+ Added@jupyterlab/codemirror@0.18.4(transitive)
+ Added@jupyterlab/docregistry@0.18.4(transitive)
+ Added@jupyterlab/mainmenu@0.7.4(transitive)
+ Added@jupyterlab/rendermime@0.18.4(transitive)
- Removed@jupyterlab/application@0.17.2(transitive)
- Removed@jupyterlab/apputils@0.17.2(transitive)
- Removed@jupyterlab/codeeditor@0.17.2(transitive)
- Removed@jupyterlab/codemirror@0.17.3(transitive)
- Removed@jupyterlab/docregistry@0.17.2(transitive)
- Removed@jupyterlab/mainmenu@0.6.2(transitive)
- Removed@jupyterlab/rendermime@0.17.3(transitive)
Updated@jupyterlab/apputils@^0.18.0
Updated@jupyterlab/coreutils@^2.1.0
Updated@jupyterlab/mainmenu@^0.7.0
Updated@jupyterlab/services@^3.1.0