diff --git a/package.json b/package.json index 08aec2e..f8fd3a7 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "files": [ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", - "src/**/*.{ts,tsx}" + "src/**/*.{ts,tsx}", + "schema/*.json" ], "main": "lib/index.js", "types": "lib/index.d.ts", @@ -99,7 +100,8 @@ "@jupyterlab/application-extension:logo" ], "extension": true, - "outputDir": "jupyterlab_nebari_mode/labextension" + "outputDir": "jupyterlab_nebari_mode/labextension", + "schemaDir": "schema" }, "eslintIgnore": [ "node_modules", diff --git a/schema/jhub-apps-integration.json b/schema/jhub-apps-integration.json new file mode 100644 index 0000000..4fd9425 --- /dev/null +++ b/schema/jhub-apps-integration.json @@ -0,0 +1,39 @@ +{ + "jupyter.lab.menus": { + "main": [ + { + "id": "jp-mainmenu-services", + "label": "Services", + "rank": 1000, + "items": [ + { + "command": "jhub-apps:deploy-app", + "args": { + "origin": "main-menu" + } + } + ] + } + ], + "context": [ + { + "command": "jhub-apps:deploy-app", + "args": { + "origin": "context-menu" + }, + "selector": ".jp-DirListing-item[data-isdir=\"false\"]", + "rank": 3 + } + ] + }, + "jupyter.lab.toolbars": { + "Notebook": [ + { + "name": "deploy-app", + "command": "jhub-apps:deploy-app" + } + ] + }, + "additionalProperties": false, + "type": "object" +} diff --git a/src/icons.ts b/src/icons.ts index 3388566..a312370 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,7 +1,13 @@ import { LabIcon } from '@jupyterlab/ui-components'; import nebariLogo from '../style/icons/nebari-logo.svg'; +import deployApp from '../style/icons/deploy-app.svg'; export const nebariIcon = new LabIcon({ name: 'nebari:logo', svgstr: nebariLogo }); + +export const deployAppIcon = new LabIcon({ + name: 'jhub-apps:deploy-app', + svgstr: deployApp +}); diff --git a/src/index.ts b/src/index.ts index ef1da62..58ac5f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,8 @@ import { ServerConnection } from '@jupyterlab/services'; import { PageConfig, URLExt } from '@jupyterlab/coreutils'; import { Widget } from '@lumino/widgets'; import { CommandRegistry } from '@lumino/commands'; -import { nebariIcon } from './icons'; +import { nebariIcon, deployAppIcon } from './icons'; +import { DocumentWidget } from '@jupyterlab/docregistry'; class NebariLogo extends Widget { constructor(options: { paths: JupyterFrontEnd.IPaths }) { @@ -60,6 +61,10 @@ namespace CommandIDs { * but assumes all properties of the first enabled command. */ export const runFirstEnabled = 'nebari:run-first-enabled'; + /** + * Opens the URL to deploy the application with pre-populated fields + */ + export const deployApp = 'jhub-apps:deploy-app'; } interface IOpenProxyArgs { @@ -88,6 +93,10 @@ interface IRunFirstEnabledArgs { commands?: ICommandDescription[]; } +interface IDeployAppArgs { + origin?: string; +} + const commandsPlugin: JupyterFrontEndPlugin = { id: 'jupyterlab-nebari-mode:commands', description: 'Adds additional commands used by nebari.', @@ -212,6 +221,52 @@ const logoPlugin: JupyterFrontEndPlugin = { } }; -const plugins = [commandsPlugin, logoPlugin]; +const jhubAppsPlugin: JupyterFrontEndPlugin = { + id: 'jupyterlab-nebari-mode:jhub-apps-integration', + description: 'Deploys an application with (almost) one click.', + autoStart: true, + requires: [], + activate: async (app: JupyterFrontEnd) => { + const openURL = async (url: string) => { + try { + window.open(url, '_blank', 'noopener,noreferrer'); + } catch (error) { + console.warn(`Error opening ${url}: ${error}`); + } + }; + + const calculateIcon = (args: IDeployAppArgs) => { + switch (args.origin) { + case 'main-menu': + return undefined; + case 'context-menu': + return deployAppIcon; + case undefined: + return deployAppIcon; + default: + return deployAppIcon; + } + }; + + app.commands.addCommand(CommandIDs.deployApp, { + execute: async () => { + const currentWidget = app.shell.currentWidget; + const currentNotebookPath = + currentWidget && currentWidget instanceof DocumentWidget + ? currentWidget.context.path + : ''; + const deployUrl = `/services/japps/create-app?filepath=${encodeURIComponent(currentNotebookPath)}`; + + await openURL(deployUrl); + }, + label: 'Deploy App', + icon: args => { + return calculateIcon(args); + } + }); + } +}; + +const plugins = [commandsPlugin, logoPlugin, jhubAppsPlugin]; export default plugins; diff --git a/style/icons/deploy-app.svg b/style/icons/deploy-app.svg new file mode 100644 index 0000000..55bdb64 --- /dev/null +++ b/style/icons/deploy-app.svg @@ -0,0 +1,3 @@ + + +