From affc90788a82ca98ebf43e78f2e4362cd0889582 Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Sun, 30 Apr 2023 20:02:59 +0100 Subject: [PATCH 1/3] Pass "dark mode" info from Spyder prefs to JavaScript This reverts commit 70c56bcc and part of commit d69b873c. --- spyder_notebook/server/main.py | 23 +++++++++++++++++-- spyder_notebook/utils/servermanager.py | 3 ++- .../utils/tests/test_servermanager.py | 7 +++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/spyder_notebook/server/main.py b/spyder_notebook/server/main.py index 17704f73..5f2f409f 100644 --- a/spyder_notebook/server/main.py +++ b/spyder_notebook/server/main.py @@ -4,18 +4,29 @@ """Entry point for server rendering notebooks for Spyder.""" import os -from notebook.app import aliases, JupyterNotebookApp, NotebookBaseHandler +from notebook.app import ( + aliases, flags, JupyterNotebookApp, NotebookBaseHandler) from tornado import web -from traitlets import default, Unicode +from traitlets import default, Bool, Unicode HERE = os.path.dirname(__file__) aliases['info-file'] = 'SpyderNotebookApp.info_file_cmdline' +flags['dark'] = ( + {'SpyderNotebookApp': {'dark_theme': True}}, + 'Use dark theme when rendering notebooks' +) + class SpyderNotebookHandler(NotebookBaseHandler): """A notebook page handler for Spyder.""" + def get_page_config(self): + page_config = super().get_page_config() + page_config['darkTheme'] = self.extensionapp.dark_theme + return page_config + @web.authenticated def get(self, path=None): """Get the notebook page.""" @@ -30,7 +41,15 @@ class SpyderNotebookApp(JupyterNotebookApp): name = 'spyder_notebook' file_url_prefix = "/spyder-notebooks" + flags = dict(flags) aliases = dict(aliases) + + dark_theme = Bool( + False, config=True, + help='Whether to use dark theme when rendering notebooks') + + flags = flags + info_file_cmdline = Unicode( '', config=True, help='Name of file in Jupyter runtime dir with connection info') diff --git a/spyder_notebook/utils/servermanager.py b/spyder_notebook/utils/servermanager.py index 726b1e7b..e07aec5f 100644 --- a/spyder_notebook/utils/servermanager.py +++ b/spyder_notebook/utils/servermanager.py @@ -210,8 +210,9 @@ def start_server(self, filename, interpreter): f'--notebook-dir={nbdir}', '--ServerApp.password=', f'--KernelSpecManager.kernel_spec_class={KERNELSPEC}'] + if self.dark_theme: + arguments.append('--dark') - # TODO: Add support for dark theme logger.debug('Arguments: %s', repr(arguments)) if DEV: diff --git a/spyder_notebook/utils/tests/test_servermanager.py b/spyder_notebook/utils/tests/test_servermanager.py index ac10b2d7..dbb96750 100755 --- a/spyder_notebook/utils/tests/test_servermanager.py +++ b/spyder_notebook/utils/tests/test_servermanager.py @@ -74,14 +74,14 @@ def test_get_server_with_server( mock_start.assert_not_called() -@pytest.mark.parametrize('under_home', [True, False]) -def test_start_server(mocker, under_home): +@pytest.mark.parametrize(('dark', 'under_home'), + [(True, True), (False, True), (False, False)]) +def test_start_server(mocker, dark, under_home): """ Test that .start_server() starts a process with the correct arguments, that it stores the server process in `.servers`; and that it calls ._check_server_running(). """ - dark = 'not used' serverManager = ServerManager(dark) mock_check = mocker.patch.object(serverManager, '_check_server_started') mock_QProcess = mocker.patch( @@ -100,6 +100,7 @@ def test_start_server(mocker, under_home): mock_QProcess.return_value.start.assert_called_once() args = mock_QProcess.return_value.start.call_args[0] assert '--notebook-dir={}'.format(nbdir) in args[1] + assert ('--dark' in args[1]) == dark assert len(serverManager.servers) == 1 assert serverManager.servers[0].process == mock_QProcess.return_value assert serverManager.servers[0].notebook_dir == nbdir From 8a906aa01f0af24f871f64ef554f9234bfdd1ab2 Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Wed, 3 May 2023 16:35:41 +0100 Subject: [PATCH 2/3] Set notebook theme in JS according to Spyder prefs --- .../application-extension/src/index.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/spyder_notebook/server/packages/application-extension/src/index.ts b/spyder_notebook/server/packages/application-extension/src/index.ts index ed65a3ab..ac6b05b8 100644 --- a/spyder_notebook/server/packages/application-extension/src/index.ts +++ b/spyder_notebook/server/packages/application-extension/src/index.ts @@ -9,6 +9,10 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; +import { IThemeManager } from '@jupyterlab/apputils'; + +import { PageConfig } from '@jupyterlab/coreutils'; + import { IDocumentManager } from '@jupyterlab/docmanager'; import { IMainMenu } from '@jupyterlab/mainmenu'; @@ -80,12 +84,33 @@ const menus: JupyterFrontEndPlugin = { } }; +/** + * A plugin to sync the notebook theme with the Spyder preferences + * + * On startup, read the `darkTheme` option from `PageConfig` and then set the + * notebook theme accordingly. This option is set in `SpyderNotebookHandler`. + */ +const theme: JupyterFrontEndPlugin = { + id: '@spyder-notebook/application-extension:theme', + requires: [IThemeManager], + autoStart: true, + activate: (app: JupyterFrontEnd, themeManager: IThemeManager) => { + const darkTheme = PageConfig.getOption('darkTheme'); + if (darkTheme == 'true') { + themeManager.setTheme('JupyterLab Dark'); + } else { + themeManager.setTheme('JupyterLab Light'); + } + } +}; + /** * Export the plugins as default. */ const plugins: JupyterFrontEndPlugin[] = [ menus, - opener + opener, + theme ]; export default plugins; From 9b1aea0f327b41aed6565c0158c4fb564688815e Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Wed, 3 May 2023 16:42:08 +0100 Subject: [PATCH 3/3] Fix bug in plugin code to connect to switcher The function is called .get_name() in the plugin and .get_title() in the widget. --- spyder_notebook/notebookplugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder_notebook/notebookplugin.py b/spyder_notebook/notebookplugin.py index 6c5db88c..35c41625 100644 --- a/spyder_notebook/notebookplugin.py +++ b/spyder_notebook/notebookplugin.py @@ -145,7 +145,7 @@ def _handle_switcher_selection(self, item, mode, search_text): corresponds to this plugin, then ignore it. Otherwise, switch to selected item in notebook plugin and hide the switcher. """ - if item.get_section() != self.get_title(): + if item.get_section() != self.get_name(): return client = item.get_data()