diff --git a/.gitignore b/.gitignore index e1545372ba..25bd27a638 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,7 @@ celerybeat-schedule .venv venv/ ENV/ +envs/ # Spyder project settings .spyderproject @@ -103,3 +104,6 @@ ENV/ node_modules/ .vscode + +# vendored js/css +vendor/ diff --git a/package.json b/package.json index 0328fe0bb3..3ef482883d 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,13 @@ "webpack-watch-files-plugin": "^1.0.3" }, "dependencies": { + "@fortawesome/fontawesome-free": "^5.13.0", + "@openfonts/lato_latin-ext": "^1.44.1", + "@openfonts/open-sans_all": "^1.44.1", "bootstrap": "^4.4.1", - "optimize-css-assets-webpack-plugin": "^5.0.3" + "jquery": "^3.5.0", + "mathjax": "2.7.5", + "optimize-css-assets-webpack-plugin": "^5.0.3", + "popper.js": "^1.16.0" } } diff --git a/pydata_sphinx_theme/__init__.py b/pydata_sphinx_theme/__init__.py index d2e0c30bd0..92677400a2 100644 --- a/pydata_sphinx_theme/__init__.py +++ b/pydata_sphinx_theme/__init__.py @@ -3,13 +3,24 @@ """ import os +import docutils + +import sphinx +import sphinx.util.logging from sphinx.errors import ExtensionError from .bootstrap_html_translator import BootstrapHTML5Translator -import docutils __version__ = "0.2.2dev0" +NAME = "pydata_sphinx_theme" + +at_least_sphinx_three = sphinx.__version__ >= "3.0.0" + +logger = sphinx.util.logging.getLogger(__name__) + +warned = {} + def add_toctree_functions(app, pagename, templatename, context, doctree): """Add functions so Jinja templates can add toctree objects. @@ -167,6 +178,66 @@ def get_edit_url(): # ----------------------------------------------------------------------------- +def setup_cdn(app, pagename, templatename, context, doctree): + """hoist the `use_public_cdns` config value to the template context + """ + + use_public_cdns = app.config.use_public_cdns + + context["use_public_cdns"] = use_public_cdns + + if not has_default_mathjax_path(app): + return + + if at_least_sphinx_three or use_public_cdns: + return + + mathjax_path = app.config.mathjax_path + + if not warned.get("mathjax_path"): + logger.warning( + "`use_public_cdns` is %s, but `mathjax_path` is configured," + " probably by default, as:\n\n" + " %s\n\n" + "> upgrade to `sphinx >=3`, which supports event `priority`...\n" + "> or configure `mathjax_path` in `conf.py`, e.g.:\n\n" + " import %s\n" + " def setup(app):\n" + " app.config.use_public_cdns = False\n" + " app.config.mathjax_path = %s.get_mathjax_path()\n", + use_public_cdns, + mathjax_path, + NAME, + NAME, + type="configuration", + location=pagename, + ) + warned["mathjax_path"] = True + + +# ----------------------------------------------------------------------------- + + +def configure_mathjax(app, env): + if not app.config.use_public_cdns and has_default_mathjax_path(app): + app.config.mathjax_path = get_mathjax_path() + + +# ----------------------------------------------------------------------------- + + +def has_default_mathjax_path(app): + if "mathjax_path" not in app.config: + return False + + return app.config.mathjax_path == app.config.values["mathjax_path"][0] + + +def get_mathjax_path(): + """Return the locally-vendored MathJax path""" + return "vendor/mathjax/latest.js?config=TeX-AMS-MML_HTMLorMML" + + def get_html_theme_path(): """Return list of HTML theme paths.""" theme_path = os.path.abspath(os.path.dirname(__file__)) @@ -175,7 +246,7 @@ def get_html_theme_path(): def setup(app): theme_path = get_html_theme_path()[0] - app.add_html_theme("pydata_sphinx_theme", theme_path) + app.add_html_theme(NAME, theme_path) app.set_translator("html", BootstrapHTML5Translator) # Read the Docs uses ``readthedocs`` as the name of the build, and also @@ -185,5 +256,11 @@ def setup(app): app.set_translator("readthedocsdirhtml", BootstrapHTML5Translator, override=True) app.connect("html-page-context", setup_edit_url) app.connect("html-page-context", add_toctree_functions) + app.connect("html-page-context", setup_cdn) + + app.add_config_value("use_public_cdns", True, "html") + + if at_least_sphinx_three: + app.connect("env-updated", configure_mathjax, priority=-1) return {"parallel_read_safe": True, "parallel_write_safe": True} diff --git a/pydata_sphinx_theme/layout.html b/pydata_sphinx_theme/layout.html index f3719e77be..31a723d068 100644 --- a/pydata_sphinx_theme/layout.html +++ b/pydata_sphinx_theme/layout.html @@ -12,8 +12,15 @@ {% endmacro %} {%- block css %} + {% if use_public_cdns %} - + + {% else %} + + + + {% endif %} + {{- css() }} {%- endblock %} @@ -21,9 +28,12 @@ - + {% if use_public_cdns %} - + + {% else %} + + {% endif %} {%- endblock %} {# Silence the sidebar's, relbar's #} @@ -93,4 +103,4 @@ {%- block footer %} {%- include "footer.html" %} -{%- endblock %} \ No newline at end of file +{%- endblock %} diff --git a/pydata_sphinx_theme/static/css/index.css b/pydata_sphinx_theme/static/css/index.css index eb00f75123..4e238e2e57 100644 --- a/pydata_sphinx_theme/static/css/index.css +++ b/pydata_sphinx_theme/static/css/index.css @@ -1,4 +1,4 @@ -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400|Lato:400);/*! +/*! * Bootstrap v4.4.1 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors * Copyright 2011-2019 Twitter, Inc. diff --git a/src/scss/index.scss b/src/scss/index.scss index dbad77f578..f4906fe602 100644 --- a/src/scss/index.scss +++ b/src/scss/index.scss @@ -10,9 +10,6 @@ $container-max-widths: ( // Include core Bootstrap @import '../../node_modules/bootstrap/scss/bootstrap'; -// Import custom fonts -@import url('https://fonts.googleapis.com/css?family=Open+Sans:400|Lato:400'); - @import './base'; @import './navbar'; @@ -83,8 +80,8 @@ table.field-list { } } -/** - * Styling for autosummary tables +/** + * Styling for autosummary tables */ // The first column (with the signature) should not wrap diff --git a/webpack.common.js b/webpack.common.js index 6f3e60b59e..e462a2e3f6 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,5 +1,9 @@ -const path = require('path'); +const { resolve } = require('path'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); + +const staticPath = resolve(__dirname, 'pydata_sphinx_theme/static'); +const vendor = resolve(staticPath, 'vendor'); module.exports = { entry: { @@ -7,7 +11,7 @@ module.exports = { }, output: { filename: 'js/[name].js?[hash]', - path: path.resolve(__dirname, 'pydata_sphinx_theme/static'), + path: staticPath, }, module: { rules: [ @@ -40,5 +44,93 @@ module.exports = { }, ], }, - plugins: [new CleanWebpackPlugin()], + plugins: [ + new CleanWebpackPlugin(), + new CopyPlugin([ + // bootstrap + { + // includes popper.js + context: './node_modules/bootstrap/dist/js/', + from: 'bootstrap.bundle.min.*', + to: resolve(vendor, 'bootstrap') + }, + { + context: './node_modules/bootstrap/', + from: 'LICENSE', + to: resolve(vendor, 'bootstrap') + }, + // fonts + { + context: './node_modules/@fortawesome/fontawesome-free/css', + from: 'all.min.css', + to: resolve(vendor, 'fontawesome', 'css') + }, + { + context: './node_modules/@fortawesome/fontawesome-free', + from: 'webfonts', + to: resolve(vendor, 'fontawesome', 'webfonts') + }, + { + context: './node_modules/@fortawesome/fontawesome-free', + from: 'LICENSE.txt', + to: resolve(vendor, 'fontawesome') + }, + { + context: './node_modules/@openfonts/open-sans_all', + from: 'files/*-400*', + to: resolve(vendor, 'open-sans_all') + }, + { + context: './node_modules/@openfonts/open-sans_all', + from: 'index.css', + to: resolve(vendor, 'open-sans_all') + }, + { + context: './node_modules/@openfonts/open-sans_all', + from: 'LICENSE.md', + to: resolve(vendor, 'open-sans_all') + }, + { + context: './node_modules/@openfonts/lato_latin-ext', + from: 'files/*-400*', + to: resolve(vendor, 'lato_latin-ext') + }, + { + context: './node_modules/@openfonts/lato_latin-ext', + from: 'index.css', + to: resolve(vendor, 'lato_latin-ext') + }, + { + context: './node_modules/@openfonts/lato_latin-ext', + from: 'LICENSE.md', + to: resolve(vendor, 'lato_latin-ext') + }, + // mathjax + { + context: './node_modules/mathjax', + from: '*.js', + to: resolve(vendor, 'mathjax') + }, + { + context: './node_modules/mathjax', + from: 'jax/output/HTML-CSS', + to: resolve(vendor, 'mathjax/jax/output/HTML-CSS') + }, + { + context: './node_modules/mathjax', + from: 'fonts/HTML-CSS/TeX', + to: resolve(vendor, 'mathjax/fonts/HTML-CSS/TeX') + }, + { + context: './node_modules/mathjax', + from: 'config/TeX-AMS-MML_HTMLorMML.js', + to: resolve(vendor, 'mathjax/config') + }, + { + context: './node_modules/mathjax', + from: 'LICENSE', + to: resolve(vendor, 'mathjax') + }, + ]) + ], }; diff --git a/yarn.lock b/yarn.lock index 84b97a21d8..9f071149c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,21 @@ # yarn lockfile v1 +"@fortawesome/fontawesome-free@^5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz#fcb113d1aca4b471b709e8c9c168674fbd6e06d9" + integrity sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg== + +"@openfonts/lato_latin-ext@^1.44.1": + version "1.44.1" + resolved "https://registry.yarnpkg.com/@openfonts/lato_latin-ext/-/lato_latin-ext-1.44.1.tgz#b36a9c5905e0143f5f72e25cf635625f645603dd" + integrity sha512-uEhdHlTQtLumrWuKHV3JpzH7H1/gGnwXrNC8PAK6StF/Tq1AQPfdjokqgwrHPVQPooj/LAwujhvBwPDdW4/5eQ== + +"@openfonts/open-sans_all@^1.44.1": + version "1.44.1" + resolved "https://registry.yarnpkg.com/@openfonts/open-sans_all/-/open-sans_all-1.44.1.tgz#4a05c454a89d0a70eb2f17bfa2ea3f21e3aae091" + integrity sha512-/3uxdiRxH1Sk+llrw4o7lunFhM5IwU/LBo2PU6EYw+iMRNuoUqKU7eyeGtFdAbvErjsU9Xvbe75zkqmsBcXGaw== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -2895,6 +2910,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +jquery@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9" + integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ== + js-base64@^2.1.8: version "2.5.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" @@ -3119,6 +3139,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +mathjax@2.7.5: + version "2.7.5" + resolved "https://registry.yarnpkg.com/mathjax/-/mathjax-2.7.5.tgz#c9c5947f86f9be31651f5f3667d3c9a8bb01efe4" + integrity sha512-OzsJNitEHAJB3y4IIlPCAvS0yoXwYjlo2Y4kmm9KQzyIBZt2d8yKRalby3uTRNN4fZQiGL2iMXjpdP1u2Rq2DQ== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -3919,6 +3944,11 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +popper.js@^1.16.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" + integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== + portfinder@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca"