diff --git a/app/index.js b/app/index.js index 0f219bd87..f654b8908 100644 --- a/app/index.js +++ b/app/index.js @@ -84,6 +84,7 @@ async function main() { let mods = [ // @retrolab plugins require('@retrolab/application-extension'), + require('@retrolab/console-extension'), require('@retrolab/docmanager-extension'), require('@retrolab/help-extension'), require('@retrolab/notebook-extension'), @@ -116,6 +117,7 @@ async function main() { require('@jupyterlab/completer-extension').default.filter(({ id }) => ['@jupyterlab/completer-extension:manager'].includes(id) ), + require('@jupyterlab/console-extension'), require('@jupyterlab/docmanager-extension').default.filter(({ id }) => ['@jupyterlab/docmanager-extension:plugin'].includes(id) ), diff --git a/app/package.json b/app/package.json index 8cc5ec34b..f3c2e89b0 100644 --- a/app/package.json +++ b/app/package.json @@ -93,6 +93,7 @@ "@jupyterlab/celltags": "^3.1.8", "@jupyterlab/codemirror-extension": "^3.1.8", "@jupyterlab/completer-extension": "^3.1.8", + "@jupyterlab/console-extension": "^3.1.8", "@jupyterlab/coreutils": "~5.1.8", "@jupyterlab/docmanager-extension": "^3.1.8", "@jupyterlab/docprovider-extension": "^3.1.8", @@ -150,6 +151,7 @@ "name": "RetroLab", "extensions": [ "@retrolab/application-extension", + "@retrolab/console-extension", "@retrolab/docmanager-extension", "@retrolab/help-extension", "@retrolab/notebook-extension", @@ -159,6 +161,7 @@ "@jupyterlab/apputils-extension", "@jupyterlab/codemirror-extension", "@jupyterlab/completer-extension", + "@jupyterlab/console-extension", "@jupyterlab/docmanager-extension", "@jupyterlab/filebrowser-extension", "@jupyterlab/fileeditor-extension", diff --git a/packages/_metapackage/package.json b/packages/_metapackage/package.json index 1be23dadd..895512c39 100644 --- a/packages/_metapackage/package.json +++ b/packages/_metapackage/package.json @@ -22,6 +22,7 @@ "dependencies": { "@retrolab/application": "file:../application", "@retrolab/application-extension": "file:../application-extension", + "@retrolab/console-extension": "file:../console-extension", "@retrolab/docmanager-extension": "file:../docmanager-extension", "@retrolab/help-extension": "file:../help-extension", "@retrolab/lab-extension": "file:../lab-extension", diff --git a/packages/_metapackage/src/index.ts b/packages/_metapackage/src/index.ts index 3045198f6..fc269fe34 100644 --- a/packages/_metapackage/src/index.ts +++ b/packages/_metapackage/src/index.ts @@ -1,5 +1,6 @@ import '@retrolab/application'; import '@retrolab/application-extension'; +import '@retrolab/console-extension'; import '@retrolab/docmanager-extension'; import '@retrolab/help-extension'; import '@retrolab/lab-extension'; diff --git a/packages/_metapackage/tsconfig.json b/packages/_metapackage/tsconfig.json index ac5fbae66..1dfe5f9a5 100644 --- a/packages/_metapackage/tsconfig.json +++ b/packages/_metapackage/tsconfig.json @@ -8,6 +8,7 @@ "references": [ { "path": "../application" }, { "path": "../application-extension" }, + { "path": "../console-extension" }, { "path": "../docmanager-extension" }, { "path": "../help-extension" }, { "path": "../lab-extension" }, diff --git a/packages/console-extension/package.json b/packages/console-extension/package.json new file mode 100644 index 000000000..5db64c549 --- /dev/null +++ b/packages/console-extension/package.json @@ -0,0 +1,58 @@ +{ + "name": "@retrolab/console-extension", + "version": "0.3.1", + "description": "RetroLab - Console Extension", + "homepage": "https://github.com/jupyterlab/retrolab", + "bugs": { + "url": "https://github.com/jupyterlab/retrolab/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jupyterlab/retrolab.git" + }, + "license": "BSD-3-Clause", + "author": "Project Jupyter", + "sideEffects": [ + "style/**/*.css", + "style/index.js" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "directories": { + "lib": "lib/" + }, + "files": [ + "lib/*.d.ts", + "lib/*.js.map", + "lib/*.js", + "schema/*.json", + "style/**/*.css", + "style/index.js" + ], + "scripts": { + "build": "tsc -b", + "build:prod": "tsc -b", + "clean": "rimraf lib && rimraf tsconfig.tsbuildinfo", + "docs": "typedoc src", + "prepublishOnly": "npm run build", + "watch": "tsc -b --watch" + }, + "dependencies": { + "@jupyterlab/application": "^3.1.8", + "@jupyterlab/console": "^3.1.8", + "@jupyterlab/coreutils": "^5.1.8", + "@lumino/algorithm": "^1.6.0" + }, + "devDependencies": { + "rimraf": "~3.0.0", + "typescript": "~4.1.3" + }, + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "extension": true + }, + "styleModule": "style/index.js" +} diff --git a/packages/console-extension/src/index.ts b/packages/console-extension/src/index.ts new file mode 100644 index 000000000..0e39c34e6 --- /dev/null +++ b/packages/console-extension/src/index.ts @@ -0,0 +1,74 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import { + IRouter, + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { IConsoleTracker } from '@jupyterlab/console'; + +import { PageConfig } from '@jupyterlab/coreutils'; + +import { find } from '@lumino/algorithm'; + +/** + * A plugin to open consoles in a new tab + */ +const opener: JupyterFrontEndPlugin = { + id: '@retrolab/console-extension:opener', + requires: [IRouter], + autoStart: true, + activate: (app: JupyterFrontEnd, router: IRouter) => { + const { commands } = app; + const consolePattern = new RegExp('/consoles/(.*)'); + + const command = 'router:console'; + commands.addCommand(command, { + execute: (args: any) => { + const parsed = args as IRouter.ILocation; + const matches = parsed.path.match(consolePattern); + if (!matches) { + return; + } + const [, name] = matches; + if (!name) { + return; + } + + commands.execute('console:create', { name }); + } + }); + + router.register({ command, pattern: consolePattern }); + } +}; + +/** + * Open consoles in a new tab. + */ +const redirect: JupyterFrontEndPlugin = { + id: '@retrolab/console-extension:redirect', + requires: [IConsoleTracker], + autoStart: true, + activate: (app: JupyterFrontEnd, tracker: IConsoleTracker) => { + const baseUrl = PageConfig.getBaseUrl(); + tracker.widgetAdded.connect((send, console) => { + const widget = find(app.shell.widgets('main'), w => w.id === console.id); + if (widget) { + // bail if the console is already added to the main area + return; + } + const name = console.sessionContext.name; + window.open(`${baseUrl}retro/consoles/${name}`, '_blank'); + }); + } +}; + +/** + * Export the plugins as default. + */ +const plugins: JupyterFrontEndPlugin[] = [opener, redirect]; + +export default plugins; diff --git a/packages/console-extension/style/base.css b/packages/console-extension/style/base.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/console-extension/style/index.css b/packages/console-extension/style/index.css new file mode 100644 index 000000000..f5246e666 --- /dev/null +++ b/packages/console-extension/style/index.css @@ -0,0 +1 @@ +@import url('./base.css'); diff --git a/packages/console-extension/style/index.js b/packages/console-extension/style/index.js new file mode 100644 index 000000000..a028a7640 --- /dev/null +++ b/packages/console-extension/style/index.js @@ -0,0 +1 @@ +import './base.css'; diff --git a/packages/console-extension/tsconfig.json b/packages/console-extension/tsconfig.json new file mode 100644 index 000000000..399b75b7a --- /dev/null +++ b/packages/console-extension/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*"] +} diff --git a/retrolab/app.py b/retrolab/app.py index f287742b6..56b50f909 100644 --- a/retrolab/app.py +++ b/retrolab/app.py @@ -130,6 +130,13 @@ class RetroTreeHandler(RetroHandler): raise web.HTTPError(404) +class RetroConsoleHandler(RetroHandler): + @web.authenticated + def get(self, path=None): + tpl = self.render_template("consoles.html", page_config=self.get_page_config()) + return self.write(tpl) + + class RetroTerminalHandler(RetroHandler): @web.authenticated def get(self, path=None): @@ -203,6 +210,7 @@ class RetroApp(NBClassicConfigShimMixin, LabServerApp): self.handlers.append(("/retro/tree(.*)", RetroTreeHandler)) self.handlers.append(("/retro/notebooks(.*)", RetroNotebookHandler)) self.handlers.append(("/retro/edit(.*)", RetroFileHandler)) + self.handlers.append(("/retro/consoles/(.*)", RetroConsoleHandler)) self.handlers.append(("/retro/terminals/(.*)", RetroTerminalHandler)) super().initialize_handlers() diff --git a/retrolab/templates/consoles.html b/retrolab/templates/consoles.html new file mode 100644 index 000000000..9fa6115bf --- /dev/null +++ b/retrolab/templates/consoles.html @@ -0,0 +1,39 @@ + + + + + + {{page_config['appName'] | e}} - Console + {% block favicon %} + + {% endblock %} + + + + {# Copy so we do not modify the page_config with updates. #} + {% set page_config_full = page_config.copy() %} + + {# Set a dummy variable - we just want the side effect of the update. #} + {% set _ = page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} + + {# Sentinel value to say that we are on the tree page #} + {% set _ = page_config_full.update(retroPage='consoles') %} + + + + + + + + diff --git a/yarn.lock b/yarn.lock index 6409ca816..f24328c34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1824,6 +1824,29 @@ "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" +"@jupyterlab/console-extension@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/console-extension/-/console-extension-3.1.8.tgz#877e14a4e4edeabd3c6ece08219123e5bf81ef98" + integrity sha512-04g8eXzvWCSg8kdu0Dy7z1GEpgEWYv+w7v204u8vTbYiz6nDqR+Mdiox415egCCuObAaMwG6knAq/WsvQai/aQ== + dependencies: + "@jupyterlab/application" "^3.1.8" + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/codeeditor" "^3.1.8" + "@jupyterlab/console" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/filebrowser" "^3.1.8" + "@jupyterlab/launcher" "^3.1.8" + "@jupyterlab/mainmenu" "^3.1.8" + "@jupyterlab/rendermime" "^3.1.8" + "@jupyterlab/settingregistry" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/widgets" "^1.19.0" + "@jupyterlab/console@^3.1.8": version "3.1.8" resolved "https://registry.yarnpkg.com/@jupyterlab/console/-/console-3.1.8.tgz#efa64e994d0128b8a9f8b1234057b9bc2b312046" @@ -3593,7 +3616,7 @@ integrity sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ== "@retrolab/application-extension@file:packages/application-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/apputils" "^3.1.8" @@ -3607,11 +3630,11 @@ "@jupyterlab/settingregistry" "^3.1.8" "@jupyterlab/translation" "^3.1.8" "@lumino/widgets" "^1.23.0" - "@retrolab/application" "^0.3.0" - "@retrolab/ui-components" "^0.3.0" + "@retrolab/application" "^0.3.1" + "@retrolab/ui-components" "^0.3.1" "@retrolab/application@file:packages/application": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/coreutils" "^5.1.8" @@ -3625,8 +3648,16 @@ "@lumino/signaling" "^1.7.0" "@lumino/widgets" "^1.23.0" +"@retrolab/console-extension@file:packages/console-extension": + version "0.3.1" + dependencies: + "@jupyterlab/application" "^3.1.8" + "@jupyterlab/console" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@lumino/algorithm" "^1.6.0" + "@retrolab/docmanager-extension@file:packages/docmanager-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/coreutils" "^5.1.8" @@ -3636,15 +3667,15 @@ "@lumino/algorithm" "^1.6.0" "@retrolab/help-extension@file:packages/help-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/apputils" "^3.1.8" "@jupyterlab/mainmenu" "^3.1.8" - "@retrolab/ui-components" "^0.3.0" + "@retrolab/ui-components" "^0.3.1" "@retrolab/lab-extension@file:packages/lab-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/apputils" "^3.1.8" @@ -3654,10 +3685,10 @@ "@jupyterlab/notebook" "^3.1.8" "@lumino/commands" "^1.15.0" "@lumino/disposable" "^1.7.0" - "@retrolab/ui-components" "^0.3.0" + "@retrolab/ui-components" "^0.3.1" "@retrolab/notebook-extension@file:packages/notebook-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/apputils" "^3.1.8" @@ -3665,10 +3696,10 @@ "@jupyterlab/notebook" "^3.1.8" "@lumino/polling" "^1.6.0" "@lumino/widgets" "^1.23.0" - "@retrolab/application" "^0.3.0" + "@retrolab/application" "^0.3.1" "@retrolab/terminal-extension@file:packages/terminal-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/coreutils" "^5.1.8" @@ -3676,7 +3707,7 @@ "@lumino/algorithm" "^1.6.0" "@retrolab/tree-extension@file:packages/tree-extension": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/application" "^3.1.8" "@jupyterlab/apputils" "^3.1.8" @@ -3692,10 +3723,10 @@ "@lumino/algorithm" "^1.6.0" "@lumino/commands" "^1.15.0" "@lumino/widgets" "^1.23.0" - "@retrolab/application" "^0.3.0" + "@retrolab/application" "^0.3.1" "@retrolab/ui-components@file:packages/ui-components": - version "0.3.0" + version "0.3.1" dependencies: "@jupyterlab/ui-components" "^3.1.8" react "^17.0.1"