Files
cleveragents-core/docs/javascripts/toc-collapse.js
T
HAL9000 18d00c04c4
CI / lint (pull_request) Failing after 1m15s
CI / quality (pull_request) Successful in 1m21s
CI / typecheck (pull_request) Successful in 1m34s
CI / security (pull_request) Successful in 1m37s
CI / coverage (pull_request) Has been skipped
CI / unit_tests (pull_request) Failing after 1m37s
CI / docker (pull_request) Has been skipped
CI / build (pull_request) Successful in 33s
CI / helm (pull_request) Successful in 26s
CI / push-validation (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 3m20s
CI / integration_tests (pull_request) Successful in 4m32s
CI / status-check (pull_request) Failing after 3s
fix(skills): implement multi-scope agent skill discovery for global, project, and local tiers
Implements AgentSkillDiscovery class to support discovering Agent Skills from
multiple configured directories across three scopes (global, project, local).
Handles name collisions with precedence: local > project > global.

Adds comprehensive BDD test coverage for multi-scope discovery scenarios including:
- Global-only, project-only, and local-only discovery
- Combined discovery from all scopes
- Name collision resolution with proper precedence
- Non-existent and empty scope directory handling
- Multiple skills in same scope discovery

ISSUES CLOSED: #9369
2026-05-06 19:55:22 +00:00

275 lines
9.4 KiB
JavaScript

/**
* Collapsible Table of Contents for mkdocs-material with toc.integrate.
*
* Behaviour:
* - ALL nested TOC items start collapsed on page load.
* - Clicking a parent item toggles it and marks it as "user-pinned" so it
* stays in whatever state the user chose.
* - As the reader scrolls, the ancestors of the currently-active TOC link
* are automatically expanded so the highlighted item is always visible.
* - When the reader scrolls past a section, any items that were only
* auto-expanded (not user-pinned) collapse back down.
* - Works with mkdocs-material instant-loading / SPA navigation.
*
* Page navigation is handled by the top navbar tabs + dropdowns.
* This script only manages the TOC (md-nav--secondary) in the left sidebar.
*/
(function () {
"use strict";
/**
* Set of TOC <li> elements the user has manually expanded.
* These survive auto-collapse and are only cleared on page navigation.
*/
var userPinned = new Set();
/**
* Set of TOC <li> elements that were auto-expanded by the scroll tracker.
* Cleared and recalculated each time the active link changes.
*/
var autoExpanded = new Set();
/** Reference to the sidebar element (cached after first lookup). */
var sidebar = null;
// ── Helpers ──────────────────────────────────────────────────────────
/**
* Return all ancestor <li.md-nav__item--nested> elements between `el`
* and the TOC root (the <ul data-md-component="toc"> list).
*/
function ancestorItems(el) {
var ancestors = [];
var node = el ? el.parentElement : null;
while (node && node !== sidebar) {
if (
node.tagName === "LI" &&
node.classList.contains("md-nav__item--nested")
) {
ancestors.push(node);
}
node = node.parentElement;
}
return ancestors;
}
/**
* Collapse a nested item (add the CSS class that hides children).
*/
function collapse(li) {
li.classList.add("toc-collapsed");
}
/**
* Expand a nested item (remove the CSS class so children are visible).
*/
function expand(li) {
li.classList.remove("toc-collapsed");
}
// ── Shared: make nested items collapsible ────────────────────────────
//
// Scans `containerEl` for <li> items with nested <nav> children and
// turns them into collapsible items: adds the --nested class, collapses
// them, and injects a toggle chevron.
function makeItemsCollapsible(containerEl) {
containerEl.querySelectorAll("li.md-nav__item").forEach(function (li) {
var nestedNav = li.querySelector(":scope > nav.md-nav, :scope > .md-nav");
if (!nestedNav) return;
li.classList.add("md-nav__item--nested");
// Collapse everything on load
collapse(li);
// Inject a dedicated toggle button (chevron) into the link row.
// Clicking the chevron only toggles expand/collapse.
// Clicking the link text still navigates to the heading.
var link = li.querySelector(":scope > a.md-nav__link");
if (link && !link.querySelector(".toc-toggle")) {
var toggle = document.createElement("span");
toggle.className = "toc-toggle";
toggle.setAttribute("role", "button");
toggle.setAttribute("aria-label", "Toggle section");
link.appendChild(toggle);
toggle.addEventListener("click", function (e) {
// Stop the click from propagating to the <a> and navigating
e.preventDefault();
e.stopPropagation();
var isCollapsed = li.classList.contains("toc-collapsed");
if (isCollapsed) {
expand(li);
userPinned.add(li);
} else {
collapse(li);
userPinned.delete(li);
}
});
}
});
}
// ── Initialisation ──────────────────────────────────────────────────
function initCollapsibleToc() {
sidebar = document.querySelector(".md-sidebar--primary");
if (!sidebar) return;
// Reset pinned / auto-expanded tracking on (re-)init
userPinned.clear();
autoExpanded.clear();
// Force the TOC visible. Material's base CSS hides the secondary
// nav via a sibling-combinator rule keyed off the #__toc checkbox.
// We force-check it and set inline display so the TOC is guaranteed
// visible regardless of the CSS cascade.
var tocCheckbox = sidebar.querySelector("#__toc");
if (tocCheckbox) {
tocCheckbox.checked = true;
}
// Find the actual TOC (md-nav--secondary). We scope to the secondary
// nav to avoid touching the page-level nav items that wrap sections.
var tocNav = sidebar.querySelector("nav.md-nav--secondary");
if (!tocNav) return;
tocNav.style.display = "block";
tocNav.style.opacity = "1";
tocNav.style.visibility = "visible";
// Make all nested TOC items collapsible, with pinning support for
// the active-link watcher.
makeItemsCollapsible(tocNav);
// Kick off the active-link watcher
watchActiveLink();
}
// ── Active-link watcher ─────────────────────────────────────────────
//
// mkdocs-material dynamically toggles `.md-nav__link--active` on the
// TOC link that corresponds to the heading currently in view. We use
// a MutationObserver on the TOC container to detect when that class
// changes and react by expanding / collapsing as needed.
var observer = null;
function watchActiveLink() {
// Disconnect any previous observer (e.g. after SPA navigation)
if (observer) {
observer.disconnect();
observer = null;
}
var tocRoot = sidebar
? sidebar.querySelector("[data-md-component='toc']")
: null;
if (!tocRoot) return;
// Respond to the current active link immediately
handleActiveChange(tocRoot);
// Observe subtree attribute changes — the theme adds / removes
// the --active class on <a> elements as the user scrolls.
observer = new MutationObserver(function () {
handleActiveChange(tocRoot);
});
observer.observe(tocRoot, {
subtree: true,
attributes: true,
attributeFilter: ["class"],
});
}
/**
* Called whenever the active TOC link may have changed.
*
* 1. Find the currently-active <a>.
* 2. Determine which ancestor <li>s need to be expanded.
* 3. Auto-expand those ancestors (unless already open).
* 4. Auto-collapse any previously auto-expanded items that are no longer
* ancestors of the active link (but leave user-pinned items alone).
*/
function handleActiveChange(tocRoot) {
var activeLink = tocRoot.querySelector("a.md-nav__link--active");
// Determine which nested <li>s must be open for the active link
var requiredOpen = activeLink ? ancestorItems(activeLink) : [];
// Also include the active link's own <li> if it is a nested item
if (activeLink) {
var parentLi = activeLink.closest("li.md-nav__item--nested");
if (parentLi && requiredOpen.indexOf(parentLi) === -1) {
requiredOpen.push(parentLi);
}
}
var requiredSet = new Set(requiredOpen);
// Collapse items that were auto-expanded last round but are no longer
// needed — skip anything the user has pinned open.
autoExpanded.forEach(function (li) {
if (!requiredSet.has(li) && !userPinned.has(li)) {
collapse(li);
}
});
// Build the new auto-expanded set
autoExpanded = new Set();
requiredOpen.forEach(function (li) {
if (li.classList.contains("toc-collapsed")) {
expand(li);
}
// Track as auto-expanded only if not user-pinned
if (!userPinned.has(li)) {
autoExpanded.add(li);
}
});
}
// ── Desktop detection ────────────────────────────────────────────────
//
// This entire script is designed for the desktop persistent sidebar
// (≥76.25em). On mobile, Material uses a slide-out drawer with its
// own drill-down navigation, scroll management, and back-button
// behaviour. Running our DOM restructuring on mobile breaks the
// drawer's scroll container and makes it impossible to scroll past
// the visible items.
//
// We use matchMedia to detect the breakpoint and only bootstrap on
// desktop. On resize/orientation changes that cross the breakpoint,
// the page reloads via Material's SPA system anyway, so we don't need
// a live resize listener.
var desktopQuery = window.matchMedia("(min-width: 76.25em)");
function isDesktop() {
return desktopQuery.matches;
}
// ── Bootstrap ───────────────────────────────────────────────────────
function bootstrap() {
if (!isDesktop()) return; // skip all DOM manipulation on mobile
initCollapsibleToc();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", bootstrap);
} else {
bootstrap();
}
// Re-init after instant navigation (mkdocs-material SPA mode)
if (typeof document$ !== "undefined") {
document$.subscribe(function () {
bootstrap();
});
}
})();