From 4ad9aca9cb9b019e84cd78c2eef7b8254c591d54 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 9 Aug 2019 17:10:58 +0100 Subject: [PATCH 001/103] Optional requires from CSV viewer extension --- packages/csvviewer-extension/src/index.ts | 98 +++++++++++++++-------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/packages/csvviewer-extension/src/index.ts b/packages/csvviewer-extension/src/index.ts index db69d5773750..a8f21c14b66c 100644 --- a/packages/csvviewer-extension/src/index.ts +++ b/packages/csvviewer-extension/src/index.ts @@ -35,8 +35,13 @@ const FACTORY_TSV = 'TSVTable'; const csv: JupyterFrontEndPlugin = { activate: activateCsv, id: '@jupyterlab/csvviewer-extension:csv', - requires: [ILayoutRestorer, IThemeManager, IMainMenu], - optional: [ISearchProviderRegistry], + requires: [], + optional: [ + ILayoutRestorer, + IThemeManager, + IMainMenu, + ISearchProviderRegistry + ], autoStart: true }; @@ -46,8 +51,13 @@ const csv: JupyterFrontEndPlugin = { const tsv: JupyterFrontEndPlugin = { activate: activateTsv, id: '@jupyterlab/csvviewer-extension:tsv', - requires: [ILayoutRestorer, IThemeManager, IMainMenu], - optional: [ISearchProviderRegistry], + requires: [], + optional: [ + ILayoutRestorer, + IThemeManager, + IMainMenu, + ISearchProviderRegistry + ], autoStart: true }; @@ -66,7 +76,7 @@ function addMenuEntries( title: 'Go to Line', value: 0 }).then(value => { - if (value.button.accept) { + if (value.button.accept && value.value !== null) { widget.content.goToLine(value.value); } }); @@ -79,10 +89,10 @@ function addMenuEntries( */ function activateCsv( app: JupyterFrontEnd, - restorer: ILayoutRestorer, - themeManager: IThemeManager, - mainMenu: IMainMenu, - searchregistry: ISearchProviderRegistry = null + restorer: ILayoutRestorer | null, + themeManager: IThemeManager | null, + mainMenu: IMainMenu | null, + searchregistry: ISearchProviderRegistry | null ): void { const factory = new CSVViewerFactory({ name: FACTORY_CSV, @@ -98,12 +108,14 @@ function activateCsv( let style: DataGrid.IStyle = Private.LIGHT_STYLE; let rendererConfig: TextRenderConfig = Private.LIGHT_TEXT_CONFIG; - // Handle state restoration. - void restorer.restore(tracker, { - command: 'docmanager:open', - args: widget => ({ path: widget.context.path, factory: FACTORY_CSV }), - name: widget => widget.context.path - }); + if (restorer) { + // Handle state restoration. + void restorer.restore(tracker, { + command: 'docmanager:open', + args: widget => ({ path: widget.context.path, factory: FACTORY_CSV }), + name: widget => widget.context.path + }); + } app.docRegistry.addWidgetFactory(factory); let ft = app.docRegistry.getFileType('csv'); @@ -116,8 +128,8 @@ function activateCsv( }); if (ft) { - widget.title.iconClass = ft.iconClass; - widget.title.iconLabel = ft.iconLabel; + widget.title.iconClass = ft.iconClass!; + widget.title.iconLabel = ft.iconLabel!; } // Set the theme for the new widget. widget.content.style = style; @@ -126,7 +138,10 @@ function activateCsv( // Keep the themes up-to-date. const updateThemes = () => { - const isLight = themeManager.isLight(themeManager.theme); + const isLight = + themeManager && themeManager.theme + ? themeManager.isLight(themeManager.theme) + : true; style = isLight ? Private.LIGHT_STYLE : Private.DARK_STYLE; rendererConfig = isLight ? Private.LIGHT_TEXT_CONFIG @@ -136,9 +151,13 @@ function activateCsv( grid.content.rendererConfig = rendererConfig; }); }; - themeManager.themeChanged.connect(updateThemes); + if (themeManager) { + themeManager.themeChanged.connect(updateThemes); + } - addMenuEntries(mainMenu, tracker); + if (mainMenu) { + addMenuEntries(mainMenu, tracker); + } if (searchregistry) { searchregistry.register('csv', CSVSearchProvider); } @@ -149,10 +168,10 @@ function activateCsv( */ function activateTsv( app: JupyterFrontEnd, - restorer: ILayoutRestorer, - themeManager: IThemeManager, - mainMenu: IMainMenu, - searchregistry: ISearchProviderRegistry = null + restorer: ILayoutRestorer | null, + themeManager: IThemeManager | null, + mainMenu: IMainMenu | null, + searchregistry: ISearchProviderRegistry | null ): void { const factory = new TSVViewerFactory({ name: FACTORY_TSV, @@ -168,12 +187,14 @@ function activateTsv( let style: DataGrid.IStyle = Private.LIGHT_STYLE; let rendererConfig: TextRenderConfig = Private.LIGHT_TEXT_CONFIG; - // Handle state restoration. - void restorer.restore(tracker, { - command: 'docmanager:open', - args: widget => ({ path: widget.context.path, factory: FACTORY_TSV }), - name: widget => widget.context.path - }); + if (restorer) { + // Handle state restoration. + void restorer.restore(tracker, { + command: 'docmanager:open', + args: widget => ({ path: widget.context.path, factory: FACTORY_TSV }), + name: widget => widget.context.path + }); + } app.docRegistry.addWidgetFactory(factory); let ft = app.docRegistry.getFileType('tsv'); @@ -186,8 +207,8 @@ function activateTsv( }); if (ft) { - widget.title.iconClass = ft.iconClass; - widget.title.iconLabel = ft.iconLabel; + widget.title.iconClass = ft.iconClass!; + widget.title.iconLabel = ft.iconLabel!; } // Set the theme for the new widget. widget.content.style = style; @@ -196,7 +217,10 @@ function activateTsv( // Keep the themes up-to-date. const updateThemes = () => { - const isLight = themeManager.isLight(themeManager.theme); + const isLight = + themeManager && themeManager.theme + ? themeManager.isLight(themeManager.theme) + : true; style = isLight ? Private.LIGHT_STYLE : Private.DARK_STYLE; rendererConfig = isLight ? Private.LIGHT_TEXT_CONFIG @@ -206,9 +230,13 @@ function activateTsv( grid.content.rendererConfig = rendererConfig; }); }; - themeManager.themeChanged.connect(updateThemes); + if (themeManager) { + themeManager.themeChanged.connect(updateThemes); + } - addMenuEntries(mainMenu, tracker); + if (mainMenu) { + addMenuEntries(mainMenu, tracker); + } if (searchregistry) { searchregistry.register('tsv', CSVSearchProvider); } From 89b737c940ecdf364662108c00749ae002750b29 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 9 Aug 2019 17:50:08 +0100 Subject: [PATCH 002/103] Make vdom-ext dependencies optional --- packages/vdom-extension/src/index.ts | 63 +++++++++++++++------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/packages/vdom-extension/src/index.ts b/packages/vdom-extension/src/index.ts index 682cd834130a..62894e24d840 100644 --- a/packages/vdom-extension/src/index.ts +++ b/packages/vdom-extension/src/index.ts @@ -34,14 +34,15 @@ const FACTORY_NAME = 'VDOM'; const plugin: JupyterFrontEndPlugin = { id: '@jupyterlab/vdom-extension:factory', - requires: [IRenderMimeRegistry, INotebookTracker, ILayoutRestorer], + requires: [IRenderMimeRegistry], + optional: [INotebookTracker, ILayoutRestorer], provides: IVDOMTracker, autoStart: true, activate: ( app: JupyterFrontEnd, rendermime: IRenderMimeRegistry, - notebooks: INotebookTracker, - restorer: ILayoutRestorer + notebooks: INotebookTracker | null, + restorer: ILayoutRestorer | null ) => { const tracker = new WidgetTracker({ namespace: 'vdom-widget' @@ -57,23 +58,25 @@ const plugin: JupyterFrontEndPlugin = { 0 ); - notebooks.widgetAdded.connect((sender, panel) => { - // Get the notebook's context and rendermime; - const { - context, - content: { rendermime } - } = panel; - - // Add the renderer factory to the notebook's rendermime registry; - rendermime.addFactory( - { - safe: false, - mimeTypes: [MIME_TYPE], - createRenderer: options => new RenderedVDOM(options, context) - }, - 0 - ); - }); + if (notebooks) { + notebooks.widgetAdded.connect((sender, panel) => { + // Get the notebook's context and rendermime; + const { + context, + content: { rendermime } + } = panel; + + // Add the renderer factory to the notebook's rendermime registry; + rendermime.addFactory( + { + safe: false, + mimeTypes: [MIME_TYPE], + createRenderer: options => new RenderedVDOM(options, context) + }, + 0 + ); + }); + } app.docRegistry.addFileType({ name: 'vdom', @@ -102,15 +105,17 @@ const plugin: JupyterFrontEndPlugin = { // Add widget factory to document registry. app.docRegistry.addWidgetFactory(factory); - // Handle state restoration. - void restorer.restore(tracker, { - command: 'docmanager:open', - args: widget => ({ - path: widget.context.path, - factory: FACTORY_NAME - }), - name: widget => widget.context.path - }); + if (restorer) { + // Handle state restoration. + void restorer.restore(tracker, { + command: 'docmanager:open', + args: widget => ({ + path: widget.context.path, + factory: FACTORY_NAME + }), + name: widget => widget.context.path + }); + } return tracker; } From 86ada79dcbd1243e65bf1417308a46bb3e9f4713 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 9 Aug 2019 17:53:34 +0100 Subject: [PATCH 003/103] strictNullCheck tweak --- packages/vdom-extension/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vdom-extension/src/index.ts b/packages/vdom-extension/src/index.ts index 62894e24d840..8a781ee03d37 100644 --- a/packages/vdom-extension/src/index.ts +++ b/packages/vdom-extension/src/index.ts @@ -90,7 +90,7 @@ const plugin: JupyterFrontEndPlugin = { dataType: 'json', rendermime, name: FACTORY_NAME, - primaryFileType: app.docRegistry.getFileType('vdom'), + primaryFileType: app.docRegistry.getFileType('vdom')!, fileTypes: ['vdom', 'json'], defaultFor: ['vdom'] }); From 2b64353632a6f70638843c52e8a14fd105f9a79a Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 15:31:55 +0100 Subject: [PATCH 004/103] Fix deprecation warning --- jupyterlab/commands.py | 6 +++--- jupyterlab/handlers/build_handler.py | 2 +- jupyterlab/labapp.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jupyterlab/commands.py b/jupyterlab/commands.py index da2ecfc009c0..42cb8412f57f 100644 --- a/jupyterlab/commands.py +++ b/jupyterlab/commands.py @@ -775,7 +775,7 @@ def update_extension(self, name): Returns `True` if a rebuild is recommended, `False` otherwise. """ if name not in self.info['extensions']: - self.logger.warn('No labextension named "%s" installed' % name) + self.logger.warning('No labextension named "%s" installed' % name) return False return self._update_extension(name) @@ -815,8 +815,8 @@ def link_package(self, path): return self.install_extension(path) # Warn that it is a linked package. - self.logger.warn('Installing %s as a linked package:', path) - [self.logger.warn(m) for m in messages] + self.logger.warning('Installing %s as a linked package:', path) + [self.logger.warning(m) for m in messages] # Add to metadata. config = self._read_build_config() diff --git a/jupyterlab/handlers/build_handler.py b/jupyterlab/handlers/build_handler.py index 80cf27e5fa89..912840c9be18 100644 --- a/jupyterlab/handlers/build_handler.py +++ b/jupyterlab/handlers/build_handler.py @@ -114,7 +114,7 @@ def get(self): @web.authenticated @gen.coroutine def delete(self): - self.log.warn('Canceling build') + self.log.warning('Canceling build') try: yield self.builder.cancel() except Exception as e: diff --git a/jupyterlab/labapp.py b/jupyterlab/labapp.py index a79cf8ff7e7b..2c54e934981a 100644 --- a/jupyterlab/labapp.py +++ b/jupyterlab/labapp.py @@ -411,7 +411,7 @@ def init_server_extensions(self): super(LabApp, self).init_server_extensions() msg = 'JupyterLab server extension not enabled, manually loading...' if not self.nbserver_extensions.get('jupyterlab', False): - self.log.warn(msg) + self.log.warning(msg) load_jupyter_server_extension(self) From 6a3c03451478b1804fa0a68963f3173208297a17 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 15:33:31 +0100 Subject: [PATCH 005/103] Add a CoreConfig object to python --- jupyterlab/commands.py | 86 +++++------ jupyterlab/coreconfig.py | 139 ++++++++++++++++++ jupyterlab/handlers/build_handler.py | 20 ++- .../handlers/extension_manager_handler.py | 20 ++- jupyterlab/labapp.py | 14 +- jupyterlab/labextensions.py | 45 ++++-- jupyterlab/tests/test_jupyterlab.py | 6 +- 7 files changed, 254 insertions(+), 76 deletions(-) create mode 100644 jupyterlab/coreconfig.py diff --git a/jupyterlab/commands.py b/jupyterlab/commands.py index 42cb8412f57f..0db625380bdb 100644 --- a/jupyterlab/commands.py +++ b/jupyterlab/commands.py @@ -29,6 +29,7 @@ from .semver import Range, gte, lt, lte, gt, make_semver from .jlpmapp import YARN_PATH, HERE +from .coreconfig import CoreConfig # The regex for expecting the webpack output. @@ -283,7 +284,7 @@ def watch_dev(logger=None): return package_procs + [wp_proc] -def watch(app_dir=None, logger=None): +def watch(app_dir=None, logger=None, core_config=None): """Watch the application. Parameters @@ -299,11 +300,11 @@ def watch(app_dir=None, logger=None): """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.watch() -def install_extension(extension, app_dir=None, logger=None): +def install_extension(extension, app_dir=None, logger=None, core_config=None): """Install an extension package into JupyterLab. The extension is first validated. @@ -312,24 +313,24 @@ def install_extension(extension, app_dir=None, logger=None): """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.install_extension(extension) -def uninstall_extension(name=None, app_dir=None, logger=None, all_=False): +def uninstall_extension(name=None, app_dir=None, logger=None, all_=False, core_config=None): """Uninstall an extension by name or path. Returns `True` if a rebuild is recommended, `False` otherwise. """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) if all_ is True: return handler.uninstall_all_extensions() return handler.uninstall_extension(name) -def update_extension(name=None, all_=False, app_dir=None, logger=None): +def update_extension(name=None, all_=False, app_dir=None, logger=None, core_config=None): """Update an extension by name, or all extensions. Either `name` must be given as a string, or `all_` must be `True`. @@ -339,7 +340,7 @@ def update_extension(name=None, all_=False, app_dir=None, logger=None): """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) if all_ is True: return handler.update_all_extensions() return handler.update_extension(name) @@ -363,96 +364,96 @@ def clean(app_dir=None, logger=None): def build(app_dir=None, name=None, version=None, static_url=None, logger=None, command='build:prod', kill_event=None, - clean_staging=False): + clean_staging=False, core_config=None): """Build the JupyterLab application. """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger, kill_event=kill_event) + handler = _AppHandler(app_dir, logger, kill_event=kill_event, core_config=core_config) return handler.build(name=name, version=version, static_url=static_url, command=command, clean_staging=clean_staging) -def get_app_info(app_dir=None, logger=None): +def get_app_info(app_dir=None, logger=None, core_config=None): """Get a dictionary of information about the app. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.info -def enable_extension(extension, app_dir=None, logger=None): +def enable_extension(extension, app_dir=None, logger=None, core_config=None): """Enable a JupyterLab extension. Returns `True` if a rebuild is recommended, `False` otherwise. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.toggle_extension(extension, False) -def disable_extension(extension, app_dir=None, logger=None): +def disable_extension(extension, app_dir=None, logger=None, core_config=None): """Disable a JupyterLab package. Returns `True` if a rebuild is recommended, `False` otherwise. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.toggle_extension(extension, True) -def check_extension(extension, app_dir=None, installed=False, logger=None): +def check_extension(extension, app_dir=None, installed=False, logger=None, core_config=None): """Check if a JupyterLab extension is enabled or disabled. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.check_extension(extension, installed) -def build_check(app_dir=None, logger=None): +def build_check(app_dir=None, logger=None, core_config=None): """Determine whether JupyterLab should be built. Returns a list of messages. """ logger = _ensure_logger(logger) _node_check(logger) - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.build_check() -def list_extensions(app_dir=None, logger=None): +def list_extensions(app_dir=None, logger=None, core_config=None): """List the extensions. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.list_extensions() -def link_package(path, app_dir=None, logger=None): +def link_package(path, app_dir=None, logger=None, core_config=None): """Link a package against the JupyterLab build. Returns `True` if a rebuild is recommended, `False` otherwise. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.link_package(path) -def unlink_package(package, app_dir=None, logger=None): +def unlink_package(package, app_dir=None, logger=None, core_config=None): """Unlink a package from JupyterLab by path or name. Returns `True` if a rebuild is recommended, `False` otherwise. """ - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.unlink_package(package) -def get_app_version(app_dir=None): +def get_app_version(app_dir=None, core_config=None): """Get the application version.""" app_dir = app_dir or get_app_dir() - handler = _AppHandler(app_dir) + handler = _AppHandler(app_dir, core_config=core_config) return handler.info['version'] -def get_latest_compatible_package_versions(names, app_dir=None, logger=None): +def get_latest_compatible_package_versions(names, app_dir=None, logger=None, core_config=None): """Get the latest compatible version of a list of packages. """ app_dir = app_dir or get_app_dir() - handler = _AppHandler(app_dir, logger) + handler = _AppHandler(app_dir, logger, core_config=core_config) return handler.latest_compatible_package_versions(names) @@ -476,12 +477,13 @@ def read_package(target): class _AppHandler(object): - def __init__(self, app_dir, logger=None, kill_event=None): + def __init__(self, app_dir, logger=None, kill_event=None, core_config=None): """Create a new _AppHandler object """ self.app_dir = app_dir or get_app_dir() self.sys_dir = get_app_dir() self.logger = _ensure_logger(logger) + self.core_data = (core_config or CoreConfig()).data self.info = self._get_app_info() self.kill_event = kill_event or Event() # TODO: Make this configurable @@ -713,7 +715,6 @@ def uninstall_extension(self, name): Returns `True` if a rebuild is recommended, `False` otherwise. """ # Allow for uninstalled core extensions. - data = self.info['core_data'] if name in self.info['core_extensions']: config = self._read_build_config() uninstalled = config.get('uninstalled_core_extensions', []) @@ -938,7 +939,7 @@ def _get_app_info(self): """ info = dict() - info['core_data'] = core_data = _get_core_data() + info['core_data'] = core_data = self.core_data info['extensions'] = extensions = self._get_extensions(core_data) page_config = self._read_page_config() info['disabled'] = page_config.get('disabledExtensions', []) @@ -963,7 +964,8 @@ def _get_app_info(self): info['sys_dir'] = self.sys_dir info['app_dir'] = self.app_dir - info['core_extensions'] = core_extensions = _get_core_extensions() + info['core_extensions'] = core_extensions = _get_core_extensions( + self.core_data) disabled_core = [] for key in core_extensions: @@ -1390,9 +1392,8 @@ def _install_extension(self, extension, tempdir): raise ValueError(msg % (extension, '\n'.join(messages))) # Verify package compatibility. - core_data = _get_core_data() deps = data.get('dependencies', dict()) - errors = _validate_compatibility(extension, deps, core_data) + errors = _validate_compatibility(extension, deps, self.core_data) if errors: msg = _format_compatibility_errors( data['name'], data['version'], errors @@ -1628,7 +1629,7 @@ def _node_check(logger): output = subprocess.check_output([node, 'node-version-check.js'], cwd=HERE) logger.debug(output.decode('utf-8')) except Exception: - data = _get_core_data() + data = CoreConfig().data ver = data['engines']['node'] msg = 'Please install nodejs %s before continuing. nodejs may be installed using conda or directly from the nodejs website.' % ver raise ValueError(msg) @@ -1728,13 +1729,6 @@ def _tarsum(input_file): return h.hexdigest() -def _get_core_data(): - """Get the data for the app template. - """ - with open(pjoin(HERE, 'staging', 'package.json')) as fid: - return json.load(fid) - - def _get_static_data(app_dir): """Get the data for the app static dir. """ @@ -1931,10 +1925,10 @@ def _compat_error_age(errors): return 0 -def _get_core_extensions(): +def _get_core_extensions(core_data): """Get the core extensions. """ - data = _get_core_data()['jupyterlab'] + data = core_data['jupyterlab'] return list(data['extensions']) + list(data['mimeExtensions']) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py new file mode 100644 index 000000000000..1b35d145ba57 --- /dev/null +++ b/jupyterlab/coreconfig.py @@ -0,0 +1,139 @@ +# coding: utf-8 +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +from itertools import filterfalse +import json +import os.path as osp + +from .jlpmapp import HERE + + +def pjoin(*args): + """Join paths to create a real path. + """ + return osp.abspath(osp.join(*args)) + + +def _get_core_data(): + """Get the data for the app template. + """ + with open(pjoin(HERE, 'staging', 'package.json')) as fid: + return json.load(fid) + + +def _is_lab_package(name): + """Whether a package name is in the lab namespace""" + return name.startswith('@jupyterlab/') + + +def _only_nonlab(collection): + """Filter a dict/sequence to remove all lab packages""" + if isinstance(collection, dict): + return dict( + (k, v) for (k, v) in collection.items() + if not _is_lab_package(k) + ) + elif isinstance(collection, (list, tuple)): + return list(filterfalse(_is_lab_package, collection)) + raise TypeError('collection arg should be either dict or list/tuple') + + +class CoreConfig: + """An object representing a core-mode package/extension configuration. + + This enables custom lab application to change + """ + + def __init__(self): + self._data = _get_core_data() + + def clear_defaults(self, lab_only=True): + """Clear the default packages/extensions. + + lab_only: string + Whether to remove all packages, or only those from + JupyterLab. Defaults to True (only lab packages). + This will leave dependencies like phosphor, react + etc untouched. + """ + data = self._data + if lab_only: + # Clear all "@jupyterlab/" dependencies + data['dependencies'] = _only_nonlab(data['dependencies']) + data['resolutions'] = _only_nonlab(data['resolutions']) + data['jupyterlab']['extensions'] = _only_nonlab( + data['jupyterlab']['extensions']) + data['jupyterlab']['mimeExtensions'] = _only_nonlab( + data['jupyterlab']['mimeExtensions']) + data['jupyterlab']['singletonPackages'] = _only_nonlab( + data['jupyterlab']['singletonPackages']) + else: + # Clear all dependencies + data['dependencies'] = {} + data['resolutions'] = {} + data['jupyterlab']['extensions'] = {} + data['jupyterlab']['mimeExtensions'] = {} + data['jupyterlab']['singletonPackages'] = [] + + def add(self, name, semver, extension=False, mimeExtension=False): + """Remove a package/extension. + + name: string + The npm package name + semver: string + The semver range for the package + extension: bool + Whether the package is an extension + mimeExtension: bool + Whether the package is a MIME extension + """ + data = self._data + if name in self._data['resolutions']: + raise ValueError('Package already present: %r' % (name,)) + data['resolutions'][name] = semver + + # If both mimeExtension and extensions are True, treat + # as mime extension + if mimeExtension: + data['jupyterlab']['mimeExtensions'][name] = "" + data['dependencies'][name] = semver + elif extension: + data['jupyterlab']['extensions'][name] = "" + data['dependencies'][name] = semver + else: + data['singletonPackages'].append(name) + + def remove(self, name): + """Remove a package/extension. + + name: string + The npm package name + """ + data = self._data + maps = ( + data['dependencies'], + data['resolutions'], + data['jupyterlab']['extensions'], + data['jupyterlab']['mimeExtensions'], + ) + for m in maps: + try: + del m[name] + except KeyError: + pass + + data['jupyterlab']['singletonPackages'].remove(name) + + + def set_static_dir(self, static_dir): + self._data['jupyterlab']['staticDir'] = static_dir + + @property + def data(self): + """Returns the raw core data. + + Its content should be considered an internal implementation + detail of lab, and should not be relied upon outide of lab. + """ + return self._data diff --git a/jupyterlab/handlers/build_handler.py b/jupyterlab/handlers/build_handler.py index 912840c9be18..c8bfbfec15db 100644 --- a/jupyterlab/handlers/build_handler.py +++ b/jupyterlab/handlers/build_handler.py @@ -21,10 +21,11 @@ class Builder(object): _kill_event = None _future = None - def __init__(self, log, core_mode, app_dir): + def __init__(self, log, core_mode, app_dir, core_config=None): self.log = log self.core_mode = core_mode self.app_dir = app_dir + self.core_config = core_config @gen.coroutine def get_status(self): @@ -34,7 +35,8 @@ def get_status(self): raise gen.Return(dict(status='building', message='')) try: - messages = yield self._run_build_check(self.app_dir, self.log) + messages = yield self._run_build_check( + self.app_dir, self.log, self.core_config) status = 'needed' if messages else 'stable' if messages: self.log.warn('Build recommended') @@ -60,7 +62,8 @@ def build(self): self.building = True self._kill_event = evt = Event() try: - yield self._run_build(self.app_dir, self.log, evt) + yield self._run_build( + self.app_dir, self.log, evt, self.core_config) future.set_result(True) except Exception as e: if str(e) == 'Aborted': @@ -84,12 +87,15 @@ def cancel(self): self.canceled = True @run_on_executor - def _run_build_check(self, app_dir, logger): - return build_check(app_dir=app_dir, logger=logger) + def _run_build_check(self, app_dir, logger, core_config): + return build_check( + app_dir=app_dir, logger=logger, core_config=core_config) @run_on_executor - def _run_build(self, app_dir, logger, kill_event): - kwargs = dict(app_dir=app_dir, logger=logger, kill_event=kill_event, command='build') + def _run_build(self, app_dir, logger, kill_event, core_config): + kwargs = dict( + app_dir=app_dir, logger=logger, kill_event=kill_event, + core_config=core_config, command='build') try: return build(**kwargs) except Exception as e: diff --git a/jupyterlab/handlers/extension_manager_handler.py b/jupyterlab/handlers/extension_manager_handler.py index 3ef873e357ba..df02348df367 100644 --- a/jupyterlab/handlers/extension_manager_handler.py +++ b/jupyterlab/handlers/extension_manager_handler.py @@ -66,9 +66,10 @@ def _build_check_info(app_dir, logger): class ExtensionManager(object): executor = ThreadPoolExecutor(max_workers=1) - def __init__(self, log, app_dir): + def __init__(self, log, app_dir, core_config=None): self.log = log self.app_dir = app_dir + self.core_config = core_config self._outdated = None # To start fetching data on outdated extensions immediately, uncomment: # IOLoop.current().spawn_callback(self._get_outdated) @@ -122,7 +123,9 @@ def list_extensions(self): def install(self, extension): """Handle an install/update request""" try: - install_extension(extension, app_dir=self.app_dir, logger=self.log) + install_extension( + extension, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) except ValueError as e: raise gen.Return(dict(status='error', message=str(e))) raise gen.Return(dict(status='ok',)) @@ -130,19 +133,25 @@ def install(self, extension): @gen.coroutine def uninstall(self, extension): """Handle an uninstall request""" - did_uninstall = uninstall_extension(extension, app_dir=self.app_dir, logger=self.log) + did_uninstall = uninstall_extension( + extension, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) raise gen.Return(dict(status='ok' if did_uninstall else 'error',)) @gen.coroutine def enable(self, extension): """Handle an enable request""" - enable_extension(extension, app_dir=self.app_dir, logger=self.log) + enable_extension( + extension, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) raise gen.Return(dict(status='ok',)) @gen.coroutine def disable(self, extension): """Handle a disable request""" - disable_extension(extension, app_dir=self.app_dir, logger=self.log) + disable_extension( + extension, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) raise gen.Return(dict(status='ok',)) @gen.coroutine @@ -187,6 +196,7 @@ def _load_outdated(self): names, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config, ) raise gen.Return(data) diff --git a/jupyterlab/labapp.py b/jupyterlab/labapp.py index 2c54e934981a..288b6a0afc86 100644 --- a/jupyterlab/labapp.py +++ b/jupyterlab/labapp.py @@ -14,7 +14,7 @@ from jupyterlab_server import slugify, WORKSPACE_EXTENSION from notebook.notebookapp import NotebookApp, aliases, flags from notebook.utils import url_path_join as ujoin -from traitlets import Bool, Unicode +from traitlets import Bool, Instance, Unicode from ._version import __version__ from .debuglog import DebugLogFileMixin @@ -23,6 +23,7 @@ build, clean, get_app_dir, get_app_version, get_user_settings_dir, get_workspaces_dir ) +from .coreconfig import CoreConfig build_aliases = dict(base_aliases) @@ -53,6 +54,9 @@ class LabBuildApp(JupyterApp, DebugLogFileMixin): aliases = build_aliases flags = build_flags + # Not configurable! + core_config = Instance(CoreConfig, allow_none=True) + app_dir = Unicode('', config=True, help="The app directory to build in") @@ -88,7 +92,8 @@ def start(self): clean(self.app_dir) self.log.info('Building in %s', app_dir) build(app_dir=app_dir, name=self.name, version=self.version, - command=command, logger=self.log) + command=command, logger=self.log, + core_config=self.core_config) clean_aliases = dict(base_aliases) @@ -105,10 +110,13 @@ class LabCleanApp(JupyterApp): """ aliases = clean_aliases + # Not configurable! + core_config = Instance(CoreConfig, allow_none=True) + app_dir = Unicode('', config=True, help='The app directory to clean') def start(self): - clean(self.app_dir, logger=self.log) + clean(self.app_dir, logger=self.log, core_config=self.core_config) class LabPathApp(JupyterApp): diff --git a/jupyterlab/labextensions.py b/jupyterlab/labextensions.py index bc5071a2c030..6d1984737eb4 100644 --- a/jupyterlab/labextensions.py +++ b/jupyterlab/labextensions.py @@ -11,7 +11,7 @@ from jupyter_core.application import JupyterApp, base_flags, base_aliases -from traitlets import Bool, Unicode +from traitlets import Bool, Instance, Unicode from .commands import ( install_extension, uninstall_extension, list_extensions, @@ -19,6 +19,7 @@ link_package, unlink_package, build, get_app_version, HERE, update_extension, ) +from .coreconfig import CoreConfig from .debuglog import DebugLogFileMixin @@ -65,6 +66,9 @@ class BaseExtensionApp(JupyterApp, DebugLogFileMixin): flags = flags aliases = aliases + # Not configurable! + core_config = Instance(CoreConfig, allow_none=True) + app_dir = Unicode('', config=True, help="The app directory to target") @@ -95,7 +99,8 @@ def start(self): command = ':'.join(parts) build(app_dir=self.app_dir, clean_staging=self.should_clean, - logger=self.log, command=command) + logger=self.log, command=command, + core_config=self.core_config) def run_task(self): pass @@ -111,7 +116,9 @@ class InstallLabExtensionApp(BaseExtensionApp): def run_task(self): self.extra_args = self.extra_args or [os.getcwd()] return any([ - install_extension(arg, self.app_dir, logger=self.log) + install_extension( + arg, self.app_dir, logger=self.log, + core_config=self.core_config) for arg in self.extra_args ]) @@ -128,9 +135,13 @@ def run_task(self): self.log.warn('Specify an extension to update, or use --all to update all extensions') return False if self.all: - return update_extension(all_=True, app_dir=self.app_dir, logger=self.log) + return update_extension( + all_=True, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) return any([ - update_extension(name=arg, app_dir=self.app_dir, logger=self.log) + update_extension( + name=arg, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) for arg in self.extra_args ]) @@ -149,7 +160,9 @@ class LinkLabExtensionApp(BaseExtensionApp): def run_task(self): self.extra_args = self.extra_args or [os.getcwd()] return any([ - link_package(arg, self.app_dir, logger=self.log) + link_package( + arg, self.app_dir, logger=self.log, + core_config=self.core_config) for arg in self.extra_args ]) @@ -160,7 +173,9 @@ class UnlinkLabExtensionApp(BaseExtensionApp): def run_task(self): self.extra_args = self.extra_args or [os.getcwd()] return any([ - unlink_package(arg, self.app_dir, logger=self.log) + unlink_package( + arg, self.app_dir, logger=self.log, + core_config=self.core_config) for arg in self.extra_args ]) @@ -175,7 +190,9 @@ class UninstallLabExtensionApp(BaseExtensionApp): def run_task(self): self.extra_args = self.extra_args or [os.getcwd()] return any([ - uninstall_extension(arg, all_=self.all, app_dir=self.app_dir, logger=self.log) + uninstall_extension( + arg, all_=self.all, app_dir=self.app_dir, logger=self.log, + core_config=self.core_config) for arg in self.extra_args ]) @@ -184,14 +201,16 @@ class ListLabExtensionsApp(BaseExtensionApp): description = "List the installed labextensions" def run_task(self): - list_extensions(self.app_dir, logger=self.log) + list_extensions( + self.app_dir, logger=self.log, core_config=self.core_config) class EnableLabExtensionsApp(BaseExtensionApp): description = "Enable labextension(s) by name" def run_task(self): - [enable_extension(arg, self.app_dir, logger=self.log) + [enable_extension( + arg, self.app_dir, logger=self.log, core_config=self.core_config) for arg in self.extra_args] @@ -199,7 +218,8 @@ class DisableLabExtensionsApp(BaseExtensionApp): description = "Disable labextension(s) by name" def run_task(self): - [disable_extension(arg, self.app_dir, logger=self.log) + [disable_extension( + arg, self.app_dir, logger=self.log, core_config=self.core_config) for arg in self.extra_args] @@ -215,7 +235,8 @@ def run_task(self): check_extension( arg, self.app_dir, self.should_check_installed_only, - logger=self.log) + logger=self.log, + core_config=self.core_config) for arg in self.extra_args) if not all_enabled: self.exit(1) diff --git a/jupyterlab/tests/test_jupyterlab.py b/jupyterlab/tests/test_jupyterlab.py index 415b19c23fd7..518b95b6d361 100644 --- a/jupyterlab/tests/test_jupyterlab.py +++ b/jupyterlab/tests/test_jupyterlab.py @@ -23,9 +23,9 @@ install_extension, uninstall_extension, list_extensions, build, link_package, unlink_package, build_check, disable_extension, enable_extension, get_app_info, - check_extension, _test_overlap, _get_core_data, - update_extension + check_extension, _test_overlap, update_extension ) +from jupyterlab.coreconfig import CoreConfig here = os.path.dirname(os.path.abspath(__file__)) @@ -477,7 +477,7 @@ def test_compatibility(self): assert _test_overlap('<0.6', '0.1') is None def test_install_compatible(self): - core_data = _get_core_data() + core_data = CoreConfig().data current_app_dep = core_data['dependencies']['@jupyterlab/application'] def _gen_dep(ver): return { "dependencies": { From 29a40e01ed6c58ff29158207d309156399e478ae Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 16:41:00 +0100 Subject: [PATCH 006/103] fix clean --- jupyterlab/labapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterlab/labapp.py b/jupyterlab/labapp.py index 288b6a0afc86..992784d02560 100644 --- a/jupyterlab/labapp.py +++ b/jupyterlab/labapp.py @@ -116,7 +116,7 @@ class LabCleanApp(JupyterApp): app_dir = Unicode('', config=True, help='The app directory to clean') def start(self): - clean(self.app_dir, logger=self.log, core_config=self.core_config) + clean(self.app_dir, logger=self.log) class LabPathApp(JupyterApp): From 361788dd3c24e424db52b0a2e3a9056b7f15e151 Mon Sep 17 00:00:00 2001 From: "M. Krassowski" <5832902+krassowski@users.noreply.github.com> Date: Mon, 12 Aug 2019 17:26:15 +0100 Subject: [PATCH 007/103] Update issue.rst Fix a typo from #6971 --- docs/source/getting_started/issue.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/getting_started/issue.rst b/docs/source/getting_started/issue.rst index 2430273e2c40..7038674bcd58 100644 --- a/docs/source/getting_started/issue.rst +++ b/docs/source/getting_started/issue.rst @@ -36,7 +36,7 @@ If you find a problem in JupyterLab, please follow the steps below to diagnose a - I can reproduce the issue with the classic Jupyter Notebook: The problem is probably not from JupyterLab. It may be in the `Jupyter Notebook server `__, your kernel, etc. Use your best judgement to file an issue with the appropriate project. - I cannot reproduce the issue in classic Jupyter Notebook: Go to step 4. -4. Try to reproduce the issue in your browser incognito or private browsing modeq. Running in private browser mode ensures your browser state is clean. +4. Try to reproduce the issue in your browser incognito or private browsing mode. Running in private browser mode ensures your browser state is clean. - I cannot reproduce the issue in private browsing mode: Perhaps resetting your cookies or other browser state would help. - I can reproduce the issue in private browsing mode: Go to :ref:`create-issue`. From 849244d282fd154fa492d241ebe8bede33009dd2 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 19:12:15 +0100 Subject: [PATCH 008/103] Expose current info on coreconfig --- jupyterlab/coreconfig.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py index 1b35d145ba57..deeb81b1d304 100644 --- a/jupyterlab/coreconfig.py +++ b/jupyterlab/coreconfig.py @@ -77,7 +77,10 @@ def clear_defaults(self, lab_only=True): data['jupyterlab']['singletonPackages'] = [] def add(self, name, semver, extension=False, mimeExtension=False): - """Remove a package/extension. + """Remove an extension/singleton. + + If neither extension or mimeExtension is True (the default) + the package is added as a singleton dependency. name: string The npm package name @@ -104,6 +107,24 @@ def add(self, name, semver, extension=False, mimeExtension=False): else: data['singletonPackages'].append(name) + @property + def extensions(self): + """A dict mapping all extension names to their semver""" + return dict(self._data['jupyterlab']['extensions']) + + @property + def mimeExtensions(self): + """A dict mapping all MIME extension names to their semver""" + return dict(self._data['jupyterlab']['mimeExtensions']) + + @property + def singletons(self): + """A dict mapping all singleton names to their semver""" + return dict( + (k, self._data['resolutions'][k]) + for k in self._data['singletonPackages'] + ) + def remove(self, name): """Remove a package/extension. From 64ba1ca1d50a0bac043e74c3774014fa222495e3 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 19:19:34 +0100 Subject: [PATCH 009/103] Avoid camelCase in Python --- jupyterlab/coreconfig.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py index deeb81b1d304..3e07f3bcba17 100644 --- a/jupyterlab/coreconfig.py +++ b/jupyterlab/coreconfig.py @@ -76,7 +76,7 @@ def clear_defaults(self, lab_only=True): data['jupyterlab']['mimeExtensions'] = {} data['jupyterlab']['singletonPackages'] = [] - def add(self, name, semver, extension=False, mimeExtension=False): + def add(self, name, semver, extension=False, mime_extension=False): """Remove an extension/singleton. If neither extension or mimeExtension is True (the default) @@ -88,7 +88,7 @@ def add(self, name, semver, extension=False, mimeExtension=False): The semver range for the package extension: bool Whether the package is an extension - mimeExtension: bool + mime_extension: bool Whether the package is a MIME extension """ data = self._data @@ -98,7 +98,7 @@ def add(self, name, semver, extension=False, mimeExtension=False): # If both mimeExtension and extensions are True, treat # as mime extension - if mimeExtension: + if mime_extension: data['jupyterlab']['mimeExtensions'][name] = "" data['dependencies'][name] = semver elif extension: @@ -113,7 +113,7 @@ def extensions(self): return dict(self._data['jupyterlab']['extensions']) @property - def mimeExtensions(self): + def mime_extensions(self): """A dict mapping all MIME extension names to their semver""" return dict(self._data['jupyterlab']['mimeExtensions']) From b937f18f161053751095e10c253bca9614656144 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 19:27:59 +0100 Subject: [PATCH 010/103] Fix CoreConfig signleton location --- jupyterlab/coreconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py index 3e07f3bcba17..74b4ca32630a 100644 --- a/jupyterlab/coreconfig.py +++ b/jupyterlab/coreconfig.py @@ -105,7 +105,7 @@ def add(self, name, semver, extension=False, mime_extension=False): data['jupyterlab']['extensions'][name] = "" data['dependencies'][name] = semver else: - data['singletonPackages'].append(name) + data['jupyterlab']['singletonPackages'].append(name) @property def extensions(self): @@ -122,7 +122,7 @@ def singletons(self): """A dict mapping all singleton names to their semver""" return dict( (k, self._data['resolutions'][k]) - for k in self._data['singletonPackages'] + for k in self._data['jupyterlab']['singletonPackages'] ) def remove(self, name): From 3c6a864e0ee1cd7e56951ff8a8f40f91d98dacb9 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 20:29:33 +0100 Subject: [PATCH 011/103] More fixes for CoreConfig --- jupyterlab/coreconfig.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py index 74b4ca32630a..8ce0d2cdf1df 100644 --- a/jupyterlab/coreconfig.py +++ b/jupyterlab/coreconfig.py @@ -92,6 +92,10 @@ def add(self, name, semver, extension=False, mime_extension=False): Whether the package is a MIME extension """ data = self._data + if not name: + raise ValueError('Missing package name') + if not semver: + raise ValueError('Missing package semver') if name in self._data['resolutions']: raise ValueError('Package already present: %r' % (name,)) data['resolutions'][name] = semver @@ -110,12 +114,16 @@ def add(self, name, semver, extension=False, mime_extension=False): @property def extensions(self): """A dict mapping all extension names to their semver""" - return dict(self._data['jupyterlab']['extensions']) + return dict( + (k, self._data['resolutions'][k]) + for k in self._data['jupyterlab']['extensions'].keys()) @property def mime_extensions(self): """A dict mapping all MIME extension names to their semver""" - return dict(self._data['jupyterlab']['mimeExtensions']) + return dict( + (k, self._data['resolutions'][k]) + for k in self._data['jupyterlab']['mimeExtensions'].keys()) @property def singletons(self): From 54e91dc1b1c2000fd83946d031bec06d0c6c0a17 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 21:19:22 +0100 Subject: [PATCH 012/103] Make dependencies of rendermime extension optional --- packages/rendermime-extension/src/index.ts | 76 ++++++++++++---------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/packages/rendermime-extension/src/index.ts b/packages/rendermime-extension/src/index.ts index abb205600cb8..3796135852c9 100644 --- a/packages/rendermime-extension/src/index.ts +++ b/packages/rendermime-extension/src/index.ts @@ -26,8 +26,8 @@ namespace CommandIDs { */ const plugin: JupyterFrontEndPlugin = { id: '@jupyterlab/rendermime-extension:plugin', - requires: [IDocumentManager], - optional: [ILatexTypesetter], + requires: [], + optional: [IDocumentManager, ILatexTypesetter], provides: IRenderMimeRegistry, activate: activate, autoStart: true @@ -43,45 +43,49 @@ export default plugin; */ function activate( app: JupyterFrontEnd, - docManager: IDocumentManager, + docManager: IDocumentManager | null, latexTypesetter: ILatexTypesetter | null ) { - app.commands.addCommand(CommandIDs.handleLink, { - label: 'Handle Local Link', - execute: args => { - const path = args['path'] as string | undefined | null; - const id = args['id'] as string | undefined | null; - if (!path) { - return; - } - // First check if the path exists on the server. - return docManager.services.contents - .get(path, { content: false }) - .then(() => { - // Open the link with the default rendered widget factory, - // if applicable. - const factory = docManager.registry.defaultRenderedWidgetFactory( - path - ); - const widget = docManager.openOrReveal(path, factory.name); + if (docManager) { + app.commands.addCommand(CommandIDs.handleLink, { + label: 'Handle Local Link', + execute: args => { + const path = args['path'] as string | undefined | null; + const id = args['id'] as string | undefined | null; + if (!path) { + return; + } + // First check if the path exists on the server. + return docManager.services.contents + .get(path, { content: false }) + .then(() => { + // Open the link with the default rendered widget factory, + // if applicable. + const factory = docManager.registry.defaultRenderedWidgetFactory( + path + ); + const widget = docManager.openOrReveal(path, factory.name); - // Handle the hash if one has been provided. - if (widget && id) { - widget.setFragment(id); - } - }); - } - }); + // Handle the hash if one has been provided. + if (widget && id) { + widget.setFragment(id); + } + }); + } + }); + } return new RenderMimeRegistry({ initialFactories: standardRendererFactories, - linkHandler: { - handleLink: (node: HTMLElement, path: string, id?: string) => { - app.commandLinker.connectNode(node, CommandIDs.handleLink, { - path, - id - }); - } - }, + linkHandler: !docManager + ? null + : { + handleLink: (node: HTMLElement, path: string, id?: string) => { + app.commandLinker.connectNode(node, CommandIDs.handleLink, { + path, + id + }); + } + }, latexTypesetter }); } From a99899e4976d4cf19e3abc62e4ff11691c612d3f Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 21:32:21 +0100 Subject: [PATCH 013/103] Lint COTRIBUTING.md --- CONTRIBUTING.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3d3e772a5f4..6a8048827258 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,13 +44,14 @@ a keyboard shortcut or automatically on save. ## Submitting a Pull Request Contribution Generally, an issue should be opened describing a piece of proposed work and the -issues it solves before a pull request is opened. +issues it solves before a pull request is opened. ### Issue Management -Opening an issue lets community members participate in the design discussion, -makes others aware of work being done, and sets the stage for a fruitful community -interaction. A pull request should reference the issue it is addressing. Once the -pull request is merged, the issue related to it will also be closed. If there is + +Opening an issue lets community members participate in the design discussion, +makes others aware of work being done, and sets the stage for a fruitful community +interaction. A pull request should reference the issue it is addressing. Once the +pull request is merged, the issue related to it will also be closed. If there is additional discussion around implemementation the issue may be re-opened. Once 30 days have passed with no additional discussion, the [lock bot](https://github.com/apps/lock) will lock the issue. If additional discussion is desired, or if the pull request doesn't fully address the From 900b84750ff84c040cf91da276e6a1b6e1e1bfdb Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Mon, 12 Aug 2019 21:36:46 +0100 Subject: [PATCH 014/103] Update coreconfig docstrings --- jupyterlab/coreconfig.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jupyterlab/coreconfig.py b/jupyterlab/coreconfig.py index 8ce0d2cdf1df..bdeeffe0aae2 100644 --- a/jupyterlab/coreconfig.py +++ b/jupyterlab/coreconfig.py @@ -42,7 +42,9 @@ def _only_nonlab(collection): class CoreConfig: """An object representing a core-mode package/extension configuration. - This enables custom lab application to change + This enables custom lab application to change the core configuration + of the various build system commands. See e.g. commands.py and + any apps that use these functions. """ def __init__(self): @@ -51,7 +53,7 @@ def __init__(self): def clear_defaults(self, lab_only=True): """Clear the default packages/extensions. - lab_only: string + lab_only: bool Whether to remove all packages, or only those from JupyterLab. Defaults to True (only lab packages). This will leave dependencies like phosphor, react From 59541695deabb0dbd4fe8805ad01a2edb3609384 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Tue, 13 Aug 2019 10:43:08 +0100 Subject: [PATCH 015/103] Auto-disable statusbar leaf components This change ensures that if the statusbar plugin is missing, all the plugins that supply components to it will silently do nothing. All of the plugins were ensured to leaf nodes, i.e. they do not `provide` an interface of their own. --- packages/codemirror-extension/src/index.ts | 11 ++++++++--- packages/docmanager-extension/src/index.ts | 22 +++++++++++++++------ packages/filebrowser-extension/src/index.ts | 11 ++++++++--- packages/fileeditor-extension/src/index.ts | 11 ++++++++--- packages/notebook-extension/src/index.ts | 20 ++++++++++++++----- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/packages/codemirror-extension/src/index.ts b/packages/codemirror-extension/src/index.ts index 16c0e1a5f3ad..f07a96c45419 100644 --- a/packages/codemirror-extension/src/index.ts +++ b/packages/codemirror-extension/src/index.ts @@ -71,13 +71,18 @@ const commands: JupyterFrontEndPlugin = { export const editorSyntaxStatus: JupyterFrontEndPlugin = { id: '@jupyterlab/codemirror-extension:editor-syntax-status', autoStart: true, - requires: [IStatusBar, IEditorTracker, ILabShell], + requires: [IEditorTracker, ILabShell], + optional: [IStatusBar], activate: ( app: JupyterFrontEnd, - statusBar: IStatusBar, tracker: IEditorTracker, - labShell: ILabShell + labShell: ILabShell, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } let item = new EditorSyntaxStatus({ commands: app.commands }); labShell.currentChanged.connect(() => { const current = labShell.currentWidget; diff --git a/packages/docmanager-extension/src/index.ts b/packages/docmanager-extension/src/index.ts index d1fec3d07a71..0e52205b8457 100644 --- a/packages/docmanager-extension/src/index.ts +++ b/packages/docmanager-extension/src/index.ts @@ -167,13 +167,18 @@ const docManagerPlugin: JupyterFrontEndPlugin = { export const savingStatusPlugin: JupyterFrontEndPlugin = { id: '@jupyterlab/docmanager-extension:saving-status', autoStart: true, - requires: [IStatusBar, IDocumentManager, ILabShell], + requires: [IDocumentManager, ILabShell], + optional: [IStatusBar], activate: ( _: JupyterFrontEnd, - statusBar: IStatusBar, docManager: IDocumentManager, - labShell: ILabShell + labShell: ILabShell, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } const saving = new SavingStatus({ docManager }); // Keep the currently active widget synchronized. @@ -197,13 +202,18 @@ export const savingStatusPlugin: JupyterFrontEndPlugin = { export const pathStatusPlugin: JupyterFrontEndPlugin = { id: '@jupyterlab/docmanager-extension:path-status', autoStart: true, - requires: [IStatusBar, IDocumentManager, ILabShell], + requires: [IDocumentManager, ILabShell], + optional: [IStatusBar], activate: ( _: JupyterFrontEnd, - statusBar: IStatusBar, docManager: IDocumentManager, - labShell: ILabShell + labShell: ILabShell, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } const path = new PathStatus({ docManager }); // Keep the file path widget up-to-date with the application active widget. diff --git a/packages/filebrowser-extension/src/index.ts b/packages/filebrowser-extension/src/index.ts index 50f5b6b4db22..1406ed352ac5 100644 --- a/packages/filebrowser-extension/src/index.ts +++ b/packages/filebrowser-extension/src/index.ts @@ -155,12 +155,17 @@ const shareFile: JupyterFrontEndPlugin = { export const fileUploadStatus: JupyterFrontEndPlugin = { id: '@jupyterlab/filebrowser-extension:file-upload-status', autoStart: true, - requires: [IStatusBar, IFileBrowserFactory], + requires: [IFileBrowserFactory], + optional: [IStatusBar], activate: ( app: JupyterFrontEnd, - statusBar: IStatusBar, - browser: IFileBrowserFactory + browser: IFileBrowserFactory, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } const item = new FileUploadStatus({ tracker: browser.tracker }); diff --git a/packages/fileeditor-extension/src/index.ts b/packages/fileeditor-extension/src/index.ts index 2379f659544c..1068ce0cee2a 100644 --- a/packages/fileeditor-extension/src/index.ts +++ b/packages/fileeditor-extension/src/index.ts @@ -114,13 +114,18 @@ const plugin: JupyterFrontEndPlugin = { export const tabSpaceStatus: JupyterFrontEndPlugin = { id: '@jupyterlab/fileeditor-extension:tab-space-status', autoStart: true, - requires: [IStatusBar, IEditorTracker, ISettingRegistry], + requires: [IEditorTracker, ISettingRegistry], + optional: [IStatusBar], activate: ( app: JupyterFrontEnd, - statusBar: IStatusBar, editorTracker: IEditorTracker, - settingRegistry: ISettingRegistry + settingRegistry: ISettingRegistry, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } // Create a menu for switching tabs vs spaces. const menu = new Menu({ commands: app.commands }); const command = 'fileeditor:change-tabs'; diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index e88150e6230a..ccadf01b9f14 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -306,12 +306,17 @@ const tools: JupyterFrontEndPlugin = { export const commandEditItem: JupyterFrontEndPlugin = { id: '@jupyterlab/notebook-extension:mode-status', autoStart: true, - requires: [IStatusBar, INotebookTracker], + requires: [INotebookTracker], + optional: [IStatusBar], activate: ( app: JupyterFrontEnd, - statusBar: IStatusBar, - tracker: INotebookTracker + tracker: INotebookTracker, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } const { shell } = app; const item = new CommandEditStatus(); @@ -340,11 +345,16 @@ export const notebookTrustItem: JupyterFrontEndPlugin = { id: '@jupyterlab/notebook-extension:trust-status', autoStart: true, requires: [IStatusBar, INotebookTracker, ILabShell], + optional: [IStatusBar], activate: ( app: JupyterFrontEnd, - statusBar: IStatusBar, - tracker: INotebookTracker + tracker: INotebookTracker, + statusBar: IStatusBar | null ) => { + if (!statusBar) { + // Automatically disable if statusbar missing + return; + } const { shell } = app; const item = new NotebookTrustStatus(); From 7eab2e77f944746cfb9c5640e46e8169ac186fce Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 24 Feb 2019 14:08:21 -0500 Subject: [PATCH 016/103] first working example of a correctly imported svg --- packages/statusbar/src/components/icon.tsx | 36 +++++++++++++++++++ .../src/defaults/runningSessions.tsx | 6 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/statusbar/src/components/icon.tsx b/packages/statusbar/src/components/icon.tsx index 74bcd444c583..7129dac9309b 100644 --- a/packages/statusbar/src/components/icon.tsx +++ b/packages/statusbar/src/components/icon.tsx @@ -33,3 +33,39 @@ export function IconItem(
); } + +// /** +// * A namespace for IconItem statics. +// */ +// export namespace SVGIconItem { +// /** +// * Props for an IconItem +// */ +// export interface IProps { +// /** +// * The inline svg +// */ +// src: string; +// } +// } +// +// export function SVGIconItem( +// props: SVGIconItem.IProps & +// React.HTMLAttributes & { +// offset: { x: number; y: number }; +// } +// ): React.ReactElement { +// const { src, className, offset } = props; +// return ( +// +// ); +// } + +// export default class SVGIcon extends Component { +// render () { +// return ; +// } +// }; diff --git a/packages/statusbar/src/defaults/runningSessions.tsx b/packages/statusbar/src/defaults/runningSessions.tsx index bb9e1d030947..8ee961e49390 100644 --- a/packages/statusbar/src/defaults/runningSessions.tsx +++ b/packages/statusbar/src/defaults/runningSessions.tsx @@ -15,6 +15,8 @@ import { import { GroupItem, IconItem, interactiveItem, TextItem } from '..'; +import KernelIcon from '../../style/kernel-icon.svg'; + /** * Half spacing between subitems in a status item. */ @@ -34,11 +36,11 @@ function RunningSessionsComponent( - + - + ); From d499b7b797cd797abb4aeddfb58c7b650fbb07b9 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 24 Feb 2019 14:36:14 -0500 Subject: [PATCH 017/103] added svg typings --- packages/statusbar/src/svg.d.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/statusbar/src/svg.d.ts diff --git a/packages/statusbar/src/svg.d.ts b/packages/statusbar/src/svg.d.ts new file mode 100644 index 000000000000..38313a16f560 --- /dev/null +++ b/packages/statusbar/src/svg.d.ts @@ -0,0 +1,5 @@ +declare module '*.svg' { + import { HTMLAttributes } from 'react'; + const value: React.ComponentType>; + export default value; +} From 8e6151f1e1c39a15d9183e90d674bfb37599f505 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 24 Feb 2019 14:38:04 -0500 Subject: [PATCH 018/103] made react-svg play nicely with the existing typestyle stuff --- packages/statusbar/src/components/icon.tsx | 59 ++++++++----------- .../src/defaults/runningSessions.tsx | 10 +++- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/packages/statusbar/src/components/icon.tsx b/packages/statusbar/src/components/icon.tsx index 7129dac9309b..64d5104980ef 100644 --- a/packages/statusbar/src/components/icon.tsx +++ b/packages/statusbar/src/components/icon.tsx @@ -1,7 +1,7 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -import * as React from 'react'; +import React, { ComponentType, HTMLAttributes } from 'react'; import { classes, style } from 'typestyle/lib'; @@ -34,38 +34,27 @@ export function IconItem( ); } -// /** -// * A namespace for IconItem statics. -// */ -// export namespace SVGIconItem { -// /** -// * Props for an IconItem -// */ -// export interface IProps { -// /** -// * The inline svg -// */ -// src: string; -// } -// } -// -// export function SVGIconItem( -// props: SVGIconItem.IProps & -// React.HTMLAttributes & { -// offset: { x: number; y: number }; -// } -// ): React.ReactElement { -// const { src, className, offset } = props; -// return ( -// -// ); -// } +/** + * A namespace for IconItem statics. + */ +export namespace SVGIconItem { + /** + * Props for an IconItem + */ + export interface IProps { + /** + * The inline svg + */ + Src: ComponentType>; + } +} -// export default class SVGIcon extends Component { -// render () { -// return ; -// } -// }; +export function SVGIconItem( + props: SVGIconItem.IProps & + React.HTMLAttributes & { + offset: { x: number; y: number }; + } +): React.ReactElement { + const { Src, className, offset } = props; + return ; +} diff --git a/packages/statusbar/src/defaults/runningSessions.tsx b/packages/statusbar/src/defaults/runningSessions.tsx index 8ee961e49390..1539743e0ee1 100644 --- a/packages/statusbar/src/defaults/runningSessions.tsx +++ b/packages/statusbar/src/defaults/runningSessions.tsx @@ -13,7 +13,13 @@ import { SessionManager } from '@jupyterlab/services'; -import { GroupItem, IconItem, interactiveItem, TextItem } from '..'; +import { + GroupItem, + IconItem, + SVGIconItem, + interactiveItem, + TextItem +} from '..'; import KernelIcon from '../../style/kernel-icon.svg'; @@ -40,7 +46,7 @@ function RunningSessionsComponent( - + ); From aab64856b1a7ff7fdc65d6549859fbcbeae16cbb Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 24 Feb 2019 21:15:27 -0500 Subject: [PATCH 019/103] added tweaks for svg imports to build system --- buildutils/src/build.ts | 5 +---- dev_mode/package.json | 1 + dev_mode/webpack.config.js | 13 ++++++++++++- jupyterlab/staging/webpack.config.js | 13 ++++++++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/buildutils/src/build.ts b/buildutils/src/build.ts index 2e14db75aab3..de622b45a756 100644 --- a/buildutils/src/build.ts +++ b/buildutils/src/build.ts @@ -183,10 +183,7 @@ export namespace Build { }, { test: /\.svg/, - use: [ - { loader: 'svg-url-loader', options: {} }, - { loader: 'svgo-loader', options: { plugins: [] } } - ] + use: [{ loader: 'svg-url-loader', options: { encoding: 'none' } }] }, { test: /\.(png|jpg|gif|ttf|woff|woff2|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/, diff --git a/dev_mode/package.json b/dev_mode/package.json index ae0b22b451d3..e52bb0c8ba00 100644 --- a/dev_mode/package.json +++ b/dev_mode/package.json @@ -72,6 +72,7 @@ "sort-package-json": "~1.22.1", "source-map-loader": "~0.2.1", "style-loader": "~0.23.1", + "svg-react-loader": "~0.4.6", "svg-url-loader": "~2.3.2", "svgo": "~1.2.1", "svgo-loader": "~2.2.0", diff --git a/dev_mode/webpack.config.js b/dev_mode/webpack.config.js index 78c1769a515d..a128d73076ec 100644 --- a/dev_mode/webpack.config.js +++ b/dev_mode/webpack.config.js @@ -189,8 +189,19 @@ module.exports = [ }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' }, { + // in css files, svg is loaded as a url formatted string test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - use: 'url-loader?limit=10000&mimetype=image/svg+xml' + issuer: { test: /\.css?$/ }, + use: { + loader: 'svg-url-loader', + options: { encoding: 'none', limit: 10000 } + } + }, + { + // in react files, svg is loaded as a react component + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + issuer: { test: /\.jsx?$/ }, + use: 'svg-react-loader' } ] }, diff --git a/jupyterlab/staging/webpack.config.js b/jupyterlab/staging/webpack.config.js index 78c1769a515d..a128d73076ec 100644 --- a/jupyterlab/staging/webpack.config.js +++ b/jupyterlab/staging/webpack.config.js @@ -189,8 +189,19 @@ module.exports = [ }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' }, { + // in css files, svg is loaded as a url formatted string test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - use: 'url-loader?limit=10000&mimetype=image/svg+xml' + issuer: { test: /\.css?$/ }, + use: { + loader: 'svg-url-loader', + options: { encoding: 'none', limit: 10000 } + } + }, + { + // in react files, svg is loaded as a react component + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + issuer: { test: /\.jsx?$/ }, + use: 'svg-react-loader' } ] }, From 856e7b618fdf542b573aa7fb6294b9ce8819a3a0 Mon Sep 17 00:00:00 2001 From: telamonian Date: Mon, 25 Feb 2019 00:45:42 -0500 Subject: [PATCH 020/103] svg theming is now working, needs a bunch of fixes --- packages/application/style/images.css | 18 ++ packages/notebook/src/svg.d.ts | 5 + packages/notebook/src/truststatus.tsx | 12 +- packages/notebook/style/index.css | 271 +++++++++++++++++- .../notebook/style/not-trusted-icon-dark.svg | 5 - .../notebook/style/not-trusted-icon-light.svg | 5 - packages/notebook/style/not-trusted-icon.svg | 5 + packages/notebook/style/trusted-icon-dark.svg | 4 - .../notebook/style/trusted-icon-light.svg | 4 - packages/notebook/style/trusted-icon.svg | 4 + packages/statusbar/src/components/icon.tsx | 26 +- packages/statusbar/src/defaults/lineCol.tsx | 15 +- .../src/defaults/runningSessions.tsx | 13 +- packages/statusbar/style/kernel-icon.svg | 6 + packages/statusbar/style/line-form.svg | 2 +- packages/statusbar/style/terminal-icon.svg | 4 + 16 files changed, 345 insertions(+), 54 deletions(-) create mode 100644 packages/notebook/src/svg.d.ts delete mode 100644 packages/notebook/style/not-trusted-icon-dark.svg delete mode 100644 packages/notebook/style/not-trusted-icon-light.svg create mode 100644 packages/notebook/style/not-trusted-icon.svg delete mode 100644 packages/notebook/style/trusted-icon-dark.svg delete mode 100644 packages/notebook/style/trusted-icon-light.svg create mode 100644 packages/notebook/style/trusted-icon.svg create mode 100644 packages/statusbar/style/kernel-icon.svg create mode 100644 packages/statusbar/style/terminal-icon.svg diff --git a/packages/application/style/images.css b/packages/application/style/images.css index 6c966fe3036f..a462ba3a6393 100644 --- a/packages/application/style/images.css +++ b/packages/application/style/images.css @@ -3,6 +3,24 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ +/* CSS for generic svg icons */ +.jp-icon { + fill: var(--jp-ui-font-color0); + stroke: var(--jp-ui-font-color0); +} + +.jp-icon-accent0 { + fill: var(--jp-layout-color0); + stroke: var(--jp-layout-color0); +} + +.jp-icon-accent1 { + fill: var(--jp-layout-color1); + stroke: var(--jp-layout-color1); +} + +/* CSS for specific icons */ + .jp-ImageJupyterLab { background-image: var(--jp-image-jupyterlab); } diff --git a/packages/notebook/src/svg.d.ts b/packages/notebook/src/svg.d.ts new file mode 100644 index 000000000000..38313a16f560 --- /dev/null +++ b/packages/notebook/src/svg.d.ts @@ -0,0 +1,5 @@ +declare module '*.svg' { + import { HTMLAttributes } from 'react'; + const value: React.ComponentType>; + export default value; +} diff --git a/packages/notebook/src/truststatus.tsx b/packages/notebook/src/truststatus.tsx index 99d7b0d0e242..b57e840f38c2 100644 --- a/packages/notebook/src/truststatus.tsx +++ b/packages/notebook/src/truststatus.tsx @@ -6,10 +6,13 @@ import { INotebookModel, Notebook } from '.'; import { Cell } from '@jupyterlab/cells'; -import { IconItem } from '@jupyterlab/statusbar'; +import { SVGIconItem } from '@jupyterlab/statusbar'; import { toArray } from '@phosphor/algorithm'; +import NotTrustedIcon from '../style/not-trusted-icon.svg'; +import TrustedIcon from '../style/trusted-icon.svg'; + /** * Determine the notebook trust status message. */ @@ -44,8 +47,11 @@ function cellTrust( function NotebookTrustComponent( props: NotebookTrustComponent.IProps ): React.ReactElement { - const source = cellTrust(props)[1]; - return ; + if (props.allCellsTrusted) { + return ; + } else { + return ; + } } /** diff --git a/packages/notebook/style/index.css b/packages/notebook/style/index.css index 9927f8adda6c..c38e4dfb0ba3 100644 --- a/packages/notebook/style/index.css +++ b/packages/notebook/style/index.css @@ -3,15 +3,262 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ -@import url('~@jupyterlab/ui-components/style/index.css'); -@import url('~@phosphor/widgets/style/index.css'); -@import url('~@jupyterlab/apputils/style/index.css'); -@import url('~@phosphor/dragdrop/style/index.css'); -@import url('~@jupyterlab/codeeditor/style/index.css'); -@import url('~@jupyterlab/statusbar/style/index.css'); -@import url('~@jupyterlab/rendermime/style/index.css'); -@import url('~@jupyterlab/cells/style/index.css'); -@import url('~@jupyterlab/docregistry/style/index.css'); - -@import url('./base.css'); +/*----------------------------------------------------------------------------- +| Private CSS variables +|----------------------------------------------------------------------------*/ + +:root { + --jp-private-notebook-dragImage-width: 304px; + --jp-private-notebook-dragImage-height: 36px; + --jp-private-notebook-selected-color: var(--md-blue-400); + --jp-private-notebook-active-color: var(--md-green-400); +} + +/*----------------------------------------------------------------------------- +| Imports +|----------------------------------------------------------------------------*/ + +@import './toolbar.css'; + +/*----------------------------------------------------------------------------- +| Notebook +|----------------------------------------------------------------------------*/ + +.jp-NotebookPanel { + display: block; + height: 100%; +} + +.jp-NotebookPanel.jp-Document { + min-width: 240px; + min-height: 120px; +} + +.jp-Notebook { + padding: var(--jp-notebook-padding); + outline: none; + overflow: auto; + background: var(--jp-layout-color0); +} + +.jp-Notebook.jp-mod-scrollPastEnd::after { + display: block; + content: ''; + min-height: var(--jp-notebook-scroll-padding); +} + +.jp-Notebook .jp-Cell { + overflow: visible; +} + +.jp-Notebook .jp-Cell .jp-InputPrompt { + cursor: move; +} + +/*----------------------------------------------------------------------------- +| Notebook state related styling +| +| The notebook and cells each have states, here are the possibilities: +| +| - Notebook +| - Command +| - Edit +| - Cell +| - None +| - Active (only one can be active) +| - Selected (the cells actions are applied to) +| - Multiselected (when multiple selected, the cursor) +| - No outputs +|----------------------------------------------------------------------------*/ + +/* Command or edit modes */ + +.jp-Notebook .jp-Cell:not(.jp-mod-active) .jp-InputPrompt { + opacity: var(--jp-cell-prompt-not-active-opacity); + color: var(--jp-cell-prompt-not-active-font-color); +} + +.jp-Notebook .jp-Cell:not(.jp-mod-active) .jp-OutputPrompt { + opacity: var(--jp-cell-prompt-not-active-opacity); + color: var(--jp-cell-prompt-not-active-font-color); +} + +/* cell is active */ +.jp-Notebook .jp-Cell.jp-mod-active .jp-Collapser { + background: var(--jp-brand-color1); +} + +/* collapser is hovered */ +.jp-Notebook .jp-Cell .jp-Collapser:hover { + box-shadow: var(--jp-elevation-z2); + background: var(--jp-brand-color1); + opacity: var(--jp-cell-collapser-not-active-hover-opacity); +} + +/* cell is active and collapser is hovered */ +.jp-Notebook .jp-Cell.jp-mod-active .jp-Collapser:hover { + background: var(--jp-brand-color0); + opacity: 1; +} + +/* Command mode */ + +.jp-Notebook.jp-mod-commandMode .jp-Cell.jp-mod-selected { + background: var(--jp-notebook-multiselected-color); +} + +.jp-Notebook.jp-mod-commandMode + .jp-Cell.jp-mod-active.jp-mod-selected:not(.jp-mod-multiSelected) { + background: transparent; +} + +/* Edit mode */ + +.jp-Notebook.jp-mod-editMode .jp-Cell.jp-mod-active .jp-InputArea-editor { + border: var(--jp-border-width) solid var(--jp-cell-editor-active-border-color); + box-shadow: var(--jp-input-box-shadow); + background-color: var(--jp-cell-editor-active-background); +} + +/*----------------------------------------------------------------------------- +| Notebook drag and drop +|----------------------------------------------------------------------------*/ + +.jp-Notebook-cell.jp-mod-dropSource { + opacity: 0.5; +} + +.jp-Notebook-cell.jp-mod-dropTarget, +.jp-Notebook.jp-mod-commandMode + .jp-Notebook-cell.jp-mod-active.jp-mod-selected.jp-mod-dropTarget { + border-top-color: var(--jp-private-notebook-selected-color); + border-top-style: solid; + border-top-width: 2px; +} + +.jp-dragImage { + display: flex; + flex-direction: row; + width: var(--jp-private-notebook-dragImage-width); + height: var(--jp-private-notebook-dragImage-height); + border: var(--jp-border-width) solid var(--jp-cell-editor-border-color); + background: var(--jp-cell-editor-background); + overflow: visible; +} + +.jp-dragImage-singlePrompt { + box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.12); +} + +.jp-dragImage .jp-dragImage-content { + flex: 1 1 auto; + z-index: 2; + font-size: var(--jp-code-font-size); + font-family: var(--jp-code-font-family); + line-height: var(--jp-code-line-height); + padding: var(--jp-code-padding); + border: var(--jp-border-width) solid var(--jp-cell-editor-border-color); + background: var(--jp-cell-editor-background-color); + color: var(--jp-content-font-color3); + text-align: left; + margin: 4px 4px 4px 0px; +} + +.jp-dragImage .jp-dragImage-prompt { + flex: 0 0 auto; + min-width: 36px; + color: var(--jp-cell-inprompt-font-color); + padding: var(--jp-code-padding); + padding-left: 12px; + font-family: var(--jp-cell-prompt-font-family); + letter-spacing: var(--jp-cell-prompt-letter-spacing); + line-height: 1.9; + font-size: var(--jp-code-font-size); + border: var(--jp-border-width) solid transparent; +} + +.jp-dragImage-multipleBack { + z-index: -1; + position: absolute; + height: 32px; + width: 300px; + top: 8px; + left: 8px; + background: var(--jp-layout-color2); + border: var(--jp-border-width) solid var(--jp-input-border-color); + box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.12); +} + +/*----------------------------------------------------------------------------- +| Cell toolbar +|----------------------------------------------------------------------------*/ + +.jp-NotebookTools { + display: block; + min-width: var(--jp-sidebar-min-width); + color: var(--jp-ui-font-color1); + background: var(--jp-layout-color1); + /* 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); + overflow: auto; +} + +.jp-ActiveCellTool { + padding: 12px; + background-color: var(--jp-layout-color1); + border-top: none !important; +} + +.jp-ActiveCellTool .jp-InputArea-prompt { + flex: 0 0 auto; + padding-left: 0px; +} + +.jp-ActiveCellTool .jp-InputArea-editor { + flex: 1 1 auto; + background: var(--jp-cell-editor-background); + border-color: var(--jp-cell-editor-border-color); +} + +.jp-ActiveCellTool .jp-InputArea-editor .CodeMirror { + background: transparent; +} + +.jp-MetadataEditorTool { + flex-direction: column; + padding: 12px 0px 12px 0px; +} + +.jp-RankedPanel > :not(:first-child) { + margin-top: 12px; +} + +.jp-KeySelector { + padding: 0px 12px 0 12px; +} + +.jp-KeySelector select.jp-mod-styled { + font-size: var(--jp-ui-font-size1); + color: var(--jp-ui-font-color0); + border: var(--jp-border-width) solid var(--jp-border-color1); +} + +.jp-KeySelector label, +.jp-MetadataEditorTool label { + line-height: 1.4; +} + +/*----------------------------------------------------------------------------- +| Presentation Mode (.jp-mod-presentationMode) +|----------------------------------------------------------------------------*/ + +.jp-mod-presentationMode .jp-Notebook { + --jp-content-font-size1: var(--jp-content-presentation-font-size1); + --jp-code-font-size: var(--jp-code-presentation-font-size); +} + +.jp-mod-presentationMode .jp-Notebook .jp-Cell .jp-InputPrompt, +.jp-mod-presentationMode .jp-Notebook .jp-Cell .jp-OutputPrompt { + flex: 0 0 110px; +} diff --git a/packages/notebook/style/not-trusted-icon-dark.svg b/packages/notebook/style/not-trusted-icon-dark.svg deleted file mode 100644 index 8439a93c6e70..000000000000 --- a/packages/notebook/style/not-trusted-icon-dark.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/notebook/style/not-trusted-icon-light.svg b/packages/notebook/style/not-trusted-icon-light.svg deleted file mode 100644 index cb63b74c2de6..000000000000 --- a/packages/notebook/style/not-trusted-icon-light.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/notebook/style/not-trusted-icon.svg b/packages/notebook/style/not-trusted-icon.svg new file mode 100644 index 000000000000..e3de1ed45dd9 --- /dev/null +++ b/packages/notebook/style/not-trusted-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/notebook/style/trusted-icon-dark.svg b/packages/notebook/style/trusted-icon-dark.svg deleted file mode 100644 index b92040c37a58..000000000000 --- a/packages/notebook/style/trusted-icon-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/notebook/style/trusted-icon-light.svg b/packages/notebook/style/trusted-icon-light.svg deleted file mode 100644 index 7f4fc23bac83..000000000000 --- a/packages/notebook/style/trusted-icon-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/notebook/style/trusted-icon.svg b/packages/notebook/style/trusted-icon.svg new file mode 100644 index 000000000000..b7ae40ec21c9 --- /dev/null +++ b/packages/notebook/style/trusted-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/statusbar/src/components/icon.tsx b/packages/statusbar/src/components/icon.tsx index 64d5104980ef..a5c9557ee170 100644 --- a/packages/statusbar/src/components/icon.tsx +++ b/packages/statusbar/src/components/icon.tsx @@ -35,26 +35,40 @@ export function IconItem( } /** - * A namespace for IconItem statics. + * A namespace for SVGIconItem statics. */ export namespace SVGIconItem { /** - * Props for an IconItem + * Props for an SVGIconItem */ export interface IProps { /** * The inline svg */ - Src: ComponentType>; + SVG: ComponentType>; } } export function SVGIconItem( props: SVGIconItem.IProps & - React.HTMLAttributes & { + React.HTMLAttributes & { offset: { x: number; y: number }; } ): React.ReactElement { - const { Src, className, offset } = props; - return ; + const { SVG, className, offset, ...rest } = props; + return ; +} + +export function SVGInputItem( + props: SVGIconItem.IProps & + React.HTMLAttributes & + React.HTMLAttributes +): React.ReactElement { + const { SVG, className, ...rest } = props; + return ( + + ); } diff --git a/packages/statusbar/src/defaults/lineCol.tsx b/packages/statusbar/src/defaults/lineCol.tsx index 2240c2da940c..68969f043d88 100644 --- a/packages/statusbar/src/defaults/lineCol.tsx +++ b/packages/statusbar/src/defaults/lineCol.tsx @@ -20,6 +20,8 @@ import { import { classes } from 'typestyle/lib'; +import LineFormIcon from '../../style/line-form.svg'; + /** * A namespace for LineFormComponent statics. */ @@ -112,11 +114,14 @@ class LineFormComponent extends React.Component< }} /> - +
+ + +