Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable default layout #248

Merged
merged 13 commits into from
Oct 29, 2022
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@jupyterlab/codeeditor": "^3.0.0",
"@jupyterlab/console": "^3.0.0",
"@jupyterlab/coreutils": "^5.0.0",
"@jupyterlab/docregistry": "^3.4.3",
"@jupyterlab/mainmenu": "^3.0.0",
"@jupyterlab/nbformat": "^3.0.0",
"@jupyterlab/notebook": "^3.0.0",
Expand All @@ -75,8 +76,8 @@
"@jupyterlab/builder": "^3.0.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^2.27.0",
"@typescript-eslint/parser": "^2.27.0",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"eslint": "^7.14.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-prettier": "^3.1.4",
Expand Down
31 changes: 29 additions & 2 deletions schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,36 @@
},
"browserDashboardCheck": {
"type": "boolean",
"title": "Determine whether to validate dashboards via browser check.",
"description": "If set to true, the test dashboard will be validated within the browser environment. This is useful for testing the dashboard when behind a browser-cookie based authentication.",
"title": "Check dashboards via browser.",
"description": "If set to true, the extension will check for the Dask dashboard from the user's browser. This is useful for testing the dashboard when behind a browser-cookie based authentication.",
"default": false
},
"defaultLayout": {
"type": "object",
"title": "",
"description": "",
"properties": {},
"additionalProperties": {
"type": "object",
"properties": {
"mode": { "type": "string" },
"ref": { "type": ["string", "null"], "default": null }
}
},
"default": {
"individual-task-stream": {
"mode": "split-right",
"ref": null
},
"individual-workers-memory": {
"mode": "split-bottom",
"ref": "individual-task-stream"
},
"individual-progress": {
"mode": "split-right",
"ref": "individual-workers-memory"
}
}
}
},
"type": "object"
Expand Down
120 changes: 105 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { CodeEditor } from '@jupyterlab/codeeditor';

import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console';

import { DocumentRegistry } from '@jupyterlab/docregistry';

import { IMainMenu } from '@jupyterlab/mainmenu';

