From 2c8f31c9c0a6a7c307d93041909b1fc46c64dd9b Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 20 Oct 2024 03:12:23 +0200 Subject: [PATCH] Update extra.js (#17040) Signed-off-by: UltralyticsAssistant Co-authored-by: UltralyticsAssistant --- .github/workflows/docs.yml | 6 +- docs/build_docs.py | 33 ++++++- docs/overrides/javascript/extra.js | 135 +++++++++++++------------- docs/overrides/javascript/giscus.js | 63 ++++++++++++ docs/overrides/partials/comments.html | 48 +-------- docs/overrides/stylesheets/style.css | 14 +-- mkdocs.yml | 2 + 7 files changed, 175 insertions(+), 126 deletions(-) create mode 100644 docs/overrides/javascript/giscus.js diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 37e99b618d..360feead0c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,8 +22,8 @@ on: workflow_dispatch: inputs: publish_docs: - description: 'Publish live to https://docs.ultralytics.com' - default: 'true' + description: "Publish live to https://docs.ultralytics.com" + default: "true" type: boolean jobs: @@ -48,7 +48,7 @@ jobs: python-version: "3.x" cache: "pip" # caching pip dependencies - name: Install Dependencies - run: pip install ruff black tqdm mkdocs-material "mkdocstrings[python]" mkdocs-jupyter mkdocs-redirects mkdocs-ultralytics-plugin mkdocs-macros-plugin + run: pip install ruff black tqdm minify-html mkdocs-material "mkdocstrings[python]" mkdocs-jupyter mkdocs-redirects mkdocs-ultralytics-plugin mkdocs-macros-plugin - name: Ruff fixes continue-on-error: true run: ruff check --fix --unsafe-fixes --select D --ignore=D100,D104,D203,D205,D212,D213,D401,D406,D407,D413 . diff --git a/docs/build_docs.py b/docs/build_docs.py index 1744aefffa..7bf0575f44 100644 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -238,8 +238,36 @@ def remove_macros(): print(f"Removed {len(macros_indices)} URLs containing '/macros/' from {sitemap}") +def minify_html_files(): + """Minifies all HTML files in the site directory and prints reduction stats.""" + try: + from minify_html import minify # pip install minify-html + except ImportError: + return + + total_original_size = 0 + total_minified_size = 0 + for html_file in tqdm(SITE.rglob("*.html"), desc="Minifying HTML files"): + with open(html_file, encoding="utf-8") as f: + content = f.read() + + original_size = len(content) + minified_content = minify(content) + minified_size = len(minified_content) + + total_original_size += original_size + total_minified_size += minified_size + + with open(html_file, "w", encoding="utf-8") as f: + f.write(minified_content) + + total_reduction = total_original_size - total_minified_size + total_percent_reduction = (total_reduction / total_original_size) * 100 + print(f"Minify HTML reduction: {total_percent_reduction:.2f}% " f"({total_reduction / 1024:.2f} KB saved)") + + def main(): - """Builds docs, updates titles and edit links, and prints local server command.""" + """Builds docs, updates titles and edit links, minifies HTML, and prints local server command.""" prepare_docs_markdown() # Build the main documentation @@ -251,6 +279,9 @@ def main(): # Update docs HTML pages update_docs_html() + # Minify HTML files + minify_html_files() + # Show command to serve built website print('Docs built correctly ✅\nServe site at http://localhost:8000 with "python -m http.server --directory site"') diff --git a/docs/overrides/javascript/extra.js b/docs/overrides/javascript/extra.js index b106acdfe0..5029ff4893 100644 --- a/docs/overrides/javascript/extra.js +++ b/docs/overrides/javascript/extra.js @@ -1,71 +1,68 @@ -// Function that applies light/dark theme based on the user's preference -const applyAutoTheme = () => { - // Determine the user's preferred color scheme - const prefersLight = window.matchMedia("(prefers-color-scheme: light)").matches; - const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; +// Apply theme based on user preference +const applyTheme = (isDark) => { + document.body.setAttribute( + "data-md-color-scheme", + isDark ? "slate" : "default", + ); + document.body.setAttribute( + "data-md-color-primary", + isDark ? "black" : "indigo", + ); +}; + +// Check and apply auto theme +const checkAutoTheme = () => { + const supportedLangCodes = [ + "en", + "zh", + "ko", + "ja", + "ru", + "de", + "fr", + "es", + "pt", + "it", + "tr", + "vi", + "ar", + ]; + const langCode = window.location.pathname.split("/")[1]; + const localStorageKey = `${supportedLangCodes.includes(langCode) ? `/${langCode}` : ""}/.__palette`; + const palette = JSON.parse(localStorage.getItem(localStorageKey) || "{}"); - // Apply the appropriate attributes based on the user's preference - if (prefersLight) { - document.body.setAttribute("data-md-color-scheme", "default"); - document.body.setAttribute("data-md-color-primary", "indigo"); - } else if (prefersDark) { - document.body.setAttribute("data-md-color-scheme", "slate"); - document.body.setAttribute("data-md-color-primary", "black"); + if (palette.index === 0) { + applyTheme(window.matchMedia("(prefers-color-scheme: dark)").matches); } }; -// Function that checks and applies light/dark theme based on the user's preference (if auto theme is enabled) -function checkAutoTheme() { - // Array of supported language codes -> each language has its own palette (stored in local storage) - const supportedLangCodes = ["en", "zh", "ko", "ja", "ru", "de", "fr", "es", "pt", "it", "tr", "vi", "nl"]; - // Get the URL path - const path = window.location.pathname; - // Extract the language code from the URL (assuming it's in the format /xx/...) - const langCode = path.split("/")[1]; - // Check if the extracted language code is in the supported languages - const isValidLangCode = supportedLangCodes.includes(langCode); - // Construct the local storage key based on the language code if valid, otherwise default to the root key - const localStorageKey = isValidLangCode ? `/${langCode}/.__palette` : "/.__palette"; - // Retrieve the palette from local storage using the constructed key - const palette = localStorage.getItem(localStorageKey); - if (palette) { - // Check if the palette's index is 0 (auto theme) - const paletteObj = JSON.parse(palette); - if (paletteObj && paletteObj.index === 0) { - applyAutoTheme(); - } - } -} +// Event listeners for theme changes +const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); +mediaQueryList.addListener(checkAutoTheme); -// Run function when the script loads +// Initial theme check checkAutoTheme(); -// Re-run the function when the user's preference changes (when the user changes their system theme) -window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", checkAutoTheme); -window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", checkAutoTheme); - -// Re-run the function when the palette changes (e.g. user switched from dark theme to auto theme) -// ! We can't use window.addEventListener("storage", checkAutoTheme) because it will NOT be triggered on the current tab -// ! So we have to use the following workaround: -// Get the palette input for auto theme -var autoThemeInput = document.getElementById("__palette_1"); -if (autoThemeInput) { - // Add a click event listener to the input - autoThemeInput.addEventListener("click", function () { - // Check if the auto theme is selected - if (autoThemeInput.checked) { - // Re-run the function after a short delay (to ensure that the palette has been updated) - setTimeout(applyAutoTheme); - } +// Auto theme input listener +document.addEventListener("DOMContentLoaded", () => { + const autoThemeInput = document.getElementById("__palette_1"); + autoThemeInput?.addEventListener("click", () => { + if (autoThemeInput.checked) setTimeout(checkAutoTheme); }); -} +}); -// Add iframe navigation -window.onhashchange = function() { - window.parent.postMessage({ - type: 'navigation', - hash: window.location.pathname + window.location.search + window.location.hash - }, '*'); +// Iframe navigation +window.onhashchange = () => { + window.parent.postMessage( + { + type: "navigation", + hash: + window.location.pathname + + window.location.search + + window.location.hash, + }, + "*", + ); }; // Add Inkeep button @@ -112,35 +109,35 @@ document.addEventListener("DOMContentLoaded", () => { }, aiChatSettings: { chatSubjectName: "Ultralytics", - botAvatarSrcUrl: "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarSrcUrl-1727908259285.png", - botAvatarDarkSrcUrl: "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarDarkSrcUrl-1727908258478.png", + botAvatarSrcUrl: + "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarSrcUrl-1729379860806.svg", quickQuestions: [ "What's new in Ultralytics YOLO11?", "How can I get started with Ultralytics HUB?", - "How does Ultralytics Enterprise Licensing work?" + "How does Ultralytics Enterprise Licensing work?", ], getHelpCallToActions: [ { name: "Ask on Ultralytics GitHub", url: "https://github.com/ultralytics/ultralytics", icon: { - builtIn: "FaGithub" - } + builtIn: "FaGithub", + }, }, { name: "Ask on Ultralytics Discourse", url: "https://community.ultralytics.com/", icon: { - builtIn: "FaDiscourse" - } + builtIn: "FaDiscourse", + }, }, { name: "Ask on Ultralytics Discord", url: "https://discord.com/invite/ultralytics", icon: { - builtIn: "FaDiscord" - } - } + builtIn: "FaDiscord", + }, + }, ], }, }, diff --git a/docs/overrides/javascript/giscus.js b/docs/overrides/javascript/giscus.js new file mode 100644 index 0000000000..327e16ae74 --- /dev/null +++ b/docs/overrides/javascript/giscus.js @@ -0,0 +1,63 @@ +// Giscus functionality +function loadGiscus() { + const script = document.createElement("script"); + script.src = "https://giscus.app/client.js"; + script.setAttribute("data-repo", "ultralytics/ultralytics"); + script.setAttribute("data-repo-id", "R_kgDOH-jzvQ"); + script.setAttribute("data-category", "Docs"); + script.setAttribute("data-category-id", "DIC_kwDOH-jzvc4CWLkL"); + script.setAttribute("data-mapping", "pathname"); + script.setAttribute("data-strict", "1"); + script.setAttribute("data-reactions-enabled", "1"); + script.setAttribute("data-emit-metadata", "0"); + script.setAttribute("data-input-position", "top"); + script.setAttribute("data-theme", "preferred_color_scheme"); + script.setAttribute("data-lang", "en"); + script.setAttribute("data-loading", "lazy"); + script.setAttribute("crossorigin", "anonymous"); + script.setAttribute("async", ""); + + const giscusContainer = document.getElementById("giscus-container"); + if (giscusContainer) { + giscusContainer.appendChild(script); + + // Synchronize Giscus theme with palette + var palette = __md_get("__palette"); + if (palette && typeof palette.color === "object") { + var theme = palette.color.scheme === "slate" ? "dark" : "light"; + script.setAttribute("data-theme", theme); + } + + // Register event handlers for theme changes + var ref = document.querySelector("[data-md-component=palette]"); + if (ref) { + ref.addEventListener("change", function () { + var palette = __md_get("__palette"); + if (palette && typeof palette.color === "object") { + var theme = palette.color.scheme === "slate" ? "dark" : "light"; + + // Instruct Giscus to change theme + var frame = document.querySelector(".giscus-frame"); + if (frame) { + frame.contentWindow.postMessage( + { giscus: { setConfig: { theme } } }, + "https://giscus.app", + ); + } + } + }); + } + } +} + +// MkDocs specific: Load Giscus when the page content is fully loaded +document.addEventListener("DOMContentLoaded", function () { + var observer = new MutationObserver(function (mutations) { + if (document.getElementById("giscus-container")) { + loadGiscus(); + observer.disconnect(); + } + }); + + observer.observe(document.body, { childList: true, subtree: true }); +}); diff --git a/docs/overrides/partials/comments.html b/docs/overrides/partials/comments.html index a99f4f814b..fdfce5d651 100644 --- a/docs/overrides/partials/comments.html +++ b/docs/overrides/partials/comments.html @@ -1,51 +1,7 @@ {% if page.meta.comments %}

{{ lang.t("meta.comments") }}

- - + +
- - {% endif %} diff --git a/docs/overrides/stylesheets/style.css b/docs/overrides/stylesheets/style.css index a5bdcc56ab..d10582db41 100644 --- a/docs/overrides/stylesheets/style.css +++ b/docs/overrides/stylesheets/style.css @@ -76,7 +76,6 @@ div.highlight { .banner-wrapper { justify-content: space-between; gap: 16px; - padding: 16px; } @@ -121,7 +120,6 @@ div.highlight { .banner-wrapper > .banner-button-wrapper, .banner-wrapper > .banner-button-wrapper > .banner-button-wrapper { padding: 2px; - background-color: rgba(222, 255, 56, 0.2); } @@ -131,13 +129,10 @@ div.highlight { .banner-wrapper > .banner-button-wrapper > .banner-button-wrapper > button { cursor: pointer; - min-width: 132px; padding: 10px; - font-weight: 500; color: #111f68; - background-color: rgb(222, 255, 56); } @@ -156,13 +151,11 @@ div.highlight { .banner-wrapper { gap: 32px; - padding: 12px; } .banner-wrapper > .banner-content-wrapper { gap: 24px; - margin: 0 auto; } } @@ -217,6 +210,13 @@ div.highlight { height: 50px; border-radius: 50%; margin-right: 3px; + background-color: #f0f0f0; /* Placeholder color */ + opacity: 0; /* Start fully transparent */ + transition: opacity 0.3s ease-in-out; +} + +.author-link .hover-item[src] { + opacity: 1; /* Fade in when src is set (image loaded) */ } .hover-item:hover { diff --git a/mkdocs.yml b/mkdocs.yml index f5298dc474..17a72c2e2f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -96,8 +96,10 @@ extra: # version: extra_css: - stylesheets/style.css + extra_javascript: - javascript/extra.js + - javascript/giscus.js markdown_extensions: - admonition