# 二次开发代码改动说明 二次开发的代码修改基于 JupyterLab v3.4.3 版本进行. 这里主要针对代码部分的 diff 进行说明, 省略了 eslint 等的配置改动, 以及文档相关改动. 使用如下命令查看改动: ```bash git diff v3.4.3 HEAD packages ``` - 替换 JupyterLab 自带的 Logo 为自定义 Logo ```diff diff --git a/packages/application-extension/src/index.tsx b/packages/application-extension/src/index.tsx index 5da68f494d..403607bd1a 100644 --- a/packages/application-extension/src/index.tsx +++ b/packages/application-extension/src/index.tsx @@ -40,7 +40,8 @@ import { ITranslator, TranslationBundle } from '@jupyterlab/translation'; import { buildIcon, ContextMenuSvg, - jupyterIcon, + // jupyterIcon, + LabIcon, RankedMenu } from '@jupyterlab/ui-components'; import { each, iter, toArray } from '@lumino/algorithm'; @@ -48,6 +49,12 @@ import { JSONExt, PromiseDelegate } from '@lumino/coreutils'; import { DisposableDelegate, DisposableSet } from '@lumino/disposable'; import { DockLayout, DockPanel, Widget } from '@lumino/widgets'; import * as React from 'react'; +import yiliLogoSvgStr from '../style/icons/YiliLogo.svg'; + +const yiliLogoIcon = new LabIcon({ + name: 'yili-logo', + svgstr: yiliLogoSvgStr +}); /** * Default context menu item rank @@ -941,14 +948,15 @@ const JupyterLogo: JupyterFrontEndPlugin = { requires: [ILabShell], activate: (app: JupyterFrontEnd, shell: ILabShell) => { const logo = new Widget(); - jupyterIcon.element({ + yiliLogoIcon.element({ container: logo.node, elementPosition: 'center', - margin: '2px 2px 2px 8px', + marginRight: '10px', + marginLeft: '10px', height: 'auto', - width: '16px' + width: '54px' }); - logo.id = 'jp-MainLogo'; + logo.id = 'jp-YiliLogo'; shell.add(logo, 'top', { rank: 0 }); } }; ``` - 修改 JupyterLab 主界面的布局, 隐藏部分面板 ```diff diff --git a/packages/application/src/shell.ts b/packages/application/src/shell.ts index c87c901fb3..931ccd369a 100644 --- a/packages/application/src/shell.ts +++ b/packages/application/src/shell.ts @@ -306,7 +306,7 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { dockPanel.node.setAttribute('role', 'main'); - hboxPanel.spacing = 0; + hboxPanel.spacing = 2; vsplitPanel.spacing = 1; dockPanel.spacing = 5; hsplitPanel.spacing = 1; @@ -330,14 +330,14 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { hsplitPanel.addWidget(leftHandler.stackedPanel); hsplitPanel.addWidget(dockPanel); - hsplitPanel.addWidget(rightHandler.stackedPanel); + // hsplitPanel.addWidget(rightHandler.stackedPanel); vsplitPanel.addWidget(hsplitPanel); - vsplitPanel.addWidget(downPanel); + // vsplitPanel.addWidget(downPanel); hboxPanel.addWidget(leftHandler.sideBar); hboxPanel.addWidget(vsplitPanel); - hboxPanel.addWidget(rightHandler.sideBar); + // hboxPanel.addWidget(rightHandler.sideBar); rootLayout.direction = 'top-to-bottom'; rootLayout.spacing = 0; // TODO make this configurable? @@ -353,10 +353,12 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { BoxLayout.setStretch(hboxPanel, 1); BoxLayout.setStretch(bottomPanel, 0); + BoxLayout.setSizeBasis(topHandler.panel, 60); + rootLayout.addWidget(headerPanel); rootLayout.addWidget(topHandler.panel); rootLayout.addWidget(hboxPanel); - rootLayout.addWidget(bottomPanel); + // rootLayout.addWidget(bottomPanel); // initially hiding header and bottom panel when no elements inside, this._headerPanel.hide(); @@ -1498,7 +1500,7 @@ namespace Private { /** * Get the panel managed by the handler. */ - get panel() { + get panel(): Panel { return this._panel; } @@ -1653,12 +1655,12 @@ namespace Private { if (title.icon instanceof LabIcon) { // bind an appropriate style to the icon - title.icon = title.icon.bindprops({ - stylesheet: 'sideBar' - }); + // title.icon = title.icon.bindprops({ + // stylesheet: 'sideBar' + // }); } else if (typeof title.icon === 'string' || !title.icon) { // add some classes to help with displaying css background imgs - title.iconClass = classes(title.iconClass, 'jp-Icon', 'jp-Icon-20'); + // title.iconClass = classes(title.iconClass, 'jp-Icon', 'jp-Icon-20'); } this._refreshVisibility(); ``` - 修改主界面 css 样式, 匹配前端原型 ```diff diff --git a/packages/application/style/base.css b/packages/application/style/base.css index 25daa9c913..3c937aa43a 100644 --- a/packages/application/style/base.css +++ b/packages/application/style/base.css @@ -13,7 +13,7 @@ body { font-family: var(--jp-ui-font-family); - background: var(--jp-layout-color3); + background: #f0f2f5; margin: 0; padding: 0; overflow: hidden; @@ -31,8 +31,13 @@ body { border-top: 4px solid red; } +#jp-main-vsplit-panel, +#jp-main-split-panel { + overflow: visible; +} + #jp-main-dock-panel { - padding: 5px; + padding: 10px; } #jp-main-dock-panel[data-mode='single-document'] { @@ -44,8 +49,7 @@ body { } #jp-top-panel { - border-bottom: var(--jp-border-width) solid var(--jp-border-color0); - background: var(--jp-layout-color1); + background-color: #222222; display: flex; min-height: var(--jp-private-menubar-height); overflow: visible; @@ -53,7 +57,23 @@ body { #jp-menu-panel { min-height: var(--jp-private-menu-panel-height); - background: var(--jp-layout-color1); + align-self: center; +} + +#jp-MainMenu { + background-color: #222222; + color: #afafaf; +} + +#jp-MainMenu .lm-MenuBar-item { + padding: 0 20px; + border: none; +} + +#jp-MainMenu .lm-MenuBar-item.lm-mod-active { + background-color: #333333; + border: none; + box-shadow: none; } #jp-down-stack { ``` - 重新定义 JupyterLab 对话框按钮样式, 匹配前端原型 ```diff diff --git a/packages/application/style/buttons.css b/packages/application/style/buttons.css index 0c25eb4b3e..2e90a454e5 100644 --- a/packages/application/style/buttons.css +++ b/packages/application/style/buttons.css @@ -13,52 +13,32 @@ |----------------------------------------------------------------------------*/ button { - border-radius: var(--jp-border-radius); -} - -button:focus-visible { - border: 1px solid var(--md-blue-900); + border-radius: 2px; } button.jp-mod-styled.jp-mod-accept { - background: var(--md-blue-700); - border: 0; + background: #4883fb; + border: 1px solid #4883fb; color: white; } button.jp-mod-styled.jp-mod-accept:hover { - background: var(--md-blue-800); -} - -button.jp-mod-styled.jp-mod-accept:active { - background: var(--md-blue-900); -} - -button.jp-mod-styled.jp-mod-accept:focus-visible { - border: 1px solid var(--md-blue-900); + background: #276fff; } button.jp-mod-styled.jp-mod-reject { - background: var(--md-grey-600); - border: 0; - color: white; + background-color: white; + border: 1px solid #dddddd; + color: #7d7d7d; } button.jp-mod-styled.jp-mod-reject:hover { - background: var(--md-grey-700); -} - -button.jp-mod-styled.jp-mod-reject:active { - background: var(--md-grey-800); -} - -button.jp-mod-styled.jp-mod-reject:focus-visible { - border: 1px solid var(--md-grey-800); + background: #eeeeee; } button.jp-mod-styled.jp-mod-warn { background: var(--md-red-700); - border: 0; + border: 1px solid var(--md-red-700); color: white; } @@ -66,14 +46,6 @@ button.jp-mod-styled.jp-mod-warn:hover { background: var(--md-red-800); } -button.jp-mod-styled.jp-mod-warn:active { - background: var(--md-red-900); -} - -button.jp-mod-styled.jp-mod-warn:focus-visible { - border: 1px solid var(--md-red-900); -} - .jp-Button-flat { text-decoration: none; padding: var(--jp-flat-button-padding); ``` - 修改主面板样式 ```diff diff --git a/packages/application/style/dockpanel.css b/packages/application/style/dockpanel.css index 827eccb72c..a189fb6727 100644 --- a/packages/application/style/dockpanel.css +++ b/packages/application/style/dockpanel.css @@ -14,9 +14,7 @@ .lm-DockPanel-widget, .lm-TabPanel-stackedPanel { background: var(--jp-layout-color0); - border-left: var(--jp-border-width) solid var(--jp-border-color1); - border-right: var(--jp-border-width) solid var(--jp-border-color1); - border-bottom: var(--jp-border-width) solid var(--jp-border-color1); + box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.1); } .lm-DockPanel-overlay { @@ -34,3 +32,11 @@ .lm-DockPanel-overlay.lm-mod-root-center { border-width: 2px; } + +#jp-main-dock-panel .lm-DockPanel-widget { + z-index: 2; +} + +#jp-main-dock-panel .lm-DockPanel-tabBar { + z-index: unset; +} diff --git a/packages/application/style/menus.css b/packages/application/style/menus.css index 36535b8571..97241b8730 100644 --- a/packages/application/style/menus.css +++ b/packages/application/style/menus.css @@ -26,10 +26,6 @@ overflow-x: auto; } -.lm-MenuBar-menu { - transform: translateY(calc(-2 * var(--jp-border-width))); -} - .lm-MenuBar-item { padding: 0px 8px; border-left: var(--jp-border-width) solid transparent; ``` - 修改侧边栏样式 ```diff diff --git a/packages/application/style/sidepanel.css b/packages/application/style/sidepanel.css index d19f2d2841..ed7ff3b8bf 100644 --- a/packages/application/style/sidepanel.css +++ b/packages/application/style/sidepanel.css @@ -21,18 +21,14 @@ font-size: var(--jp-ui-font-size1); } -.jp-SideBar.lm-TabBar, -#jp-down-stack .lm-TabBar { - color: var(--jp-ui-font-color1); - background: var(--jp-layout-color2); - font-size: var(--jp-ui-font-size1); - overflow: visible; -} - .jp-SideBar.lm-TabBar { - min-width: calc(var(--jp-private-sidebar-tab-width) + var(--jp-border-width)); - max-width: calc(var(--jp-private-sidebar-tab-width) + var(--jp-border-width)); + color: #4a4a4a; + background-color: white; + font-size: var(--jp-ui-font-size1); + min-width: 180px; + max-width: 180px; display: block; + height: 100%; } .jp-SideBar .lm-TabBar-content { @@ -41,103 +37,44 @@ display: flex; align-items: stretch; list-style-type: none; - height: var(--jp-private-sidebar-tab-width); - transform-origin: 0 0 0; } .jp-SideBar .lm-TabBar-tab { - padding: 0 16px; + display: flex; + align-items: center; + height: 50px; + padding: 0 25px; border: none; - overflow: visible; + color: #4a4a4a; } .jp-SideBar .lm-TabBar-tab.lm-mod-current { - min-height: calc( - var(--jp-private-sidebar-tab-width) + var(--jp-border-width) - ); - max-height: calc( - var(--jp-private-sidebar-tab-width) + var(--jp-border-width) - ); - /* transform: translateY(var(--jp-border-width)); */ -} - -.jp-SideBar .lm-TabBar-tab:not(.lm-mod-current), -#jp-down-stack .lm-TabBar-tab:not(.lm-mod-current) { - background: var(--jp-layout-color2); -} - -.jp-SideBar .lm-TabBar-tabIcon.jp-SideBar-tabIcon { - min-width: 20px; - min-height: 20px; - background-size: 20px; - display: inline-block; - vertical-align: middle; - background-repeat: no-repeat; - background-position: center; -} - -.jp-SideBar .lm-TabBar-tabLabel { - line-height: var(--jp-private-sidebar-tab-width); -} - -.jp-SideBar .lm-TabBar-tab:hover:not(.lm-mod-current), -#jp-down-stack .lm-TabBar-tab:hover:not(.lm-mod-current) { - background: var(--jp-layout-color1); -} - -/* Left */ - -/* Borders */ - -.jp-SideBar.lm-TabBar.jp-mod-left { - border-right: var(--jp-border-width) solid var(--jp-border-color0); -} - -.jp-SideBar.lm-TabBar.jp-mod-left .lm-TabBar-tab + .lm-TabBar-tab { - border-right: var(--jp-border-width) solid var(--jp-layout-color2); + background-color: #147bd1; + color: white; } -.jp-SideBar.lm-TabBar.jp-mod-left - .lm-TabBar-tab.lm-mod-current - + .lm-TabBar-tab { - border-right: var(--jp-border-width) solid var(--jp-border-color0); +.jp-SideBar .lm-TabBar-tab .jp-icon3[fill] { + fill: #4a4a4a; } -.jp-SideBar.lm-TabBar.jp-mod-left - .lm-TabBar-tab - + .lm-TabBar-tab.lm-mod-current { - border-right: var(--jp-border-width) solid var(--jp-border-color0); +.jp-SideBar .lm-TabBar-tab.lm-mod-current .jp-icon3[fill] { + fill: white; } -.jp-SideBar.lm-TabBar.jp-mod-left .lm-TabBar-tab.lm-mod-current:last-child { - border-left: var(--jp-border-width) solid var(--jp-border-color0); -} - -/* Transforms */ - -.jp-SideBar.lm-TabBar.jp-mod-left .lm-TabBar-content { - flex-direction: row-reverse; - transform: rotate(-90deg) translateX(-100%); +.jp-SideBar .lm-TabBar-tabIcon svg { + width: 16px; + height: 16px; + display: block; } -.jp-SideBar.lm-TabBar.jp-mod-left - .lm-TabBar-tab:not(.lm-mod-current) - .lm-TabBar-tabIcon { - transform: rotate(90deg); +.jp-SideBar .lm-TabBar-tabIcon { + margin-right: 16px; } -.jp-SideBar.lm-TabBar.jp-mod-left - .lm-TabBar-tab.lm-mod-current - .lm-TabBar-tabIcon { - transform: rotate(90deg) - translate( - calc(-0.5 * var(--jp-border-width)), - calc(-0.5 * var(--jp-border-width)) - ); +.jp-SideBar .lm-TabBar-tab:hover:not(.lm-mod-current) { + background-color: #eeeeee; } -/* Right */ - /* Borders */ .jp-SideBar.lm-TabBar.jp-mod-right { @@ -217,6 +154,7 @@ #jp-left-stack > .lm-Widget, #jp-right-stack > .lm-Widget { min-width: var(--jp-sidebar-min-width); + background-color: #f8f8f9; } #jp-right-stack { @@ -224,7 +162,7 @@ } #jp-left-stack { - border-right: var(--jp-border-width) solid var(--jp-border-color1); + box-shadow: 1px 0px 6px 0px rgba(0, 0, 0, 0.12); } #jp-down-stack > .lm-TabPanel-stackedPanel { ``` - 重新设置 Tab 的样式, 将原来的长方形改为梯形 ```diff diff --git a/packages/application/style/tabs.css b/packages/application/style/tabs.css index bb23cb958f..171e3d34ef 100644 --- a/packages/application/style/tabs.css +++ b/packages/application/style/tabs.css @@ -20,7 +20,6 @@ .lm-DockPanel-tabBar, .lm-TabPanel-tabBar { - border-bottom: var(--jp-border-width) solid var(--jp-border-color1); overflow: visible; color: var(--jp-ui-font-color1); font-size: var(--jp-ui-font-size1); @@ -52,18 +51,38 @@ var(--jp-private-horizontal-tab-height) + var(--jp-border-width) ); min-width: 0px; - margin-left: calc(-1 * var(--jp-border-width)); + margin-left: -10px; + margin-right: 25px; line-height: var(--jp-private-horizontal-tab-height); padding: 0px 8px; - background: var(--jp-layout-color2); - border: var(--jp-border-width) solid var(--jp-border-color1); - border-bottom: none; + background: #eeeeee; + border: none; position: relative; + overflow: visible; + color: #4a4a4a; + box-shadow: -2px -2px 4px -2px rgba(0, 0, 0, 0.1); +} + +.lm-DockPanel-tabBar .lm-TabBar-tab::after { + content: ' '; + position: absolute; + top: 0; + height: 100%; + background-color: inherit; + z-index: 1; + width: 22px; + right: -12px; + transform: skew(40deg); + box-shadow: 4px 0px 4px -4px rgba(0, 0, 0, 0.1); +} + +.lm-DockPanel-tabBar .lm-TabBar-tab .lm-TabBar-tabCloseIcon { + z-index: 2; } .lm-DockPanel-tabBar .lm-TabBar-tab:hover:not(.lm-mod-current), .lm-TabPanel-tabBar .lm-TabBar-tab:hover:not(.lm-mod-current) { - background: var(--jp-layout-color1); + background-color: #f5f5f5; } .lm-DockPanel-tabBar .lm-TabBar-tab:first-child, @@ -74,11 +93,10 @@ /* This is a current tab of a tab bar in the dock panel: each tab bar has 1. */ .lm-DockPanel-tabBar .lm-TabBar-tab.lm-mod-current { background: var(--jp-layout-color1); - color: var(--jp-ui-font-color0); + color: #4a4a4a; min-height: calc( var(--jp-private-horizontal-tab-height) + 2 * var(--jp-border-width) ); - transform: translateY(var(--jp-border-width)); } .lm-TabPanel-tabBar .lm-TabBar-tab.lm-mod-current { @@ -86,17 +104,6 @@ color: var(--jp-ui-font-color0); } -/* This is the main application level current tab: only 1 exists. */ -.lm-DockPanel-tabBar .lm-TabBar-tab.jp-mod-current:before { - position: absolute; - top: calc(-1 * var(--jp-border-width) + 1px); - left: calc(-1 * var(--jp-border-width)); - content: ''; - height: var(--jp-private-horizontal-tab-active-top-border); - width: calc(100% + 2 * var(--jp-border-width)); - background: var(--jp-brand-color1); -} - /* This is the left tab bar current tab: only 1 exists. */ .lm-TabBar-tab.lm-mod-current { color: var(--jp-ui-font-color0); ``` - 去除一些可能导致 Notebook 被下载的插件 ```diff diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index fe5cee38f2..cdf726aa6e 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -22,7 +22,7 @@ import { ISplashScreen, IWindowResolver, MainAreaWidget, - Printing, + // Printing, sessionContextDialogs, WindowResolver } from '@jupyterlab/apputils'; @@ -268,28 +268,28 @@ Would you like to clear the workspace or keep waiting?`), } }; -const print: JupyterFrontEndPlugin = { - id: '@jupyterlab/apputils-extension:print', - autoStart: true, - requires: [ITranslator], - activate: (app: JupyterFrontEnd, translator: ITranslator) => { - const trans = translator.load('jupyterlab'); - app.commands.addCommand(CommandIDs.print, { - label: trans.__('Print…'), - isEnabled: () => { - const widget = app.shell.currentWidget; - return Printing.getPrintFunction(widget) !== null; - }, - execute: async () => { - const widget = app.shell.currentWidget; - const printFunction = Printing.getPrintFunction(widget); - if (printFunction) { - await printFunction(); - } - } - }); - } -}; +// const print: JupyterFrontEndPlugin = { +// id: '@jupyterlab/apputils-extension:print', +// autoStart: true, +// requires: [ITranslator], +// activate: (app: JupyterFrontEnd, translator: ITranslator) => { +// const trans = translator.load('jupyterlab'); +// app.commands.addCommand(CommandIDs.print, { +// label: trans.__('Print…'), +// isEnabled: () => { +// const widget = app.shell.currentWidget; +// return Printing.getPrintFunction(widget) !== null; +// }, +// execute: async () => { +// const widget = app.shell.currentWidget; +// const printFunction = Printing.getPrintFunction(widget); +// if (printFunction) { +// await printFunction(); +// } +// } +// }); +// } +// }; export const toggleHeader: JupyterFrontEndPlugin = { id: '@jupyterlab/apputils-extension:toggle-header', @@ -620,7 +620,7 @@ const sanitizer: JupyterFrontEndPlugin = { const plugins: JupyterFrontEndPlugin[] = [ palette, paletteRestorer, - print, + // print, resolver, sanitizer, settingsPlugin, diff --git a/packages/apputils-extension/src/workspacesplugin.ts b/packages/apputils-extension/src/workspacesplugin.ts index ffcefb6ded..bf8b93a806 100644 --- a/packages/apputils-extension/src/workspacesplugin.ts +++ b/packages/apputils-extension/src/workspacesplugin.ts @@ -24,11 +24,11 @@ import { IStateDB } from '@jupyterlab/statedb'; import { ITranslator, nullTranslator } from '@jupyterlab/translation'; import { Widget } from '@lumino/widgets'; -namespace CommandIDs { - export const saveWorkspace = 'workspace-ui:save'; +// namespace CommandIDs { +// export const saveWorkspace = 'workspace-ui:save'; - export const saveWorkspaceAs = 'workspace-ui:save-as'; -} +// export const saveWorkspaceAs = 'workspace-ui:save-as'; +// } const WORKSPACE_NAME = 'jupyterlab-workspace'; const WORKSPACE_EXT = '.' + WORKSPACE_NAME; @@ -78,39 +78,39 @@ export const workspacesPlugin: JupyterFrontEndPlugin = { iconClass: ICON_NAME }); app.docRegistry.addWidgetFactory(factory); - app.commands.addCommand(CommandIDs.saveWorkspaceAs, { - label: trans.__('Save Current Workspace As…'), - execute: async () => { - const data = app.serviceManager.workspaces.fetch(resolver.name); - await Private.saveAs( - fbf.defaultBrowser, - app.serviceManager.contents, - data, - state, - translator - ); - } - }); + // app.commands.addCommand(CommandIDs.saveWorkspaceAs, { + // label: trans.__('Save Current Workspace As…'), + // execute: async () => { + // const data = app.serviceManager.workspaces.fetch(resolver.name); + // await Private.saveAs( + // fbf.defaultBrowser, + // app.serviceManager.contents, + // data, + // state, + // translator + // ); + // } + // }); - app.commands.addCommand(CommandIDs.saveWorkspace, { - label: trans.__('Save Current Workspace'), - execute: async () => { - const { contents } = app.serviceManager; - const data = app.serviceManager.workspaces.fetch(resolver.name); - const lastSave = (await state.fetch(LAST_SAVE_ID)) as string; - if (lastSave === undefined) { - await Private.saveAs( - fbf.defaultBrowser, - contents, - data, - state, - translator - ); - } else { - await Private.save(lastSave, contents, data, state); - } - } - }); + // app.commands.addCommand(CommandIDs.saveWorkspace, { + // label: trans.__('Save Current Workspace'), + // execute: async () => { + // const { contents } = app.serviceManager; + // const data = app.serviceManager.workspaces.fetch(resolver.name); + // const lastSave = (await state.fetch(LAST_SAVE_ID)) as string; + // if (lastSave === undefined) { + // await Private.saveAs( + // fbf.defaultBrowser, + // contents, + // data, + // state, + // translator + // ); + // } else { + // await Private.save(lastSave, contents, data, state); + // } + // } + // }); } }; ``` - 优化 JupyterLab 对话框的代码 ```diff diff --git a/packages/apputils/src/dialog.tsx b/packages/apputils/src/dialog.tsx index 2003f99c5a..9f3854c394 100644 --- a/packages/apputils/src/dialog.tsx +++ b/packages/apputils/src/dialog.tsx @@ -33,9 +33,9 @@ export function showDialog( * @param error - the error to show in the dialog body (either a string * or an object with a string `message` property). */ -export function showErrorMessage( +export function showErrorMessage( title: string, - error: any, + error: string | E, buttons: ReadonlyArray = [ Dialog.okButton({ label: 'Dismiss' }) ] @@ -415,8 +415,6 @@ export class Dialog extends Widget { this.dispose(); return; } - this._promise = null; - ArrayExt.removeFirstOf(Private.launchQueue, promise.promise); const body = this._body; let value: T | null = null; if ( @@ -426,6 +424,8 @@ export class Dialog extends Widget { ) { value = body.getValue(); } + this._promise = null; + ArrayExt.removeFirstOf(Private.launchQueue, promise.promise); this.dispose(); promise.resolve({ button, value }); } @@ -693,7 +693,7 @@ export namespace Dialog { /** * The default implementation of a dialog renderer. */ - export class Renderer { + export class Renderer implements Dialog.IRenderer { /** * Create the header of the dialog. * diff --git a/packages/apputils/src/mainareawidget.ts b/packages/apputils/src/mainareawidget.ts index baa4eaa21f..f01dbd18f1 100644 --- a/packages/apputils/src/mainareawidget.ts +++ b/packages/apputils/src/mainareawidget.ts @@ -60,8 +60,8 @@ export class MainAreaWidget BoxLayout.setStretch(toolbar, 0); BoxLayout.setStretch(contentHeader, 0); BoxLayout.setStretch(content, 1); - layout.addWidget(toolbar); layout.addWidget(contentHeader); + layout.addWidget(toolbar); layout.addWidget(content); if (!content.id) { ``` - 更新对话框样式, 匹配前端原型 ```diff diff --git a/packages/apputils/style/dialog.css b/packages/apputils/style/dialog.css index 7994fb4b59..17dfd7307f 100644 --- a/packages/apputils/style/dialog.css +++ b/packages/apputils/style/dialog.css @@ -6,7 +6,7 @@ .jp-Dialog { position: absolute; - z-index: 10000; + z-index: 1000; display: flex; flex-direction: column; align-items: center; @@ -17,7 +17,7 @@ padding: 0; width: 100%; height: 100%; - background: var(--jp-dialog-background); + background: rgba(0, 0, 0, 0.4); } .jp-Dialog-content { @@ -26,20 +26,17 @@ margin-left: auto; margin-right: auto; background: var(--jp-layout-color1); - padding: 24px 24px 12px 24px; min-width: 300px; min-height: 150px; max-width: 1000px; - max-height: 500px; box-sizing: border-box; - box-shadow: var(--jp-elevation-z20); + box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.33); word-wrap: break-word; border-radius: var(--jp-border-radius); /* This is needed so that all font sizing of children done in ems is * relative to this base size */ font-size: var(--jp-ui-font-size1); color: var(--jp-ui-font-color1); - resize: both; } .jp-Dialog-content.jp-Dialog-content-small { @@ -50,50 +47,32 @@ overflow: visible; } -button.jp-Dialog-button:focus { - outline: 1px solid var(--jp-brand-color1); - outline-offset: 4px; - -moz-outline-radius: 0px; -} - button.jp-Dialog-button:focus::-moz-focus-inner { border: 0; } -button.jp-Dialog-button.jp-mod-styled.jp-mod-accept:focus, -button.jp-Dialog-button.jp-mod-styled.jp-mod-warn:focus, -button.jp-Dialog-button.jp-mod-styled.jp-mod-reject:focus { - outline-offset: 4px; - -moz-outline-radius: 0px; -} - -button.jp-Dialog-button.jp-mod-styled.jp-mod-accept:focus { - outline: 1px solid var(--md-blue-700); -} - -button.jp-Dialog-button.jp-mod-styled.jp-mod-warn:focus { - outline: 1px solid var(--md-red-600); -} - -button.jp-Dialog-button.jp-mod-styled.jp-mod-reject:focus { - outline: 1px solid var(--md-grey-700); -} - button.jp-Dialog-close-button { padding: 0; - height: 100%; + height: auto; min-width: unset; min-height: unset; + transform: translateY(2px); +} + +button.jp-Dialog-close-button svg { + width: 22px; } .jp-Dialog-header { display: flex; justify-content: space-between; flex: 0 0 auto; - padding-bottom: 12px; - font-size: var(--jp-ui-font-size3); - font-weight: 400; - color: var(--jp-ui-font-color0); + align-items: center; + height: 48px; + padding: 0 30px; + font-weight: bold; + color: #4a4a4a; + background-color: #edf2f8; } .jp-Dialog-body { @@ -103,16 +82,15 @@ button.jp-Dialog-close-button { font-size: var(--jp-ui-font-size1); background: var(--jp-layout-color1); overflow: auto; + padding: 40px; } .jp-Dialog-footer { display: flex; flex-direction: row; - justify-content: flex-end; + justify-content: center; flex: 0 0 auto; - margin-left: -12px; - margin-right: -12px; - padding: 12px; + padding: 10px 30px 30px; } .jp-Dialog-title { @@ -130,10 +108,10 @@ button.jp-Dialog-close-button { } .jp-Dialog-body > label { - line-height: 1.4; - color: var(--jp-ui-font-color0); + color: #4a4a4a; + margin-bottom: 5px; } .jp-Dialog-button.jp-mod-styled:not(:last-child) { - margin-right: 12px; + margin-right: 50px; } diff --git a/packages/apputils/style/styling.css b/packages/apputils/style/styling.css index de5146bb28..1b21b48370 100644 --- a/packages/apputils/style/styling.css +++ b/packages/apputils/style/styling.css @@ -10,9 +10,8 @@ button.jp-mod-styled { border: none; box-sizing: border-box; text-align: center; - line-height: 32px; height: 32px; - padding: 0px 12px; + width: 80px; letter-spacing: 0.8px; outline: none; appearance: none; @@ -21,18 +20,19 @@ button.jp-mod-styled { } input.jp-mod-styled { - background: var(--jp-input-background); - height: 28px; + height: 32px; box-sizing: border-box; - border: var(--jp-border-width) solid var(--jp-border-color1); - padding-left: 7px; - padding-right: 7px; - font-size: var(--jp-ui-font-size2); + border: 1px solid #c8d3e9; + border-radius: 2px; + padding: 0 15px; + font-size: 12px; color: var(--jp-ui-font-color0); outline: none; appearance: none; -webkit-appearance: none; -moz-appearance: none; + transition: all 100ms; + outline: 1px solid transparent; } input[type='checkbox'].jp-mod-styled { @@ -43,8 +43,7 @@ input[type='checkbox'].jp-mod-styled { } input.jp-mod-styled:focus { - border: var(--jp-border-width) solid var(--md-blue-500); - box-shadow: inset 0 0 4px var(--md-blue-300); + outline-color: #147bd1; } .jp-FileDialog-Checkbox { diff --git a/packages/cells/style/inputarea.css b/packages/cells/style/inputarea.css index ec6f1620c0..5831ab644d 100644 --- a/packages/cells/style/inputarea.css +++ b/packages/cells/style/inputarea.css @@ -36,7 +36,7 @@ body[data-format='mobile'] .jp-InputArea-editor { .jp-InputPrompt { flex: 0 0 var(--jp-cell-prompt-width); - color: var(--jp-cell-inprompt-font-color); + color: #147bd1; font-family: var(--jp-cell-prompt-font-family); padding: var(--jp-code-padding); letter-spacing: var(--jp-cell-prompt-letter-spacing); ``` - 去除一些不符合需求的菜单项和组件 ```diff diff --git a/packages/console-extension/src/index.ts b/packages/console-extension/src/index.ts index fe4c7d1d05..16cdb587e8 100644 --- a/packages/console-extension/src/index.ts +++ b/packages/console-extension/src/index.ts @@ -25,12 +25,12 @@ import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console'; import { IFileBrowserFactory } from '@jupyterlab/filebrowser'; import { ILauncher } from '@jupyterlab/launcher'; import { - IEditMenu, - IFileMenu, - IHelpMenu, - IKernelMenu, - IMainMenu, - IRunMenu + // IEditMenu, + // IFileMenu, + // IHelpMenu, + // IKernelMenu, + IMainMenu + // IRunMenu } from '@jupyterlab/mainmenu'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; @@ -673,79 +673,79 @@ async function activateConsole( }); } - if (mainMenu) { - // Add a close and shutdown command to the file menu. - mainMenu.fileMenu.closeAndCleaners.add({ - tracker, - closeAndCleanupLabel: (n: number) => trans.__('Shutdown Console'), - closeAndCleanup: (current: ConsolePanel) => { - return showDialog({ - title: trans.__('Shut down the Console?'), - body: trans.__( - 'Are you sure you want to close "%1"?', - current.title.label - ), - buttons: [Dialog.cancelButton(), Dialog.warnButton()] - }).then(result => { - if (result.button.accept) { - return current.console.sessionContext.shutdown().then(() => { - current.dispose(); - }); - } else { - return void 0; - } - }); - } - } as IFileMenu.ICloseAndCleaner); - - // Add a kernel user to the Kernel menu - mainMenu.kernelMenu.kernelUsers.add({ - tracker, - restartKernelAndClearLabel: n => - trans.__('Restart Kernel and Clear Console'), - interruptKernel: current => { - const kernel = current.console.sessionContext.session?.kernel; - if (kernel) { - return kernel.interrupt(); - } - return Promise.resolve(void 0); - }, - restartKernel: current => - sessionDialogs!.restart(current.console.sessionContext, translator), - restartKernelAndClear: current => { - return sessionDialogs! - .restart(current.console.sessionContext) - .then(restarted => { - if (restarted) { - current.console.clear(); - } - return restarted; - }); - }, - changeKernel: current => - sessionDialogs!.selectKernel( - current.console.sessionContext, - translator - ), - shutdownKernel: current => current.console.sessionContext.shutdown() - } as IKernelMenu.IKernelUser); - - // Add a code runner to the Run menu. - mainMenu.runMenu.codeRunners.add({ - tracker, - runLabel: (n: number) => trans.__('Run Cell'), - run: current => current.console.execute(true) - } as IRunMenu.ICodeRunner); - - // Add a clearer to the edit menu - mainMenu.editMenu.clearers.add({ - tracker, - clearCurrentLabel: (n: number) => trans.__('Clear Console Cell'), - clearCurrent: (current: ConsolePanel) => { - return current.console.clear(); - } - } as IEditMenu.IClearer); - } + // if (mainMenu) { + // // Add a close and shutdown command to the file menu. + // mainMenu.fileMenu.closeAndCleaners.add({ + // tracker, + // closeAndCleanupLabel: (n: number) => trans.__('Shutdown Console'), + // closeAndCleanup: (current: ConsolePanel) => { + // return showDialog({ + // title: trans.__('Shut down the Console?'), + // body: trans.__( + // 'Are you sure you want to close "%1"?', + // current.title.label + // ), + // buttons: [Dialog.cancelButton(), Dialog.warnButton()] + // }).then(result => { + // if (result.button.accept) { + // return current.console.sessionContext.shutdown().then(() => { + // current.dispose(); + // }); + // } else { + // return void 0; + // } + // }); + // } + // } as IFileMenu.ICloseAndCleaner); + + // // Add a kernel user to the Kernel menu + // mainMenu.kernelMenu.kernelUsers.add({ + // tracker, + // restartKernelAndClearLabel: n => + // trans.__('Restart Kernel and Clear Console'), + // interruptKernel: current => { + // const kernel = current.console.sessionContext.session?.kernel; + // if (kernel) { + // return kernel.interrupt(); + // } + // return Promise.resolve(void 0); + // }, + // restartKernel: current => + // sessionDialogs!.restart(current.console.sessionContext, translator), + // restartKernelAndClear: current => { + // return sessionDialogs! + // .restart(current.console.sessionContext) + // .then(restarted => { + // if (restarted) { + // current.console.clear(); + // } + // return restarted; + // }); + // }, + // changeKernel: current => + // sessionDialogs!.selectKernel( + // current.console.sessionContext, + // translator + // ), + // shutdownKernel: current => current.console.sessionContext.shutdown() + // } as IKernelMenu.IKernelUser); + + // // Add a code runner to the Run menu. + // mainMenu.runMenu.codeRunners.add({ + // tracker, + // runLabel: (n: number) => trans.__('Run Cell'), + // run: current => current.console.execute(true) + // } as IRunMenu.ICodeRunner); + + // // Add a clearer to the edit menu + // mainMenu.editMenu.clearers.add({ + // tracker, + // clearCurrentLabel: (n: number) => trans.__('Clear Console Cell'), + // clearCurrent: (current: ConsolePanel) => { + // return current.console.clear(); + // } + // } as IEditMenu.IClearer); + // } // For backwards compatibility and clarity, we explicitly label the run // keystroke with the actual effected change, rather than the generic @@ -775,13 +775,13 @@ async function activateConsole( isToggled: args => args['interactionMode'] === interactionMode }); - if (mainMenu) { - // Add kernel information to the application help menu. - mainMenu.helpMenu.kernelUsers.add({ - tracker, - getKernel: current => current.sessionContext.session?.kernel - } as IHelpMenu.IKernelUser); - } + // if (mainMenu) { + // // Add kernel information to the application help menu. + // mainMenu.helpMenu.kernelUsers.add({ + // tracker, + // getKernel: current => current.sessionContext.session?.kernel + // } as IHelpMenu.IKernelUser); + // } return tracker; } diff --git a/packages/debugger-extension/src/index.ts b/packages/debugger-extension/src/index.ts index fe710a9d2a..659f5c0692 100644 --- a/packages/debugger-extension/src/index.ts +++ b/packages/debugger-extension/src/index.ts @@ -15,7 +15,7 @@ import { ICommandPalette, IThemeManager, MainAreaWidget, - sessionContextDialogs, + // sessionContextDialogs, WidgetTracker } from '@jupyterlab/apputils'; import { IEditorServices } from '@jupyterlab/codeeditor'; @@ -35,7 +35,7 @@ import { FileEditor, IEditorTracker } from '@jupyterlab/fileeditor'; import { ILoggerRegistry } from '@jupyterlab/logconsole'; import { INotebookTracker, - NotebookActions, + // NotebookActions, NotebookPanel } from '@jupyterlab/notebook'; import { @@ -167,88 +167,88 @@ const files: JupyterFrontEndPlugin = { /** * A plugin that provides visual debugging support for notebooks. */ -const notebooks: JupyterFrontEndPlugin = { - id: '@jupyterlab/debugger-extension:notebooks', - autoStart: true, - requires: [IDebugger, INotebookTracker, ITranslator], - optional: [ILabShell, ICommandPalette], - provides: IDebuggerHandler, - activate: ( - app: JupyterFrontEnd, - service: IDebugger, - notebookTracker: INotebookTracker, - translator: ITranslator, - labShell: ILabShell | null, - palette: ICommandPalette | null - ): Debugger.Handler => { - const handler = new Debugger.Handler({ - type: 'notebook', - shell: app.shell, - service - }); - - const trans = translator.load('jupyterlab'); - app.commands.addCommand(Debugger.CommandIDs.restartDebug, { - label: trans.__('Restart Kernel and Debug…'), - caption: trans.__('Restart Kernel and Debug…'), - isEnabled: () => service.isStarted, - execute: async () => { - const state = service.getDebuggerState(); - await service.stop(); - - const widget = notebookTracker.currentWidget; - if (!widget) { - return; - } - - const { content, sessionContext } = widget; - const restarted = await sessionContextDialogs.restart(sessionContext); - if (!restarted) { - return; - } - - await service.restoreDebuggerState(state); - await handler.updateWidget(widget, sessionContext.session); - await NotebookActions.runAll(content, sessionContext); - } - }); - - const updateHandlerAndCommands = async ( - widget: NotebookPanel - ): Promise => { - if (widget) { - const { sessionContext } = widget; - await sessionContext.ready; - await handler.updateContext(widget, sessionContext); - } - app.commands.notifyCommandChanged(); - }; - - if (labShell) { - labShell.currentChanged.connect((_, update) => { - const widget = update.newValue; - if (widget instanceof NotebookPanel) { - void updateHandlerAndCommands(widget); - } - }); - } else { - notebookTracker.currentChanged.connect((_, notebookPanel) => { - if (notebookPanel) { - void updateHandlerAndCommands(notebookPanel); - } - }); - } - - if (palette) { - palette.addItem({ - category: 'Notebook Operations', - command: Debugger.CommandIDs.restartDebug - }); - } - - return handler; - } -}; +// const notebooks: JupyterFrontEndPlugin = { +// id: '@jupyterlab/debugger-extension:notebooks', +// autoStart: true, +// requires: [IDebugger, INotebookTracker, ITranslator], +// optional: [ILabShell, ICommandPalette], +// provides: IDebuggerHandler, +// activate: ( +// app: JupyterFrontEnd, +// service: IDebugger, +// notebookTracker: INotebookTracker, +// translator: ITranslator, +// labShell: ILabShell | null, +// palette: ICommandPalette | null +// ): Debugger.Handler => { +// const handler = new Debugger.Handler({ +// type: 'notebook', +// shell: app.shell, +// service +// }); + +// const trans = translator.load('jupyterlab'); +// app.commands.addCommand(Debugger.CommandIDs.restartDebug, { +// label: trans.__('Restart Kernel and Debug…'), +// caption: trans.__('Restart Kernel and Debug…'), +// isEnabled: () => service.isStarted, +// execute: async () => { +// const state = service.getDebuggerState(); +// await service.stop(); + +// const widget = notebookTracker.currentWidget; +// if (!widget) { +// return; +// } + +// const { content, sessionContext } = widget; +// const restarted = await sessionContextDialogs.restart(sessionContext); +// if (!restarted) { +// return; +// } + +// await service.restoreDebuggerState(state); +// await handler.updateWidget(widget, sessionContext.session); +// await NotebookActions.runAll(content, sessionContext); +// } +// }); + +// const updateHandlerAndCommands = async ( +// widget: NotebookPanel +// ): Promise => { +// if (widget) { +// const { sessionContext } = widget; +// await sessionContext.ready; +// await handler.updateContext(widget, sessionContext); +// } +// app.commands.notifyCommandChanged(); +// }; + +// if (labShell) { +// labShell.currentChanged.connect((_, update) => { +// const widget = update.newValue; +// if (widget instanceof NotebookPanel) { +// void updateHandlerAndCommands(widget); +// } +// }); +// } else { +// notebookTracker.currentChanged.connect((_, notebookPanel) => { +// if (notebookPanel) { +// void updateHandlerAndCommands(notebookPanel); +// } +// }); +// } + +// if (palette) { +// palette.addItem({ +// category: 'Notebook Operations', +// command: Debugger.CommandIDs.restartDebug +// }); +// } + +// return handler; +// } +// }; /** * A plugin that provides a debugger service. @@ -313,7 +313,7 @@ const sources: JupyterFrontEndPlugin = { */ const variables: JupyterFrontEndPlugin = { id: '@jupyterlab/debugger-extension:variables', - autoStart: true, + autoStart: false, requires: [IDebugger, IDebuggerHandler, ITranslator], optional: [IThemeManager, IRenderMimeRegistry], activate: ( @@ -868,7 +868,7 @@ const plugins: JupyterFrontEndPlugin[] = [ service, consoles, files, - notebooks, + // notebooks, variables, sidebar, main, diff --git a/packages/docmanager-extension/src/index.tsx b/packages/docmanager-extension/src/index.tsx index fac8aadb2b..4a0a90ae55 100644 --- a/packages/docmanager-extension/src/index.tsx +++ b/packages/docmanager-extension/src/index.tsx @@ -342,49 +342,49 @@ export const pathStatusPlugin: JupyterFrontEndPlugin = { /** * A plugin providing download commands in the file menu and command palette. */ -export const downloadPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/docmanager-extension:download', - autoStart: true, - requires: [ITranslator, IDocumentManager], - optional: [ICommandPalette], - activate: ( - app: JupyterFrontEnd, - translator: ITranslator, - docManager: IDocumentManager, - palette: ICommandPalette | null - ) => { - const trans = translator.load('jupyterlab'); - const { commands, shell } = app; - const isEnabled = () => { - const { currentWidget } = shell; - return !!(currentWidget && docManager.contextForWidget(currentWidget)); - }; - commands.addCommand(CommandIDs.download, { - label: trans.__('Download'), - caption: trans.__('Download the file to your computer'), - isEnabled, - execute: () => { - // Checks that shell.currentWidget is valid: - if (isEnabled()) { - const context = docManager.contextForWidget(shell.currentWidget!); - if (!context) { - return showDialog({ - title: trans.__('Cannot Download'), - body: trans.__('No context found for current widget!'), - buttons: [Dialog.okButton({ label: trans.__('OK') })] - }); - } - return context.download(); - } - } - }); - - const category = trans.__('File Operations'); - if (palette) { - palette.addItem({ command: CommandIDs.download, category }); - } - } -}; +// export const downloadPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/docmanager-extension:download', +// autoStart: true, +// requires: [ITranslator, IDocumentManager], +// optional: [ICommandPalette], +// activate: ( +// app: JupyterFrontEnd, +// translator: ITranslator, +// docManager: IDocumentManager, +// palette: ICommandPalette | null +// ) => { +// const trans = translator.load('jupyterlab'); +// const { commands, shell } = app; +// const isEnabled = () => { +// const { currentWidget } = shell; +// return !!(currentWidget && docManager.contextForWidget(currentWidget)); +// }; +// commands.addCommand(CommandIDs.download, { +// label: trans.__('Download'), +// caption: trans.__('Download the file to your computer'), +// isEnabled, +// execute: () => { +// // Checks that shell.currentWidget is valid: +// if (isEnabled()) { +// const context = docManager.contextForWidget(shell.currentWidget!); +// if (!context) { +// return showDialog({ +// title: trans.__('Cannot Download'), +// body: trans.__('No context found for current widget!'), +// buttons: [Dialog.okButton({ label: trans.__('OK') })] +// }); +// } +// return context.download(); +// } +// } +// }); + +// const category = trans.__('File Operations'); +// if (palette) { +// palette.addItem({ command: CommandIDs.download, category }); +// } +// } +// }; /** * A plugin providing open-browser-tab commands. @@ -438,7 +438,7 @@ const plugins: JupyterFrontEndPlugin[] = [ docManagerPlugin, pathStatusPlugin, savingStatusPlugin, - downloadPlugin, + // downloadPlugin, openBrowserTabPlugin ]; export default plugins; diff --git a/packages/docregistry/src/context.ts b/packages/docregistry/src/context.ts index 966dadf146..f9bb12ed26 100644 --- a/packages/docregistry/src/context.ts +++ b/packages/docregistry/src/context.ts @@ -1047,6 +1047,8 @@ namespace Private { function createSaveNode(path: string): HTMLElement { const input = document.createElement('input'); input.value = path; - return input; + const container = document.createElement('div'); + container.appendChild(input); + return container; } } diff --git a/packages/extensionmanager-extension/examples/listings/index.js b/packages/extensionmanager-extension/examples/listings/index.js index 9d05d78fa7..19776fc525 100644 --- a/packages/extensionmanager-extension/examples/listings/index.js +++ b/packages/extensionmanager-extension/examples/listings/index.js @@ -37,7 +37,6 @@ window.addEventListener('load', async function () { require('@jupyterlab/shortcuts-extension'), require('@jupyterlab/statusbar-extension'), require('@jupyterlab/terminal-extension'), - require('@jupyterlab/theme-dark-extension'), require('@jupyterlab/theme-light-extension'), require('@jupyterlab/tooltip-extension'), require('@jupyterlab/ui-components-extension') diff --git a/packages/extensionmanager-extension/examples/listings/package.json b/packages/extensionmanager-extension/examples/listings/package.json index c7e3b09207..6dca5df7b2 100644 --- a/packages/extensionmanager-extension/examples/listings/package.json +++ b/packages/extensionmanager-extension/examples/listings/package.json @@ -34,7 +34,6 @@ "@jupyterlab/shortcuts-extension": "^2.0.2", "@jupyterlab/statusbar-extension": "^2.0.2", "@jupyterlab/terminal-extension": "^2.0.2", - "@jupyterlab/theme-dark-extension": "^2.0.2", "@jupyterlab/theme-light-extension": "^2.0.2", "@jupyterlab/tooltip-extension": "^2.0.2", "@jupyterlab/ui-components-extension": "^2.0.2", diff --git a/packages/extensionmanager-extension/schema/plugin.json b/packages/extensionmanager-extension/schema/plugin.json index dda72c020d..3f2c190d75 100644 --- a/packages/extensionmanager-extension/schema/plugin.json +++ b/packages/extensionmanager-extension/schema/plugin.json @@ -28,7 +28,7 @@ "enabled": { "title": "Enabled Status", "description": "Enables extension manager (requires Node.js/npm).\nWARNING: installing untrusted extensions may be unsafe.", - "default": true, + "default": false, "type": "boolean" }, "disclaimed": { diff --git a/packages/filebrowser-extension/src/index.ts b/packages/filebrowser-extension/src/index.ts index b6c810262b..18d20e6bb7 100644 --- a/packages/filebrowser-extension/src/index.ts +++ b/packages/filebrowser-extension/src/index.ts @@ -29,7 +29,7 @@ import { IDocumentManager } from '@jupyterlab/docmanager'; import { DocumentRegistry } from '@jupyterlab/docregistry'; import { FileBrowser, - FileUploadStatus, + // FileUploadStatus, FilterFileBrowserModel, IFileBrowserCommands, IFileBrowserFactory, @@ -39,18 +39,19 @@ import { Launcher } from '@jupyterlab/launcher'; import { Contents } from '@jupyterlab/services'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { IStateDB } from '@jupyterlab/statedb'; -import { IStatusBar } from '@jupyterlab/statusbar'; +// import { IStatusBar } from '@jupyterlab/statusbar'; import { ITranslator } from '@jupyterlab/translation'; import { addIcon, closeIcon, copyIcon, cutIcon, - downloadIcon, + // downloadIcon, editIcon, fileIcon, folderIcon, - linkIcon, + LabIcon, + // linkIcon, markdownIcon, newFolderIcon, pasteIcon, @@ -60,8 +61,14 @@ import { } from '@jupyterlab/ui-components'; import { find, IIterator, map, reduce, toArray } from '@lumino/algorithm'; import { CommandRegistry } from '@lumino/commands'; -import { ContextMenu } from '@lumino/widgets'; +// import { ContextMenu } from '@lumino/widgets'; import { JSONObject } from '@lumino/coreutils'; +import analysisSvgStr from '../style/icons/analysis.svg'; + +const analysisIcon = new LabIcon({ + name: 'analysis', + svgstr: analysisSvgStr +}); const FILE_BROWSER_FACTORY = 'FileBrowser'; @@ -176,10 +183,10 @@ const browser: JupyterFrontEndPlugin = { } // Navigate to preferred-dir trait if found - const preferredPath = PageConfig.getOption('preferredPath'); - if (preferredPath) { - await browser.model.cd(preferredPath); - } + // const preferredPath = PageConfig.getOption('preferredPath'); + // if (preferredPath) { + // await browser.model.cd(preferredPath); + // } addCommands(app, factory, translator, settingRegistry, commandPalette); @@ -322,50 +329,50 @@ const factory: JupyterFrontEndPlugin = { * Users will still be able to retrieve files from the file download URLs the * server provides. */ -const downloadPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:download', - requires: [IFileBrowserFactory, ITranslator], - autoStart: true, - activate: ( - app: JupyterFrontEnd, - factory: IFileBrowserFactory, - translator: ITranslator - ): void => { - const trans = translator.load('jupyterlab'); - const { commands } = app; - const { tracker } = factory; - - commands.addCommand(CommandIDs.download, { - execute: () => { - const widget = tracker.currentWidget; - - if (widget) { - return widget.download(); - } - }, - icon: downloadIcon.bindprops({ stylesheet: 'menuItem' }), - label: trans.__('Download') - }); - - commands.addCommand(CommandIDs.copyDownloadLink, { - execute: () => { - const widget = tracker.currentWidget; - if (!widget) { - return; - } - - return widget.model.manager.services.contents - .getDownloadUrl(widget.selectedItems().next()!.path) - .then(url => { - Clipboard.copyToSystem(url); - }); - }, - icon: copyIcon.bindprops({ stylesheet: 'menuItem' }), - label: trans.__('Copy Download Link'), - mnemonic: 0 - }); - } -}; +// const downloadPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:download', +// requires: [IFileBrowserFactory, ITranslator], +// autoStart: true, +// activate: ( +// app: JupyterFrontEnd, +// factory: IFileBrowserFactory, +// translator: ITranslator +// ): void => { +// const trans = translator.load('jupyterlab'); +// const { commands } = app; +// const { tracker } = factory; + +// commands.addCommand(CommandIDs.download, { +// execute: () => { +// const widget = tracker.currentWidget; + +// if (widget) { +// return widget.download(); +// } +// }, +// icon: downloadIcon.bindprops({ stylesheet: 'menuItem' }), +// label: trans.__('Download') +// }); + +// commands.addCommand(CommandIDs.copyDownloadLink, { +// execute: () => { +// const widget = tracker.currentWidget; +// if (!widget) { +// return; +// } + +// return widget.model.manager.services.contents +// .getDownloadUrl(widget.selectedItems().next()!.path) +// .then(url => { +// Clipboard.copyToSystem(url); +// }); +// }, +// icon: copyIcon.bindprops({ stylesheet: 'menuItem' }), +// label: trans.__('Copy Download Link'), +// mnemonic: 0 +// }); +// } +// }; /** * A plugin to add the file browser widget to an ILabShell @@ -398,7 +405,8 @@ const browserWidget: JupyterFrontEndPlugin = { // Set attributes when adding the browser to the UI browser.node.setAttribute('role', 'region'); browser.node.setAttribute('aria-label', trans.__('File Browser Section')); - browser.title.icon = folderIcon; + browser.title.icon = analysisIcon; + browser.title.label = '可视化编程'; // Toolbar toolbarRegistry.registerFactory( @@ -519,43 +527,43 @@ const browserWidget: JupyterFrontEndPlugin = { * /user-redirect URL for JupyterHub), disable this plugin and replace it * with another implementation. */ -const shareFile: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:share-file', - requires: [IFileBrowserFactory, ITranslator], - autoStart: true, - activate: ( - app: JupyterFrontEnd, - factory: IFileBrowserFactory, - translator: ITranslator - ): void => { - const trans = translator.load('jupyterlab'); - const { commands } = app; - const { tracker } = factory; - - commands.addCommand(CommandIDs.copyShareableLink, { - execute: () => { - const widget = tracker.currentWidget; - const model = widget?.selectedItems().next(); - if (!model) { - return; - } - - Clipboard.copyToSystem( - PageConfig.getUrl({ - workspace: PageConfig.defaultWorkspace, - treePath: model.path, - toShare: true - }) - ); - }, - isVisible: () => - !!tracker.currentWidget && - toArray(tracker.currentWidget.selectedItems()).length === 1, - icon: linkIcon.bindprops({ stylesheet: 'menuItem' }), - label: trans.__('Copy Shareable Link') - }); - } -}; +// const shareFile: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:share-file', +// requires: [IFileBrowserFactory, ITranslator], +// autoStart: true, +// activate: ( +// app: JupyterFrontEnd, +// factory: IFileBrowserFactory, +// translator: ITranslator +// ): void => { +// const trans = translator.load('jupyterlab'); +// const { commands } = app; +// const { tracker } = factory; + +// commands.addCommand(CommandIDs.copyShareableLink, { +// execute: () => { +// const widget = tracker.currentWidget; +// const model = widget?.selectedItems().next(); +// if (!model) { +// return; +// } + +// Clipboard.copyToSystem( +// PageConfig.getUrl({ +// workspace: PageConfig.defaultWorkspace, +// treePath: model.path, +// toShare: true +// }) +// ); +// }, +// isVisible: () => +// !!tracker.currentWidget && +// toArray(tracker.currentWidget.selectedItems()).length === 1, +// icon: linkIcon.bindprops({ stylesheet: 'menuItem' }), +// label: trans.__('Copy Shareable Link') +// }); +// } +// }; /** * The "Open With" context menu. @@ -563,51 +571,51 @@ const shareFile: JupyterFrontEndPlugin = { * This is its own plugin in case you would like to disable this feature. * e.g. jupyter labextension disable @jupyterlab/filebrowser-extension:open-with */ -const openWithPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:open-with', - requires: [IFileBrowserFactory], - autoStart: true, - activate: (app: JupyterFrontEnd, factory: IFileBrowserFactory): void => { - const { docRegistry } = app; - const { tracker } = factory; - - function updateOpenWithMenu(contextMenu: ContextMenu) { - const openWith = - contextMenu.menu.items.find( - item => - item.type === 'submenu' && - item.submenu?.id === 'jp-contextmenu-open-with' - )?.submenu ?? null; - - if (!openWith) { - return; // Bail early if the open with menu is not displayed - } - - // clear the current menu items - openWith.clearItems(); - - // get the widget factories that could be used to open all of the items - // in the current filebrowser selection - const factories = tracker.currentWidget - ? Private.OpenWith.intersection( - map(tracker.currentWidget.selectedItems(), i => { - return Private.OpenWith.getFactories(docRegistry, i); - }) - ) - : new Set(); - - // make new menu items from the widget factories - factories.forEach(factory => { - openWith.addItem({ - args: { factory: factory }, - command: CommandIDs.open - }); - }); - } - - app.contextMenu.opened.connect(updateOpenWithMenu); - } -}; +// const openWithPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:open-with', +// requires: [IFileBrowserFactory], +// autoStart: true, +// activate: (app: JupyterFrontEnd, factory: IFileBrowserFactory): void => { +// const { docRegistry } = app; +// const { tracker } = factory; + +// function updateOpenWithMenu(contextMenu: ContextMenu) { +// const openWith = +// contextMenu.menu.items.find( +// item => +// item.type === 'submenu' && +// item.submenu?.id === 'jp-contextmenu-open-with' +// )?.submenu ?? null; + +// if (!openWith) { +// return; // Bail early if the open with menu is not displayed +// } + +// // clear the current menu items +// openWith.clearItems(); + +// // get the widget factories that could be used to open all of the items +// // in the current filebrowser selection +// const factories = tracker.currentWidget +// ? Private.OpenWith.intersection( +// map(tracker.currentWidget.selectedItems(), i => { +// return Private.OpenWith.getFactories(docRegistry, i); +// }) +// ) +// : new Set(); + +// // make new menu items from the widget factories +// factories.forEach(factory => { +// openWith.addItem({ +// args: { factory: factory }, +// command: CommandIDs.open +// }); +// }); +// } + +// app.contextMenu.opened.connect(updateOpenWithMenu); +// } +// }; /** * The "Open in New Browser Tab" context menu. @@ -618,181 +626,181 @@ const openWithPlugin: JupyterFrontEndPlugin = { * Note: If disabling this, you may also want to disable: * @jupyterlab/docmanager-extension:open-browser-tab */ -const openBrowserTabPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:open-browser-tab', - requires: [IFileBrowserFactory, ITranslator], - autoStart: true, - activate: ( - app: JupyterFrontEnd, - factory: IFileBrowserFactory, - translator: ITranslator - ): void => { - const { commands } = app; - const trans = translator.load('jupyterlab'); - const { tracker } = factory; - - commands.addCommand(CommandIDs.openBrowserTab, { - execute: args => { - const widget = tracker.currentWidget; - - if (!widget) { - return; - } - - const mode = args['mode'] as string | undefined; - - return Promise.all( - toArray( - map(widget.selectedItems(), item => { - if (mode === 'single-document') { - const url = PageConfig.getUrl({ - mode: 'single-document', - treePath: item.path - }); - const opened = window.open(); - if (opened) { - opened.opener = null; - opened.location.href = url; - } else { - throw new Error('Failed to open new browser tab.'); - } - } else { - return commands.execute('docmanager:open-browser-tab', { - path: item.path - }); - } - }) - ) - ); - }, - icon: addIcon.bindprops({ stylesheet: 'menuItem' }), - label: args => - args['mode'] === 'single-document' - ? trans.__('Open in Simple Mode') - : trans.__('Open in New Browser Tab'), - mnemonic: 0 - }); - } -}; +// const openBrowserTabPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:open-browser-tab', +// requires: [IFileBrowserFactory, ITranslator], +// autoStart: true, +// activate: ( +// app: JupyterFrontEnd, +// factory: IFileBrowserFactory, +// translator: ITranslator +// ): void => { +// const { commands } = app; +// const trans = translator.load('jupyterlab'); +// const { tracker } = factory; + +// commands.addCommand(CommandIDs.openBrowserTab, { +// execute: args => { +// const widget = tracker.currentWidget; + +// if (!widget) { +// return; +// } + +// const mode = args['mode'] as string | undefined; + +// return Promise.all( +// toArray( +// map(widget.selectedItems(), item => { +// if (mode === 'single-document') { +// const url = PageConfig.getUrl({ +// mode: 'single-document', +// treePath: item.path +// }); +// const opened = window.open(); +// if (opened) { +// opened.opener = null; +// opened.location.href = url; +// } else { +// throw new Error('Failed to open new browser tab.'); +// } +// } else { +// return commands.execute('docmanager:open-browser-tab', { +// path: item.path +// }); +// } +// }) +// ) +// ); +// }, +// icon: addIcon.bindprops({ stylesheet: 'menuItem' }), +// label: args => +// args['mode'] === 'single-document' +// ? trans.__('Open in Simple Mode') +// : trans.__('Open in New Browser Tab'), +// mnemonic: 0 +// }); +// } +// }; /** * A plugin providing file upload status. */ -export const fileUploadStatus: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:file-upload-status', - autoStart: true, - requires: [IFileBrowserFactory, ITranslator], - optional: [IStatusBar], - activate: ( - app: JupyterFrontEnd, - browser: IFileBrowserFactory, - translator: ITranslator, - statusBar: IStatusBar | null - ) => { - if (!statusBar) { - // Automatically disable if statusbar missing - return; - } - const item = new FileUploadStatus({ - tracker: browser.tracker, - translator - }); - - statusBar.registerStatusItem( - '@jupyterlab/filebrowser-extension:file-upload-status', - { - item, - align: 'middle', - isActive: () => { - return !!item.model && item.model.items.length > 0; - }, - activeStateChanged: item.model.stateChanged - } - ); - } -}; +// export const fileUploadStatus: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:file-upload-status', +// autoStart: true, +// requires: [IFileBrowserFactory, ITranslator], +// optional: [IStatusBar], +// activate: ( +// app: JupyterFrontEnd, +// browser: IFileBrowserFactory, +// translator: ITranslator, +// statusBar: IStatusBar | null +// ) => { +// if (!statusBar) { +// // Automatically disable if statusbar missing +// return; +// } +// const item = new FileUploadStatus({ +// tracker: browser.tracker, +// translator +// }); + +// statusBar.registerStatusItem( +// '@jupyterlab/filebrowser-extension:file-upload-status', +// { +// item, +// align: 'middle', +// isActive: () => { +// return !!item.model && item.model.items.length > 0; +// }, +// activeStateChanged: item.model.stateChanged +// } +// ); +// } +// }; /** * A plugin to open files from remote URLs */ -const openUrlPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/filebrowser-extension:open-url', - autoStart: true, - requires: [IFileBrowserFactory, ITranslator], - optional: [ICommandPalette], - activate: ( - app: JupyterFrontEnd, - factory: IFileBrowserFactory, - translator: ITranslator, - palette: ICommandPalette | null - ) => { - const { commands } = app; - const trans = translator.load('jupyterlab'); - const { defaultBrowser: browser } = factory; - const command = CommandIDs.openUrl; - - commands.addCommand(command, { - label: args => - args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'), - caption: args => - args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL'), - execute: async args => { - let url: string | undefined = (args?.url as string) ?? ''; - if (!url) { - url = - ( - await InputDialog.getText({ - label: trans.__('URL'), - placeholder: 'https://example.com/path/to/file', - title: trans.__('Open URL'), - okLabel: trans.__('Open') - }) - ).value ?? undefined; - } - if (!url) { - return; - } - - let type = ''; - let blob; - - // fetch the file from the URL - try { - const req = await fetch(url); - blob = await req.blob(); - type = req.headers.get('Content-Type') ?? ''; - } catch (reason) { - if (reason.response && reason.response.status !== 200) { - reason.message = trans.__('Could not open URL: %1', url); - } - return showErrorMessage(trans.__('Cannot fetch'), reason); - } - - // upload the content of the file to the server - try { - const name = PathExt.basename(url); - const file = new File([blob], name, { type }); - const model = await browser.model.upload(file); - return commands.execute('docmanager:open', { - path: model.path - }); - } catch (error) { - return showErrorMessage( - trans._p('showErrorMessage', 'Upload Error'), - error - ); - } - } - }); - - if (palette) { - palette.addItem({ - command, - category: trans.__('File Operations') - }); - } - } -}; +// const openUrlPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/filebrowser-extension:open-url', +// autoStart: true, +// requires: [IFileBrowserFactory, ITranslator], +// optional: [ICommandPalette], +// activate: ( +// app: JupyterFrontEnd, +// factory: IFileBrowserFactory, +// translator: ITranslator, +// palette: ICommandPalette | null +// ) => { +// const { commands } = app; +// const trans = translator.load('jupyterlab'); +// const { defaultBrowser: browser } = factory; +// const command = CommandIDs.openUrl; + +// commands.addCommand(command, { +// label: args => +// args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'), +// caption: args => +// args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL'), +// execute: async args => { +// let url: string | undefined = (args?.url as string) ?? ''; +// if (!url) { +// url = +// ( +// await InputDialog.getText({ +// label: trans.__('URL'), +// placeholder: 'https://example.com/path/to/file', +// title: trans.__('Open URL'), +// okLabel: trans.__('Open') +// }) +// ).value ?? undefined; +// } +// if (!url) { +// return; +// } + +// let type = ''; +// let blob; + +// // fetch the file from the URL +// try { +// const req = await fetch(url); +// blob = await req.blob(); +// type = req.headers.get('Content-Type') ?? ''; +// } catch (reason) { +// if (reason.response && reason.response.status !== 200) { +// reason.message = trans.__('Could not open URL: %1', url); +// } +// return showErrorMessage(trans.__('Cannot fetch'), reason); +// } + +// // upload the content of the file to the server +// try { +// const name = PathExt.basename(url); +// const file = new File([blob], name, { type }); +// const model = await browser.model.upload(file); +// return commands.execute('docmanager:open', { +// path: model.path +// }); +// } catch (error) { +// return showErrorMessage( +// trans._p('showErrorMessage', 'Upload Error'), +// error +// ); +// } +// } +// }); + +// if (palette) { +// palette.addItem({ +// command, +// category: trans.__('File Operations') +// }); +// } +// } +// }; /** * Add the main file browser commands to the application's command registry. @@ -1342,13 +1350,13 @@ namespace Private { const plugins: JupyterFrontEndPlugin[] = [ factory, browser, - shareFile, - fileUploadStatus, - downloadPlugin, - browserWidget, - openWithPlugin, - openBrowserTabPlugin, - openUrlPlugin + // shareFile, + // fileUploadStatus, + // downloadPlugin, + browserWidget + // openWithPlugin, + // openBrowserTabPlugin, + // openUrlPlugin ]; export default plugins; ``` - 侧边栏图标改动 ```diff diff --git a/packages/filebrowser-extension/src/svg.d.ts b/packages/filebrowser-extension/src/svg.d.ts new file mode 100644 index 0000000000..0ca27598c5 --- /dev/null +++ b/packages/filebrowser-extension/src/svg.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const value: string; + export default value; +} diff --git a/packages/filebrowser-extension/style/icons/analysis.svg b/packages/filebrowser-extension/style/icons/analysis.svg new file mode 100644 index 0000000000..780413c090 --- /dev/null +++ b/packages/filebrowser-extension/style/icons/analysis.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file ``` - 将原来的文件浏览器改为编程列表, 修改布局和样式 ```diff diff --git a/packages/filebrowser/src/browser.ts b/packages/filebrowser/src/browser.ts index d7fde430e2..fbe49b2562 100644 --- a/packages/filebrowser/src/browser.ts +++ b/packages/filebrowser/src/browser.ts @@ -106,10 +106,15 @@ export class FileBrowser extends Widget { this._filenameSearcher.addClass(FILTERBOX_CLASS); this.listing.addClass(LISTING_CLASS); + const header = new Widget(); + header.node.textContent = '编程列表'; + header.addClass('jp-FileBrowser-header'); + this.layout = new PanelLayout(); - this.layout.addWidget(this.toolbar); - this.layout.addWidget(this._filenameSearcher); - this.layout.addWidget(this.crumbs); + this.layout.addWidget(header); + // this.layout.addWidget(this.toolbar); + // this.layout.addWidget(this._filenameSearcher); + // this.layout.addWidget(this.crumbs); this.layout.addWidget(this.listing); if (options.restore !== false) { @@ -180,12 +185,12 @@ export class FileBrowser extends Widget { }); this._filenameSearcher.addClass(FILTERBOX_CLASS); - this.layout.removeWidget(this._filenameSearcher); - this.layout.removeWidget(this.crumbs); + // this.layout.removeWidget(this._filenameSearcher); + // this.layout.removeWidget(this.crumbs); this.layout.removeWidget(this.listing); - this.layout.addWidget(this._filenameSearcher); - this.layout.addWidget(this.crumbs); + // this.layout.addWidget(this._filenameSearcher); + // this.layout.addWidget(this.crumbs); this.layout.addWidget(this.listing); } diff --git a/packages/filebrowser/src/crumbs.ts b/packages/filebrowser/src/crumbs.ts index b67e132a31..cd098f37fb 100644 --- a/packages/filebrowser/src/crumbs.ts +++ b/packages/filebrowser/src/crumbs.ts @@ -73,8 +73,9 @@ export class BreadCrumbs extends Widget { this.addClass(BREADCRUMB_CLASS); this._crumbs = Private.createCrumbs(); this._crumbSeps = Private.createCrumbSeparators(); - const hasPreferred = PageConfig.getOption('preferredPath'); - this._hasPreferred = hasPreferred && hasPreferred !== '/' ? true : false; + // const hasPreferred = PageConfig.getOption('preferredPath'); + // this._hasPreferred = hasPreferred && hasPreferred !== '/' ? true : false; + this._hasPreferred = false; if (this._hasPreferred) { this.node.appendChild(this._crumbs[Private.Crumb.Preferred]); } diff --git a/packages/filebrowser/src/directoryswitcher.ts b/packages/filebrowser/src/directoryswitcher.ts new file mode 100644 index 0000000000..2a96059486 --- /dev/null +++ b/packages/filebrowser/src/directoryswitcher.ts @@ -0,0 +1,19 @@ +import { Widget } from '@lumino/widgets'; + +export default class DirectorySwitcher extends Widget { + constructor(options: Widget.IOptions) { + super(options); + this._createButtons(); + } + + private _createButtons() { + const btn1 = document.createElement('button'); + btn1.textContent = '开发目录'; + btn1.classList.add('jldbq-btn', 'jldbq-btn-current'); + this.node.appendChild(btn1); + const btn2 = document.createElement('button'); + btn2.textContent = '通用算子库'; + btn2.classList.add('jldbq-btn'); + this.node.appendChild(btn2); + } +} diff --git a/packages/filebrowser/style/base.css b/packages/filebrowser/style/base.css index 940da54f23..1973c3f35a 100644 --- a/packages/filebrowser/style/base.css +++ b/packages/filebrowser/style/base.css @@ -36,6 +36,13 @@ justify-content: flex-start; } +.jp-FileBrowser-header { + margin: 5px; + padding: 15px; + font-weight: bold; + border-bottom: 1px solid #dbe3ea; +} + .jp-BreadCrumbs { flex: 0 0 auto; margin: 8px 12px 8px 12px; @@ -127,13 +134,9 @@ outline: 0; } -.jp-DirListing:focus-visible { - border: 1px solid var(--jp-brand-color1); -} - .jp-DirListing-header { flex: 0 0 auto; - display: flex; + display: none; flex-direction: row; overflow: hidden; border-top: var(--jp-border-width) solid var(--jp-border-color2); @@ -190,7 +193,6 @@ padding: 0; list-style-type: none; overflow: auto; - background-color: var(--jp-layout-color1); } .jp-DirListing-content mark { @@ -251,6 +253,7 @@ } .jp-DirListing-itemModified { + display: none; flex: 0 0 125px; text-align: right; } ``` - 添加数据资产相关代码 ```diff diff --git a/packages/jldbq-extenison/package.json b/packages/jldbq-extenison/package.json new file mode 100644 index 0000000000..02e5c0f464 --- /dev/null +++ b/packages/jldbq-extenison/package.json @@ -0,0 +1,61 @@ +{ + "name": "@jupyterlab/jldbq-extension", + "version": "1.0.0", + "description": "JLDBQ Extension", + "homepage": "", + "license": "MIT", + "author": "yili", + "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,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "schema/*.json" + ], + "scripts": { + "build": "tsc -b", + "clean": "rimraf lib && rimraf tsconfig.tsbuildinfo", + "docs": "typedoc src", + "watch": "tsc -b --watch" + }, + "dependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@jupyterlab/application": "^3.4.3", + "@jupyterlab/apputils": "^3.4.3", + "@jupyterlab/settingregistry": "^3.4.3", + "@jupyterlab/ui-components": "^3.4.3", + "@lumino/signaling": "^1.10.0", + "@lumino/widgets": "^1.30.0", + "@mui/icons-material": "^5.8.0", + "@mui/lab": "^5.0.0-alpha.83", + "@mui/material": "^5.8.1", + "@silevis/reactgrid": "^4.0.3", + "antd": "^4.22.3", + "moment": "^2.29.4", + "react": "^17.0.1", + "react-select": "^5.4.0", + "swr": "^1.3.0" + }, + "devDependencies": { + "rimraf": "~3.0.0", + "typedoc": "~0.21.2", + "typescript": "~4.1.3" + }, + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "extension": true, + "schemaDir": "schema" + }, + "styleModule": "style/index.js" +} ``` - 数据资产前端连接后端的配置项 ```diff diff --git a/packages/jldbq-extenison/schema/plugin.json b/packages/jldbq-extenison/schema/plugin.json new file mode 100644 index 0000000000..8a5b32c902 --- /dev/null +++ b/packages/jldbq-extenison/schema/plugin.json @@ -0,0 +1,14 @@ +{ + "title": "JLDBQ", + "description": "JLDBQ settings.", + "type": "object", + "properties": { + "flaskBackend": { + "title": "Flask Backend address.", + "description": "Flask Backend address.", + "type": "string", + "default": "http://127.0.0.1:5000" + } + }, + "additionalProperties": false +} ``` - 实现数据预览的表格 ```diff diff --git a/packages/jldbq-extenison/src/DataTable.tsx b/packages/jldbq-extenison/src/DataTable.tsx new file mode 100644 index 0000000000..2f797b7356 --- /dev/null +++ b/packages/jldbq-extenison/src/DataTable.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Cell, Column, Id, ReactGrid, Row } from '@silevis/reactgrid'; +import { useMemo, useState } from 'react'; +import { ITableData } from './api/datasource'; +import '@silevis/reactgrid/styles.css'; + +interface IProps { + data: ITableData; +} + +const getText = (value: any): string => { + if (value === null || value === undefined) { + return ''; + } else if (typeof value === 'object') { + return JSON.stringify(value); + } else { + return value.toString(); + } +}; + +const DataTable = ({ data }: IProps): JSX.Element => { + const [cols, setCols] = useState(() => + Object.keys(data).map(columnId => { + return { columnId, width: 100, resizable: true }; + }) + ); + + const rows = useMemo((): Row[] => { + const colNames = Object.keys(data); + const header = { + rowId: 'header', + cells: colNames.map(text => { + return { + text, + type: 'header' + }; + }) + }; + const dataRows = Object.keys(data[colNames[0]]).map(rowId => { + return { + rowId, + cells: colNames.map(c => { + return { + type: 'text', + text: getText(data[c][rowId]), + nonEditable: true + }; + }) + }; + }); + return [header, ...dataRows]; + }, [data]); + + const handleColumnResize = (ci: Id, width: number) => { + setCols(prevColumns => { + const columnIndex = prevColumns.findIndex(el => el.columnId === ci); + const resizedColumn = prevColumns[columnIndex]; + const updatedColumn = { ...resizedColumn, width }; + prevColumns[columnIndex] = updatedColumn; + return [...prevColumns]; + }); + }; + + return ( +
+ +
+ ); +}; + +export default DataTable; diff --git a/packages/jldbq-extenison/src/DataTableWidget.tsx b/packages/jldbq-extenison/src/DataTableWidget.tsx new file mode 100644 index 0000000000..ff8ed85207 --- /dev/null +++ b/packages/jldbq-extenison/src/DataTableWidget.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { ReactWidget } from '@jupyterlab/apputils'; +import { Widget } from '@lumino/widgets'; +import DataTable from './DataTable'; +import { fetchTableContent, IDatasource, ITableData } from './api/datasource'; + +class DataTableWidget extends ReactWidget { + constructor( + datasource: IDatasource, + tableName: string, + options?: Widget.IOptions + ) { + super(options); + void this._requestData(datasource, tableName); + } + + private _requestData = async (datasource: IDatasource, tableName: string) => { + const res = await fetchTableContent(datasource, tableName); + if (res === 'error') { + this._err = res; + } else { + this._data = res; + } + this.update(); + }; + + render(): JSX.Element { + if (this._err) { + return
Error
; + } else if (!this._data) { + return
Loading ...
; + } else { + return ; + } + } + + private _data: ITableData | null = null; + private _err: any; +} + +export default DataTableWidget; ``` - 实现数据源管理面板 ```diff diff --git a/packages/jldbq-extenison/src/DataView.tsx b/packages/jldbq-extenison/src/DataView.tsx new file mode 100644 index 0000000000..b2af5d4cf8 --- /dev/null +++ b/packages/jldbq-extenison/src/DataView.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { useState } from 'react'; + +interface IProps { + dataSourceElement: JSX.Element; + dataSyncElement: JSX.Element; +} + +type ViewType = 'datasource' | 'datasync'; + +const SwitchButton: React.FunctionComponent<{ + viewType: ViewType; + current: ViewType; + label: string; + onClick: (t: ViewType) => void; +}> = props => { + return ( + + ); +}; + +const DataView: React.FunctionComponent = props => { + const [view, setView] = useState('datasource' as ViewType); + + const el = + view === 'datasource' ? props.dataSourceElement : props.dataSyncElement; + + return ( +
+
+ + +
+ {el} +
+ ); +}; + +export default DataView; diff --git a/packages/jldbq-extenison/src/DataViewWidget.tsx b/packages/jldbq-extenison/src/DataViewWidget.tsx new file mode 100644 index 0000000000..0ab84b92f9 --- /dev/null +++ b/packages/jldbq-extenison/src/DataViewWidget.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { ReactWidget } from '@jupyterlab/apputils'; +import { ISignal, Signal } from '@lumino/signaling'; +import DatasourceView from './DatasourceView'; +import DatasyncView from './DatasyncView'; +import DataView from './DataView'; +import { IDatasource } from './api/datasource'; +import { Widget } from '@lumino/widgets'; + +interface ITableOpenSignalData { + datasource: IDatasource; + tableName: string; +} + +class DataViewWidget extends ReactWidget { + constructor(manager: string, options?: Widget.IOptions) { + super(options); + this._manager = manager; + } + + public get tableOpened(): ISignal { + return this._tableOpened; + } + + render(): JSX.Element { + const datasourceView = ( + + this._tableOpened.emit({ datasource, tableName }) + } + /> + ); + + const datasyncView = ; + + return ( +
+ +
+ ); + } + + private _manager: string; + private _tableOpened = new Signal(this); +} + +export default DataViewWidget; ``` - 实现添加数据源的表单 ```diff diff --git a/packages/jldbq-extenison/src/DatasourceForm.tsx b/packages/jldbq-extenison/src/DatasourceForm.tsx new file mode 100644 index 0000000000..e4a470440d --- /dev/null +++ b/packages/jldbq-extenison/src/DatasourceForm.tsx @@ -0,0 +1,185 @@ +import React from 'react'; +import { Dialog, ReactWidget } from '@jupyterlab/apputils'; +import { IDatasource } from './api/datasource'; +import { Widget } from '@lumino/widgets'; + +interface IDatasourceItem { + address: string; + username: string; + password: string; + databasename: string; + type: 'mysql' | 'hive'; +} + +interface IProps { + value: Partial; + error: keyof IDatasourceItem | null; + onChange: (v: Partial) => void; +} + +const DatasourceForm: React.FunctionComponent = ({ + value, + error, + onChange +}) => { + return ( +
+ + + + + +
+ ); +}; + +export class DatasourceFormDialogBody + extends ReactWidget + implements Dialog.IBodyWidget { + constructor(manager: string, options?: Widget.IOptions) { + super(options); + this._manager = manager; + } + render(): JSX.Element { + return ( + { + this._datasourceItem = { ...this._datasourceItem, ...v }; + this._error = null; + this.update(); + }} + /> + ); + } + + getValue(): IDatasource { + const { + address, + username: user, + password, + databasename, + type: source + } = this._datasourceItem; + if (!source) { + this._setError('type'); + } + if (!address || !validateIpAndPort(address)) { + this._setError('address'); + } + const [host, portStr] = address.split(':'); + const port = portStr ? +portStr : 3306; + if (!user) { + this._setError('username'); + } + if (!password) { + this._setError('password'); + } + if (!databasename) { + this._setError('databasename'); + } + return { + host, + user, + port, + password, + charset: 'utf8', + source, + databasename, + sourcename: databasename, + manager: this._manager + }; + } + + private _setError(error: keyof IDatasourceItem): never { + this._error = error; + this.update(); + throw new Error(error); + } + + private _datasourceItem: Partial = {}; + private _error: IProps['error'] = null; + private _manager: string; +} + +function validateIpAndPort(input: string): boolean { + const parts = input.split(':'); + if (parts.length > 2) { + return false; + } + const ip = parts[0].split('.'); + if ( + ip.length !== 4 || + !ip.every(function (segment) { + return validateNum(segment, 0, 255); + }) + ) { + return false; + } + const port = parts[1]; + if (port && !validateNum(port, 1, 65535)) { + return false; + } + return true; +} + +function validateNum(input: string, min: number, max: number): boolean { + const num = +input; + return num >= min && num <= max && input === num.toString(); +} ``` - 实现数据源预览的面板 ```diff diff --git a/packages/jldbq-extenison/src/DatasourceView.tsx b/packages/jldbq-extenison/src/DatasourceView.tsx new file mode 100644 index 0000000000..f7e97e0702 --- /dev/null +++ b/packages/jldbq-extenison/src/DatasourceView.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { useState } from 'react'; +import { Dialog, showDialog } from '@jupyterlab/apputils'; +import TreeView from '@mui/lab/TreeView'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import TreeItem from '@mui/lab/TreeItem'; +import databaseSvgStr from '../style/icons/database-solid-ghost.svg'; +import { + fetchTables, + IDatasource, + setDatasource, + useDatasourceList +} from './api/datasource'; +import { DatasourceFormDialogBody } from './DatasourceForm'; + +const dragImg = new Image(); +dragImg.src = 'data:image/svg+xml;base64,' + btoa(databaseSvgStr); + +interface IProps { + manager: string; + onOpenTable: (datasource: IDatasource, tableName: string) => void; +} + +const DatasourceView: React.FunctionComponent = ({ + manager, + onOpenTable +}) => { + const { datasources, loading, error, refresh } = useDatasourceList(manager); + const [tables, setTables] = useState< + Partial<{ + [key: string]: string[] | 'error'; + }> + >({}); + + const handleNodeToggle = async (ds: IDatasource) => { + if (!tables[ds.sourcename]) { + const tables = await fetchTables(ds); + setTables(prev => { + return { [ds.sourcename]: tables, ...prev }; + }); + } + }; + + let tree: JSX.Element[] | JSX.Element; + if (error) { + tree =
Error
; + } else if (loading) { + tree =
Loading ...
; + } else { + tree = datasources!.map(ds => { + const table = tables[ds.sourcename]; + let tableItems; + if (!table) { + tableItems = ( + + ); + } else if (table === 'error') { + tableItems = ( + + ); + } else if (table.length === 0) { + tableItems = ( + + ); + } else { + tableItems = table.map(t => { + return ( + onOpenTable(ds, t)} + draggable="true" + onDragStart={evt => { + const data = { + operation: 'addToCanvas', + data: { + editType: 'createNode', + finalized: true, + nodeTemplate: { + id: `${ds.manager}-${ds.sourcename}-${t}`, + op: `database-${ds.source}-input`, + description: t, + type: 'execution_node', + label: t, + inputs: [], + parameters: {}, + outputs: [ + { + id: 'outPort', + app_data: { + ui_data: { + cardinality: { + min: 0, + max: -1 + }, + label: 'Output Port' + } + } + } + ], + app_data: { + ui_data: { + description: t, + image: '', + label: t + } + } + } + } + }; + evt.dataTransfer.setData('text/plain', JSON.stringify(data)); + evt.dataTransfer.setDragImage(dragImg, 10, 10); + }} + onFocusCapture={e => e.stopPropagation()} + /> + ); + }); + } + return ( + handleNodeToggle(ds)} + > + {tableItems} + + ); + }); + } + + const handleAddDatasource = async () => { + const { value } = await showDialog({ + title: '添加数据源', + body: new DatasourceFormDialogBody(manager), + hasClose: true, + buttons: [ + Dialog.okButton({ label: '确定' }), + Dialog.cancelButton({ label: '取消' }) + ] + }); + if (!value) { + return; + } + await setDatasource(value); + refresh(); + }; + + return ( +
+
+ +
+ } + defaultExpandIcon={} + sx={{ overflow: 'auto', flexGrow: 1 }} + > + {tree} + +
+ ); +}; + +export default DatasourceView; ``` - 合并了数据同步表单的实现代码 ```diff diff --git a/packages/jldbq-extenison/src/DatasyncForm.tsx b/packages/jldbq-extenison/src/DatasyncForm.tsx new file mode 100644 index 0000000000..d638ff736e --- /dev/null +++ b/packages/jldbq-extenison/src/DatasyncForm.tsx @@ -0,0 +1,322 @@ +import React, { useState } from 'react'; +import { + Button, + DatePicker, + Form, + InputNumber, + Radio, + RadioChangeEvent, + Select, + Space +} from 'antd'; +import { IDatasource } from './api/datasource'; +import arrowImgUrl from '../style/img/arrow.png'; +import deleteImgUrl from '../style/img/delete.png'; + +type FreqUnit = 'day' | 'week' | 'month'; + +interface ISyncMeta { + startTime: Date; + frequency: number; + freqUnit: FreqUnit; +} + +interface ISyncMapping { + srcDatasource: IDatasource; + srcTableName: string; + dstDatasource: IDatasource; + dstTableName: string; +} + +export interface ISyncData { + mapping: ISyncMapping; + meta: ISyncMeta; +} + +interface IProps { + datasourceList: IDatasource[]; + tableListFetcher: (ds: IDatasource) => Promise; + onConfirm: (data: ISyncData[]) => void; + onCancel: () => void; +} + +const DatasyncForm: React.FunctionComponent = props => { + const [srcDatasource, setSrcDatasource] = useState(null); + const [srcTableName, setSrcTableName] = useState(null); + const [srcTableList, setSrcTableList] = useState([]); + const [dstDatasource, setDstDatasource] = useState(null); + const [dstTableName, setDstTableName] = useState(null); + const [dstTableList, setDstTableList] = useState([]); + const [mappingList, setMappingList] = useState([]); + const [form] = Form.useForm(); + + const onFinish = (meta: any) => { + // startTime = moment(startTime).toDate(); + const results = mappingList.map(mapping => { + return { + mapping, + meta: { + startTime: meta.startTime.toDate(), + frequency: Math.max(parseInt(meta.frequency), 0), + freqUnit: meta.freqUnit as FreqUnit + } + }; + }); + props.onConfirm(results); + }; + + const sourceDatasourceChange = (sourcename: string) => { + const ds = props.datasourceList.find( + item => item.sourcename === sourcename + )!; + setSrcDatasource(ds); + setSrcTableName(null); + setSrcTableList([]); + void props.tableListFetcher(ds).then(lst => { + setSrcTableList(lst); + }); + }; + + const destDatasourceChange = (sourcename: string) => { + const ds = props.datasourceList.find( + item => item.sourcename === sourcename + )!; + setDstDatasource(ds); + setDstTableName(null); + setDstTableList([]); + void props.tableListFetcher(ds).then(lst => { + setDstTableList(lst); + }); + }; + + const sourceTableChange = (e: RadioChangeEvent) => { + setSrcTableName(e.target.value as string); + }; + + const destTableChange = (e: RadioChangeEvent) => { + setDstTableName(e.target.value as string); + }; + + const addMapping = () => { + if (!srcDatasource || !srcTableName || !dstDatasource || !dstTableName) { + return; + } + if ( + mappingList.find( + item => + item.srcDatasource.sourcename === srcDatasource.sourcename && + item.srcTableName === srcTableName && + item.dstDatasource.sourcename === dstDatasource.sourcename && + item.dstTableName === dstTableName + ) + ) { + return; + } + setMappingList([ + ...mappingList, + { + srcDatasource, + srcTableName, + dstDatasource, + dstTableName + } + ]); + }; + + const deleteMapping = (mapping: ISyncMapping) => { + setMappingList(mappingList.filter(item => item !== mapping)); + }; + + const freqUnitSelector = ( + + + + ); + + return ( +
+
+
+ + + + + + +
+ + + {srcTableList.map(item => { + return ( + + {item} + + ); + })} + + +
+
+
+
+ +
+ + + + + +
+ + + {dstTableList.map(item => { + return ( + + {item} + + ); + })} + + +
+
+
+
+
+

同步映射

+
+ {mappingList.map(item => { + const key = `${item.dstDatasource.sourcename}-${item.dstTableName}-${item.srcDatasource.sourcename}-${item.srcTableName}`; + return ( +

+ {item.srcTableName} + + {item.dstTableName} + { + deleteMapping(item); + }} + style={{ + width: '10px', + height: '10px', + marginLeft: '30px', + cursor: 'pointer' + }} + /> +

+ ); + })} +
+
+
+ + + + + + + + + /次 + + + + + +
+
+ ); +}; + +export default DatasyncForm; diff --git a/packages/jldbq-extenison/src/DatasyncView.tsx b/packages/jldbq-extenison/src/DatasyncView.tsx new file mode 100644 index 0000000000..910eef1ed6 --- /dev/null +++ b/packages/jldbq-extenison/src/DatasyncView.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { Dialog, ReactWidget } from '@jupyterlab/apputils'; +import { Widget } from '@lumino/widgets'; +import { Signal } from '@lumino/signaling'; +import DatasyncForm, { ISyncData } from './DatasyncForm'; + +interface IProps {} + +const DatasyncView: React.FunctionComponent = props => { + return ( +
+
+ +
+
+ ); +}; + +export default DatasyncView; + +class DatasyncFormDialogBody extends ReactWidget { + constructor(options?: Widget.IOptions) { + super(options); + } + + render(): JSX.Element { + Signal; + return ( + { + return ['1', '2', '3']; + }} + onConfirm={v => this._confirm.emit(v)} + onCancel={() => this._cancel.emit()} + /> + ); + } + + get confirm() { + return this._confirm; + } + + get cancel() { + return this._cancel; + } + + private _confirm = new Signal(this); + private _cancel = new Signal(this); +} + +class DatasyncFormDialogRenderer extends Dialog.Renderer { + createFooter(_buttons: ReadonlyArray): Widget { + return new Widget(); + } +} ``` - 调度监控 Stub (尚未实现) ```diff diff --git a/packages/jldbq-extenison/src/MonitorViewWidget.tsx b/packages/jldbq-extenison/src/MonitorViewWidget.tsx new file mode 100644 index 0000000000..02baa7af47 --- /dev/null +++ b/packages/jldbq-extenison/src/MonitorViewWidget.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { ReactWidget } from '@jupyterlab/apputils'; + +export default class MonitorWidget extends ReactWidget { + render(): JSX.Element { + return
; + } +} ``` - 定时任务 Stub (尚未实现) ```diff diff --git a/packages/jldbq-extenison/src/TaskViewWidget.tsx b/packages/jldbq-extenison/src/TaskViewWidget.tsx new file mode 100644 index 0000000000..9497654443 --- /dev/null +++ b/packages/jldbq-extenison/src/TaskViewWidget.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { ReactWidget } from '@jupyterlab/apputils'; + +export default class TaskViewWidget extends ReactWidget { + render(): JSX.Element { + return
; + } +} ``` - Backend API 相关代码 ```diff diff --git a/packages/jldbq-extenison/src/api/config.ts b/packages/jldbq-extenison/src/api/config.ts new file mode 100644 index 0000000000..78b468134b --- /dev/null +++ b/packages/jldbq-extenison/src/api/config.ts @@ -0,0 +1,5 @@ +const config = { + endpoint: 'http://192.168.51.19:5000' +}; + +export default config; diff --git a/packages/jldbq-extenison/src/api/datasource.ts b/packages/jldbq-extenison/src/api/datasource.ts new file mode 100644 index 0000000000..8927b678bb --- /dev/null +++ b/packages/jldbq-extenison/src/api/datasource.ts @@ -0,0 +1,106 @@ +import useSWR from 'swr'; +import config from './config'; + +type DbEnc = 'utf8'; +type DbType = 'mysql' | 'hive'; + +export interface IDatasource { + host: string; + user: string; + port: number; + password: string; + charset: DbEnc; + manager: string; + source: DbType; + databasename: string; + sourcename: string; +} + +interface IDatasourceResponse { + datasources?: IDatasource[]; + loading: boolean; + error?: any; + refresh: () => void; +} + +export type ITableData = Record>; + +export const useDatasourceList = (manager: string): IDatasourceResponse => { + const endpoint = config.endpoint; + const item = encodeURIComponent(manager); + const url = `${endpoint}/managelist/item=${item}`; + const { data, error, mutate } = useSWR(url, (...args) => + fetch(...args).then(res => res.json()) + ); + return { + datasources: data, + loading: !error && !data, + error, + refresh: mutate + }; +}; + +export const setDatasource = async ( + datasource: IDatasource +): Promise => { + const endpoint = config.endpoint; + const url = `${endpoint}/manage`; + try { + const resp = await fetch(url, { + method: 'post', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(datasource) + }); + if (!resp.ok) { + return 'error'; + } + const data = await resp.text(); + return data; + } catch (e) { + console.error(e); + return 'error'; + } +}; + +export const fetchTables = async ( + ds: IDatasource +): Promise<'error' | string[]> => { + const endpoint = config.endpoint; + const item = encodeURIComponent( + `${ds.manager};${ds.sourcename};${ds.databasename}` + ); + const url = `${endpoint}/show/${ds.source}/tables/item=${item}`; + try { + const resp = await fetch(url); + if (!resp.ok) { + return 'error'; + } + const data = (await resp.json()) as string[]; + return data; + } catch (e) { + console.error(e); + return 'error'; + } +}; + +export const fetchTableContent = async ( + ds: IDatasource, + t: string +): Promise => { + const endpoint = config.endpoint; + const item = encodeURIComponent( + `${ds.manager};${ds.sourcename};${ds.databasename};${t}` + ); + const url = `${endpoint}/show/${ds.source}/results/item=${item}`; + try { + const resp = await fetch(url); + if (!resp.ok) { + return 'error'; + } + const data = (await resp.json()) as ITableData; + return data; + } catch (e) { + console.error(e); + return 'error'; + } +}; diff --git a/packages/jldbq-extenison/src/api/index.ts b/packages/jldbq-extenison/src/api/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/jldbq-extenison/src/icons.ts b/packages/jldbq-extenison/src/icons.ts new file mode 100644 index 0000000000..9e2b76f812 --- /dev/null +++ b/packages/jldbq-extenison/src/icons.ts @@ -0,0 +1,19 @@ +import { LabIcon } from '@jupyterlab/ui-components'; +import cliSvgStr from '../style/icons/cli.svg'; +import timerSvgStr from '../style/icons/timer.svg'; +import monitorSvgStr from '../style/icons/monitor.svg'; + +export const cliIcon = new LabIcon({ + name: 'cli', + svgstr: cliSvgStr +}); + +export const timerIcon = new LabIcon({ + name: 'timer', + svgstr: timerSvgStr +}); + +export const monitorIcon = new LabIcon({ + name: 'monitor', + svgstr: monitorSvgStr +}); ``` - 实现数据开发的 JupyterLab 插件 (添加左侧侧边栏) ```diff diff --git a/packages/jldbq-extenison/src/index.ts b/packages/jldbq-extenison/src/index.ts new file mode 100644 index 0000000000..28da880058 --- /dev/null +++ b/packages/jldbq-extenison/src/index.ts @@ -0,0 +1,71 @@ +/** + * @packageDocumentation + * @module jldbq-extension + */ + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; +import { MainAreaWidget } from '@jupyterlab/apputils'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import DataViewWidget from './DataViewWidget'; +import DataTableWidget from './DataTableWidget'; +import TaskViewWidget from './TaskViewWidget'; +import MonitorViewWidget from './MonitorViewWidget'; +import { cliIcon, monitorIcon, timerIcon } from './icons'; +import config from './api/config'; + +const PLUGIN_ID = '@jupyterlab/jldbq-extension:plugin'; + +const plugin: JupyterFrontEndPlugin = { + id: PLUGIN_ID, + autoStart: true, + requires: [ISettingRegistry], + activate: async (app: JupyterFrontEnd, registry: ISettingRegistry) => { + registry.pluginChanged.connect(async (_sender, plugin) => { + if (plugin === PLUGIN_ID) { + await updateConfig(registry); + } + }); + await updateConfig(registry); + + const manager = 'test1'; // TODO: 读取当前用户 + + const dataViewWidget = new DataViewWidget(manager); + dataViewWidget.id = 'jldbq-data'; + dataViewWidget.title.icon = cliIcon; + dataViewWidget.title.caption = '数据开发'; + dataViewWidget.title.label = '数据开发'; + dataViewWidget.tableOpened.connect((_sender, { datasource, tableName }) => { + const content = new DataTableWidget(datasource, tableName); + const widget = new MainAreaWidget({ content }); + widget.title.label = tableName; + widget.title.icon = cliIcon; + app.shell.add(widget, 'main'); + }); + app.shell.add(dataViewWidget, 'left', { rank: 100 }); + + const taskViewWidget = new TaskViewWidget(); + taskViewWidget.id = 'jldbq-sync'; + taskViewWidget.title.icon = timerIcon; + taskViewWidget.title.caption = '定时任务'; + taskViewWidget.title.label = '定时任务'; + app.shell.add(taskViewWidget, 'left', { rank: 1000 }); + + const monitorViewWidget = new MonitorViewWidget(); + monitorViewWidget.id = 'jldbq-monitor'; + monitorViewWidget.title.icon = monitorIcon; + monitorViewWidget.title.caption = '调度监控'; + monitorViewWidget.title.label = '调度监控'; + app.shell.add(monitorViewWidget, 'left', { rank: 1100 }); + } +}; + +export default plugin; + +async function updateConfig(registry: ISettingRegistry) { + const settings = await registry.load(PLUGIN_ID); + const endpoint = settings.composite['flaskBackend'] as string; + config.endpoint = endpoint; +} ``` - 数据开发的相关样式代码 ```diff diff --git a/packages/jldbq-extenison/src/png.d.ts b/packages/jldbq-extenison/src/png.d.ts new file mode 100644 index 0000000000..1c5923252c --- /dev/null +++ b/packages/jldbq-extenison/src/png.d.ts @@ -0,0 +1,4 @@ +declare module '*.png' { + const value: string; + export default value; +} diff --git a/packages/jldbq-extenison/src/svg.d.ts b/packages/jldbq-extenison/src/svg.d.ts new file mode 100644 index 0000000000..0ca27598c5 --- /dev/null +++ b/packages/jldbq-extenison/src/svg.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const value: string; + export default value; +} diff --git a/packages/jldbq-extenison/style/base.css b/packages/jldbq-extenison/style/base.css new file mode 100644 index 0000000000..e2026de0cb --- /dev/null +++ b/packages/jldbq-extenison/style/base.css @@ -0,0 +1,9 @@ +/* + See the JupyterLab Developer Guide for useful CSS Patterns: + + https://jupyterlab.readthedocs.io/en/stable/developer/css.html +*/ + +@import './syncform.css'; +@import './button.css'; +@import './dsform.css'; diff --git a/packages/jldbq-extenison/style/button.css b/packages/jldbq-extenison/style/button.css new file mode 100644 index 0000000000..ea9c093f0b --- /dev/null +++ b/packages/jldbq-extenison/style/button.css @@ -0,0 +1,31 @@ +.jldbq-btn { + width: 95px; + height: 33px; + padding: 0; + border: none; + background-color: #edf0f6; + color: #4a4a4a; + border-radius: 0; +} + +.jldbq-btn.jldbq-btn-current { + background-color: #d1e4f6; +} + +.jldbq-btn:hover:not(.jldbq-btn-current) { + opacity: 0.8; +} + +.jldbq-btn-add { + display: flex; + align-items: center; + padding: 0; + background: none; + border: none; + color: #147bd1; + cursor: pointer; +} + +.jldbq-btn-add:hover { + opacity: 0.8; +} diff --git a/packages/jldbq-extenison/style/dsform.css b/packages/jldbq-extenison/style/dsform.css new file mode 100644 index 0000000000..f952bc34f4 --- /dev/null +++ b/packages/jldbq-extenison/style/dsform.css @@ -0,0 +1,105 @@ +.jldbq-form { + display: flex; + flex-direction: column; +} + +.jldbq-form label { + margin-bottom: 30px; + display: flex; + align-items: center; +} + +.jldbq-form label:last-child { + margin-bottom: 0; +} + +.jldbq-form label.jldbq-form-error:last-child { + border: 1px solid #ed5555; +} + +.jldbq-form .jldbq-form-label { + color: #4a4a4a; + width: 75px; + text-align: end; +} + +.jldbq-form .jldbq-form-switcher { + margin-left: 15px; + display: flex; + justify-content: space-around; + align-items: center; + width: 320px; +} + +.jldbq-form .jldbq-form-input { + font-family: inherit; + box-sizing: border-box; + width: 320px; + height: 32px; + border-radius: 2px; + border: 1px solid #c8d3e9; + margin-left: 15px; + padding: 0 15px; + font-size: 12px; + transition: all 100ms; + outline: 1px solid transparent; +} + +.jldbq-form .jldbq-form-input:focus { + outline-color: #147bd1; +} + +.jldbq-form .react-select-container { + width: 320px; + margin-left: 15px; +} + +.jldbq-form .react-select__control { + border-radius: 2px; + border: 1px solid #c8d3e9; + min-height: 32px; +} + +.jldbq-form .react-select__control:hover { + border-color: #c8d3e9; +} + +.jldbq-form .react-select__indicator-separator { + background-color: #c8d3e9; + margin: 4px 0; +} + +.jldbq-form .react-select__indicator { + color: #4883fb; + padding: 0 8px; +} + +.jldbq-form .react-select__value-container { + padding: 0 15px; + font-size: 12px; +} + +.jldbq-form .react-select__single-value { + padding: 0; + margin: 0; +} + +.jldbq-form .react-select__placeholder { + padding: 0; + margin: 0; +} + +.jldbq-form .react-select__input-container { + padding: 0; + margin: 0; +} + +.jldbq-form .react-select__menu-notice { + font-size: 12px; +} + +.jldbq-form .react-select__option { + font-size: 12px; + padding-left: 16px; + padding-right: 16px; +} diff --git a/packages/jldbq-extenison/style/icons/cli.svg b/packages/jldbq-extenison/style/icons/cli.svg new file mode 100644 index 0000000000..d712aada18 --- /dev/null +++ b/packages/jldbq-extenison/style/icons/cli.svg @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/packages/jldbq-extenison/style/icons/clock-fill.svg b/packages/jldbq-extenison/style/icons/clock-fill.svg new file mode 100644 index 0000000000..7cece1ee7b --- /dev/null +++ b/packages/jldbq-extenison/style/icons/clock-fill.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/packages/jldbq-extenison/style/icons/database-solid-ghost.svg b/packages/jldbq-extenison/style/icons/database-solid-ghost.svg new file mode 100644 index 0000000000..8edad9815c --- /dev/null +++ b/packages/jldbq-extenison/style/icons/database-solid-ghost.svg @@ -0,0 +1,43 @@ + + + + + + + diff --git a/packages/jldbq-extenison/style/icons/database-solid.svg b/packages/jldbq-extenison/style/icons/database-solid.svg new file mode 100644 index 0000000000..e867c2bf34 --- /dev/null +++ b/packages/jldbq-extenison/style/icons/database-solid.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/packages/jldbq-extenison/style/icons/monitor.svg b/packages/jldbq-extenison/style/icons/monitor.svg new file mode 100644 index 0000000000..5cd7ad7c83 --- /dev/null +++ b/packages/jldbq-extenison/style/icons/monitor.svg @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/packages/jldbq-extenison/style/icons/timer.svg b/packages/jldbq-extenison/style/icons/timer.svg new file mode 100644 index 0000000000..94d2d8509f --- /dev/null +++ b/packages/jldbq-extenison/style/icons/timer.svg @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/packages/jldbq-extenison/style/img/arrow.png b/packages/jldbq-extenison/style/img/arrow.png new file mode 100644 index 0000000000..50c9571f4e Binary files /dev/null and b/packages/jldbq-extenison/style/img/arrow.png differ diff --git a/packages/jldbq-extenison/style/img/delete.png b/packages/jldbq-extenison/style/img/delete.png new file mode 100644 index 0000000000..a64b86a722 Binary files /dev/null and b/packages/jldbq-extenison/style/img/delete.png differ diff --git a/packages/jldbq-extenison/style/index.css b/packages/jldbq-extenison/style/index.css new file mode 100644 index 0000000000..2257bf1a6e --- /dev/null +++ b/packages/jldbq-extenison/style/index.css @@ -0,0 +1,12 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ +@import url('~@lumino/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); +@import url('~@jupyterlab/apputils/style/index.css'); +@import url('~@jupyterlab/application/style/index.css'); + +@import url('./base.css'); diff --git a/packages/jldbq-extenison/style/index.js b/packages/jldbq-extenison/style/index.js new file mode 100644 index 0000000000..6f57f28e14 --- /dev/null +++ b/packages/jldbq-extenison/style/index.js @@ -0,0 +1,12 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ +import '@lumino/widgets/style/index.js'; +import '@jupyterlab/ui-components/style/index.js'; +import '@jupyterlab/apputils/style/index.js'; +import '@jupyterlab/application/style/index.js'; + +import './base.css'; diff --git a/packages/jldbq-extenison/style/syncform.css b/packages/jldbq-extenison/style/syncform.css new file mode 100644 index 0000000000..701d4a2e25 --- /dev/null +++ b/packages/jldbq-extenison/style/syncform.css @@ -0,0 +1,45 @@ +@import '~antd/dist/antd.css'; + +.arrow { + display: inline-flex; + width: 10%; + height: 300px; + justify-content: center; + align-items: center; +} + +.plane { + height: 265px; + border: 1px solid #d9d9d9; + margin-top: 10px; + padding: 10px 0 0 10px; + overflow-y: scroll; +} + +.line { + height: 307px; + border-left: 1px solid #d9d9d9; + margin: 0 5%; +} + +.mappings { + width: 30%; + height: 307px; + border: 1px solid #d9d9d9; + padding-left: 15px; +} + +.mappings__title { + height: 32px; + line-height: 32px; + font-weight: 500; +} + +.mappings__content { + height: 260px; + overflow-y: scroll; +} + +#datasyncForm > .ant-form-item:last-child { + margin-bottom: 0; +} ``` - 去除一些不符合需求的组件 ```diff diff --git a/packages/launcher-extension/src/index.ts b/packages/launcher-extension/src/index.ts index 94dd0d7538..da8d738526 100644 --- a/packages/launcher-extension/src/index.ts +++ b/packages/launcher-extension/src/index.ts @@ -32,9 +32,9 @@ const plugin: JupyterFrontEndPlugin = { activate, id: '@jupyterlab/launcher-extension:plugin', requires: [ITranslator], - optional: [ILabShell, ICommandPalette], - provides: ILauncher, - autoStart: true + optional: [ILabShell, ICommandPalette] + // provides: ILauncher, + // autoStart: true }; /** diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index 2832703b72..921f346507 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -15,13 +15,16 @@ "sideEffects": false, "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" + "lib/*.js", + "style/index.css", + "style/index.js" ], "scripts": { "build": "tsc -b", @@ -75,6 +78,7 @@ "@jupyterlab/inspector": "^3.4.3", "@jupyterlab/inspector-extension": "^3.4.3", "@jupyterlab/javascript-extension": "^3.4.3", + "@jupyterlab/jldbq-extension": "^1.0.0", "@jupyterlab/json-extension": "^3.4.3", "@jupyterlab/launcher": "^3.4.3", "@jupyterlab/launcher-extension": "^3.4.3", @@ -86,6 +90,7 @@ "@jupyterlab/markdownviewer-extension": "^3.4.3", "@jupyterlab/mathjax2": "^3.4.3", "@jupyterlab/mathjax2-extension": "^3.4.3", + "@jupyterlab/mytest-extension": "^1.0.0", "@jupyterlab/nbconvert-css": "^3.4.3", "@jupyterlab/nbformat": "^3.4.3", "@jupyterlab/notebook": "^3.4.3", @@ -110,7 +115,6 @@ "@jupyterlab/statusbar-extension": "^3.4.3", "@jupyterlab/terminal": "^3.4.3", "@jupyterlab/terminal-extension": "^3.4.3", - "@jupyterlab/theme-dark-extension": "^3.4.3", "@jupyterlab/theme-light-extension": "^3.4.3", "@jupyterlab/toc": "^5.4.3", "@jupyterlab/toc-extension": "^5.4.3", @@ -132,5 +136,6 @@ }, "publishConfig": { "access": "public" - } + }, + "styleModule": "style/index.js" } diff --git a/packages/theme-dark-extension/style/theme.css b/packages/metapackage/style/index.css similarity index 52% rename from packages/theme-dark-extension/style/theme.css rename to packages/metapackage/style/index.css index 4761c94d2b..b2f19abe63 100644 --- a/packages/theme-dark-extension/style/theme.css +++ b/packages/metapackage/style/index.css @@ -3,15 +3,6 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -@import './variables.css'; - -/* Set the default typography for monospace elements */ -tt, -code, -kbd, -samp, -pre { - font-family: var(--jp-code-font-family); - font-size: var(--jp-code-font-size); - line-height: var(--jp-code-line-height); -} +/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ +@import url('~@jupyterlab/jldbq-extension/style/index.css'); +@import url('~@jupyterlab/mytest-extension/style/index.css'); diff --git a/packages/metapackage/style/index.js b/packages/metapackage/style/index.js new file mode 100644 index 0000000000..cae86f17db --- /dev/null +++ b/packages/metapackage/style/index.js @@ -0,0 +1,8 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ +import '@jupyterlab/jldbq-extension/style/index.js'; +import '@jupyterlab/mytest-extension/style/index.js'; ``` - 修改 Notebook 的样式和布局, 匹配前端原型 ```diff diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 39a8057b24..c1524b80ad 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -21,7 +21,6 @@ import { MainAreaWidget, sessionContextDialogs, showDialog, - Toolbar, WidgetTracker } from '@jupyterlab/apputils'; import { Cell, CodeCell, ICellModel, MarkdownCell } from '@jupyterlab/cells'; @@ -94,7 +93,8 @@ import { } from '@lumino/coreutils'; import { DisposableSet, IDisposable } from '@lumino/disposable'; import { Message, MessageLoop } from '@lumino/messaging'; -import { Menu, Panel } from '@lumino/widgets'; +// import { Menu, Panel } from '@lumino/widgets'; +import { Panel } from '@lumino/widgets'; import { logNotebookOutput } from './nboutput'; /** @@ -270,7 +270,7 @@ const FACTORY = 'Notebook'; * The excluded Export To ... * (returned from nbconvert's export list) */ -const FORMAT_EXCLUDE = ['notebook', 'python', 'custom']; +// const FORMAT_EXCLUDE = ['notebook', 'python', 'custom']; /** * Setting Id storing the customized toolbar definition. @@ -477,111 +477,111 @@ export const executionIndicator: JupyterFrontEndPlugin = { /** * A plugin providing export commands in the main menu and command palette */ -export const exportPlugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/notebook-extension:export', - autoStart: true, - requires: [ITranslator, INotebookTracker], - optional: [IMainMenu, ICommandPalette], - activate: ( - app: JupyterFrontEnd, - translator: ITranslator, - tracker: INotebookTracker, - mainMenu: IMainMenu | null, - palette: ICommandPalette | null - ) => { - const trans = translator.load('jupyterlab'); - const { commands, shell } = app; - const services = app.serviceManager; - - const isEnabled = (): boolean => { - return Private.isEnabled(shell, tracker); - }; - - commands.addCommand(CommandIDs.exportToFormat, { - label: args => { - const formatLabel = args['label'] as string; - return args['isPalette'] - ? trans.__('Save and Export Notebook: %1', formatLabel) - : formatLabel; - }, - execute: args => { - const current = getCurrent(tracker, shell, args); - - if (!current) { - return; - } - - const url = PageConfig.getNBConvertURL({ - format: args['format'] as string, - download: true, - path: current.context.path - }); - const { context } = current; - - if (context.model.dirty && !context.model.readOnly) { - return context.save().then(() => { - window.open(url, '_blank', 'noopener'); - }); - } - - return new Promise(resolve => { - window.open(url, '_blank', 'noopener'); - resolve(undefined); - }); - }, - isEnabled - }); - - // Add a notebook group to the File menu. - let exportTo: Menu | null | undefined; - if (mainMenu) { - exportTo = mainMenu.fileMenu.items.find( - item => - item.type === 'submenu' && - item.submenu?.id === 'jp-mainmenu-file-notebookexport' - )?.submenu; - } - - void services.nbconvert.getExportFormats().then(response => { - if (response) { - const formatLabels: any = Private.getFormatLabels(translator); - - // Convert export list to palette and menu items. - const formatList = Object.keys(response); - formatList.forEach(function (key) { - const capCaseKey = trans.__(key[0].toUpperCase() + key.substr(1)); - const labelStr = formatLabels[key] ? formatLabels[key] : capCaseKey; - let args = { - format: key, - label: labelStr, - isPalette: false - }; - if (FORMAT_EXCLUDE.indexOf(key) === -1) { - if (exportTo) { - exportTo.addItem({ - command: CommandIDs.exportToFormat, - args: args - }); - } - if (palette) { - args = { - format: key, - label: labelStr, - isPalette: true - }; - const category = trans.__('Notebook Operations'); - palette.addItem({ - command: CommandIDs.exportToFormat, - category, - args - }); - } - } - }); - } - }); - } -}; +// export const exportPlugin: JupyterFrontEndPlugin = { +// id: '@jupyterlab/notebook-extension:export', +// autoStart: true, +// requires: [ITranslator, INotebookTracker], +// optional: [IMainMenu, ICommandPalette], +// activate: ( +// app: JupyterFrontEnd, +// translator: ITranslator, +// tracker: INotebookTracker, +// mainMenu: IMainMenu | null, +// palette: ICommandPalette | null +// ) => { +// const trans = translator.load('jupyterlab'); +// const { commands, shell } = app; +// const services = app.serviceManager; + +// const isEnabled = (): boolean => { +// return Private.isEnabled(shell, tracker); +// }; + +// commands.addCommand(CommandIDs.exportToFormat, { +// label: args => { +// const formatLabel = args['label'] as string; +// return args['isPalette'] +// ? trans.__('Save and Export Notebook: %1', formatLabel) +// : formatLabel; +// }, +// execute: args => { +// const current = getCurrent(tracker, shell, args); + +// if (!current) { +// return; +// } + +// const url = PageConfig.getNBConvertURL({ +// format: args['format'] as string, +// download: true, +// path: current.context.path +// }); +// const { context } = current; + +// if (context.model.dirty && !context.model.readOnly) { +// return context.save().then(() => { +// window.open(url, '_blank', 'noopener'); +// }); +// } + +// return new Promise(resolve => { +// window.open(url, '_blank', 'noopener'); +// resolve(undefined); +// }); +// }, +// isEnabled +// }); + +// // Add a notebook group to the File menu. +// let exportTo: Menu | null | undefined; +// if (mainMenu) { +// exportTo = mainMenu.fileMenu.items.find( +// item => +// item.type === 'submenu' && +// item.submenu?.id === 'jp-mainmenu-file-notebookexport' +// )?.submenu; +// } + +// void services.nbconvert.getExportFormats().then(response => { +// if (response) { +// const formatLabels: any = Private.getFormatLabels(translator); + +// // Convert export list to palette and menu items. +// const formatList = Object.keys(response); +// formatList.forEach(function (key) { +// const capCaseKey = trans.__(key[0].toUpperCase() + key.substr(1)); +// const labelStr = formatLabels[key] ? formatLabels[key] : capCaseKey; +// let args = { +// format: key, +// label: labelStr, +// isPalette: false +// }; +// if (FORMAT_EXCLUDE.indexOf(key) === -1) { +// if (exportTo) { +// exportTo.addItem({ +// command: CommandIDs.exportToFormat, +// args: args +// }); +// } +// if (palette) { +// args = { +// format: key, +// label: labelStr, +// isPalette: true +// }; +// const category = trans.__('Notebook Operations'); +// palette.addItem({ +// command: CommandIDs.exportToFormat, +// category, +// args +// }); +// } +// } +// }); +// } +// }); +// } +// }; /** * A plugin that adds a notebook trust status item to the status bar. @@ -682,7 +682,7 @@ const plugins: JupyterFrontEndPlugin[] = [ factory, trackerPlugin, executionIndicator, - exportPlugin, + // exportPlugin, tools, commandEditItem, notebookTrustItem, @@ -844,13 +844,6 @@ function activateWidgetFactory( toolbarRegistry.registerFactory(FACTORY, 'cellType', panel => ToolbarItems.createCellTypeItem(panel, translator) ); - toolbarRegistry.registerFactory(FACTORY, 'kernelName', panel => - Toolbar.createKernelNameItem( - panel.sessionContext, - sessionContextDialogs, - translator - ) - ); toolbarRegistry.registerFactory( FACTORY, diff --git a/packages/notebook/src/default-toolbar.tsx b/packages/notebook/src/default-toolbar.tsx index bf94760cc6..5e931bb525 100644 --- a/packages/notebook/src/default-toolbar.tsx +++ b/packages/notebook/src/default-toolbar.tsx @@ -278,15 +278,7 @@ export namespace ToolbarItems { widget: createRestartRunAllButton(panel, sessionDialogs, translator) }, { name: 'cellType', widget: createCellTypeItem(panel, translator) }, - { name: 'spacer', widget: Toolbar.createSpacerItem() }, - { - name: 'kernelName', - widget: Toolbar.createKernelNameItem( - panel.sessionContext, - sessionDialogs, - translator - ) - } + { name: 'spacer', widget: Toolbar.createSpacerItem() } ]; } } diff --git a/packages/notebook/src/panel.ts b/packages/notebook/src/panel.ts index e1f70352f4..a30e01bd00 100644 --- a/packages/notebook/src/panel.ts +++ b/packages/notebook/src/panel.ts @@ -17,6 +17,7 @@ import { TranslationBundle } from '@jupyterlab/translation'; import { each } from '@lumino/algorithm'; +import { BoxLayout } from '@lumino/widgets'; import { Token } from '@lumino/coreutils'; import { INotebookModel } from './model'; import { Notebook, StaticNotebook } from './widget'; @@ -54,6 +55,8 @@ export class NotebookPanel extends DocumentWidget { // Set up CSS classes this.addClass(NOTEBOOK_PANEL_CLASS); this.toolbar.addClass(NOTEBOOK_PANEL_TOOLBAR_CLASS); + BoxLayout.setSizeBasis(this.toolbar, 42); + (this.layout as BoxLayout).spacing = 10; this.content.addClass(NOTEBOOK_PANEL_NOTEBOOK_CLASS); // Set up things related to the context @@ -136,7 +139,7 @@ export class NotebookPanel extends DocumentWidget { /** * Set URI fragment identifier. */ - setFragment(fragment: string) { + setFragment(fragment: string): void { void this.context.ready.then(() => { this.content.setFragment(fragment); }); @@ -154,7 +157,7 @@ export class NotebookPanel extends DocumentWidget { * Prints the notebook by converting to HTML with nbconvert. */ [Printing.symbol]() { - return async () => { + return async (): Promise => { // Save before generating HTML if (this.context.model.dirty && !this.context.model.readOnly) { await this.context.save(); diff --git a/packages/notebook/style/base.css b/packages/notebook/style/base.css index 4d90131c53..11b23e5da2 100644 --- a/packages/notebook/style/base.css +++ b/packages/notebook/style/base.css @@ -28,6 +28,7 @@ .jp-NotebookPanel { display: block; height: 100%; + padding: 20px; } .jp-NotebookPanel.jp-Document { @@ -36,7 +37,7 @@ } .jp-Notebook { - padding: var(--jp-notebook-padding); + padding-right: var(--jp-notebook-padding); outline: none; overflow: auto; background: var(--jp-layout-color0); @@ -95,7 +96,7 @@ /* cell is active */ .jp-Notebook .jp-Cell.jp-mod-active .jp-Collapser { - background: var(--jp-brand-color1); + background: #147bd1; } /* cell is dirty */ diff --git a/packages/notebook/style/toolbar.css b/packages/notebook/style/toolbar.css index c1a3259af0..28a4859c59 100644 --- a/packages/notebook/style/toolbar.css +++ b/packages/notebook/style/toolbar.css @@ -17,8 +17,16 @@ | Styles |----------------------------------------------------------------------------*/ -.jp-NotebookPanel-toolbar { - padding: var(--jp-notebook-toolbar-padding); +.jp-Toolbar.jp-NotebookPanel-toolbar { + align-items: center; + background-color: #f7f9fd; + border: none; + box-shadow: none; + padding: 0 24px; +} + +.jp-Toolbar.jp-NotebookPanel-toolbar > .jp-Toolbar-item { + height: auto; } .jp-Toolbar-item.jp-Notebook-toolbarCellType .jp-select-wrapper.jp-mod-focused { ``` - 去除不符合需求的组件和菜单项 ```diff diff --git a/packages/running-extension/src/index.ts b/packages/running-extension/src/index.ts index fa4e785803..5a8efd480b 100644 --- a/packages/running-extension/src/index.ts +++ b/packages/running-extension/src/index.ts @@ -68,7 +68,7 @@ function activate( addKernelRunningSessionManager(runningSessionManagers, translator, app); // Rank has been chosen somewhat arbitrarily to give priority to the running // sessions widget in the sidebar. - app.shell.add(running, 'left', { rank: 200 }); + // app.shell.add(running, 'left', { rank: 200 }); return runningSessionManagers; } diff --git a/packages/statusbar/src/statusbar.ts b/packages/statusbar/src/statusbar.ts index 58da932baa..dadfbfcdc1 100644 --- a/packages/statusbar/src/statusbar.ts +++ b/packages/statusbar/src/statusbar.ts @@ -116,7 +116,7 @@ export class StatusBar extends Widget implements IStatusBar { /** * Dispose of the status bar. */ - dispose() { + dispose(): void { this._leftRankItems.length = 0; this._rightRankItems.length = 0; this._disposables.dispose(); @@ -126,7 +126,7 @@ export class StatusBar extends Widget implements IStatusBar { /** * Handle an 'update-request' message to the status bar. */ - protected onUpdateRequest(msg: Message) { + protected onUpdateRequest(msg: Message): void { this._refreshAll(); super.onUpdateRequest(msg); } diff --git a/packages/terminal-extension/src/index.ts b/packages/terminal-extension/src/index.ts index 6aca46fbd2..5a18f9f165 100644 --- a/packages/terminal-extension/src/index.ts +++ b/packages/terminal-extension/src/index.ts @@ -17,7 +17,8 @@ import { WidgetTracker } from '@jupyterlab/apputils'; import { ILauncher } from '@jupyterlab/launcher'; -import { IFileMenu, IMainMenu } from '@jupyterlab/mainmenu'; +// import { IFileMenu, IMainMenu } from '@jupyterlab/mainmenu'; +import { IMainMenu } from '@jupyterlab/mainmenu'; import { IRunningSessionManagers, IRunningSessions } from '@jupyterlab/running'; import { Terminal, TerminalAPI } from '@jupyterlab/services'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; @@ -27,7 +28,7 @@ import * as WidgetModuleType from '@jupyterlab/terminal/lib/widget'; import { ITranslator } from '@jupyterlab/translation'; import { terminalIcon } from '@jupyterlab/ui-components'; import { toArray } from '@lumino/algorithm'; -import { Menu } from '@lumino/widgets'; +// import { Menu } from '@lumino/widgets'; /** * The command IDs used by the terminal plugin. @@ -85,7 +86,8 @@ function activate( runningSessionManagers: IRunningSessionManagers | null ): ITerminalTracker { const trans = translator.load('jupyterlab'); - const { serviceManager, commands } = app; + // const { serviceManager, commands } = app; + const { serviceManager } = app; const category = trans.__('Terminal'); const namespace = 'terminal'; const tracker = new WidgetTracker>({ @@ -171,57 +173,57 @@ function activate( addCommands(app, tracker, settingRegistry, translator, options); - if (mainMenu) { - // Add "Terminal Theme" menu below "Theme" menu. - const themeMenu = new Menu({ commands }); - themeMenu.title.label = trans._p('menu', 'Terminal Theme'); - themeMenu.addItem({ - command: CommandIDs.setTheme, - args: { - theme: 'inherit', - displayName: trans.__('Inherit'), - isPalette: false - } - }); - themeMenu.addItem({ - command: CommandIDs.setTheme, - args: { - theme: 'light', - displayName: trans.__('Light'), - isPalette: false - } - }); - themeMenu.addItem({ - command: CommandIDs.setTheme, - args: { theme: 'dark', displayName: trans.__('Dark'), isPalette: false } - }); - - // Add some commands to the "View" menu. - mainMenu.settingsMenu.addGroup( - [ - { command: CommandIDs.increaseFont }, - { command: CommandIDs.decreaseFont }, - { type: 'submenu', submenu: themeMenu } - ], - 40 - ); - - // Add terminal creation to the file menu. - mainMenu.fileMenu.newMenu.addItem({ - command: CommandIDs.createNew, - rank: 20 - }); - - // Add terminal close-and-shutdown to the file menu. - mainMenu.fileMenu.closeAndCleaners.add({ - tracker, - closeAndCleanupLabel: (n: number) => trans.__('Shutdown Terminal'), - closeAndCleanup: (current: MainAreaWidget) => { - // The widget is automatically disposed upon session shutdown. - return current.content.session.shutdown(); - } - } as IFileMenu.ICloseAndCleaner>); - } + // if (mainMenu) { + // // Add "Terminal Theme" menu below "Theme" menu. + // const themeMenu = new Menu({ commands }); + // themeMenu.title.label = trans._p('menu', 'Terminal Theme'); + // themeMenu.addItem({ + // command: CommandIDs.setTheme, + // args: { + // theme: 'inherit', + // displayName: trans.__('Inherit'), + // isPalette: false + // } + // }); + // themeMenu.addItem({ + // command: CommandIDs.setTheme, + // args: { + // theme: 'light', + // displayName: trans.__('Light'), + // isPalette: false + // } + // }); + // themeMenu.addItem({ + // command: CommandIDs.setTheme, + // args: { theme: 'dark', displayName: trans.__('Dark'), isPalette: false } + // }); + + // // Add some commands to the "View" menu. + // mainMenu.settingsMenu.addGroup( + // [ + // { command: CommandIDs.increaseFont }, + // { command: CommandIDs.decreaseFont }, + // { type: 'submenu', submenu: themeMenu } + // ], + // 40 + // ); + + // // Add terminal creation to the file menu. + // mainMenu.fileMenu.newMenu.addItem({ + // command: CommandIDs.createNew, + // rank: 20 + // }); + + // // Add terminal close-and-shutdown to the file menu. + // mainMenu.fileMenu.closeAndCleaners.add({ + // tracker, + // closeAndCleanupLabel: (n: number) => trans.__('Shutdown Terminal'), + // closeAndCleanup: (current: MainAreaWidget) => { + // // The widget is automatically disposed upon session shutdown. + // return current.content.session.shutdown(); + // } + // } as IFileMenu.ICloseAndCleaner>); + // } if (palette) { // Add command palette items. diff --git a/packages/theme-dark-extension/README.md b/packages/theme-dark-extension/README.md deleted file mode 100644 index 6c19ca27cd..0000000000 --- a/packages/theme-dark-extension/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @jupyterlab/theme-dark-extension - -A JupyterLab theme extension which provides the default dark-colored theme. diff --git a/packages/theme-dark-extension/package.json b/packages/theme-dark-extension/package.json deleted file mode 100644 index eaded8c880..0000000000 --- a/packages/theme-dark-extension/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@jupyterlab/theme-dark-extension", - "version": "3.4.3", - "description": "JupyterLab - Default Dark Theme", - "homepage": "https://github.com/jupyterlab/jupyterlab", - "bugs": { - "url": "https://github.com/jupyterlab/jupyterlab/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/jupyterlab/jupyterlab.git" - }, - "license": "BSD-3-Clause", - "author": "Project Jupyter", - "sideEffects": true, - "main": "lib/index.js", - "types": "lib/index.d.ts", - "directories": { - "lib": "lib/" - }, - "files": [ - "lib/*.d.ts", - "lib/*.js.map", - "lib/*.js", - "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", - "style/index.js" - ], - "scripts": { - "build": "tsc -b", - "clean": "rimraf lib && rimraf tsconfig.tsbuildinfo", - "watch": "tsc -b --watch" - }, - "dependencies": { - "@jupyterlab/application": "^3.4.3", - "@jupyterlab/apputils": "^3.4.3", - "@jupyterlab/translation": "^3.4.3" - }, - "devDependencies": { - "rimraf": "~3.0.0", - "typedoc": "~0.21.2", - "typescript": "~4.1.3" - }, - "publishConfig": { - "access": "public" - }, - "jupyterlab": { - "extension": true, - "themePath": "style/theme.css" - } -} diff --git a/packages/theme-dark-extension/src/index.ts b/packages/theme-dark-extension/src/index.ts deleted file mode 100644 index 6f20ea8e9a..0000000000 --- a/packages/theme-dark-extension/src/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Jupyter Development Team. -// Distributed under the terms of the Modified BSD License. -/** - * @packageDocumentation - * @module theme-dark-extension - */ - -import { - JupyterFrontEnd, - JupyterFrontEndPlugin -} from '@jupyterlab/application'; -import { IThemeManager } from '@jupyterlab/apputils'; -import { ITranslator } from '@jupyterlab/translation'; - -/** - * A plugin for the Jupyter Dark Theme. - */ -const plugin: JupyterFrontEndPlugin = { - id: '@jupyterlab/theme-dark-extension:plugin', - requires: [IThemeManager, ITranslator], - activate: ( - app: JupyterFrontEnd, - manager: IThemeManager, - translator: ITranslator - ) => { - const trans = translator.load('jupyterlab'); - const style = '@jupyterlab/theme-dark-extension/index.css'; - manager.register({ - name: 'JupyterLab Dark', - displayName: trans.__('JupyterLab Dark'), - isLight: false, - themeScrollbars: true, - load: () => manager.loadCSS(style), - unload: () => Promise.resolve(undefined) - }); - }, - autoStart: true -}; - -export default plugin; diff --git a/packages/theme-dark-extension/style/variables.css b/packages/theme-dark-extension/style/variables.css deleted file mode 100644 index ff87a764c3..0000000000 --- a/packages/theme-dark-extension/style/variables.css +++ /dev/null @@ -1,410 +0,0 @@ -/*----------------------------------------------------------------------------- -| Copyright (c) Jupyter Development Team. -| Distributed under the terms of the Modified BSD License. -|----------------------------------------------------------------------------*/ - -/* -The following CSS variables define the main, public API for styling JupyterLab. -These variables should be used by all plugins wherever possible. In other -words, plugins should not define custom colors, sizes, etc unless absolutely -necessary. This enables users to change the visual theme of JupyterLab -by changing these variables. - -Many variables appear in an ordered sequence (0,1,2,3). These sequences -are designed to work well together, so for example, `--jp-border-color1` should -be used with `--jp-layout-color1`. The numbers have the following meanings: - -* 0: super-primary, reserved for special emphasis -* 1: primary, most important under normal situations -* 2: secondary, next most important under normal situations -* 3: tertiary, next most important under normal situations - -Throughout JupyterLab, we are mostly following principles from Google's -Material Design when selecting colors. We are not, however, following -all of MD as it is not optimized for dense, information rich UIs. -*/ - -:root { - /* Elevation - * - * We style box-shadows using Material Design's idea of elevation. These particular numbers are taken from here: - * - * https://github.com/material-components/material-components-web - * https://material-components-web.appspot.com/elevation.html - */ - - /* The dark theme shadows need a bit of work, but this will probably also require work on the core layout - * colors used in the theme as well. */ - --jp-shadow-base-lightness: 32; - --jp-shadow-umbra-color: rgba( - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - 0.2 - ); - --jp-shadow-penumbra-color: rgba( - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - 0.14 - ); - --jp-shadow-ambient-color: rgba( - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - var(--jp-shadow-base-lightness), - 0.12 - ); - --jp-elevation-z0: none; - --jp-elevation-z1: 0px 2px 1px -1px var(--jp-shadow-umbra-color), - 0px 1px 1px 0px var(--jp-shadow-penumbra-color), - 0px 1px 3px 0px var(--jp-shadow-ambient-color); - --jp-elevation-z2: 0px 3px 1px -2px var(--jp-shadow-umbra-color), - 0px 2px 2px 0px var(--jp-shadow-penumbra-color), - 0px 1px 5px 0px var(--jp-shadow-ambient-color); - --jp-elevation-z4: 0px 2px 4px -1px var(--jp-shadow-umbra-color), - 0px 4px 5px 0px var(--jp-shadow-penumbra-color), - 0px 1px 10px 0px var(--jp-shadow-ambient-color); - --jp-elevation-z6: 0px 3px 5px -1px var(--jp-shadow-umbra-color), - 0px 6px 10px 0px var(--jp-shadow-penumbra-color), - 0px 1px 18px 0px var(--jp-shadow-ambient-color); - --jp-elevation-z8: 0px 5px 5px -3px var(--jp-shadow-umbra-color), - 0px 8px 10px 1px var(--jp-shadow-penumbra-color), - 0px 3px 14px 2px var(--jp-shadow-ambient-color); - --jp-elevation-z12: 0px 7px 8px -4px var(--jp-shadow-umbra-color), - 0px 12px 17px 2px var(--jp-shadow-penumbra-color), - 0px 5px 22px 4px var(--jp-shadow-ambient-color); - --jp-elevation-z16: 0px 8px 10px -5px var(--jp-shadow-umbra-color), - 0px 16px 24px 2px var(--jp-shadow-penumbra-color), - 0px 6px 30px 5px var(--jp-shadow-ambient-color); - --jp-elevation-z20: 0px 10px 13px -6px var(--jp-shadow-umbra-color), - 0px 20px 31px 3px var(--jp-shadow-penumbra-color), - 0px 8px 38px 7px var(--jp-shadow-ambient-color); - --jp-elevation-z24: 0px 11px 15px -7px var(--jp-shadow-umbra-color), - 0px 24px 38px 3px var(--jp-shadow-penumbra-color), - 0px 9px 46px 8px var(--jp-shadow-ambient-color); - - /* Borders - * - * The following variables, specify the visual styling of borders in JupyterLab. - */ - - --jp-border-width: 1px; - --jp-border-color0: var(--md-grey-700); - --jp-border-color1: var(--md-grey-700); - --jp-border-color2: var(--md-grey-800); - --jp-border-color3: var(--md-grey-900); - --jp-inverse-border-color: var(--md-grey-600); - --jp-border-radius: 2px; - - /* UI Fonts - * - * The UI font CSS variables are used for the typography all of the JupyterLab - * user interface elements that are not directly user generated content. - * - * The font sizing here is done assuming that the body font size of --jp-ui-font-size1 - * is applied to a parent element. When children elements, such as headings, are sized - * in em all things will be computed relative to that body size. - */ - - --jp-ui-font-scale-factor: 1.2; - --jp-ui-font-size0: 0.83333em; - --jp-ui-font-size1: 13px; /* Base font size */ - --jp-ui-font-size2: 1.2em; - --jp-ui-font-size3: 1.44em; - - --jp-ui-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; - - /* - * Use these font colors against the corresponding main layout colors. - * In a light theme, these go from dark to light. - */ - - /* Defaults use Material Design specification */ - --jp-ui-font-color0: rgba(255, 255, 255, 1); - --jp-ui-font-color1: rgba(255, 255, 255, 0.87); - --jp-ui-font-color2: rgba(255, 255, 255, 0.54); - --jp-ui-font-color3: rgba(255, 255, 255, 0.38); - - /* - * Use these against the brand/accent/warn/error colors. - * These will typically go from light to darker, in both a dark and light theme. - */ - - --jp-ui-inverse-font-color0: rgba(0, 0, 0, 1); - --jp-ui-inverse-font-color1: rgba(0, 0, 0, 0.8); - --jp-ui-inverse-font-color2: rgba(0, 0, 0, 0.5); - --jp-ui-inverse-font-color3: rgba(0, 0, 0, 0.3); - - /* Content Fonts - * - * Content font variables are used for typography of user generated content. - * - * The font sizing here is done assuming that the body font size of --jp-content-font-size1 - * is applied to a parent element. When children elements, such as headings, are sized - * in em all things will be computed relative to that body size. - */ - - --jp-content-line-height: 1.6; - --jp-content-font-scale-factor: 1.2; - --jp-content-font-size0: 0.83333em; - --jp-content-font-size1: 14px; /* Base font size */ - --jp-content-font-size2: 1.2em; - --jp-content-font-size3: 1.44em; - --jp-content-font-size4: 1.728em; - --jp-content-font-size5: 2.0736em; - - /* This gives a magnification of about 125% in presentation mode over normal. */ - --jp-content-presentation-font-size1: 17px; - - --jp-content-heading-line-height: 1; - --jp-content-heading-margin-top: 1.2em; - --jp-content-heading-margin-bottom: 0.8em; - --jp-content-heading-font-weight: 500; - - /* Defaults use Material Design specification */ - --jp-content-font-color0: rgba(255, 255, 255, 1); - --jp-content-font-color1: rgba(255, 255, 255, 1); - --jp-content-font-color2: rgba(255, 255, 255, 0.7); - --jp-content-font-color3: rgba(255, 255, 255, 0.5); - - --jp-content-link-color: var(--md-blue-300); - - --jp-content-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', - Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', - 'Segoe UI Symbol'; - - /* - * Code Fonts - * - * Code font variables are used for typography of code and other monospaces content. - */ - - --jp-code-font-size: 13px; - --jp-code-line-height: 1.3077; /* 17px for 13px base */ - --jp-code-padding: 5px; /* 5px for 13px base, codemirror highlighting needs integer px value */ - --jp-code-font-family-default: Menlo, Consolas, 'DejaVu Sans Mono', monospace; - --jp-code-font-family: var(--jp-code-font-family-default); - - /* This gives a magnification of about 125% in presentation mode over normal. */ - --jp-code-presentation-font-size: 16px; - - /* may need to tweak cursor width if you change font size */ - --jp-code-cursor-width0: 1.4px; - --jp-code-cursor-width1: 2px; - --jp-code-cursor-width2: 4px; - - /* Layout - * - * The following are the main layout colors use in JupyterLab. In a light - * theme these would go from light to dark. - */ - - --jp-layout-color0: #111111; - --jp-layout-color1: var(--md-grey-900); - --jp-layout-color2: var(--md-grey-800); - --jp-layout-color3: var(--md-grey-700); - --jp-layout-color4: var(--md-grey-600); - - /* Inverse Layout - * - * The following are the inverse layout colors use in JupyterLab. In a light - * theme these would go from dark to light. - */ - - --jp-inverse-layout-color0: white; - --jp-inverse-layout-color1: white; - --jp-inverse-layout-color2: var(--md-grey-200); - --jp-inverse-layout-color3: var(--md-grey-400); - --jp-inverse-layout-color4: var(--md-grey-600); - - /* Brand/accent */ - - --jp-brand-color0: var(--md-blue-700); - --jp-brand-color1: var(--md-blue-500); - --jp-brand-color2: var(--md-blue-300); - --jp-brand-color3: var(--md-blue-100); - --jp-brand-color4: var(--md-blue-50); - - --jp-accent-color0: var(--md-green-700); - --jp-accent-color1: var(--md-green-500); - --jp-accent-color2: var(--md-green-300); - --jp-accent-color3: var(--md-green-100); - - /* State colors (warn, error, success, info) */ - - --jp-warn-color0: var(--md-orange-700); - --jp-warn-color1: var(--md-orange-500); - --jp-warn-color2: var(--md-orange-300); - --jp-warn-color3: var(--md-orange-100); - - --jp-error-color0: var(--md-red-700); - --jp-error-color1: var(--md-red-500); - --jp-error-color2: var(--md-red-300); - --jp-error-color3: var(--md-red-100); - - --jp-success-color0: var(--md-green-700); - --jp-success-color1: var(--md-green-500); - --jp-success-color2: var(--md-green-300); - --jp-success-color3: var(--md-green-100); - - --jp-info-color0: var(--md-cyan-700); - --jp-info-color1: var(--md-cyan-500); - --jp-info-color2: var(--md-cyan-300); - --jp-info-color3: var(--md-cyan-100); - - /* Cell specific styles */ - - --jp-cell-padding: 5px; - - --jp-cell-collapser-width: 8px; - --jp-cell-collapser-min-height: 20px; - --jp-cell-collapser-not-active-hover-opacity: 0.6; - - --jp-cell-editor-background: var(--jp-layout-color1); - --jp-cell-editor-border-color: var(--md-grey-700); - --jp-cell-editor-box-shadow: inset 0 0 2px var(--md-blue-300); - --jp-cell-editor-active-background: var(--jp-layout-color0); - --jp-cell-editor-active-border-color: var(--jp-brand-color1); - - --jp-cell-prompt-width: 64px; - --jp-cell-prompt-font-family: var(--jp-code-font-family-default); - --jp-cell-prompt-letter-spacing: 0px; - --jp-cell-prompt-opacity: 1; - --jp-cell-prompt-not-active-opacity: 1; - --jp-cell-prompt-not-active-font-color: var(--md-grey-300); - - /* A custom blend of MD grey and blue 600 - * See https://meyerweb.com/eric/tools/color-blend/#546E7A:1E88E5:5:hex */ - --jp-cell-inprompt-font-color: #307fc1; - /* A custom blend of MD grey and orange 600 - * https://meyerweb.com/eric/tools/color-blend/#546E7A:F4511E:5:hex */ - --jp-cell-outprompt-font-color: #bf5b3d; - - /* Notebook specific styles */ - - --jp-notebook-padding: 10px; - --jp-notebook-select-background: var(--jp-layout-color1); - --jp-notebook-multiselected-color: rgba(33, 150, 243, 0.24); - - /* The scroll padding is calculated to fill enough space at the bottom of the - notebook to show one single-line cell (with appropriate padding) at the top - when the notebook is scrolled all the way to the bottom. We also subtract one - pixel so that no scrollbar appears if we have just one single-line cell in the - notebook. This padding is to enable a 'scroll past end' feature in a notebook. - */ - --jp-notebook-scroll-padding: calc( - 100% - var(--jp-code-font-size) * var(--jp-code-line-height) - - var(--jp-code-padding) - var(--jp-cell-padding) - 1px - ); - - /* Rendermime styles */ - - --jp-rendermime-error-background: rgba(244, 67, 54, 0.28); - --jp-rendermime-table-row-background: var(--md-grey-900); - --jp-rendermime-table-row-hover-background: rgba(3, 169, 244, 0.2); - - /* Dialog specific styles */ - - --jp-dialog-background: rgba(0, 0, 0, 0.6); - - /* Console specific styles */ - - --jp-console-padding: 10px; - - /* Toolbar specific styles */ - - --jp-toolbar-border-color: var(--jp-border-color2); - --jp-toolbar-micro-height: 8px; - --jp-toolbar-background: var(--jp-layout-color1); - --jp-toolbar-box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.8); - --jp-toolbar-header-margin: 4px 4px 0px 4px; - --jp-toolbar-active-background: var(--jp-layout-color0); - - /* Statusbar specific styles */ - - --jp-statusbar-height: 24px; - - /* Input field styles */ - - --jp-input-box-shadow: inset 0 0 2px var(--md-blue-300); - --jp-input-active-background: var(--jp-layout-color0); - --jp-input-hover-background: var(--jp-layout-color2); - --jp-input-background: var(--md-grey-800); - --jp-input-border-color: var(--jp-inverse-border-color); - --jp-input-active-border-color: var(--jp-brand-color1); - --jp-input-active-box-shadow-color: rgba(19, 124, 189, 0.3); - - /* General editor styles */ - - --jp-editor-selected-background: var(--jp-layout-color2); - --jp-editor-selected-focused-background: rgba(33, 150, 243, 0.24); - --jp-editor-cursor-color: var(--jp-ui-font-color0); - - /* Code mirror specific styles */ - - --jp-mirror-editor-keyword-color: var(--md-green-500); - --jp-mirror-editor-atom-color: var(--md-blue-300); - --jp-mirror-editor-number-color: var(--md-green-400); - --jp-mirror-editor-def-color: var(--md-blue-600); - --jp-mirror-editor-variable-color: var(--md-grey-300); - --jp-mirror-editor-variable-2-color: var(--md-blue-400); - --jp-mirror-editor-variable-3-color: var(--md-green-600); - --jp-mirror-editor-punctuation-color: var(--md-blue-400); - --jp-mirror-editor-property-color: var(--md-blue-400); - --jp-mirror-editor-operator-color: #aa22ff; - --jp-mirror-editor-comment-color: #408080; - --jp-mirror-editor-string-color: #ff7070; - --jp-mirror-editor-string-2-color: var(--md-purple-300); - --jp-mirror-editor-meta-color: #aa22ff; - --jp-mirror-editor-qualifier-color: #555; - --jp-mirror-editor-builtin-color: var(--md-green-600); - --jp-mirror-editor-bracket-color: #997; - --jp-mirror-editor-tag-color: var(--md-green-700); - --jp-mirror-editor-attribute-color: var(--md-blue-700); - --jp-mirror-editor-header-color: var(--md-blue-500); - --jp-mirror-editor-quote-color: var(--md-green-300); - --jp-mirror-editor-link-color: var(--md-blue-700); - --jp-mirror-editor-error-color: #f00; - --jp-mirror-editor-hr-color: #999; - - /* Vega extension styles */ - - --jp-vega-background: var(--md-grey-400); - - /* Sidebar-related styles */ - - --jp-sidebar-min-width: 250px; - - /* Search-related styles */ - - --jp-search-toggle-off-opacity: 0.6; - --jp-search-toggle-hover-opacity: 0.8; - --jp-search-toggle-on-opacity: 1; - --jp-search-selected-match-background-color: rgb(255, 225, 0); - --jp-search-selected-match-color: black; - --jp-search-unselected-match-background-color: var( - --jp-inverse-layout-color0 - ); - --jp-search-unselected-match-color: var(--jp-ui-inverse-font-color0); - - /* scrollbar related styles. Supports every browser except Edge. */ - - /* colors based on JetBrain's Darcula theme */ - - --jp-scrollbar-background-color: #3f4244; - --jp-scrollbar-thumb-color: 88, 96, 97; /* need to specify thumb color as an RGB triplet */ - - --jp-scrollbar-endpad: 3px; /* the minimum gap between the thumb and the ends of a scrollbar */ - - /* hacks for setting the thumb shape. These do nothing in Firefox */ - - --jp-scrollbar-thumb-margin: 3.5px; /* the space in between the sides of the thumb and the track */ - --jp-scrollbar-thumb-radius: 9px; /* set to a large-ish value for rounded endcaps on the thumb */ - - /* Icon colors that work well with light or dark backgrounds */ - --jp-icon-contrast-color0: var(--md-purple-600); - --jp-icon-contrast-color1: var(--md-green-600); - --jp-icon-contrast-color2: var(--md-pink-600); - --jp-icon-contrast-color3: var(--md-blue-600); -} diff --git a/packages/theme-dark-extension/typedoc.json b/packages/theme-dark-extension/typedoc.json deleted file mode 100644 index 7ddb378afe..0000000000 --- a/packages/theme-dark-extension/typedoc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "out": "../../docs/api/theme-dark-extension", - "theme": "../../typedoc-theme" -} diff --git a/packages/toc-extension/src/index.ts b/packages/toc-extension/src/index.ts index 334a5a2a2d..94ac4441ed 100644 --- a/packages/toc-extension/src/index.ts +++ b/packages/toc-extension/src/index.ts @@ -85,7 +85,7 @@ async function activateTOC( toc.node.setAttribute('role', 'region'); toc.node.setAttribute('aria-label', trans.__('Table of Contents section')); - app.shell.add(toc, 'left', { rank: 400 }); + // app.shell.add(toc, 'left', { rank: 400 }); app.commands.addCommand(CommandIDs.runCells, { execute: args => { ``` - 更改默认的 Locale 为 zh_CN ```diff diff --git a/packages/translation-extension/schema/plugin.json b/packages/translation-extension/schema/plugin.json index 2ef8ed8bb7..b6cf3f74e9 100644 --- a/packages/translation-extension/schema/plugin.json +++ b/packages/translation-extension/schema/plugin.json @@ -34,7 +34,7 @@ "type": "string", "title": "Language locale", "description": "Set the interface display language. Examples: 'es_CO', 'fr'.", - "default": "en" + "default": "zh_CN" }, "stringsPrefix": { "type": "string", diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index beb11f76d7..78abe9a098 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -50,6 +50,7 @@ "@lumino/commands": "^1.19.0", "@lumino/coreutils": "^1.11.0", "@lumino/disposable": "^1.10.0", + "@lumino/messaging": "^1.10.0", "@lumino/signaling": "^1.10.0", "@lumino/virtualdom": "^1.14.0", "@lumino/widgets": "^1.30.0", ``` - 修正中间面板 Tab 的 z-index ```diff diff --git a/packages/ui-components/src/icon/widgets/tabbarsvg.ts b/packages/ui-components/src/icon/widgets/tabbarsvg.ts index fbcae09f57..330b751260 100644 --- a/packages/ui-components/src/icon/widgets/tabbarsvg.ts +++ b/packages/ui-components/src/icon/widgets/tabbarsvg.ts @@ -7,6 +7,8 @@ import { LabIconStyle } from '../../style'; import { classes } from '../../utils'; import { addIcon, closeIcon } from '../iconimports'; import { ITranslator, nullTranslator } from '@jupyterlab/translation'; +import { Message } from '@lumino/messaging'; +import { ElementInlineStyle, VirtualDOM } from '@lumino/virtualdom'; /** * a widget which displays titles as a single row or column of tabs. @@ -29,6 +31,20 @@ export class TabBarSvg extends TabBar { title: trans.__('New Launcher') }); } + + protected onUpdateRequest(msg: Message): void { + let titles = this.titles; + let renderer = this.renderer; + let currentTitle = this.currentTitle; + let content = new Array(titles.length); + for (let i = 0, n = titles.length; i < n; ++i) { + let title = titles[i]; + let current = title === currentTitle; + let zIndex = current ? 3 : 0; + content[i] = renderer.renderTab({ title, current, zIndex }); + } + VirtualDOM.render(content, this.contentNode); + } } export namespace TabBarSvg { @@ -59,6 +75,13 @@ export namespace TabBarSvg { closeIcon ) as unknown) as VirtualElement; } + + createTabStyle(data: TabBar.IRenderData): ElementInlineStyle { + if (data.zIndex === 0) { + return {}; + } + return { zIndex: `${data.zIndex}` }; + } } export const defaultRenderer = new Renderer(); ```