diff --git a/public/_/readthedocs-addons.json b/public/_/readthedocs-addons.json index 81e3ec3f..ebb8e768 100644 --- a/public/_/readthedocs-addons.json +++ b/public/_/readthedocs-addons.json @@ -101,7 +101,7 @@ "load_when_embedded": false }, "ethicalads": { - "ad_free": false, + "ad_free": true, "enabled": true, "publisher": "readthedocs", "campaign_types": ["community", "house", "paid"], diff --git a/public/global.css b/public/global.css new file mode 100644 index 00000000..9ceeab91 --- /dev/null +++ b/public/global.css @@ -0,0 +1,5 @@ +/* Theme defaults? */ +:root { + --readthedocs-font-size: 10px; + --readthedocs-flyout-font-size: 30px; +} diff --git a/public/index.html b/public/index.html index f24ff41a..35ee16e5 100644 --- a/public/index.html +++ b/public/index.html @@ -15,6 +15,7 @@ ); +

Documentation Addons

diff --git a/src/constants.js b/src/constants.js index 463b61f3..26589586 100644 --- a/src/constants.js +++ b/src/constants.js @@ -8,4 +8,11 @@ export const ASCIIDOCTOR = "asciidoctor"; export const JEKYLL = "jekyll"; export const DOCSIFY = "docsify"; export const ANTORA = "antora"; +export const MDBOOK = "mdbook"; export const FALLBACK_DOCTOOL = "fallback"; + +// Known documentation tools themes +export const SPHINX_ALABASTER = "alabaster"; +export const SPHINX_FURO = "furo"; +export const SPHINX_READTHEDOCS = "readthedocs"; +export const SPHINX_IMMATERIAL = "immaterial"; diff --git a/src/defaults.js b/src/defaults.js new file mode 100644 index 00000000..49a506e7 --- /dev/null +++ b/src/defaults.js @@ -0,0 +1,55 @@ +import styleSheetFlyout from "./flyout.css"; + +import { CSSResult } from "lit"; + +// We use a native construct here as Lit's CSSResult is largely read only. +export const defaultStyleSheet = new CSSStyleSheet(); + +/* + * Specific styles based on documentation tools and themes + * + * Usage of `@layer` at-rule pushes this rules down a step in + * precedence/priority. This allows a user `:root` rule to override these + * values. + **/ +defaultStyleSheet.replaceSync(` +@layer defaults { + :root[data-readthedocs-tool="mkdocs-material"] { + --readthedocs-font-size: 0.58rem; + --readthedocs-flyout-font-size: 0.58rem; + } + + :root[data-readthedocs-tool="antora"] { + --readthedocs-font-size: 0.7rem; + --readthedocs-flyout-font-size: 0.7rem; + } + + :root[data-readthedocs-tool="mdbook"] { + --readthedocs-font-size: 1.3rem; + --readthedocs-flyout-font-size: 1.3rem; + } + + :root[data-readthedocs-tool="sphinx"][data-readthedocs-tool-theme="furo"] { + --readthedocs-font-size: 0.75rem; + --readthedocs-flyout-font-size: 0.75rem; + } + + :root[data-readthedocs-tool="sphinx"][data-readthedocs-tool-theme="immaterial"] { + --readthedocs-font-size: 0.58rem; + --readthedocs-flyout-font-size: 0.58rem; + } +} +`); + +const styleSheets = [styleSheetFlyout]; + +for (let styleSheet of styleSheets) { + if (styleSheet instanceof CSSResult) { + styleSheet = styleSheet.styleSheet; + } + for (const rule of styleSheet.cssRules) { + if (rule instanceof CSSLayerBlockRule && rule.name == "defaults") { + defaultStyleSheet.insertRule(rule.cssText); + } + } +} diff --git a/src/flyout.css b/src/flyout.css index 7d175aed..5d033d72 100644 --- a/src/flyout.css +++ b/src/flyout.css @@ -1,4 +1,24 @@ -/* New */ +/* Flyout styles */ + +@layer defaults { + :root { + --readthedocs-flyout-font-family: var(--readthedocs-font-family); + --readthedocs-flyout-font-size: var(--readthedocs-font-size); + --readthedocs-flyout-line-height: 1.25em; + --readthedocs-flyout-header-font-size: 1.125em; + --readthedocs-flyout-dt-font-size: 1.125em; + --readthedocs-flyout-dd-font-size: 1.125em; + --readthedocs-flyout-line-height: 1.25em; + + --readthedocs-flyout-color: rgb(128, 128, 128); + --readthedocs-flyout-background-color: rgb(39, 39, 37); + --readthedocs-flyout-current-version-color: #27ae60; + --readthedocs-flyout-section-heading-color: rgb(128, 128, 128); + --readthedocs-flyout-item-link-color: rgb(252, 252, 252); + --readthedocs-flyout-divider-color: #413d3d; + --readthedocs-flyout-link-color: rgb(42, 128, 185); + } +} .container { position: fixed; @@ -7,6 +27,7 @@ height: auto; max-height: calc(100% - 100px); overflow-y: auto; + line-height: var(--readthedocs-flyout-line-height); } .container.bottom-right { @@ -29,25 +50,11 @@ top: 50px; } -:host { - --addons-flyout-font-size: var( - --readthedocs-flyout-font-size, - var(--readthedocs-font-size, 0.8rem) - ); -} - :host > div { - font-family: var( - --readthedocs-flyout-font-family, - "Lato", - "proxima-nova", - "Helvetica Neue", - "Arial", - "sans-serif" - ); - font-size: var(--addons-flyout-font-size); - color: var(--readthedocs-flyout-color, rgb(128, 128, 128)); - background-color: var(--readthedocs-flyout-background-color, rgb(39, 39, 37)); + font-family: var(--readthedocs-flyout-font-family); + font-size: var(--readthedocs-flyout-font-size); + color: var(--readthedocs-flyout-color); + background-color: var(--readthedocs-flyout-background-color); z-index: 3000; padding: 0 10px; overflow-y: auto; @@ -59,27 +66,21 @@ header { justify-content: space-between; align-items: center; cursor: pointer; - background-color: var(--readthedocs-flyout-background-color, rgb(39, 39, 37)); + background-color: var(--readthedocs-flyout-background-color); position: sticky; top: 0; padding: 10px 0; } header > span { - color: var(--readthedocs-flyout-current-version-color, #27ae60); - font-size: var( - --readthedocs-flyout-header-font-size, - calc(var(--addons-flyout-font-size) * 1.125) - ); + color: var(--readthedocs-flyout-current-version-color); + font-size: var(--readthedocs-flyout-header-font-size); margin-left: 10px; } header > span svg.icon { color: rgb(128, 128, 128); - height: var( - --readthedocs-flyout-header-font-size, - calc(var(--addons-flyout-font-size) * 1.125) - ); + height: var(--readthedocs-flyout-header-font-size); padding-right: 5px; vertical-align: middle; } @@ -107,25 +108,19 @@ dl { } dl > dt { - font-size: var( - --readthedocs-flyout-dt-font-size, - calc(var(--addons-flyout-font-size) * 1.125) - ); - color: var(--readthedocs-flyout-section-heading-color, rgb(128, 128, 128)); + font-size: var(--readthedocs-flyout-dt-font-size); + color: var(--readthedocs-flyout-section-heading-color); } dl > dd { display: inline-block; margin: 0; - font-size: var( - --readthedocs-flyout-dd-font-size, - calc(var(--addons-flyout-font-size) * 1.125) - ); + font-size: var(--readthedocs-flyout-dd-font-size); } dd a { text-decoration: none; - color: var(--readthedocs-flyout-item-link-color, rgb(252, 252, 252)); + color: var(--readthedocs-flyout-item-link-color); padding: 6px; display: inline-block; } @@ -148,10 +143,10 @@ hr { padding: 0; border-top-width: 1px; border-top-style: solid; - border-top-color: var(--readthedocs-flyout-divider-color, #413d3d); + border-top-color: var(--readthedocs-flyout-divider-color); } small a { text-decoration: none; - color: var(--readthedocs-flyout-link-color, rgb(42, 128, 185)); + color: var(--readthedocs-flyout-link-color); } diff --git a/src/flyout.js b/src/flyout.js index a10c9753..3f0f41ba 100644 --- a/src/flyout.js +++ b/src/flyout.js @@ -9,6 +9,7 @@ import { default as objectPath } from "object-path"; import styleSheet from "./flyout.css"; import { AddonBase, addUtmParameters, getLinkWithFilename } from "./utils"; +import { SPHINX, MKDOCS_MATERIAL } from "./constants"; import { EVENT_READTHEDOCS_SEARCH_SHOW, EVENT_READTHEDOCS_FLYOUT_HIDE, @@ -31,9 +32,12 @@ export class FlyoutElement extends LitElement { super(); this.config = null; + this.classes = {}; this.opened = false; this.floating = true; this.position = "bottom-right"; + this.classes = { floating: this.floating, container: true }; + this.classes[this.position] = true; this.readthedocsLogo = READTHEDOCS_LOGO; } @@ -310,11 +314,8 @@ export class FlyoutElement extends LitElement { return nothing; } - const classes = { floating: this.floating, container: true }; - classes[this.position] = true; - return html` -
+
${this.renderHeader()}
${this.renderLanguages()} ${this.renderVersions()} diff --git a/src/index.js b/src/index.js index b6827651..b376fd4c 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ import * as filetreediff from "./filetreediff"; import * as customscript from "./customscript"; import { default as objectPath } from "object-path"; import { + docTool, domReady, isEmbedded, IS_PRODUCTION, @@ -18,6 +19,8 @@ import { getMetadataValue, } from "./utils"; +import { defaultStyleSheet } from "./defaults.js"; + export function setup() { const addons = [ flyout.FlyoutAddon, @@ -50,6 +53,28 @@ export function setup() { } } + // Apply fixes to variables for individual documentation tools + const elementHtml = document.querySelector("html"); + if (elementHtml) { + // Inject styles at the parent DOM to set variables at :root + document.adoptedStyleSheets = [defaultStyleSheet]; + + // If we detect a documentation tool, set attributes on :root to allow + // for CSS selectors to utilize these values. + if (docTool.documentationTool) { + elementHtml.setAttribute( + "data-readthedocs-tool", + docTool.documentationTool, + ); + } + if (docTool.documentationTheme) { + elementHtml.setAttribute( + "data-readthedocs-tool-theme", + docTool.documentationTheme, + ); + } + } + return getReadTheDocsConfig(sendUrlParam); }) .then((config) => { diff --git a/src/utils.js b/src/utils.js index e144e051..0a4f3f33 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,6 +2,11 @@ import { ajv } from "./data-validation"; import { default as objectPath } from "object-path"; import { SPHINX, + SPHINX_FURO, + SPHINX_ALABASTER, + SPHINX_READTHEDOCS, + SPHINX_IMMATERIAL, + MDBOOK, MKDOCS, MKDOCS_MATERIAL, DOCUSAURUS, @@ -297,6 +302,7 @@ export class DocumentationTool { constructor() { this.documentationTool = this.getDocumentationTool(); + this.documentationTheme = this.getDocumentationTheme(); console.debug(`Documentation tool detected: ${this.documentationTool}`); } @@ -411,10 +417,63 @@ export class DocumentationTool { return DOCSIFY; } + if (this.isMdBook()) { + return MDBOOK; + } + + if (this.isAntora()) { + return ANTORA; + } + console.debug("We were not able to detect the documentation tool."); return null; } + getDocumentationTheme() { + const documentationTool = + this.documentationTool || this.getDocumentationTool(); + + if (documentationTool === SPHINX) { + if (this.isSphinxAlabasterLikeTheme()) { + return SPHINX_ALABASTER; + } else if (this.isSphinxReadTheDocsLikeTheme()) { + return SPHINX_READTHEDOCS; + } else if (this.isSphinxFuroLikeTheme()) { + return SPHINX_FURO; + } else if (this.isSphinxImmaterialLikeTheme()) { + return SPHINX_IMMATERIAL; + } + } + + // TODO: add the other known themes + return null; + } + + isAntora() { + if ( + document.querySelectorAll('meta[name="generator"][content^="Antora"]') + .length + ) { + return true; + } + return false; + } + + isMdBook() { + // + // + // + // ... + if ( + document?.head?.firstChild?.nextSibling?.textContent.includes( + "Book generated using mdBook", + ) + ) { + return true; + } + return false; + } + isDocsify() { if (document.querySelectorAll("head > link[href*=docsify]").length) { return true; @@ -427,7 +486,8 @@ export class DocumentationTool { this.isSphinxAlabasterLikeTheme() || this.isSphinxReadTheDocsLikeTheme() || this.isSphinxFuroLikeTheme() || - this.isSphinxBookThemeLikeTheme() + this.isSphinxBookThemeLikeTheme() || + this.isSphinxImmaterialLikeTheme() ); } @@ -476,7 +536,7 @@ export class DocumentationTool { // MkDocs version : 1.4.2 // Build Date UTC : 2023-07-11 16:08:07.379780+00:00 // --> - if (document.lastChild.textContent.includes("MkDocs version :")) { + if (document?.lastChild?.textContent.includes("MkDocs version :")) { return true; } return false; @@ -531,6 +591,18 @@ export class DocumentationTool { return false; } + isSphinxImmaterialLikeTheme() { + if ( + document.querySelectorAll( + 'link[href^="_static/sphinx_immaterial_theme"]', + 'a[href="https://github.com/jbms/sphinx-immaterial/"][rel="noopener"]', + ).length + ) { + return true; + } + return false; + } + isMaterialMkDocsTheme() { if ( document.querySelectorAll(