mirror of
https://github.com/jupyter/notebook.git
synced 2025-02-17 12:39:54 +08:00
Load custom CSS (#6841)
* wip loading custom css * read css file from profile directory and apply * update css handler, add CLI flag to disable custom CSS, documentation * import ExtensionAppJinjaMixin * remove ExtensionAppJinjaMixin as a JupyterNotebookApp base class * add type hint to ignore jinja2_env type * fix prettier issue in docs * add empty line; trigger new docs build * new line caused prettier error, remove * move custom css to different directory and update handler * modify regex * satisfy mypy: check for match in regex before grabbing the directory path * load custom css in consoles page
This commit is contained in:
parent
bdcadda22d
commit
7c1d88780f
@ -26,6 +26,10 @@ and editing settings is similar for all the Jupyter applications.
|
||||
> - [traitlets](https://traitlets.readthedocs.io/en/latest/config.html#module-traitlets.config)
|
||||
> provide a low-level architecture for configuration.
|
||||
|
||||
### Disabling Custom CSS
|
||||
|
||||
Custom CSS is loaded by default as was done with Jupyter Notebook 6. In the jupyter configuration directory, the `/.jupyter/custom/custom.css` file will be loaded unless the the application is initialized with the `custom_css` flag with the argument set to `False` as in `--JupyterNotebookApp.custom_css=False`.
|
||||
|
||||
(configure-jupyter-server)=
|
||||
|
||||
## Jupyter server
|
||||
|
@ -1,11 +1,16 @@
|
||||
"""Jupyter notebook application."""
|
||||
import os
|
||||
import re
|
||||
from os.path import join as pjoin
|
||||
|
||||
from jupyter_client.utils import ensure_async
|
||||
from jupyter_core.application import base_aliases
|
||||
from jupyter_core.paths import jupyter_config_dir
|
||||
from jupyter_server.base.handlers import JupyterHandler
|
||||
from jupyter_server.extension.handler import ExtensionHandlerJinjaMixin, ExtensionHandlerMixin
|
||||
from jupyter_server.extension.handler import (
|
||||
ExtensionHandlerJinjaMixin,
|
||||
ExtensionHandlerMixin,
|
||||
)
|
||||
from jupyter_server.serverapp import flags
|
||||
from jupyter_server.utils import url_escape, url_is_absolute
|
||||
from jupyter_server.utils import url_path_join as ujoin
|
||||
@ -32,6 +37,10 @@ version = __version__
|
||||
class NotebookBaseHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler):
|
||||
"""The base notebook API handler."""
|
||||
|
||||
@property
|
||||
def custom_css(self):
|
||||
return self.settings.get("custom_css", True)
|
||||
|
||||
def get_page_config(self):
|
||||
"""Get the page config."""
|
||||
config = LabConfig()
|
||||
@ -87,6 +96,7 @@ class NotebookBaseHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, Jup
|
||||
|
||||
page_config.setdefault("mathjaxConfig", mathjax_config)
|
||||
page_config.setdefault("fullMathjaxUrl", mathjax_url)
|
||||
page_config.setdefault("jupyterConfigDir", jupyter_config_dir())
|
||||
|
||||
# Put all our config in page_config
|
||||
for name in config.trait_names():
|
||||
@ -203,6 +213,27 @@ class NotebookHandler(NotebookBaseHandler):
|
||||
return self.write(tpl)
|
||||
|
||||
|
||||
class CustomCssHandler(NotebookBaseHandler):
|
||||
"""A custom CSS handler."""
|
||||
|
||||
@web.authenticated
|
||||
def get(self):
|
||||
"""Get the custom css file."""
|
||||
|
||||
self.set_header("Content-Type", 'text/css')
|
||||
page_config = self.get_page_config()
|
||||
custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css"
|
||||
|
||||
if not os.path.isfile(custom_css_file):
|
||||
static_path_root = re.match('^(.*?)static', page_config['staticDir'])
|
||||
if static_path_root is not None:
|
||||
custom_dir = static_path_root.groups()[0]
|
||||
custom_css_file = f"{custom_dir}custom/custom.css"
|
||||
|
||||
with open(custom_css_file) as css_f:
|
||||
return self.write(css_f.read())
|
||||
|
||||
|
||||
aliases = dict(base_aliases)
|
||||
|
||||
|
||||
@ -227,12 +258,25 @@ class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp):
|
||||
help="Whether to expose the global app instance to browser via window.jupyterapp",
|
||||
)
|
||||
|
||||
custom_css = Bool(
|
||||
True,
|
||||
config=True,
|
||||
help="""Whether custom CSS is loaded on the page.
|
||||
Defaults to True and custom CSS is loaded.
|
||||
""",
|
||||
)
|
||||
|
||||
flags = flags
|
||||
flags["expose-app-in-browser"] = (
|
||||
{"JupyterNotebookApp": {"expose_app_in_browser": True}},
|
||||
"Expose the global app instance to browser via window.jupyterapp.",
|
||||
)
|
||||
|
||||
flags["custom-css"] = (
|
||||
{"JupyterNotebookApp": {"custom_css": True}},
|
||||
"Load custom CSS in template html files. Default is True",
|
||||
)
|
||||
|
||||
@default("static_dir")
|
||||
def _default_static_dir(self):
|
||||
return os.path.join(HERE, "static")
|
||||
@ -261,6 +305,10 @@ class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp):
|
||||
def _default_workspaces_dir(self):
|
||||
return get_workspaces_dir()
|
||||
|
||||
def _prepare_templates(self):
|
||||
super(LabServerApp, self)._prepare_templates()
|
||||
self.jinja2_env.globals.update(custom_css=self.custom_css) # type:ignore
|
||||
|
||||
def server_extension_is_enabled(self, extension):
|
||||
"""Check if server extension is enabled."""
|
||||
try:
|
||||
@ -290,6 +338,7 @@ class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp):
|
||||
self.handlers.append(("/edit(.*)", FileHandler))
|
||||
self.handlers.append(("/consoles/(.*)", ConsoleHandler))
|
||||
self.handlers.append(("/terminals/(.*)", TerminalHandler))
|
||||
self.handlers.append(("/custom/custom.css", CustomCssHandler))
|
||||
super().initialize_handlers()
|
||||
|
||||
def initialize(self, argv=None):
|
||||
|
7
notebook/custom/custom.css
Normal file
7
notebook/custom/custom.css
Normal file
@ -0,0 +1,7 @@
|
||||
/*
|
||||
Placeholder for custom user CSS
|
||||
|
||||
mainly to be overridden in profile/static/custom/custom.css
|
||||
|
||||
This will always be an empty file
|
||||
*/
|
@ -7,6 +7,10 @@
|
||||
{% block favicon %}
|
||||
<link rel="icon" type="image/x-icon" href="{{ page_config['fullStaticUrl'] | e }}/favicons/favicon-console.ico" class="favicon">
|
||||
{% endblock %}
|
||||
|
||||
{% if custom_css %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ base_url | escape }}custom/custom.css">
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -7,6 +7,10 @@
|
||||
{% block favicon %}
|
||||
<link rel="icon" type="image/x-icon" href="{{ base_url | escape }}static/favicons/favicon-notebook.ico" class="favicon">
|
||||
{% endblock %}
|
||||
|
||||
{% if custom_css %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ base_url | escape }}custom/custom.css">
|
||||
{% endif %}
|
||||
</head>
|
||||
<body data-notebook="notebooks">
|
||||
|
||||
|
@ -7,6 +7,10 @@
|
||||
{% block favicon %}
|
||||
<link rel="icon" type="image/x-icon" href="{{ base_url | escape }}static/favicons/favicon-terminal.ico" class="favicon">
|
||||
{% endblock %}
|
||||
|
||||
{% if custom_css %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ base_url | escape }}custom/custom.css">
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -7,6 +7,10 @@
|
||||
{% block favicon %}
|
||||
<link rel="icon" type="image/x-icon" href="{{ base_url | escape }}static/favicons/favicon.ico" class="favicon">
|
||||
{% endblock %}
|
||||
|
||||
{% if custom_css %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ base_url | escape }}custom/custom.css">
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user