单目3D初始代码
This commit is contained in:
236
docs/overrides/javascript/benchmark.js
Executable file
236
docs/overrides/javascript/benchmark.js
Executable file
@@ -0,0 +1,236 @@
|
||||
// Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
// Auto-load chart-widget.js if not already loaded
|
||||
const loadChartWidget = () =>
|
||||
new Promise((resolve) => {
|
||||
if (window.ChartWidget) {
|
||||
return resolve();
|
||||
}
|
||||
const s = document.createElement("script");
|
||||
const base =
|
||||
(document.currentScript || document.querySelector('script[src*="benchmark.js"]'))?.src.replace(/[^/]*$/, "") ||
|
||||
"./";
|
||||
s.src = `${base}chart-widget.js`;
|
||||
s.onload = s.onerror = resolve;
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
|
||||
// YOLO models chart ---------------------------------------------------------------------------------------------------
|
||||
const data = {
|
||||
YOLO26: {
|
||||
n: { speed: 1.7, mAP: 40.9 },
|
||||
s: { speed: 2.5, mAP: 48.6 },
|
||||
m: { speed: 4.7, mAP: 53.1 },
|
||||
l: { speed: 6.2, mAP: 55.0 },
|
||||
x: { speed: 11.8, mAP: 57.5 },
|
||||
},
|
||||
YOLO11: {
|
||||
n: { speed: 1.5, mAP: 39.5 },
|
||||
s: { speed: 2.5, mAP: 47.0 },
|
||||
m: { speed: 4.7, mAP: 51.5 },
|
||||
l: { speed: 6.2, mAP: 53.4 },
|
||||
x: { speed: 11.3, mAP: 54.7 },
|
||||
},
|
||||
YOLOv10: {
|
||||
n: { speed: 1.56, mAP: 39.5 },
|
||||
s: { speed: 2.66, mAP: 46.7 },
|
||||
m: { speed: 5.48, mAP: 51.3 },
|
||||
b: { speed: 6.54, mAP: 52.7 },
|
||||
l: { speed: 8.33, mAP: 53.3 },
|
||||
x: { speed: 12.2, mAP: 54.4 },
|
||||
},
|
||||
YOLOv9: {
|
||||
t: { speed: 2.3, mAP: 38.3 },
|
||||
s: { speed: 3.54, mAP: 46.8 },
|
||||
m: { speed: 6.43, mAP: 51.4 },
|
||||
c: { speed: 7.16, mAP: 53.0 },
|
||||
e: { speed: 16.77, mAP: 55.6 },
|
||||
},
|
||||
YOLOv8: {
|
||||
n: { speed: 1.47, mAP: 37.3 },
|
||||
s: { speed: 2.66, mAP: 44.9 },
|
||||
m: { speed: 5.86, mAP: 50.2 },
|
||||
l: { speed: 9.06, mAP: 52.9 },
|
||||
x: { speed: 14.37, mAP: 53.9 },
|
||||
},
|
||||
YOLOv7: { l: { speed: 6.84, mAP: 51.4 }, x: { speed: 11.57, mAP: 53.1 } },
|
||||
"YOLOv6-3.0": {
|
||||
n: { speed: 1.17, mAP: 37.5 },
|
||||
s: { speed: 2.66, mAP: 45.0 },
|
||||
m: { speed: 5.28, mAP: 50.0 },
|
||||
l: { speed: 8.95, mAP: 52.8 },
|
||||
},
|
||||
YOLOv5: {
|
||||
n: { speed: 1.12, mAP: 28.0 },
|
||||
s: { speed: 1.92, mAP: 37.4 },
|
||||
m: { speed: 4.03, mAP: 45.4 },
|
||||
l: { speed: 6.61, mAP: 49.0 },
|
||||
x: { speed: 11.89, mAP: 50.7 },
|
||||
},
|
||||
"PP-YOLOE+": {
|
||||
t: { speed: 2.84, mAP: 39.9 },
|
||||
s: { speed: 2.62, mAP: 43.7 },
|
||||
m: { speed: 5.56, mAP: 49.8 },
|
||||
l: { speed: 8.36, mAP: 52.9 },
|
||||
x: { speed: 14.3, mAP: 54.7 },
|
||||
},
|
||||
"DAMO-YOLO": {
|
||||
t: { speed: 2.32, mAP: 42.0 },
|
||||
s: { speed: 3.45, mAP: 46.0 },
|
||||
m: { speed: 5.09, mAP: 49.2 },
|
||||
l: { speed: 7.18, mAP: 50.8 },
|
||||
},
|
||||
YOLOX: {
|
||||
s: { speed: 2.56, mAP: 40.5 },
|
||||
m: { speed: 5.43, mAP: 46.9 },
|
||||
l: { speed: 9.04, mAP: 49.7 },
|
||||
x: { speed: 16.1, mAP: 51.1 },
|
||||
},
|
||||
RTDETRv2: {
|
||||
s: { speed: 5.03, mAP: 48.1 },
|
||||
m: { speed: 7.51, mAP: 51.9 },
|
||||
l: { speed: 9.76, mAP: 53.4 },
|
||||
x: { speed: 15.03, mAP: 54.3 },
|
||||
},
|
||||
EfficientDet: {
|
||||
d0: { speed: 3.92, mAP: 34.6 },
|
||||
d1: { speed: 7.31, mAP: 40.5 },
|
||||
d2: { speed: 10.92, mAP: 43.0 },
|
||||
d3: { speed: 19.59, mAP: 47.5 },
|
||||
// d4: { speed: 33.55, mAP: 49.4 },
|
||||
// d5: { speed: 67.86, mAP: 50.7 },
|
||||
// d6: { speed: 89.29, mAP: 51.7 },
|
||||
// d7: { speed: 128.07, mAP: 53.7 },
|
||||
// d8: { speed: 157.57, mAP: 55.1 }
|
||||
},
|
||||
};
|
||||
|
||||
// Color overrides for specific models
|
||||
const colorOverrides = {
|
||||
YOLO26: "#0b23a9",
|
||||
YOLO11: "#1e90ff",
|
||||
YOLOv10: "#ff7f0e",
|
||||
YOLOv9: "#2ca02c",
|
||||
YOLOv8: "#d62728",
|
||||
YOLOv7: "#9467bd",
|
||||
"YOLOv6-3.0": "#8c564b",
|
||||
YOLOv5: "#e377c2",
|
||||
"PP-YOLOE+": "#7f7f7f",
|
||||
"DAMO-YOLO": "#bcbd22",
|
||||
YOLOX: "#17becf",
|
||||
RTDETRv2: "#eccd22",
|
||||
EfficientDet: "#000000",
|
||||
};
|
||||
|
||||
let chart = null;
|
||||
let chartWidget = null;
|
||||
|
||||
const lighten = (hex, amt = 0.6) => {
|
||||
const [r, g, b] = [1, 3, 5].map((i) => Number.parseInt(hex.slice(i, i + 2), 16));
|
||||
return `#${[r, g, b]
|
||||
.map((c) =>
|
||||
Math.min(255, Math.round(c + (255 - c) * amt))
|
||||
.toString(16)
|
||||
.padStart(2, "0"),
|
||||
)
|
||||
.join("")}`;
|
||||
};
|
||||
|
||||
const createDataset = (algo, i, activeModels) => {
|
||||
const baseColor = colorOverrides[algo] || `hsl(${(i * 137) % 360}, 70%, 50%)`;
|
||||
const isFirst = i === 0;
|
||||
return {
|
||||
label: algo,
|
||||
data: Object.entries(data[algo]).map(([ver, pt]) => ({
|
||||
x: pt.speed,
|
||||
y: pt.mAP,
|
||||
version: ver.toUpperCase(),
|
||||
})),
|
||||
fill: false,
|
||||
borderColor: isFirst ? baseColor : lighten(baseColor),
|
||||
tension: 0.2,
|
||||
pointRadius: isFirst ? 7 : 4,
|
||||
pointHoverRadius: isFirst ? 9 : 6,
|
||||
pointBackgroundColor: isFirst ? baseColor : lighten(baseColor),
|
||||
pointBorderColor: "#ffffff",
|
||||
borderWidth: isFirst ? 3 : 1.5,
|
||||
hidden: activeModels.length > 0 && !activeModels.includes(algo),
|
||||
};
|
||||
};
|
||||
|
||||
const chartConfig = {
|
||||
type: "line",
|
||||
data: { datasets: Object.keys(data).map(createDataset) },
|
||||
options: {
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right",
|
||||
align: "start",
|
||||
labels: { color: "#808080" },
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: ({ dataset, dataIndex }) => {
|
||||
const pt = dataset.data[dataIndex];
|
||||
return `${dataset.label}${pt.version.toLowerCase()}: Speed = ${pt.x}ms/img, mAP50-95 = ${pt.y}`;
|
||||
},
|
||||
},
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
interaction: { mode: "nearest", axis: "x", intersect: false },
|
||||
scales: {
|
||||
x: {
|
||||
type: "linear",
|
||||
position: "bottom",
|
||||
title: {
|
||||
display: true,
|
||||
text: "Latency T4 TensorRT10 FP16 (ms/img)",
|
||||
color: "#808080",
|
||||
},
|
||||
grid: { color: "#e0e0e0" },
|
||||
ticks: { color: "#808080" },
|
||||
min: 0,
|
||||
max: 18,
|
||||
},
|
||||
y: {
|
||||
title: { display: true, text: "COCO mAP 50-95", color: "#808080" },
|
||||
grid: { color: "#e0e0e0" },
|
||||
ticks: { color: "#808080" },
|
||||
min: 36,
|
||||
max: 58,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updateChart = async (activeModels = []) => {
|
||||
chart?.destroy();
|
||||
chartWidget?.destroy();
|
||||
|
||||
chartConfig.data.datasets = Object.keys(data).map((algo, i) => createDataset(algo, i, activeModels));
|
||||
|
||||
chart = new Chart(document.getElementById("modelComparisonChart").getContext("2d"), chartConfig);
|
||||
|
||||
// Load widget and add to chart
|
||||
await loadChartWidget();
|
||||
if (window.ChartWidget) {
|
||||
chartWidget = new ChartWidget(chart, { position: "top-right" });
|
||||
}
|
||||
};
|
||||
|
||||
// Get active models from page config or use default
|
||||
// e.g. <canvas id="modelComparisonChart" width="1024" height="400" active-models='["YOLOv8", "YOLO11"]'></canvas>
|
||||
const initChart = () => {
|
||||
const activeModels = JSON.parse(
|
||||
document.getElementById("modelComparisonChart").getAttribute("active-models") || "[]",
|
||||
);
|
||||
updateChart(activeModels);
|
||||
};
|
||||
|
||||
document$.subscribe(() => {
|
||||
const init = () => (typeof Chart !== "undefined" ? initChart() : setTimeout(init, 50));
|
||||
init();
|
||||
});
|
||||
171
docs/overrides/javascript/chart-widget.js
Executable file
171
docs/overrides/javascript/chart-widget.js
Executable file
@@ -0,0 +1,171 @@
|
||||
// Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
//Reusable Chart.js toolbar widget with download functionality
|
||||
class ChartWidget {
|
||||
constructor(chart, options = {}) {
|
||||
this.chart = chart;
|
||||
this.options = { position: "top-right", ...options };
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const { canvas } = this.chart;
|
||||
const container = canvas.parentElement;
|
||||
container.style.position = "relative";
|
||||
|
||||
this.toolbar = document.createElement("div");
|
||||
this.toolbar.innerHTML = this.getHTML();
|
||||
container.appendChild(this.toolbar);
|
||||
|
||||
// Wait for chart to settle before positioning
|
||||
requestAnimationFrame(() => {
|
||||
this.toolbar.style.cssText = this.getCSS(canvas);
|
||||
this.attachEvents();
|
||||
this.setupHover(canvas);
|
||||
});
|
||||
}
|
||||
|
||||
getHTML() {
|
||||
return `
|
||||
<button data-action="png" data-tip="Download chart as PNG image">📷</button>
|
||||
<button data-action="csv" data-tip="Download chart data as CSV file">📊</button>
|
||||
<button data-action="ultralytics" data-tip="Made with Chart.js and Ultralytics">
|
||||
<img src="https://github.com/ultralytics/assets/raw/main/logo/Ultralytics-logomark-color.png" width="18" height="18">
|
||||
</button>
|
||||
<div class="tip" style="position:absolute;bottom:100%;left:50%;transform:translateX(-50%);background:CanvasText;color:Canvas;padding:4px 8px;border-radius:3px;font-size:11px;white-space:nowrap;display:none;margin-bottom:5px;z-index:1001;pointer-events:none;"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
getCSS(canvas) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const containerRect = canvas.parentElement.getBoundingClientRect();
|
||||
const top = rect.top - containerRect.top - 50;
|
||||
const left = rect.right - containerRect.left - 110;
|
||||
|
||||
return `
|
||||
position: absolute; top: ${top}px; left: ${left}px;
|
||||
background: Canvas; border: 1px solid rgba(128,128,128,0.3);
|
||||
border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
backdrop-filter: blur(8px); display: flex; gap: 2px; padding: 4px;
|
||||
opacity: 0; pointer-events: none; z-index: 1000;
|
||||
transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
|
||||
transform: translateY(-5px) scale(0.95);
|
||||
`;
|
||||
}
|
||||
|
||||
attachEvents() {
|
||||
const tip = this.toolbar.querySelector(".tip");
|
||||
|
||||
this.toolbar.addEventListener("click", (e) => {
|
||||
const { action } = e.target.closest("button").dataset;
|
||||
if (action === "png") {
|
||||
this.downloadPNG();
|
||||
}
|
||||
if (action === "csv") {
|
||||
this.downloadCSV();
|
||||
}
|
||||
if (action === "ultralytics") {
|
||||
window.open("https://ultralytics.com", "_blank");
|
||||
}
|
||||
});
|
||||
|
||||
this.toolbar.querySelectorAll("button").forEach((btn) => {
|
||||
Object.assign(btn.style, {
|
||||
border: "none",
|
||||
background: "none",
|
||||
padding: "8px",
|
||||
cursor: "pointer",
|
||||
borderRadius: "4px",
|
||||
fontSize: "18px",
|
||||
minWidth: "32px",
|
||||
minHeight: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
transition: "all 0.15s ease",
|
||||
position: "relative",
|
||||
});
|
||||
|
||||
btn.onmouseenter = () => {
|
||||
btn.style.background = "ButtonFace";
|
||||
btn.style.transform = "scale(1.20)";
|
||||
tip.textContent = btn.dataset.tip;
|
||||
tip.style.display = "block";
|
||||
};
|
||||
btn.onmouseleave = () => {
|
||||
btn.style.background = "none";
|
||||
btn.style.transform = "scale(1)";
|
||||
tip.style.display = "none";
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
setupHover(canvas) {
|
||||
let timeout;
|
||||
const show = () => {
|
||||
clearTimeout(timeout);
|
||||
Object.assign(this.toolbar.style, {
|
||||
opacity: "1",
|
||||
pointerEvents: "auto",
|
||||
transform: "translateY(0) scale(1)",
|
||||
});
|
||||
};
|
||||
const hide = () => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
Object.assign(this.toolbar.style, {
|
||||
opacity: "0",
|
||||
pointerEvents: "none",
|
||||
transform: "translateY(-5px) scale(0.95)",
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
canvas.addEventListener("mouseenter", show);
|
||||
canvas.addEventListener("mouseleave", hide);
|
||||
this.toolbar.addEventListener("mouseenter", show);
|
||||
this.toolbar.addEventListener("mouseleave", hide);
|
||||
}
|
||||
|
||||
downloadPNG() {
|
||||
const a = document.createElement("a");
|
||||
a.download = `chart-${Date.now()}.png`;
|
||||
a.href = this.chart.toBase64Image("image/png", 1);
|
||||
a.click();
|
||||
}
|
||||
|
||||
downloadCSV() {
|
||||
const xTitle = this.chart.options?.scales?.x?.title?.text || "x";
|
||||
const yTitle = this.chart.options?.scales?.y?.title?.text || "y";
|
||||
|
||||
const data = [];
|
||||
this.chart.data.datasets.forEach((dataset, i) => {
|
||||
if (this.chart.getDatasetMeta(i).hidden) {
|
||||
return;
|
||||
} // Skip unselected models
|
||||
|
||||
dataset.data.forEach((point) => {
|
||||
data.push({
|
||||
model: dataset.label,
|
||||
version: point.version || "",
|
||||
[xTitle]: point.x,
|
||||
[yTitle]: point.y,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const headers = Object.keys(data[0]);
|
||||
const csv = [headers.join(","), ...data.map((row) => headers.map((h) => `"${row[h] || ""}"`).join(","))].join("\n");
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.download = `chart-data-${Date.now()}.csv`;
|
||||
a.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
|
||||
a.click();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.toolbar?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
window.ChartWidget = ChartWidget;
|
||||
191
docs/overrides/javascript/extra.js
Executable file
191
docs/overrides/javascript/extra.js
Executable file
@@ -0,0 +1,191 @@
|
||||
// Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
// Block sitemap.xml fetches triggered by Weglot's hreflang tags detected by MkDocs Material
|
||||
(() => {
|
||||
const EMPTY_SITEMAP = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>`;
|
||||
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function (url, options) {
|
||||
if (typeof url === "string" && url.includes("/sitemap.xml")) {
|
||||
return Promise.resolve(
|
||||
new Response(EMPTY_SITEMAP, { status: 200, headers: { "Content-Type": "application/xml" } }),
|
||||
);
|
||||
}
|
||||
return originalFetch.apply(this, arguments);
|
||||
};
|
||||
|
||||
const originalXHROpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function (method, url) {
|
||||
if (typeof url === "string" && url.includes("/sitemap.xml")) {
|
||||
this._blockRequest = true;
|
||||
}
|
||||
return originalXHROpen.apply(this, arguments);
|
||||
};
|
||||
|
||||
const originalXHRSend = XMLHttpRequest.prototype.send;
|
||||
XMLHttpRequest.prototype.send = function () {
|
||||
if (this._blockRequest) {
|
||||
Object.defineProperty(this, "status", { value: 200 });
|
||||
Object.defineProperty(this, "responseText", { value: EMPTY_SITEMAP });
|
||||
Object.defineProperty(this, "response", { value: EMPTY_SITEMAP });
|
||||
Object.defineProperty(this, "responseXML", {
|
||||
value: new DOMParser().parseFromString(EMPTY_SITEMAP, "application/xml"),
|
||||
});
|
||||
this.dispatchEvent(new Event("load"));
|
||||
return;
|
||||
}
|
||||
return originalXHRSend.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
|
||||
// Apply theme colors based on dark/light mode
|
||||
const applyTheme = (isDark) => {
|
||||
document.body.setAttribute("data-md-color-scheme", isDark ? "slate" : "default");
|
||||
document.body.setAttribute("data-md-color-primary", isDark ? "black" : "indigo");
|
||||
};
|
||||
|
||||
// Sync widget theme with Material theme
|
||||
const syncWidgetTheme = () => {
|
||||
const isDark = document.body.getAttribute("data-md-color-scheme") === "slate";
|
||||
document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light");
|
||||
};
|
||||
|
||||
// Check and apply appropriate theme based on system/user preference
|
||||
const checkTheme = () => {
|
||||
const palette = JSON.parse(localStorage.getItem(".__palette") || "{}");
|
||||
if (palette.index === 0) {
|
||||
applyTheme(window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
syncWidgetTheme();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize theme handling on page load
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
checkTheme();
|
||||
syncWidgetTheme();
|
||||
|
||||
// Watch for system theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", checkTheme);
|
||||
|
||||
// Watch for theme toggle changes
|
||||
document.getElementById("__palette_1")?.addEventListener("change", (e) => {
|
||||
if (e.target.checked) setTimeout(checkTheme);
|
||||
});
|
||||
|
||||
// Watch for Material theme changes and sync to widget
|
||||
new MutationObserver(syncWidgetTheme).observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-md-color-scheme"],
|
||||
});
|
||||
});
|
||||
|
||||
// Ultralytics Chat Widget ---------------------------------------------------------------------------------------------
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const ultralyticsChat = new UltralyticsChat({
|
||||
welcome: {
|
||||
title: "Hello 👋",
|
||||
message: "Ask about YOLO, tutorials, training, export, deployment, or troubleshooting.",
|
||||
chatExamples: [
|
||||
"What's new in SAM 3?",
|
||||
"How can I get started with YOLO26?",
|
||||
"How does Enterprise Licensing work?",
|
||||
],
|
||||
searchExamples: [
|
||||
"YOLO26 quickstart",
|
||||
"custom dataset training",
|
||||
"model export formats",
|
||||
"object detection tutorial",
|
||||
"hyperparameter tuning",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const headerElement = document.querySelector(".md-header__inner");
|
||||
const searchContainer = headerElement?.querySelector(".md-header__source");
|
||||
|
||||
if (headerElement && searchContainer) {
|
||||
const searchBar = document.createElement("div");
|
||||
searchBar.className = "ult-header-search";
|
||||
const hotkey = /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? "⌘K" : "Ctrl+K";
|
||||
searchBar.innerHTML = `
|
||||
<button class="ult-search-button" title="Search documentation (${hotkey})" aria-label="Search documentation">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<span>Search</span>
|
||||
<span class="ult-search-hotkey" aria-hidden="true">${hotkey}</span>
|
||||
</button>
|
||||
`;
|
||||
headerElement.insertBefore(searchBar, searchContainer);
|
||||
|
||||
const defaultSearchToggle = headerElement.querySelector('label[for="__search"]');
|
||||
const defaultSearchInput = document.getElementById("__search");
|
||||
const defaultSearchDialog = document.querySelector(".md-search");
|
||||
if (defaultSearchToggle) {
|
||||
defaultSearchToggle.setAttribute("aria-hidden", "true");
|
||||
defaultSearchToggle.style.display = "none";
|
||||
}
|
||||
if (defaultSearchInput) {
|
||||
defaultSearchInput.setAttribute("tabindex", "-1");
|
||||
defaultSearchInput.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
if (defaultSearchDialog) defaultSearchDialog.style.display = "none";
|
||||
|
||||
searchBar.querySelector(".ult-search-button").addEventListener("click", () => {
|
||||
ultralyticsChat?.toggle(true, "search");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Fix language switcher links to preserve current page path, query string, and hash
|
||||
(() => {
|
||||
function fixLanguageLinks() {
|
||||
const path = location.pathname;
|
||||
const links = document.querySelectorAll(".md-select__link[hreflang]");
|
||||
if (!links.length) return;
|
||||
|
||||
// Derive language codes from the actual links (config-driven)
|
||||
const langCodes = Array.from(links)
|
||||
.map((link) => link.getAttribute("hreflang"))
|
||||
.filter(Boolean);
|
||||
const defaultLang =
|
||||
Array.from(links)
|
||||
.find((link) => link.getAttribute("href") === "/")
|
||||
?.getAttribute("hreflang") || "en";
|
||||
|
||||
// Extract base path (without leading slash and language prefix)
|
||||
let basePath = path.startsWith("/") ? path.slice(1) : path;
|
||||
for (const code of langCodes) {
|
||||
if (code === defaultLang) continue;
|
||||
const prefix = `${code}/`;
|
||||
if (basePath === code || basePath === prefix) {
|
||||
basePath = "";
|
||||
break;
|
||||
}
|
||||
if (basePath.startsWith(prefix)) {
|
||||
basePath = basePath.slice(prefix.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve query string and hash
|
||||
const suffix = location.search + location.hash;
|
||||
|
||||
// Update all language links
|
||||
links.forEach((link) => {
|
||||
const lang = link.getAttribute("hreflang");
|
||||
link.href =
|
||||
lang === defaultLang
|
||||
? `${location.origin}/${basePath}${suffix}`
|
||||
: `${location.origin}/${lang}/${basePath}${suffix}`;
|
||||
});
|
||||
}
|
||||
|
||||
// Run on load and navigation
|
||||
fixLanguageLinks();
|
||||
|
||||
if (typeof document$ !== "undefined") {
|
||||
document$.subscribe(() => setTimeout(fixLanguageLinks, 50));
|
||||
}
|
||||
})();
|
||||
84
docs/overrides/javascript/giscus.js
Executable file
84
docs/overrides/javascript/giscus.js
Executable file
@@ -0,0 +1,84 @@
|
||||
// Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
// Giscus functionality
|
||||
function loadGiscus() {
|
||||
const giscusContainer = document.getElementById("giscus-container");
|
||||
if (!giscusContainer || giscusContainer.querySelector("script")) {
|
||||
return;
|
||||
}
|
||||
|
||||
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", "");
|
||||
|
||||
giscusContainer.appendChild(script);
|
||||
|
||||
// Synchronize Giscus theme with palette
|
||||
const palette = __md_get("__palette");
|
||||
if (palette && typeof palette.color === "object") {
|
||||
const theme = palette.color.scheme === "slate" ? "dark" : "light";
|
||||
script.setAttribute("data-theme", theme);
|
||||
}
|
||||
|
||||
// Register event handlers for theme changes
|
||||
const ref = document.querySelector("[data-md-component=palette]");
|
||||
if (ref) {
|
||||
ref.addEventListener("change", () => {
|
||||
const palette = __md_get("__palette");
|
||||
if (palette && typeof palette.color === "object") {
|
||||
const theme = palette.color.scheme === "slate" ? "dark" : "light";
|
||||
|
||||
// Instruct Giscus to change theme
|
||||
const frame = document.querySelector(".giscus-frame");
|
||||
if (frame) {
|
||||
frame.contentWindow.postMessage({ giscus: { setConfig: { theme } } }, "https://giscus.app");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Use Intersection Observer to load Giscus when the container is visible
|
||||
function setupGiscusLoader() {
|
||||
const giscusContainer = document.getElementById("giscus-container");
|
||||
|
||||
if (giscusContainer) {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
loadGiscus();
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 },
|
||||
); // Trigger when 10% of the element is visible
|
||||
|
||||
observer.observe(giscusContainer);
|
||||
}
|
||||
}
|
||||
|
||||
// Hook into MkDocs' navigation system
|
||||
if (typeof document$ !== "undefined") {
|
||||
document$.subscribe(() => {
|
||||
// This function is called on every page load/change
|
||||
setupGiscusLoader();
|
||||
});
|
||||
} else {
|
||||
console.warn("MkDocs document$ not found. Falling back to DOMContentLoaded.");
|
||||
document.addEventListener("DOMContentLoaded", setupGiscusLoader);
|
||||
}
|
||||
76
docs/overrides/javascript/tablesort.js
Executable file
76
docs/overrides/javascript/tablesort.js
Executable file
@@ -0,0 +1,76 @@
|
||||
// Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
// tablesort.filesize.min.js
|
||||
!(() => {
|
||||
const filesizeRegex = /^(\d+(\.\d+)?) ?((K|M|G|T|P|E|Z|Y|B$)i?B?)$/i;
|
||||
|
||||
function r(t) {
|
||||
t = t.match(filesizeRegex);
|
||||
if (!t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const value = Number.parseFloat(t[1].replace(/[^\-?0-9.]/g, ""));
|
||||
const unit = t[3].toLowerCase();
|
||||
const base = unit[1] === "i" ? 1024 : 1e3;
|
||||
const powers = { k: 2, m: 3, g: 4, t: 5, p: 6, e: 7, z: 8, y: 9 };
|
||||
|
||||
return value * (powers[unit[0]] ? base ** powers[unit[0]] : base);
|
||||
}
|
||||
|
||||
Tablesort.extend(
|
||||
"filesize",
|
||||
(t) => filesizeRegex.test(t),
|
||||
(t, e) => {
|
||||
t = r(t);
|
||||
e = r(e);
|
||||
return (Number.isNaN(e) ? 0 : e) - (Number.isNaN(t) ? 0 : t);
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
// tablesort.dotsep.min.js
|
||||
Tablesort.extend(
|
||||
"dotsep",
|
||||
(t) => /^(\d+\.)+\d+$/.test(t),
|
||||
(t, r) => {
|
||||
t = t.split(".");
|
||||
r = r.split(".");
|
||||
|
||||
for (let i = 0, s = t.length; i < s; i++) {
|
||||
const e = Number.parseInt(t[i], 10);
|
||||
const n = Number.parseInt(r[i], 10);
|
||||
|
||||
if (e !== n) {
|
||||
return n < e ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
);
|
||||
|
||||
// tablesort.number.min.js
|
||||
(() => {
|
||||
const cleanNumber = (i) =>
|
||||
i
|
||||
.split("±")[0]
|
||||
.trim()
|
||||
.replace(/[^\-?0-9.]/g, "");
|
||||
const compareNumber = (a, b) => (Number.parseFloat(a) || 0) - (Number.parseFloat(b) || 0);
|
||||
|
||||
Tablesort.extend(
|
||||
"number",
|
||||
(item) =>
|
||||
item.match(/^[-+]?[£\x24Û¢´€]?\d+\s*([,.]\d{0,2})/) || // Prefixed currency
|
||||
item.match(/^[-+]?\d+\s*([,.]\d{0,2})?[£\x24Û¢´€]/) || // Suffixed currency
|
||||
item.match(/^[-+]?(\d)*-?([,.]){0,1}-?(\d)+([E,e][-+][\d]+)?%?$/), // Number
|
||||
(a, b) => compareNumber(cleanNumber(b), cleanNumber(a)),
|
||||
);
|
||||
})();
|
||||
|
||||
// subscribe
|
||||
document$.subscribe(() => {
|
||||
document.querySelectorAll("article table:not([class])").forEach((table) => {
|
||||
new Tablesort(table);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user