Update extra.js (#17040)

Signed-off-by: UltralyticsAssistant <web@ultralytics.com>
Co-authored-by: UltralyticsAssistant <web@ultralytics.com>
pull/17042/head
Glenn Jocher 1 month ago committed by GitHub
parent e38228774f
commit 2c8f31c9c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .github/workflows/docs.yml
  2. 33
      docs/build_docs.py
  3. 135
      docs/overrides/javascript/extra.js
  4. 63
      docs/overrides/javascript/giscus.js
  5. 48
      docs/overrides/partials/comments.html
  6. 14
      docs/overrides/stylesheets/style.css
  7. 2
      mkdocs.yml

@ -22,8 +22,8 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
publish_docs: publish_docs:
description: 'Publish live to https://docs.ultralytics.com' description: "Publish live to https://docs.ultralytics.com"
default: 'true' default: "true"
type: boolean type: boolean
jobs: jobs:
@ -48,7 +48,7 @@ jobs:
python-version: "3.x" python-version: "3.x"
cache: "pip" # caching pip dependencies cache: "pip" # caching pip dependencies
- name: Install 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 - name: Ruff fixes
continue-on-error: true continue-on-error: true
run: ruff check --fix --unsafe-fixes --select D --ignore=D100,D104,D203,D205,D212,D213,D401,D406,D407,D413 . run: ruff check --fix --unsafe-fixes --select D --ignore=D100,D104,D203,D205,D212,D213,D401,D406,D407,D413 .

@ -238,8 +238,36 @@ def remove_macros():
print(f"Removed {len(macros_indices)} URLs containing '/macros/' from {sitemap}") 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(): 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() prepare_docs_markdown()
# Build the main documentation # Build the main documentation
@ -251,6 +279,9 @@ def main():
# Update docs HTML pages # Update docs HTML pages
update_docs_html() update_docs_html()
# Minify HTML files
minify_html_files()
# Show command to serve built website # Show command to serve built website
print('Docs built correctly ✅\nServe site at http://localhost:8000 with "python -m http.server --directory site"') print('Docs built correctly ✅\nServe site at http://localhost:8000 with "python -m http.server --directory site"')

@ -1,71 +1,68 @@
// Function that applies light/dark theme based on the user's preference // Apply theme based on user preference
const applyAutoTheme = () => { const applyTheme = (isDark) => {
// Determine the user's preferred color scheme document.body.setAttribute(
const prefersLight = window.matchMedia("(prefers-color-scheme: light)").matches; "data-md-color-scheme",
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; 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 (palette.index === 0) {
if (prefersLight) { applyTheme(window.matchMedia("(prefers-color-scheme: dark)").matches);
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");
} }
}; };
// Function that checks and applies light/dark theme based on the user's preference (if auto theme is enabled) // Event listeners for theme changes
function checkAutoTheme() { const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
// Array of supported language codes -> each language has its own palette (stored in local storage) mediaQueryList.addListener(checkAutoTheme);
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();
}
}
}
// Run function when the script loads // Initial theme check
checkAutoTheme(); checkAutoTheme();
// Re-run the function when the user's preference changes (when the user changes their system theme) // Auto theme input listener
window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", checkAutoTheme); document.addEventListener("DOMContentLoaded", () => {
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", checkAutoTheme); const autoThemeInput = document.getElementById("__palette_1");
autoThemeInput?.addEventListener("click", () => {
// Re-run the function when the palette changes (e.g. user switched from dark theme to auto theme) if (autoThemeInput.checked) setTimeout(checkAutoTheme);
// ! 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);
}
}); });
}
// Add iframe navigation // Iframe navigation
window.onhashchange = function() { window.onhashchange = () => {
window.parent.postMessage({ window.parent.postMessage(
type: 'navigation', {
hash: window.location.pathname + window.location.search + window.location.hash type: "navigation",
}, '*'); hash:
window.location.pathname +
window.location.search +
window.location.hash,
},
"*",
);
}; };
// Add Inkeep button // Add Inkeep button
@ -112,35 +109,35 @@ document.addEventListener("DOMContentLoaded", () => {
}, },
aiChatSettings: { aiChatSettings: {
chatSubjectName: "Ultralytics", chatSubjectName: "Ultralytics",
botAvatarSrcUrl: "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarSrcUrl-1727908259285.png", botAvatarSrcUrl:
botAvatarDarkSrcUrl: "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarDarkSrcUrl-1727908258478.png", "https://storage.googleapis.com/organization-image-assets/ultralytics-botAvatarSrcUrl-1729379860806.svg",
quickQuestions: [ quickQuestions: [
"What's new in Ultralytics YOLO11?", "What's new in Ultralytics YOLO11?",
"How can I get started with Ultralytics HUB?", "How can I get started with Ultralytics HUB?",
"How does Ultralytics Enterprise Licensing work?" "How does Ultralytics Enterprise Licensing work?",
], ],
getHelpCallToActions: [ getHelpCallToActions: [
{ {
name: "Ask on Ultralytics GitHub", name: "Ask on Ultralytics GitHub",
url: "https://github.com/ultralytics/ultralytics", url: "https://github.com/ultralytics/ultralytics",
icon: { icon: {
builtIn: "FaGithub" builtIn: "FaGithub",
} },
}, },
{ {
name: "Ask on Ultralytics Discourse", name: "Ask on Ultralytics Discourse",
url: "https://community.ultralytics.com/", url: "https://community.ultralytics.com/",
icon: { icon: {
builtIn: "FaDiscourse" builtIn: "FaDiscourse",
} },
}, },
{ {
name: "Ask on Ultralytics Discord", name: "Ask on Ultralytics Discord",
url: "https://discord.com/invite/ultralytics", url: "https://discord.com/invite/ultralytics",
icon: { icon: {
builtIn: "FaDiscord" builtIn: "FaDiscord",
} },
} },
], ],
}, },
}, },

