diff --git a/builder/index.js b/builder/index.js index b6c2a20b9..b25419f93 100644 --- a/builder/index.js +++ b/builder/index.js @@ -68,7 +68,11 @@ async function main() { // TODO: formalize the way the set of initial extensions and plugins are specified let mods = [ + // @jupyterlab-classic plugins require('@jupyterlab-classic/application-extension'), + require('@jupyterlab-classic/notebook-extension'), + + // @jupyterlab plugins require('@jupyterlab/apputils-extension').default.filter(({ id }) => [ '@jupyterlab/apputils-extension:palette', diff --git a/builder/package.json b/builder/package.json index f863269b2..11036c5d5 100644 --- a/builder/package.json +++ b/builder/package.json @@ -13,6 +13,7 @@ "@jupyterlab-classic/application": "^0.1.0", "@jupyterlab-classic/application-extension": "^0.1.0", "@jupyterlab-classic/filebrowser-extension": "^0.1.0", + "@jupyterlab-classic/notebook-extension": "^0.1.0", "@jupyterlab-classic/ui-components": "^0.1.0", "@jupyterlab/apputils-extension": "^3.0.0-rc.12", "@jupyterlab/codemirror-extension": "^3.0.0-rc.12", diff --git a/builder/style.css b/builder/style.css index 3a3c0da66..7a8664963 100644 --- a/builder/style.css +++ b/builder/style.css @@ -1,5 +1,6 @@ @import url('~@jupyterlab-classic/application-extension/style/index.css'); @import url('~@jupyterlab-classic/filebrowser-extension/style/index.css'); +@import url('~@jupyterlab-classic/notebook-extension/style/index.css'); @import url('~@jupyterlab-classic/ui-components/style/index.css'); /* TODO: check is the the extension package can be used directly */ diff --git a/packages/application-extension/package.json b/packages/application-extension/package.json index c33d3d32c..7220c9f1b 100644 --- a/packages/application-extension/package.json +++ b/packages/application-extension/package.json @@ -36,16 +36,14 @@ "watch": "tsc -b --watch" }, "dependencies": { - "@jupyterlab-classic/application": "0.1.0", - "@jupyterlab-classic/ui-components": "0.1.0", + "@jupyterlab-classic/application": "^0.1.0", + "@jupyterlab-classic/ui-components": "^0.1.0", "@jupyterlab/application": "^3.0.0-rc.12", "@jupyterlab/apputils": "^3.0.0-rc.12", "@jupyterlab/codeeditor": "^3.0.0-rc.12", "@jupyterlab/codemirror": "^3.0.0-rc.12", "@jupyterlab/docregistry": "^3.0.0-rc.12", - "@jupyterlab/docmanager": "^3.0.0-rc.12", "@jupyterlab/mainmenu": "^3.0.0-rc.12", - "@jupyterlab/notebook": "^3.0.0-rc.12", "@jupyterlab/settingregistry": "^3.0.0-rc.12", "@jupyterlab/translation": "^3.0.0-rc.12", "@lumino/widgets": "^1.14.0" diff --git a/packages/application-extension/src/index.ts b/packages/application-extension/src/index.ts index 2a4525959..94aa79d14 100644 --- a/packages/application-extension/src/index.ts +++ b/packages/application-extension/src/index.ts @@ -11,19 +11,12 @@ import { import { sessionContextDialogs, ISessionContextDialogs, - ISessionContext, DOMUtils, ICommandPalette } from '@jupyterlab/apputils'; -import { PageConfig, Text, Time } from '@jupyterlab/coreutils'; - -import { IDocumentManager, renameDialog } from '@jupyterlab/docmanager'; - import { IMainMenu } from '@jupyterlab/mainmenu'; -import { NotebookPanel } from '@jupyterlab/notebook'; - import { ITranslator, TranslationManager } from '@jupyterlab/translation'; import { @@ -41,26 +34,6 @@ import { Widget } from '@lumino/widgets'; */ const NOTEBOOK_FACTORY = 'Notebook'; -/** - * The class for kernel status errors. - */ -const KERNEL_STATUS_ERROR_CLASS = 'jp-ClassicKernelStatus-error'; - -/** - * The class for kernel status warnings. - */ -const KERNEL_STATUS_WARN_CLASS = 'jp-ClassicKernelStatus-warn'; - -/** - * The class for kernel status infos. - */ -const KERNEL_STATUS_INFO_CLASS = 'jp-ClassicKernelStatus-info'; - -/** - * The class to fade out the kernel status. - */ -const KERNEL_STATUS_FADE_OUT_CLASS = 'jp-ClassicKernelStatus-fade'; - /** * The command IDs used by the application plugin. */ @@ -76,168 +49,6 @@ namespace CommandIDs { export const toggleZen = 'application:toggle-zen'; } -/** - * A plugin for the checkpoint indicator - */ -const checkpoints: JupyterFrontEndPlugin = { - id: '@jupyterlab-classic/application-extension:checkpoints', - autoStart: true, - requires: [IDocumentManager], - optional: [IClassicShell], - activate: ( - app: JupyterFrontEnd, - docManager: IDocumentManager, - classicShell: IClassicShell - ) => { - const { shell } = app; - const widget = new Widget(); - widget.id = DOMUtils.createDomID(); - widget.addClass('jp-ClassicCheckpoint'); - app.shell.add(widget, 'top', { rank: 100 }); - - const onChange = async () => { - const current = shell.currentWidget; - if (!current) { - return; - } - const context = docManager.contextForWidget(current); - - context?.fileChanged.disconnect(onChange); - context?.fileChanged.connect(onChange); - - const checkpoints = await context?.listCheckpoints(); - if (!checkpoints) { - return; - } - const checkpoint = checkpoints[checkpoints.length - 1]; - widget.node.textContent = `Last Checkpoint: ${Time.formatHuman( - new Date(checkpoint.last_modified) - )}`; - }; - - if (classicShell) { - classicShell.currentChanged.connect(onChange); - } - // TODO: replace by a Poll - onChange(); - setInterval(onChange, 2000); - } -}; - -/** - * The kernel logo plugin. - */ -const kernelLogo: JupyterFrontEndPlugin = { - id: '@jupyterlab-classic/application-extension:kernel-logo', - autoStart: true, - requires: [IClassicShell], - activate: (app: JupyterFrontEnd, shell: IClassicShell) => { - const { serviceManager } = app; - const baseUrl = PageConfig.getBaseUrl(); - - let widget: Widget; - // TODO: this signal might not be needed if we assume there is always only - // one notebook in the main area - const onChange = async () => { - if (widget) { - widget.dispose(); - widget.parent = null; - } - const current = shell.currentWidget; - if (!(current instanceof NotebookPanel)) { - return; - } - - await current.sessionContext.ready; - current.sessionContext.kernelChanged.disconnect(onChange); - current.sessionContext.kernelChanged.connect(onChange); - - const name = current.sessionContext.session?.kernel?.name ?? ''; - const spec = serviceManager.kernelspecs?.specs?.kernelspecs[name]; - if (!spec) { - return; - } - - let kernelIconUrl = spec.resources['logo-64x64']; - if (!kernelIconUrl) { - return; - } - - const index = kernelIconUrl.indexOf('kernelspecs'); - kernelIconUrl = baseUrl + kernelIconUrl.slice(index); - const node = document.createElement('div'); - const img = document.createElement('img'); - img.src = kernelIconUrl; - img.title = spec.display_name; - node.appendChild(img); - widget = new Widget({ node }); - widget.addClass('jp-ClassicKernelLogo'); - app.shell.add(widget, 'top', { rank: 10_010 }); - }; - - shell.currentChanged.connect(onChange); - } -}; - -/** - * A plugin to display the kernel status; - */ -const kernelStatus: JupyterFrontEndPlugin = { - id: '@jupyterlab-classic/application-extension:kernel-status', - autoStart: true, - requires: [IClassicShell], - activate: (app: JupyterFrontEnd, shell: IClassicShell) => { - const widget = new Widget(); - widget.addClass('jp-ClassicKernelStatus'); - app.shell.add(widget, 'menu', { rank: 10_010 }); - - const removeClasses = () => { - widget.removeClass(KERNEL_STATUS_ERROR_CLASS); - widget.removeClass(KERNEL_STATUS_WARN_CLASS); - widget.removeClass(KERNEL_STATUS_INFO_CLASS); - widget.removeClass(KERNEL_STATUS_FADE_OUT_CLASS); - }; - - const onStatusChanged = (sessionContext: ISessionContext) => { - const status = sessionContext.kernelDisplayStatus; - let text = `Kernel ${Text.titleCase(status)}`; - removeClasses(); - switch (status) { - case 'busy': - case 'idle': - text = ''; - widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS); - break; - case 'dead': - case 'terminating': - widget.addClass(KERNEL_STATUS_ERROR_CLASS); - break; - case 'unknown': - widget.addClass(KERNEL_STATUS_WARN_CLASS); - break; - default: - widget.addClass(KERNEL_STATUS_INFO_CLASS); - widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS); - break; - } - widget.node.textContent = text; - }; - - // TODO: this signal might not be needed if we assume there is always only - // one notebook in the main area - const onChange = async () => { - const current = shell.currentWidget; - if (!(current instanceof NotebookPanel)) { - return; - } - const sessionContext = current.sessionContext; - sessionContext.statusChanged.connect(onStatusChanged); - }; - - shell.currentChanged.connect(onChange); - } -}; - /** * A plugin to dispose the Tabs menu */ @@ -270,17 +81,6 @@ const logo: JupyterFrontEndPlugin = { } }; -/** - * The main plugin. - */ -const main: JupyterFrontEndPlugin = { - id: '@jupyterlab-classic/application-extension:main', - autoStart: true, - activate: () => { - console.log(main.id, 'activated'); - } -}; - /** * The default paths for a JupyterLab Classic app. */ @@ -365,56 +165,6 @@ const spacer: JupyterFrontEndPlugin = { } }; -/** - * A plugin to display the title of the notebook - */ -const title: JupyterFrontEndPlugin = { - id: '@jupyterlab-classic/application-extension:title', - autoStart: true, - requires: [IClassicShell], - optional: [IDocumentManager, IRouter], - activate: ( - app: JupyterFrontEnd, - shell: IClassicShell, - docManager: IDocumentManager | null, - router: IRouter | null - ) => { - // TODO: this signal might not be needed if we assume there is always only - // one notebook in the main area - const widget = new Widget(); - widget.id = 'jp-title'; - app.shell.add(widget, 'top', { rank: 10 }); - - shell.currentChanged.connect(async () => { - const current = shell.currentWidget; - if (!(current instanceof NotebookPanel)) { - return; - } - const h = document.createElement('h1'); - h.textContent = current.title.label; - widget.node.appendChild(h); - widget.node.style.marginLeft = '10px'; - if (docManager) { - widget.node.onclick = async () => { - const result = await renameDialog( - docManager, - current.sessionContext.path - ); - if (result) { - h.textContent = result.path; - if (router) { - // TODO: better handle this - router.navigate(`/classic/notebooks/${result.path}`, { - skipRouting: true - }); - } - } - }; - } - }); - } -}; - /** * Plugin to toggle the top header visibility. */ @@ -555,18 +305,13 @@ const zen: JupyterFrontEndPlugin = { * Export the plugins as default. */ const plugins: JupyterFrontEndPlugin[] = [ - checkpoints, - kernelLogo, - kernelStatus, logo, - main, noTabsMenu, paths, router, sessionDialogs, shell, spacer, - title, topVisibility, translator, tree, diff --git a/packages/application-extension/style/base.css b/packages/application-extension/style/base.css index fac71eb20..70f46ed68 100644 --- a/packages/application-extension/style/base.css +++ b/packages/application-extension/style/base.css @@ -8,77 +8,3 @@ flex-grow: 1; flex-shrink: 1; } - -.jp-ClassicKernelLogo { - flex: 0 0 auto; - display: flex; - align-items: center; - text-align: center; - margin-right: 8px; -} - -.jp-ClassicKernelLogo img { - max-width: 28px; - max-height: 28px; - display: flex; -} - -.jp-ClassicKernelStatus { - font-size: 12px; - margin: 0; - font-weight: normal; - color: var(--jp-ui-font-color0); - font-family: var(--jp-ui-font-family); - line-height: var(--jp-private-title-panel-height); - padding-left: 5px; - padding-right: 5px; -} - -.jp-ClassicKernelStatus-error { - background-color: var(--jp-error-color0); -} - -.jp-ClassicKernelStatus-warn { - background-color: var(--jp-warn-color0); -} - -.jp-ClassicKernelStatus-info { - background-color: var(--jp-info-color0); -} - -.jp-ClassicKernelStatus-fade { - animation: 0.5s fade-out forwards; -} - -@keyframes fade-out { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - -#jp-title h1 { - cursor: pointer; - font-size: 18px; - margin: 0; - font-weight: normal; - color: var(--jp-ui-font-color0); - font-family: var(--jp-ui-font-family); - line-height: calc(1.5 * var(--jp-private-title-panel-height)); -} - -#jp-title h1:hover { - background: var(--jp-layout-color2); -} - -.jp-ClassicCheckpoint { - font-size: 14px; - margin-left: 5px; - margin-right: 5px; - font-weight: normal; - color: var(--jp-ui-font-color0); - font-family: var(--jp-ui-font-family); - line-height: calc(1.5 * var(--jp-private-title-panel-height)); -} diff --git a/packages/filebrowser-extension/package.json b/packages/filebrowser-extension/package.json index 0ba9c4ccc..7f018fa7b 100644 --- a/packages/filebrowser-extension/package.json +++ b/packages/filebrowser-extension/package.json @@ -36,7 +36,7 @@ "watch": "tsc -b --watch" }, "dependencies": { - "@jupyterlab-classic/application": "0.1.0", + "@jupyterlab-classic/application": "^0.1.0", "@jupyterlab/application": "^3.0.0-rc.13", "@jupyterlab/apputils": "^3.0.0-rc.13", "@jupyterlab/coreutils": "^5.0.0-rc.13", diff --git a/packages/notebook-extension/package.json b/packages/notebook-extension/package.json new file mode 100644 index 000000000..c649fd008 --- /dev/null +++ b/packages/notebook-extension/package.json @@ -0,0 +1,56 @@ +{ + "name": "@jupyterlab-classic/notebook-extension", + "version": "0.1.0", + "description": "JupyterLab Classic - Notebook Extension", + "homepage": "https://github.com/jtpio/jupyterlab-classic", + "bugs": { + "url": "https://github.com/jtpio/jupyterlab-classic/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jtpio/jupyterlab-classic.git" + }, + "license": "BSD-3-Clause", + "author": "Project Jupyter", + "sideEffects": [ + "style/**/*.css" + ], + "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" + ], + "scripts": { + "build": "tsc -b", + "clean": "rimraf lib && rimraf tsconfig.tsbuildinfo", + "docs": "typedoc src", + "prepublishOnly": "npm run build", + "watch": "tsc -b --watch" + }, + "dependencies": { + "@jupyterlab-classic/application": "^0.1.0", + "@jupyterlab/application": "^3.0.0-rc.12", + "@jupyterlab/apputils": "^3.0.0-rc.12", + "@jupyterlab/docmanager": "^3.0.0-rc.12", + "@jupyterlab/notebook": "^3.0.0-rc.12", + "@lumino/widgets": "^1.14.0" + }, + "devDependencies": { + "rimraf": "~3.0.0", + "typescript": "~4.0.2" + }, + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "extension": true + } +} diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts new file mode 100644 index 000000000..2b652c964 --- /dev/null +++ b/packages/notebook-extension/src/index.ts @@ -0,0 +1,298 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import { + IRouter, + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { ISessionContext, DOMUtils } from '@jupyterlab/apputils'; + +import { PageConfig, Text, Time } from '@jupyterlab/coreutils'; + +import { IDocumentManager, renameDialog } from '@jupyterlab/docmanager'; + +import { NotebookPanel } from '@jupyterlab/notebook'; + +import { + App, + ClassicShell, + IClassicShell +} from '@jupyterlab-classic/application'; + +import { Widget } from '@lumino/widgets'; + +/** + * The class for kernel status errors. + */ +const KERNEL_STATUS_ERROR_CLASS = 'jp-ClassicKernelStatus-error'; + +/** + * The class for kernel status warnings. + */ +const KERNEL_STATUS_WARN_CLASS = 'jp-ClassicKernelStatus-warn'; + +/** + * The class for kernel status infos. + */ +const KERNEL_STATUS_INFO_CLASS = 'jp-ClassicKernelStatus-info'; + +/** + * The class to fade out the kernel status. + */ +const KERNEL_STATUS_FADE_OUT_CLASS = 'jp-ClassicKernelStatus-fade'; + +/** + * A plugin for the checkpoint indicator + */ +const checkpoints: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:checkpoints', + autoStart: true, + requires: [IDocumentManager], + optional: [IClassicShell], + activate: ( + app: JupyterFrontEnd, + docManager: IDocumentManager, + classicShell: IClassicShell + ) => { + const { shell } = app; + const widget = new Widget(); + widget.id = DOMUtils.createDomID(); + widget.addClass('jp-ClassicCheckpoint'); + app.shell.add(widget, 'top', { rank: 100 }); + + const onChange = async () => { + const current = shell.currentWidget; + if (!current) { + return; + } + const context = docManager.contextForWidget(current); + + context?.fileChanged.disconnect(onChange); + context?.fileChanged.connect(onChange); + + const checkpoints = await context?.listCheckpoints(); + if (!checkpoints) { + return; + } + const checkpoint = checkpoints[checkpoints.length - 1]; + widget.node.textContent = `Last Checkpoint: ${Time.formatHuman( + new Date(checkpoint.last_modified) + )}`; + }; + + if (classicShell) { + classicShell.currentChanged.connect(onChange); + } + // TODO: replace by a Poll + onChange(); + setInterval(onChange, 2000); + } +}; + +/** + * The kernel logo plugin. + */ +const kernelLogo: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:kernel-logo', + autoStart: true, + requires: [IClassicShell], + activate: (app: JupyterFrontEnd, shell: IClassicShell) => { + const { serviceManager } = app; + const baseUrl = PageConfig.getBaseUrl(); + + let widget: Widget; + // TODO: this signal might not be needed if we assume there is always only + // one notebook in the main area + const onChange = async () => { + if (widget) { + widget.dispose(); + widget.parent = null; + } + const current = shell.currentWidget; + if (!(current instanceof NotebookPanel)) { + return; + } + + await current.sessionContext.ready; + current.sessionContext.kernelChanged.disconnect(onChange); + current.sessionContext.kernelChanged.connect(onChange); + + const name = current.sessionContext.session?.kernel?.name ?? ''; + const spec = serviceManager.kernelspecs?.specs?.kernelspecs[name]; + if (!spec) { + return; + } + + let kernelIconUrl = spec.resources['logo-64x64']; + if (!kernelIconUrl) { + return; + } + + const index = kernelIconUrl.indexOf('kernelspecs'); + kernelIconUrl = baseUrl + kernelIconUrl.slice(index); + const node = document.createElement('div'); + const img = document.createElement('img'); + img.src = kernelIconUrl; + img.title = spec.display_name; + node.appendChild(img); + widget = new Widget({ node }); + widget.addClass('jp-ClassicKernelLogo'); + app.shell.add(widget, 'top', { rank: 10_010 }); + }; + + shell.currentChanged.connect(onChange); + } +}; + +/** + * A plugin to display the kernel status; + */ +const kernelStatus: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:kernel-status', + autoStart: true, + requires: [IClassicShell], + activate: (app: JupyterFrontEnd, shell: IClassicShell) => { + const widget = new Widget(); + widget.addClass('jp-ClassicKernelStatus'); + app.shell.add(widget, 'menu', { rank: 10_010 }); + + const removeClasses = () => { + widget.removeClass(KERNEL_STATUS_ERROR_CLASS); + widget.removeClass(KERNEL_STATUS_WARN_CLASS); + widget.removeClass(KERNEL_STATUS_INFO_CLASS); + widget.removeClass(KERNEL_STATUS_FADE_OUT_CLASS); + }; + + const onStatusChanged = (sessionContext: ISessionContext) => { + const status = sessionContext.kernelDisplayStatus; + let text = `Kernel ${Text.titleCase(status)}`; + removeClasses(); + switch (status) { + case 'busy': + case 'idle': + text = ''; + widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS); + break; + case 'dead': + case 'terminating': + widget.addClass(KERNEL_STATUS_ERROR_CLASS); + break; + case 'unknown': + widget.addClass(KERNEL_STATUS_WARN_CLASS); + break; + default: + widget.addClass(KERNEL_STATUS_INFO_CLASS); + widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS); + break; + } + widget.node.textContent = text; + }; + + // TODO: this signal might not be needed if we assume there is always only + // one notebook in the main area + const onChange = async () => { + const current = shell.currentWidget; + if (!(current instanceof NotebookPanel)) { + return; + } + const sessionContext = current.sessionContext; + sessionContext.statusChanged.connect(onStatusChanged); + }; + + shell.currentChanged.connect(onChange); + } +}; + +/** + * The default paths for a JupyterLab Classic app. + */ +const paths: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:paths', + activate: (app: JupyterFrontEnd): JupyterFrontEnd.IPaths => { + if (!(app instanceof App)) { + throw new Error(`${paths.id} must be activated in JupyterLab Classic.`); + } + return app.paths; + }, + autoStart: true, + provides: JupyterFrontEnd.IPaths +}; + +/** + * The default JupyterLab Classic application shell. + */ +const shell: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:shell', + activate: (app: JupyterFrontEnd) => { + if (!(app.shell instanceof ClassicShell)) { + throw new Error(`${shell.id} did not find a ClassicShell instance.`); + } + return app.shell; + }, + autoStart: true, + provides: IClassicShell +}; + +/** + * A plugin to display the title of the notebook + */ +const title: JupyterFrontEndPlugin = { + id: '@jupyterlab-classic/application-extension:title', + autoStart: true, + requires: [IClassicShell], + optional: [IDocumentManager, IRouter], + activate: ( + app: JupyterFrontEnd, + shell: IClassicShell, + docManager: IDocumentManager | null, + router: IRouter | null + ) => { + // TODO: this signal might not be needed if we assume there is always only + // one notebook in the main area + const widget = new Widget(); + widget.id = 'jp-title'; + app.shell.add(widget, 'top', { rank: 10 }); + + shell.currentChanged.connect(async () => { + const current = shell.currentWidget; + if (!(current instanceof NotebookPanel)) { + return; + } + const h = document.createElement('h1'); + h.textContent = current.title.label; + widget.node.appendChild(h); + widget.node.style.marginLeft = '10px'; + if (docManager) { + widget.node.onclick = async () => { + const result = await renameDialog( + docManager, + current.sessionContext.path + ); + if (result) { + h.textContent = result.path; + if (router) { + // TODO: better handle this + router.navigate(`/classic/notebooks/${result.path}`, { + skipRouting: true + }); + } + } + }; + } + }); + } +}; + +/** + * Export the plugins as default. + */ +const plugins: JupyterFrontEndPlugin[] = [ + checkpoints, + kernelLogo, + kernelStatus, + title +]; + +export default plugins; diff --git a/packages/notebook-extension/style/base.css b/packages/notebook-extension/style/base.css new file mode 100644 index 000000000..80215fcd2 --- /dev/null +++ b/packages/notebook-extension/style/base.css @@ -0,0 +1,79 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +.jp-ClassicKernelLogo { + flex: 0 0 auto; + display: flex; + align-items: center; + text-align: center; + margin-right: 8px; +} + +.jp-ClassicKernelLogo img { + max-width: 28px; + max-height: 28px; + display: flex; +} + +.jp-ClassicKernelStatus { + font-size: 12px; + margin: 0; + font-weight: normal; + color: var(--jp-ui-font-color0); + font-family: var(--jp-ui-font-family); + line-height: var(--jp-private-title-panel-height); + padding-left: 5px; + padding-right: 5px; +} + +.jp-ClassicKernelStatus-error { + background-color: var(--jp-error-color0); +} + +.jp-ClassicKernelStatus-warn { + background-color: var(--jp-warn-color0); +} + +.jp-ClassicKernelStatus-info { + background-color: var(--jp-info-color0); +} + +.jp-ClassicKernelStatus-fade { + animation: 0.5s fade-out forwards; +} + +@keyframes fade-out { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +#jp-title h1 { + cursor: pointer; + font-size: 18px; + margin: 0; + font-weight: normal; + color: var(--jp-ui-font-color0); + font-family: var(--jp-ui-font-family); + line-height: calc(1.5 * var(--jp-private-title-panel-height)); +} + +#jp-title h1:hover { + background: var(--jp-layout-color2); +} + +.jp-ClassicCheckpoint { + font-size: 14px; + margin-left: 5px; + margin-right: 5px; + font-weight: normal; + color: var(--jp-ui-font-color0); + font-family: var(--jp-ui-font-family); + line-height: calc(1.5 * var(--jp-private-title-panel-height)); +} diff --git a/packages/notebook-extension/style/index.css b/packages/notebook-extension/style/index.css new file mode 100644 index 000000000..f5246e666 --- /dev/null +++ b/packages/notebook-extension/style/index.css @@ -0,0 +1 @@ +@import url('./base.css'); diff --git a/packages/notebook-extension/tsconfig.json b/packages/notebook-extension/tsconfig.json new file mode 100644 index 000000000..b223e1a1b --- /dev/null +++ b/packages/notebook-extension/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*"], + "references": [ + { + "path": "../application" + } + ] +}