import { ISettingRegistry } from '@jupyterlab/settingregistry';
Expand All @@ -30,6 +32,8 @@ import {

import { Kernel, KernelMessage, Session } from '@jupyterlab/services';

import { topologicSort } from '@lumino/algorithm';

import { Signal } from '@lumino/signaling';

import { IClusterModel, DaskClusterManager } from './clusters';
Expand All @@ -46,6 +50,12 @@ namespace CommandIDs {
*/
export const launchPanel = 'dask:launch-dashboard';

/**
* Launch a dask custom layout.
*/
export const launchLayout = 'dask:launch-layout';

/**
/**
* Inject client code into the active editor.
*/
Expand Down Expand Up @@ -147,7 +157,7 @@ async function activate(
// Create the Dask sidebar panel.
const sidebar = new DaskSidebar({
launchDashboardItem: (item: IDashboardItem) => {
void app.commands.execute(CommandIDs.launchPanel, item);
void app.commands.execute(CommandIDs.launchPanel, { item });
},
linkFinder,
clientCodeInjector,
Expand All @@ -168,7 +178,7 @@ async function activate(
restorer.add(sidebar, id);
void restorer.restore(tracker, {
command: CommandIDs.launchPanel,
args: widget => widget.item || {},
args: widget => ({ item: widget.item } || {}),
name: widget => (widget.item && widget.item.route) || ''
});

Expand Down Expand Up @@ -286,6 +296,9 @@ async function activate(
// with default behavior.
let browserDashboardCheck: boolean = false;

// The default layout for dashboards.
let defaultLayout: { [x: string]: { mode: string; ref: string } };

// Update the existing trackers and signals in light of a change to the
// settings system. In particular, this reacts to a change in the setting
// for auto-starting cluster client.
Expand Down Expand Up @@ -356,6 +369,11 @@ async function activate(
hideClusterManager = settings.get('hideClusterManager')
.composite as boolean;
sidebar.clusterManager.setHidden(hideClusterManager);

// Get the default layout
defaultLayout = settings.get('defaultLayout').composite as {
[x: string]: { mode: string; ref: string };
};
};
onSettingsChanged();
// React to a change in the settings.
Expand All @@ -371,14 +389,18 @@ async function activate(

// Add the command for launching a new dashboard item.
app.commands.addCommand(CommandIDs.launchPanel, {
label: args => `Launch Dask ${(args['label'] as string) || ''} Dashboard`,
label: args =>
`Launch Dask ${
((args.item as IDashboardItem)['label'] as string) || ''
} Dashboard`,
caption: 'Launch a Dask dashboard',
execute: args => {
// Construct the url for the dashboard.
const urlInfo = sidebar.dashboardLauncher.input.urlInfo;
const dashboardUrl = urlInfo.effectiveUrl || urlInfo.url;
const active = urlInfo.isActive;
const dashboardItem = args as IDashboardItem;
const dashboardItem = args.item as IDashboardItem;
const addOptions = args?.options as DocumentRegistry.IOpenOptions;

// If we already have a dashboard open to this url, activate it
// but don't create a duplicate.
Expand All @@ -387,7 +409,7 @@ async function activate(
});
if (w) {
if (!w.isAttached) {
labShell.add(w, 'main');
labShell.add(w, 'main', addOptions);
}
labShell.activateById(w.id);
return;
Expand All @@ -402,12 +424,77 @@ async function activate(
dashboard.title.label = `${dashboardItem.label}`;
dashboard.title.icon = 'dask-DaskLogo';

labShell.add(dashboard, 'main');
labShell.add(dashboard, 'main', addOptions);
void tracker.add(dashboard); // no need to wait on this
return dashboard;
}
});

const _normalize_ref = (r: string) => {
if (r.startsWith('/')) {
r = r.slice(1);
}
if (!r.startsWith('individual-')) {
r = 'individual-' + r;
}
return r;
};

app.commands.addCommand(CommandIDs.launchLayout, {
label: 'Launch Dask Dashboard Layout',
caption: 'Launch a pre-configured Dask Dashboard Layout',
isEnabled: () => sidebar.dashboardLauncher.input.urlInfo.isActive,
execute: async () => {
const dashboards = sidebar.dashboardLauncher.items;

// Compute the order that we have to add the panes so that the refs
// exist when we need them.
const dependencies: Array<[string, string]> = [];
for (let k of Object.keys(defaultLayout)) {
dependencies.push([defaultLayout[k].ref || null, k]);
}
const order = topologicSort(dependencies).filter(d => d); // sort and remove nulls

for (let k of order) {
const opts = defaultLayout[k];

const dashboard = dashboards.find(
d => _normalize_ref(d.route) === _normalize_ref(k)
);
if (!dashboard) {
console.warn(`Non-existent dashboard found in Dask layout spec ${k}`);
continue;
}

const options: { mode: string; ref?: string } = { mode: opts.mode };
if (opts.ref) {
const ref = tracker.find(w => {
return !!(
w &&
w.item &&
_normalize_ref(w.item.route) === _normalize_ref(opts.ref)
);
});
if (!ref) {
console.warn(
`Non-existent dashboard found in Dask layout spec ${opts.ref}`
);
options.ref = null;
}
{
options.ref = ref.id;
}
} else {
options.ref = null;
}
await app.commands.execute(CommandIDs.launchPanel, {
item: dashboard,
options: options
});
}
}
});

// Add a command to inject client connection code for a given cluster model.
// This looks for a cluster model in the application context menu,
// and looks for an editor among the currently active notebooks and consoles.
Expand Down Expand Up @@ -480,18 +567,21 @@ async function activate(
});

// Add some commands to the menu and command palette.
mainMenu.fileMenu.addGroup([{ command: CommandIDs.launchLayout }], 50);
mainMenu.settingsMenu.addGroup([
{ command: CommandIDs.toggleAutoStartClient }
]);
[CommandIDs.launchCluster, CommandIDs.toggleAutoStartClient].forEach(
command => {
commandPalette.addItem({
category: 'Dask',
command,
args: { isPalette: true }
});
}
);
[
CommandIDs.launchCluster,
CommandIDs.launchLayout,
CommandIDs.toggleAutoStartClient
].forEach(command => {
commandPalette.addItem({
category: 'Dask',
command,
args: { isPalette: true }
});
});

// Add a context menu items.
app.contextMenu.addItem({
Expand Down
Loading