@ -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 });
});

@ -1,51 +1,7 @@
{% if page.meta.comments %} {% if page.meta.comments %}
<h2 id="__comments">{{ lang.t("meta.comments") }}</h2> <h2 id="__comments">{{ lang.t("meta.comments") }}</h2>
<!-- Insert Giscus code snippet from https://giscus.app/ here --> <!-- Giscus container -->
<script async <div id="giscus-container"></div>
crossorigin="anonymous"
data-category="Docs"
data-category-id="DIC_kwDOH-jzvc4CWLkL"
data-emit-metadata="0"
data-input-position="top"
data-lang="en"
data-loading="lazy"
data-mapping="pathname"
data-reactions-enabled="1"
data-repo="ultralytics/ultralytics"
data-repo-id="R_kgDOH-jzvQ"
data-strict="1"
data-theme="preferred_color_scheme"
src="https://giscus.app/client.js">
</script>
<!-- Synchronize Giscus theme with palette -->
<script>
var giscus = document.querySelector("script[src*=giscus]")
/* Set palette on initial load */
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate" ? "dark" : "light"
giscus.setAttribute("data-theme", theme)
}
/* Register event handlers after documented loaded */
document.addEventListener("DOMContentLoaded", function() {
var ref = document.querySelector("[data-md-component=palette]")
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")
frame.contentWindow.postMessage(
{ giscus: { setConfig: { theme } } },
"https://giscus.app"
)
}
})
})
</script>
{% endif %} {% endif %}

@ -76,7 +76,6 @@ div.highlight {
.banner-wrapper { .banner-wrapper {
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
padding: 16px; padding: 16px;
} }
@ -121,7 +120,6 @@ div.highlight {
.banner-wrapper > .banner-button-wrapper, .banner-wrapper > .banner-button-wrapper,
.banner-wrapper > .banner-button-wrapper > .banner-button-wrapper { .banner-wrapper > .banner-button-wrapper > .banner-button-wrapper {
padding: 2px; padding: 2px;
background-color: rgba(222, 255, 56, 0.2); background-color: rgba(222, 255, 56, 0.2);
} }
@ -131,13 +129,10 @@ div.highlight {
.banner-wrapper > .banner-button-wrapper > .banner-button-wrapper > button { .banner-wrapper > .banner-button-wrapper > .banner-button-wrapper > button {
cursor: pointer; cursor: pointer;
min-width: 132px; min-width: 132px;
padding: 10px; padding: 10px;
font-weight: 500; font-weight: 500;
color: #111f68; color: #111f68;
background-color: rgb(222, 255, 56); background-color: rgb(222, 255, 56);
} }
@ -156,13 +151,11 @@ div.highlight {
.banner-wrapper { .banner-wrapper {
gap: 32px; gap: 32px;
padding: 12px; padding: 12px;
} }
.banner-wrapper > .banner-content-wrapper { .banner-wrapper > .banner-content-wrapper {
gap: 24px; gap: 24px;
margin: 0 auto; margin: 0 auto;
} }
} }
@ -217,6 +210,13 @@ div.highlight {
height: 50px; height: 50px;
border-radius: 50%; border-radius: 50%;
margin-right: 3px; 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 { .hover-item:hover {

@ -96,8 +96,10 @@ extra: # version:
extra_css: extra_css:
- stylesheets/style.css - stylesheets/style.css
extra_javascript: extra_javascript:
- javascript/extra.js - javascript/extra.js
- javascript/giscus.js
markdown_extensions: markdown_extensions:
- admonition - admonition

Loading…
Cancel
Save