diff --git a/assets/build/build.js b/assets/build/build.js index c46619056..84543164c 100644 --- a/assets/build/build.js +++ b/assets/build/build.js @@ -26,6 +26,7 @@ const formatters = [ formatter: 'html', outdir: path.resolve('../formatters/html/dist'), entryPoints: [ + 'js/entry/html_inline.js', 'js/entry/html.js', 'css/entry/html-elixir.css', 'css/entry/html-erlang.css' diff --git a/assets/js/constants.js b/assets/js/constants.js new file mode 100644 index 000000000..14f99194f --- /dev/null +++ b/assets/js/constants.js @@ -0,0 +1,3 @@ +// Constants separated to allow importing into html_inline.js without +// bringing in other code. +export const SETTINGS_KEY = 'ex_doc:settings' diff --git a/assets/js/entry/html_inline.js b/assets/js/entry/html_inline.js new file mode 100644 index 000000000..98322463e --- /dev/null +++ b/assets/js/entry/html_inline.js @@ -0,0 +1,18 @@ +// CAREFUL +// This file is inlined into each HTML document. +// Only code that must be executed ASAP belongs here. +// Imports should only bring in inlinable constants. +// Check compiled output to make sure no unnecessary code is imported. +import { SETTINGS_KEY } from '../constants' + +// Immediately apply night mode preference to avoid a flash effect +try { + const {theme} = JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}') + + if (theme === 'dark' || + ((theme === 'system' || theme == null) && + window.matchMedia('(prefers-color-scheme: dark)').matches) + ) { + document.body.classList.add('dark') + } +} catch (error) { } diff --git a/assets/js/settings-store.js b/assets/js/settings-store.js index 817743b32..d35aa8ba0 100644 --- a/assets/js/settings-store.js +++ b/assets/js/settings-store.js @@ -1,4 +1,4 @@ -const SETTINGS_KEY = 'ex_doc:settings' +import { SETTINGS_KEY } from './constants' const DEFAULT_SETTINGS = { // Whether to show tooltips on function/module links diff --git a/lib/ex_doc/formatter/html/templates.ex b/lib/ex_doc/formatter/html/templates.ex index 72407b672..ea4afd673 100644 --- a/lib/ex_doc/formatter/html/templates.ex +++ b/lib/ex_doc/formatter/html/templates.ex @@ -192,19 +192,27 @@ defmodule ExDoc.Formatter.HTML.Templates do defp sidebar_type(:livemd), do: "extras" defp sidebar_type(:extra), do: "extras" - def asset_rev(output, pattern) do + defp resolve_asset(output, pattern) do output = Path.expand(output) - output - |> Path.join(pattern) - |> Path.wildcard() - |> relative_asset(output, pattern) + matches = + output + |> Path.join(pattern) + |> Path.wildcard() + + case matches do + [] -> raise("could not find matching #{output}/#{pattern}") + [asset | _] -> asset + end end - defp relative_asset([], output, pattern), - do: raise("could not find matching #{output}/#{pattern}") + def asset_rev(output, pattern) do + resolve_asset(output, pattern) |> Path.relative_to(output) + end - defp relative_asset([h | _], output, _pattern), do: Path.relative_to(h, output) + def asset_inline(output, pattern) do + resolve_asset(output, pattern) |> File.read!() + end # TODO: Move link_headings and friends to html.ex or even to autolinking code, # so content is built with it upfront instead of added at the template level. diff --git a/lib/ex_doc/formatter/html/templates/head_template.eex b/lib/ex_doc/formatter/html/templates/head_template.eex index b8aa6414e..6f466e0b3 100644 --- a/lib/ex_doc/formatter/html/templates/head_template.eex +++ b/lib/ex_doc/formatter/html/templates/head_template.eex @@ -23,16 +23,4 @@ <%= before_closing_head_tag(config, :html) %> - +