diff --git a/docs/_ext/sphinx_fonts.py b/docs/_ext/sphinx_fonts.py deleted file mode 100644 index 7a7e1a2..0000000 --- a/docs/_ext/sphinx_fonts.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Sphinx extension for self-hosted fonts via Fontsource CDN. - -Downloads font files at build time, caches them locally, and passes -structured font data to the template context for inline @font-face CSS. -""" - -from __future__ import annotations - -import logging -import pathlib -import shutil -import typing as t -import urllib.error -import urllib.request - -if t.TYPE_CHECKING: - from sphinx.application import Sphinx - -logger = logging.getLogger(__name__) - -CDN_TEMPLATE = ( - "https://cdn.jsdelivr.net/npm/{package}@{version}" - "/files/{font_id}-{subset}-{weight}-{style}.woff2" -) - - -class SetupDict(t.TypedDict): - """Return type for Sphinx extension setup().""" - - version: str - parallel_read_safe: bool - parallel_write_safe: bool - - -def _cache_dir() -> pathlib.Path: - return pathlib.Path.home() / ".cache" / "sphinx-fonts" - - -def _cdn_url( - package: str, - version: str, - font_id: str, - subset: str, - weight: int, - style: str, -) -> str: - return CDN_TEMPLATE.format( - package=package, - version=version, - font_id=font_id, - subset=subset, - weight=weight, - style=style, - ) - - -# Unicode range descriptors per subset — tells the browser to only download -# the file when characters from this range appear on the page. Ranges are -# from Fontsource / Google Fonts CSS (CSS unicode-range values). -_UNICODE_RANGES: dict[str, str] = { - "latin": ( - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6," - " U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F," - " U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215," - " U+FEFF, U+FFFD" - ), - "latin-ext": ( - "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7," - " U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF," - " U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB," - " U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF" - ), - "cyrillic": ("U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116"), - "cyrillic-ext": ( - "U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F" - ), - "greek": ( - "U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF" - ), - "vietnamese": ( - "U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169," - " U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304," - " U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB" - ), -} - - -def _unicode_range(subset: str) -> str: - """Return the CSS ``unicode-range`` descriptor for *subset*. - - Falls back to an empty string for unknown subsets (omitting the - descriptor causes the browser to treat the face as covering all - codepoints, which is the correct fallback). - - Parameters - ---------- - subset : str - Fontsource subset name (e.g. ``"latin"``, ``"latin-ext"``). - - Returns - ------- - str - CSS ``unicode-range`` value, or ``""`` if unknown. - """ - return _UNICODE_RANGES.get(subset, "") - - -def _download_font(url: str, dest: pathlib.Path) -> bool: - if dest.exists(): - logger.debug("font cached: %s", dest.name) - return True - dest.parent.mkdir(parents=True, exist_ok=True) - try: - urllib.request.urlretrieve(url, dest) - logger.info("downloaded font: %s", dest.name) - except (urllib.error.URLError, OSError): - if dest.exists(): - dest.unlink() - logger.warning("failed to download font: %s", url) - return False - return True - - -def _on_builder_inited(app: Sphinx) -> None: - if app.builder.format != "html": - return - - fonts: list[dict[str, t.Any]] = app.config.sphinx_fonts - variables: dict[str, str] = app.config.sphinx_font_css_variables - if not fonts: - return - - cache = _cache_dir() - static_dir = pathlib.Path(app.outdir) / "_static" - fonts_dir = static_dir / "fonts" - fonts_dir.mkdir(parents=True, exist_ok=True) - - font_faces: list[dict[str, str]] = [] - for font in fonts: - font_id = font["package"].split("/")[-1] - version = font["version"] - package = font["package"] - # Accept "subsets" (list) or legacy "subset" (str). - subsets: list[str] = font.get("subsets", [font.get("subset", "latin")]) - for subset in subsets: - for weight in font["weights"]: - for style in font["styles"]: - filename = f"{font_id}-{subset}-{weight}-{style}.woff2" - cached = cache / filename - url = _cdn_url(package, version, font_id, subset, weight, style) - if _download_font(url, cached): - shutil.copy2(cached, fonts_dir / filename) - font_faces.append( - { - "family": font["family"], - "style": style, - "weight": str(weight), - "filename": filename, - "unicode_range": _unicode_range(subset), - } - ) - - preload_hrefs: list[str] = [] - preload_specs: list[tuple[str, int, str]] = app.config.sphinx_font_preload - for family_name, weight, style in preload_specs: - for font in fonts: - if font["family"] == family_name: - font_id = font["package"].split("/")[-1] - # Preload the first (primary) subset only — typically "latin". - subsets = font.get("subsets", [font.get("subset", "latin")]) - primary = subsets[0] if subsets else "latin" - filename = f"{font_id}-{primary}-{weight}-{style}.woff2" - preload_hrefs.append(filename) - break - - fallbacks: list[dict[str, str]] = app.config.sphinx_font_fallbacks - - app._font_preload_hrefs = preload_hrefs # type: ignore[attr-defined] - app._font_faces = font_faces # type: ignore[attr-defined] - app._font_fallbacks = fallbacks # type: ignore[attr-defined] - app._font_css_variables = variables # type: ignore[attr-defined] - - -def _on_html_page_context( - app: Sphinx, - pagename: str, - templatename: str, - context: dict[str, t.Any], - doctree: t.Any, -) -> None: - context["font_preload_hrefs"] = getattr(app, "_font_preload_hrefs", []) - context["font_faces"] = getattr(app, "_font_faces", []) - context["font_fallbacks"] = getattr(app, "_font_fallbacks", []) - context["font_css_variables"] = getattr(app, "_font_css_variables", {}) - - -def setup(app: Sphinx) -> SetupDict: - """Register config values, events, and return extension metadata.""" - app.add_config_value("sphinx_fonts", [], "html") - app.add_config_value("sphinx_font_fallbacks", [], "html") - app.add_config_value("sphinx_font_css_variables", {}, "html") - app.add_config_value("sphinx_font_preload", [], "html") - app.connect("builder-inited", _on_builder_inited) - app.connect("html-page-context", _on_html_page_context) - return { - "version": "1.0", - "parallel_read_safe": True, - "parallel_write_safe": True, - } diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css deleted file mode 100644 index 3a61646..0000000 --- a/docs/_static/css/custom.css +++ /dev/null @@ -1,618 +0,0 @@ -.sidebar-tree p.indented-block { - padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0 - var(--sidebar-item-spacing-horizontal); - margin-bottom: 0; -} - -.sidebar-tree p.indented-block span.indent { - margin-left: var(--sidebar-item-spacing-horizontal); - display: block; -} - -.sidebar-tree p.indented-block .project-name { - font-size: var(--sidebar-item-font-size); - font-weight: bold; - margin-right: calc(var(--sidebar-item-spacing-horizontal) / 2.5); -} - -#sidebar-projects:not(.ready) { - visibility: hidden; -} - -.sidebar-tree .active { - font-weight: bold; -} - - -/* ── Global heading refinements ───────────────────────────── - * Biome-inspired scale: medium weight (500) throughout — size - * and spacing carry hierarchy, not boldness. H4-H6 add eyebrow - * treatment (uppercase, muted). `article` prefix overrides - * Furo's bare h1-h6 selectors. - * ────────────────────────────────────────────────────────── */ -article h1 { - font-size: 1.8em; - font-weight: 500; - margin-top: 1.5rem; - margin-bottom: 0.75rem; -} - -article h2 { - font-size: 1.6em; - font-weight: 500; - margin-top: 2.5rem; - margin-bottom: 0.5rem; -} - -article h3 { - font-size: 1.15em; - font-weight: 500; - margin-top: 1.5rem; - margin-bottom: 0.375rem; -} - -article h4 { - font-size: 0.85em; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-foreground-secondary); - margin-top: 1rem; - margin-bottom: 0.25rem; -} - -article h5 { - font-size: 0.8em; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-foreground-secondary); -} - -article h6 { - font-size: 0.75em; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-foreground-secondary); -} - -/* ── Changelog heading extras ─────────────────────────────── - * Vertical spacing separates consecutive version entries. - * Category headings (h3) are muted. Item headings (h4) are - * subtle. Targets #history section from CHANGES markdown. - * ────────────────────────────────────────────────────────── */ - -/* Spacing between consecutive version entries */ -#history > section + section { - margin-top: 2.5rem; -} - -/* Category headings — muted secondary color */ -#history h3 { - color: var(--color-foreground-secondary); - margin-top: 1.25rem; -} - -/* Item headings — subtle, same size as body */ -#history h4 { - font-size: 1em; - margin-top: 1rem; - text-transform: none; - letter-spacing: normal; - color: inherit; -} - -/* ── Right-panel TOC refinements ──────────────────────────── - * Adjust Furo's table-of-contents proportions for better - * readability. Inspired by Starlight defaults (Biome docs). - * Uses Furo CSS variable overrides where possible. - * ────────────────────────────────────────────────────────── */ - -/* TOC font sizes: override Furo defaults (75% → 87.5%) */ -:root { - --toc-font-size: var(--font-size--small); /* 87.5% = 14px */ - --toc-title-font-size: var(--font-size--small); /* 87.5% = 14px */ -} - -/* More generous line-height for wrapped TOC entries */ -.toc-tree { - line-height: 1.4; -} - -/* ── Flexible right-panel TOC (inner-panel padding) ───────── - * Furo hardcodes .toc-drawer to width: 15em (SASS, compiled). - * min-width: 18em overrides it; long TOC entries wrap inside - * the box instead of blowing past the viewport. - * - * Padding lives on .toc-sticky (the inner panel), not on - * .toc-drawer (the outer aside). This matches Biome/Starlight - * where the aside defines dimensions and an inner wrapper - * (.right-sidebar-panel) controls content insets. The - * scrollbar sits naturally between content and viewport edge. - * - * Content area gets flex: 1 to absorb extra space on wide - * screens. At ≤82em Furo collapses the TOC to position: fixed; - * override right offset so the drawer fully hides off-screen. - * ────────────────────────────────────────────────────────── */ -.toc-drawer { - min-width: 18em; - flex-shrink: 0; - padding-right: 0; -} - -.toc-sticky { - padding-right: 1.5em; -} - -.content { - width: auto; - max-width: 46em; - flex: 1 1 46em; - padding: 0 2em; -} - -@media (max-width: 82em) { - .toc-drawer { - right: -18em; - } -} - -/* ── Body typography refinements ──────────────────────────── - * Improve paragraph readability with wider line-height and - * sharper text rendering. Furo already sets font-smoothing. - * - * IBM Plex tracks slightly wide at default spacing; -0.01em - * tightens it to feel more natural (matches tony.sh/tony.nl). - * Kerning + ligatures polish AV/To pairs and fi/fl combos. - * ────────────────────────────────────────────────────────── */ -body { - text-rendering: optimizeLegibility; - font-kerning: normal; - font-variant-ligatures: common-ligatures; - letter-spacing: -0.01em; -} - -/* ── Code block text rendering ──────────────────────────── - * Monospace needs fixed-width columns: disable kerning, - * ligatures, and letter-spacing that body sets for prose. - * optimizeSpeed skips heuristics that can shift the grid. - * ────────────────────────────────────────────────────────── */ -pre, -code, -kbd, -samp { - text-rendering: optimizeSpeed; - font-kerning: none; - font-variant-ligatures: none; - letter-spacing: normal; -} - -article { - line-height: 1.6; -} - -/* ── Prompt blocks (user → LLM) ─────────────────────────── - * Styled admonition for copy-paste prompts. Chat-bubble - * aesthetic with speech icon. Says "type this into your LLM." - * ────────────────────────────────────────────────────────── */ -div.admonition.prompt { - border: 1px solid color-mix(in srgb, var(--color-link) 25%, transparent); - border-left: 3px solid var(--color-link); - background: color-mix(in srgb, var(--color-link) 4%, var(--color-background-primary)); - border-radius: 6px; - padding: 0.75rem 1rem 0.75rem 2.2rem; - margin: 1.25rem 0; - box-shadow: none; - position: relative; -} - -/* Speech bubble icon */ -div.admonition.prompt::before { - content: "\1F4AC"; - position: absolute; - left: 0.65rem; - top: 0.7rem; - font-size: 0.85rem; - line-height: 1; - opacity: 0.45; -} - -div.admonition.prompt > .admonition-title { - display: none; -} - -div.admonition.prompt > p { - font-size: 0.95rem; - font-style: italic; - color: var(--color-foreground-primary); -} - -div.admonition.prompt > p:last-of-type { - margin-bottom: 0; -} - -/* Copy button — inserted afterend of p:last-child, so it - * lands inside the prompt div. position:relative on the - * prompt div provides the positioning context. */ -div.admonition.prompt > button.copybtn { - background: transparent !important; - cursor: pointer; - border: none !important; -} - -div.admonition.prompt:hover > button.copybtn, -div.admonition.prompt > button.copybtn.success { - opacity: 1; -} - -div.admonition.prompt > p:last-child { - margin-bottom: 0; -} - -/* ── Agent reasoning blocks ────────────────────────────── - * Styled admonition for agent chain-of-thought. Neutral - * gray bar + italic + muted opacity = "internal narration, - * not something you do." - * ────────────────────────────────────────────────────────── */ -div.admonition.agent-thought { - border: none; - border-left: 3px solid var(--color-foreground-border); - background: transparent; - padding: 0.6rem 1rem; - margin: 1rem 0; - box-shadow: none; -} - -div.admonition.agent-thought > .admonition-title { - display: none; -} - -div.admonition.agent-thought > p { - font-style: italic; - color: var(--color-foreground-secondary); - font-size: 0.9rem; -} - -div.admonition.agent-thought > p:last-child { - margin-bottom: 0; -} - -/* ── Inline prompt dialog ──────────────────────────────── - * Inline styled span for user-to-LLM prompts. Supports - * full nested markup via MyST attrs_inline extension: - * [Run `pytest` in my build pane]{.prompt} - * No line-height or word-wrap disruption. WCAG AA contrast. - * ────────────────────────────────────────────────────────── */ -span.prompt { - font-style: italic; - color: var(--color-foreground-primary); -} - -span.prompt::before { - content: "\201C"; - color: var(--color-foreground-muted); -} - -span.prompt::after { - content: "\201D"; - color: var(--color-foreground-muted); -} - -/* ── Labeled code panels ───────────────────────────────── - * Copyable prose in labeled dark panels. Two variants: - * .system-prompt — user-authored fragments for AGENTS.md - * .server-prompt — libtmux-mcp's built-in instructions - * Keeps sphinx-copybutton via .highlight > pre selector. - * ────────────────────────────────────────────────────────── */ -div.system-prompt, -div.server-prompt { - margin: 1.25rem 0; - position: relative; -} - -div.system-prompt > div.highlight, -div.server-prompt > div.highlight { - background: #1f2329 !important; - border: 1px solid color-mix(in srgb, var(--color-link) 20%, transparent); - border-left: 3px solid var(--color-link); - border-radius: 6px; - position: relative; - padding-top: 1.3rem; -} - -div.system-prompt > div.highlight > pre, -div.server-prompt > div.highlight > pre { - background: transparent; - font-size: 13px; - line-height: 1.6; - white-space: pre-wrap; - word-break: break-word; -} - -/* Internal label — quiet uppercase whisper */ -div.system-prompt > div.highlight::before, -div.server-prompt > div.highlight::before { - position: absolute; - top: 0.45rem; - left: 0.85rem; - font-family: var(--font-stack); - font-size: 0.55rem; - font-weight: 500; - letter-spacing: 0.05em; - text-transform: uppercase; - color: #9590b8; - background: transparent; - padding: 0; - line-height: 1; - opacity: 0.8; -} - -div.system-prompt > div.highlight::before { - content: "System prompt"; -} - -div.server-prompt > div.highlight::before { - content: "Built-in server prompt"; -} - -/* Fix copy button background to match panel */ -div.system-prompt .copybtn, -div.server-prompt .copybtn { - background: #1f2329 !important; -} - -/* ── Image layout shift prevention ──────────────────────── - * Reserve space for images before they load. Furo already - * sets max-width: 100%; height: auto on img. We add - * content-visibility and badge-specific height to prevent CLS. - * ────────────────────────────────────────────────────────── */ - - -img { - content-visibility: auto; -} - -/* Docutils emits :width:/:height: as inline CSS (style="width: Xpx; - * height: Ypx;") rather than HTML attributes. When Furo's - * max-width: 100% constrains width below the declared value, - * the fixed height causes distortion. height: auto lets the - * browser recompute height proportionally once the image loads. - * Note: this does not reserve space before load — images without - * explicit HTML width/height attributes will cause layout shift. */ -article img[loading="lazy"] { - height: auto !important; -} - -img[src*="shields.io"], -img[src*="badge.svg"], -img[src*="codecov.io"] { - height: 20px; - width: auto; - min-width: 60px; - border-radius: 3px; - background: var(--color-background-secondary); -} - -/* ── View Transitions (SPA navigation) ──────────────────── - * Crossfade between pages during SPA navigation. - * Browsers without View Transitions API get instant swap. - * Respects prefers-reduced-motion for accessibility. - * ────────────────────────────────────────────────────────── */ -@media (prefers-reduced-motion: no-preference) { - ::view-transition-old(root), - ::view-transition-new(root) { - animation-duration: 150ms; - } -} - -/* ── MCP Tool safety badges ────────────────────────────── - * Small semantic tags — secondary to the tool name, quietly - * color-coded. Target: GitHub-label density, not app-store pills. - * - * Dot icon via ::before circle. No emoji. - * Sidebar/TOC compresses badges to colored dots only. - * ────────────────────────────────────────────────────────── */ - -/* ── Heading flex alignment ────────────────────────────── - * Makes headings containing badges into flex rows so the - * badge vertically centers against the heading cap-height - * instead of fighting baseline math. - * ────────────────────────────────────────────────────────── */ -h2:has(> .sd-badge), -h3:has(> .sd-badge), -h4:has(> .sd-badge) { - display: inline-flex; - align-items: center; - gap: 0.45rem; -} - -/* ── Base badge ─────────────────────────────────────────── */ -.sd-badge { - display: inline-flex !important; - align-items: center; - vertical-align: middle; - gap: 0.28rem; - font-size: 0.67rem; - font-weight: 650; - line-height: 1; - letter-spacing: 0.01em; - padding: 0.16rem 0.38rem; - border-radius: 0.22rem; - user-select: none; - -webkit-user-select: none; -} - -/* ── Matte color palette ────────────────────────────────── */ -.sd-badge.sd-bg-success { - background-color: #1f7a3f !important; - color: #f3fff7 !important; - border: 1px solid #2a8d4d; -} - -.sd-badge.sd-bg-warning { - background-color: #b96a1a !important; - color: #fff8ef !important; - border: 1px solid #cf7a23; -} - -.sd-badge.sd-bg-danger { - background-color: #b4232c !important; - color: #fff5f5 !important; - border: 1px solid #cb3640; -} - -/* ── Monochrome icons via VS15 (text presentation) ────── - * \FE0E (VS15) tells the OS to render the text/outline - * variant instead of the color bitmap. The glyph inherits - * currentColor — no opacity hack, no grayscale filter. - * - * Picks: geometrically simple glyphs with strong text-style - * variants. ⚡ replaces 💥 (too much detail at small sizes). - * ────────────────────────────────────────────────────────── */ -.sd-badge::before { - font-style: normal; - font-weight: normal; - font-size: 1em; - line-height: 1; - flex-shrink: 0; -} - -.sd-badge.sd-bg-success::before { content: "🔍"; } /* readonly */ -.sd-badge.sd-bg-warning::before { content: "✏️"; } /* mutating */ -.sd-badge.sd-bg-danger::before { content: "💣"; } /* destructive */ - -/* ── Icon-only badge ({toolicon*} roles) ───────────────── - * Outside variants (l/r): colored square box with emoji. - * Inside variants (il/ir): bare emoji, no box — blends - * into the code chip as a seamless annotation. - * ────────────────────────────────────────────────────────── */ - -/* Outside icon links: flexbox collapses whitespace nodes */ -a.reference:has(> .sd-badge.icon-only) { - display: inline-flex; - align-items: center; - gap: 3px; -} - -a.reference:has(> .sd-badge.icon-only) > code { - margin: 0; -} - -/* Outside variants: colored square box */ -.sd-badge.icon-only { - display: inline-flex !important; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - padding: 0; - box-sizing: border-box; - border-radius: 3px; - gap: 0; - font-size: 0; - line-height: 1; - min-width: 0; - min-height: 0; - margin: 0; -} - -.sd-badge.icon-only::before { - font-size: 10px; - line-height: 1; - font-style: normal; - font-weight: normal; - margin: 0; - display: block; - opacity: 0.9; -} - -/* Inline variants (inside ): bare emoji, no box */ -.sd-badge.icon-only-inline { - background: transparent !important; - border: none !important; - padding: 0; - width: auto; - height: auto; - border-radius: 0; - vertical-align: -0.01em; - margin-right: 0.12em; - margin-left: 0; -} - -.sd-badge.icon-only-inline::before { - font-size: 0.78rem; - opacity: 0.85; -} - -/* Inline-right: tighter than inline-left */ -code.docutils .sd-badge.icon-only-inline:last-child { - margin-left: 0.1em; - margin-right: 0; -} - -/* ── Context-aware badge sizing ─────────────────────────── */ -h2 .sd-badge, -h3 .sd-badge { - font-size: 0.68rem; - padding: 0.17rem 0.4rem; -} - -p .sd-badge, -li .sd-badge, -td .sd-badge, -a .sd-badge { - font-size: 0.62rem; - padding: 0.12rem 0.32rem; -} - -/* ── Consistent code → badge spacing ────────────────────── */ -code.docutils + .sd-badge, -.sd-badge + code.docutils { - margin-left: 0.4em; -} - -/* ── Link behavior: underline code only, on hover ───────── */ -a.reference .sd-badge { - text-decoration: none; - vertical-align: middle; -} - -a.reference:has(.sd-badge) code { - vertical-align: middle; -} - -a.reference:has(.sd-badge) { - text-decoration: none; -} - -a.reference:has(.sd-badge) code { - text-decoration: none; -} - -a.reference:has(.sd-badge):hover code { - text-decoration: underline; -} - -/* ── Sidebar TOC: compress badges to colored dots ───────── */ -.toc-tree .sd-badge { - color: transparent !important; - padding: 0; - width: 0.5rem; - height: 0.5rem; - border-radius: 999px; - overflow: hidden; - font-size: 0; - gap: 0; - border: none; -} - -.toc-tree .sd-badge::before { - content: ""; - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; - background: currentColor; - opacity: 1; - font-size: 0; -} diff --git a/docs/_static/js/spa-nav.js b/docs/_static/js/spa-nav.js deleted file mode 100644 index c0d4b46..0000000 --- a/docs/_static/js/spa-nav.js +++ /dev/null @@ -1,283 +0,0 @@ -/** - * SPA-like navigation for Sphinx/Furo docs. - * - * Intercepts internal link clicks and swaps only the content that changes - * (article, sidebar nav tree, TOC drawer), preserving sidebar scroll - * position, theme state, and avoiding full-page reloads. - * - * Progressive enhancement: no-op when fetch/DOMParser/pushState unavailable. - */ -(function () { - "use strict"; - - if (!window.fetch || !window.DOMParser || !window.history?.pushState) return; - - // --- Theme toggle (replicates Furo's cycleThemeOnce) --- - - function cycleTheme() { - var current = localStorage.getItem("theme") || "auto"; - var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; - var next; - if (current === "auto") next = prefersDark ? "light" : "dark"; - else if (current === "dark") next = prefersDark ? "auto" : "light"; - else next = prefersDark ? "dark" : "auto"; - document.body.dataset.theme = next; - localStorage.setItem("theme", next); - } - - // --- Copy button injection --- - // - // Matches sphinx-copybutton's button HTML and selector so buttons work - // identically after SPA navigation. Uses an inline template instead of - // cloning an existing button — the initial page may have no code blocks - // (e.g. index.html), leaving nothing to clone. - - var iconCopy = - '' + - "Copy to clipboard" + - '' + - '' + - '' + - ""; - - function makeCopyButton(targetId) { - return ( - '" - ); - } - - function addCopyButtons() { - // Code blocks - var cells = document.querySelectorAll("div.highlight pre"); - cells.forEach(function (cell, i) { - var id = "codecell" + i; - cell.id = id; - var next = cell.nextElementSibling; - if (next && next.classList.contains("copybtn")) { - next.setAttribute("data-clipboard-target", "#" + id); - } else { - cell.insertAdjacentHTML("afterend", makeCopyButton(id)); - } - }); - // Prompt blocks — match sphinx-copybutton's copybutton_selector - var prompts = document.querySelectorAll( - "div.admonition.prompt > p:last-child" - ); - prompts.forEach(function (p, i) { - var id = "promptcell" + i; - p.id = id; - var next = p.nextElementSibling; - if (next && next.classList.contains("copybtn")) { - next.setAttribute("data-clipboard-target", "#" + id); - } else { - p.insertAdjacentHTML("afterend", makeCopyButton(id)); - } - }); - } - - // --- Minimal scrollspy --- - - var scrollCleanup = null; - - function initScrollSpy() { - if (scrollCleanup) scrollCleanup(); - scrollCleanup = null; - - var links = document.querySelectorAll(".toc-tree a"); - if (!links.length) return; - - var entries = []; - links.forEach(function (a) { - var id = (a.getAttribute("href") || "").split("#")[1]; - var el = id && document.getElementById(id); - var li = a.closest("li"); - if (el && li) entries.push({ el: el, li: li }); - }); - if (!entries.length) return; - - function update() { - var offset = - parseFloat(getComputedStyle(document.documentElement).fontSize) * 4; - var active = null; - for (var i = entries.length - 1; i >= 0; i--) { - if (entries[i].el.getBoundingClientRect().top <= offset) { - active = entries[i]; - break; - } - } - entries.forEach(function (e) { - e.li.classList.remove("scroll-current"); - }); - if (active) active.li.classList.add("scroll-current"); - } - - window.addEventListener("scroll", update, { passive: true }); - update(); - scrollCleanup = function () { - window.removeEventListener("scroll", update); - }; - } - - // --- Link interception --- - - function shouldIntercept(link, e) { - if (e.defaultPrevented || e.button !== 0) return false; - if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return false; - if (link.origin !== location.origin) return false; - if (link.target && link.target !== "_self") return false; - if (link.hasAttribute("download")) return false; - - var path = link.pathname; - if (!path.endsWith(".html") && !path.endsWith("/")) return false; - - var base = path.split("/").pop() || ""; - if ( - base === "search.html" || - base === "genindex.html" || - base === "py-modindex.html" - ) - return false; - - if (link.closest("#sidebar-projects")) return false; - if (link.pathname === location.pathname && link.hash) return false; - - return true; - } - - // --- DOM swap --- - - function swap(doc) { - [".article-container", ".sidebar-tree", ".toc-drawer"].forEach( - function (sel) { - var fresh = doc.querySelector(sel); - var stale = document.querySelector(sel); - if (fresh && stale) stale.replaceWith(fresh); - }, - ); - var title = doc.querySelector("title"); - if (title) document.title = title.textContent || ""; - - // Brand links and logo images live outside swapped regions. - // Their relative hrefs/srcs go stale after cross-depth navigation. - // Copy the correct values from the fetched document. - [".sidebar-brand", ".header-center a"].forEach(function (sel) { - var fresh = doc.querySelector(sel); - if (!fresh) return; - document.querySelectorAll(sel).forEach(function (el) { - el.setAttribute("href", fresh.getAttribute("href")); - }); - }); - var freshLogos = doc.querySelectorAll(".sidebar-logo"); - var staleLogos = document.querySelectorAll(".sidebar-logo"); - freshLogos.forEach(function (fresh, i) { - if (staleLogos[i]) { - staleLogos[i].setAttribute("src", fresh.getAttribute("src")); - } - }); - } - - function reinit() { - addCopyButtons(); - initScrollSpy(); - var btn = document.querySelector(".content-icon-container .theme-toggle"); - if (btn) btn.addEventListener("click", cycleTheme); - } - - // --- Navigation --- - - var currentCtrl = null; - - async function navigate(url, isPop) { - if (currentCtrl) currentCtrl.abort(); - var ctrl = new AbortController(); - currentCtrl = ctrl; - - try { - var resp = await fetch(url, { signal: ctrl.signal }); - if (!resp.ok) throw new Error(resp.status); - - var html = await resp.text(); - var doc = new DOMParser().parseFromString(html, "text/html"); - - if (!doc.querySelector(".article-container")) - throw new Error("no article"); - - var applySwap = function () { - swap(doc); - - if (!isPop) history.pushState({ spa: true }, "", url); - - if (!isPop) { - var hash = new URL(url, location.href).hash; - if (hash) { - var el = document.querySelector(hash); - if (el) el.scrollIntoView(); - } else { - window.scrollTo(0, 0); - } - } - - reinit(); - }; - - if (document.startViewTransition) { - document.startViewTransition(applySwap); - } else { - applySwap(); - } - } catch (err) { - if (err.name === "AbortError") return; - window.location.href = url; - } finally { - if (currentCtrl === ctrl) currentCtrl = null; - } - } - - // --- Events --- - - document.addEventListener("click", function (e) { - var link = e.target.closest("a[href]"); - if (link && shouldIntercept(link, e)) { - e.preventDefault(); - navigate(link.href, false); - } - }); - - history.replaceState({ spa: true }, ""); - - window.addEventListener("popstate", function () { - navigate(location.href, true); - }); - - // --- Hover prefetch --- - - var prefetchTimer = null; - - document.addEventListener("mouseover", function (e) { - var link = e.target.closest("a[href]"); - if (!link || link.origin !== location.origin) return; - if (!link.pathname.endsWith(".html") && !link.pathname.endsWith("/")) - return; - - clearTimeout(prefetchTimer); - prefetchTimer = setTimeout(function () { - fetch(link.href, { priority: "low" }).catch(function () {}); - }, 65); - }); - - document.addEventListener("mouseout", function (e) { - if (e.target.closest("a[href]")) clearTimeout(prefetchTimer); - }); - - // --- Init --- - - // No DOMContentLoaded setup needed for copy buttons — addCopyButtons() - // creates buttons from an inline template, independent of the initial page. -})(); diff --git a/docs/_templates/page.html b/docs/_templates/page.html deleted file mode 100644 index 312bad2..0000000 --- a/docs/_templates/page.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends "!page.html" %} -{%- block extrahead %} - {{ super() }} - {%- for href in font_preload_hrefs|default([]) %} - - {%- endfor %} - {%- if font_faces is defined and font_faces %} - - {%- endif %} - {%- if theme_show_meta_manifest_tag == true %} - - {% endif -%} - {%- if theme_show_meta_og_tags == true %} - - - - - - - - - - - - - - - - {% endif -%} - {%- if theme_show_meta_app_icon_tags == true %} - - - - - - - - - - - - - - - - - - - - {% endif -%} -{% endblock %} diff --git a/docs/_templates/sidebar/brand.html b/docs/_templates/sidebar/brand.html deleted file mode 100644 index 7fe241c..0000000 --- a/docs/_templates/sidebar/brand.html +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/docs/_templates/sidebar/projects.html b/docs/_templates/sidebar/projects.html deleted file mode 100644 index f70e6fe..0000000 --- a/docs/_templates/sidebar/projects.html +++ /dev/null @@ -1,84 +0,0 @@ - - diff --git a/docs/conf.py b/docs/conf.py index 796851f..32b63d0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,14 +1,12 @@ -# flake8: NOQA: E501 """Sphinx configuration for libtmux-mcp.""" from __future__ import annotations -import contextlib -import inspect import pathlib import sys import typing as t -from os.path import relpath + +from gp_sphinx.config import make_linkcode_resolve, merge_sphinx_config import libtmux_mcp @@ -28,268 +26,49 @@ with (project_src / "libtmux_mcp" / "__about__.py").open() as fp: exec(fp.read(), about) -extensions = [ - "sphinx.ext.autodoc", - "sphinx_fonts", - "sphinx.ext.intersphinx", - "sphinx_autodoc_typehints", - "sphinx.ext.todo", - "sphinx.ext.linkcode", - "sphinx.ext.napoleon", - "sphinx_inline_tabs", - "sphinx_copybutton", - "sphinxext.opengraph", - "sphinxext.rediraffe", - "sphinx_design", - "myst_parser", - "linkify_issues", - "fastmcp_autodoc", -] - -myst_heading_anchors = 4 - -myst_enable_extensions = [ - "colon_fence", - "substitution", - "replacements", - "strikethrough", - "linkify", - "attrs_inline", -] - -templates_path = ["_templates"] - -source_suffix = {".rst": "restructuredtext", ".md": "markdown"} - -master_doc = "index" - -project = about["__title__"] -project_copyright = about["__copyright__"] - -version = "{}".format(".".join(about["__version__"].split(".")[:2])) -release = "{}".format(about["__version__"]) - -exclude_patterns = ["_build"] - -pygments_style = "monokai" -pygments_dark_style = "monokai" - -html_favicon = "_static/favicon.ico" -html_static_path = ["_static"] -html_css_files = ["css/custom.css"] -html_extra_path = ["manifest.json"] -html_theme = "furo" -html_theme_path: list[str] = [] -html_theme_options: dict[str, str | list[dict[str, str]]] = { - "light_logo": "img/libtmux.svg", - "dark_logo": "img/libtmux.svg", - "footer_icons": [ - { - "name": "GitHub", - "url": about["__repository__"], - "html": """ - - - - """, - "class": "", - }, - ], - "source_repository": f"{about['__repository__']}/", - "source_branch": "main", - "source_directory": "docs/", - "announcement": "Pre-alpha. APIs may change. Feedback welcome.", -} -html_sidebars = { - "**": [ - "sidebar/scroll-start.html", - "sidebar/brand.html", - "sidebar/search.html", - "sidebar/navigation.html", - "sidebar/projects.html", - "sidebar/scroll-end.html", - ], -} - -# linkify_issues -issue_url_tpl = f"{about['__repository__']}/issues/{{issue_id}}" - -# sphinx.ext.autodoc -autoclass_content = "both" -autodoc_member_order = "bysource" -# Automatically extract typehints when specified and place them in -# descriptions of the relevant function/method. -autodoc_typehints = "description" -# Don't show class signature with the class' name. -autodoc_class_signature = "separated" -toc_object_entries_show_parents = "hide" - -# sphinx-autodoc-typehints -# Suppress warnings for forward references that can't be resolved -# (types in TYPE_CHECKING blocks used for circular import avoidance) -suppress_warnings = [ - "sphinx_autodoc_typehints.forward_reference", -] - -# sphinx-copybutton -copybutton_prompt_text = ( - r">>> |\.\.\. |> |\$ |\# | In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " -) -copybutton_prompt_is_regexp = True -copybutton_remove_prompts = True -copybutton_line_continuation_character = "\\" -copybutton_selector = "div.highlight pre, div.admonition.prompt > p:last-child" -copybutton_exclude = ".linenos, .admonition-title" - -# sphinxext-rediraffe -rediraffe_redirects = "redirects.txt" -rediraffe_branch = "main~1" - -# sphinxext.opengraph -ogp_site_url = about["__url__"] -ogp_image = "_static/img/icons/icon-192x192.png" -ogp_site_name = about["__title__"] - -# sphinx_fonts — self-hosted IBM Plex via Fontsource CDN -sphinx_fonts = [ - { - "family": "IBM Plex Sans", - "package": "@fontsource/ibm-plex-sans", - "version": "5.2.8", - "weights": [400, 500, 600, 700], - "styles": ["normal", "italic"], - "subsets": ["latin", "latin-ext"], - }, - { - "family": "IBM Plex Mono", - "package": "@fontsource/ibm-plex-mono", - "version": "5.2.7", - "weights": [400, 500, 600, 700], - "styles": ["normal", "italic"], - "subsets": ["latin", "latin-ext"], - }, -] - -sphinx_font_preload = [ - ("IBM Plex Sans", 400, "normal"), # body text - ("IBM Plex Sans", 700, "normal"), # headings - ("IBM Plex Mono", 400, "normal"), # code blocks -] - -sphinx_font_fallbacks = [ - { - "family": "IBM Plex Sans Fallback", - "src": 'local("Arial"), local("Helvetica Neue"), local("Helvetica")', - "size_adjust": "110.6%", - "ascent_override": "92.7%", - "descent_override": "24.9%", - "line_gap_override": "0%", - }, - { - "family": "IBM Plex Mono Fallback", - "src": 'local("Courier New"), local("Courier")', - "size_adjust": "100%", - "ascent_override": "102.5%", - "descent_override": "27.5%", - "line_gap_override": "0%", +conf = merge_sphinx_config( + project=about["__title__"], + version=about["__version__"], + copyright=about["__copyright__"], + source_repository=f"{about['__repository__']}/", + docs_url=about["__url__"], + source_branch="main", + light_logo="img/libtmux.svg", + dark_logo="img/libtmux.svg", + extra_extensions=["sphinx.ext.todo", "fastmcp_autodoc"], + intersphinx_mapping={ + "python": ("https://docs.python.org/", None), + "pytest": ("https://docs.pytest.org/en/stable/", None), + "libtmux": ("https://libtmux.git-pull.com/", None), + "pydantic": ("https://docs.pydantic.dev/latest/", None), }, -] - -sphinx_font_css_variables = { - "--font-stack": '"IBM Plex Sans", "IBM Plex Sans Fallback", -apple-system, BlinkMacSystemFont, sans-serif', - "--font-stack--monospace": '"IBM Plex Mono", "IBM Plex Mono Fallback", SFMono-Regular, Menlo, Consolas, monospace', - "--font-stack--headings": "var(--font-stack)", -} - -intersphinx_mapping = { - "python": ("https://docs.python.org/", None), - "pytest": ("https://docs.pytest.org/en/stable/", None), - "libtmux": ("https://libtmux.git-pull.com/", None), - "pydantic": ("https://docs.pydantic.dev/latest/", None), -} - - -def linkcode_resolve(domain: str, info: dict[str, str]) -> None | str: - """ - Determine the URL corresponding to Python object. - - Notes - ----- - From https://github.com/numpy/numpy/blob/v1.15.1/doc/source/conf.py, 7c49cfa - on Jul 31. License BSD-3. https://github.com/numpy/numpy/blob/v1.15.1/LICENSE.txt - """ - if domain != "py": - return None - - modname = info["module"] - fullname = info["fullname"] - - submod = sys.modules.get(modname) - if submod is None: - return None - - obj = submod - for part in fullname.split("."): - try: - obj = getattr(obj, part) - except Exception: # noqa: PERF203 - return None - - # strip decorators, which would resolve to the source of the decorator - # possibly an upstream bug in getsourcefile, bpo-1764286 - try: - unwrap = inspect.unwrap - except AttributeError: - pass - else: - if callable(obj): - obj = unwrap(obj) - - try: - fn = inspect.getsourcefile(obj) - except Exception: - fn = None - if not fn: - return None - - try: - source, lineno = inspect.getsourcelines(obj) - except Exception: - lineno = None - - linespec = f"#L{lineno}-L{lineno + len(source) - 1}" if lineno else "" - - fn = relpath(fn, start=pathlib.Path(libtmux_mcp.__file__).parent) - - if "dev" in about["__version__"]: - return "{}/blob/main/{}/{}/{}{}".format( - about["__repository__"], - "src", - about["__package_name__"], - fn, - linespec, - ) - return "{}/blob/v{}/{}/{}/{}{}".format( + linkcode_resolve=make_linkcode_resolve( + libtmux_mcp, about["__repository__"], - about["__version__"], - "src", - about["__package_name__"], - fn, - linespec, - ) + ), + theme_options={ + "announcement": ( + "Pre-alpha. APIs may change." + " " + "Feedback welcome." + ), + }, + html_favicon="_static/favicon.ico", + html_extra_path=["manifest.json"], + rediraffe_redirects="redirects.txt", + copybutton_selector="div.highlight pre, div.admonition.prompt > p:last-child", + copybutton_exclude=".linenos, .admonition-title", +) +conf["myst_enable_extensions"] = [*conf["myst_enable_extensions"], "attrs_inline"] -def remove_tabs_js(app: Sphinx, exc: Exception) -> None: - """Remove tabs.js from _static after build.""" - # Fix for sphinx-inline-tabs#18 - if app.builder.format == "html" and not exc: - tabs_js = pathlib.Path(app.builder.outdir) / "_static" / "tabs.js" - with contextlib.suppress(FileNotFoundError): - tabs_js.unlink() # When python 3.7 deprecated, use missing_ok=True +_gp_setup = conf.pop("setup") def setup(app: Sphinx) -> None: - """Configure Sphinx app hooks.""" - app.add_js_file("js/spa-nav.js", loading_method="defer") + """Configure Sphinx app hooks and register project-specific JS.""" + _gp_setup(app) app.add_js_file("js/prompt-copy.js", loading_method="defer") - app.connect("build-finished", remove_tabs_js) + + +globals().update(conf) diff --git a/pyproject.toml b/pyproject.toml index b5e9f61..b37c9d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,18 +56,9 @@ libtmux-mcp = "libtmux_mcp:main" [dependency-groups] dev = [ # Docs - "sphinx<9", - "furo", + "gp-sphinx==0.0.1a1", "gp-libs", "sphinx-autobuild", - "sphinx-autodoc-typehints", - "sphinx-inline-tabs", - "sphinx-design", - "sphinxext-opengraph", - "sphinx-copybutton", - "sphinxext-rediraffe", - "myst-parser", - "linkify-it-py", # Testing "typing-extensions; python_version < '3.11'", "gp-libs", @@ -86,18 +77,9 @@ dev = [ ] docs = [ - "sphinx<9", - "furo", + "gp-sphinx==0.0.1a1", "gp-libs", "sphinx-autobuild", - "sphinx-autodoc-typehints", - "sphinx-inline-tabs", - "sphinx-design", - "sphinxext-opengraph", - "sphinx-copybutton", - "sphinxext-rediraffe", - "myst-parser", - "linkify-it-py", ] testing = [ "typing-extensions; python_version < '3.11'", @@ -130,10 +112,6 @@ files = [ "tests", ] -[[tool.mypy.overrides]] -module = ["sphinx_fonts"] -ignore_missing_imports = true - [[tool.mypy.overrides]] module = ["fastmcp_autodoc", "docutils", "docutils.*"] ignore_missing_imports = true diff --git a/uv.lock b/uv.lock index 6d8f37d..e9e86fc 100644 --- a/uv.lock +++ b/uv.lock @@ -711,6 +711,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/f9/5d78d1dda9cb0f27d6f2305e95a58edbff935a62d53ec3227a3518cb4f72/gp_libs-0.0.17-py3-none-any.whl", hash = "sha256:7ce96d5e09980c0dc82062ab3e3b911600bd44da97a64fb78379f1af9a79d4d3", size = 16157, upload-time = "2025-12-07T22:44:48.036Z" }, ] +[[package]] +name = "gp-sphinx" +version = "0.0.1a1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "gp-libs" }, + { name = "linkify-it-py" }, + { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-copybutton" }, + { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-fonts" }, + { name = "sphinx-gptheme" }, + { name = "sphinx-inline-tabs" }, + { name = "sphinxext-opengraph" }, + { name = "sphinxext-rediraffe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/89/aa7d03025bbcd036806a67299f04c1de302eda265b35046a1355240503da/gp_sphinx-0.0.1a1.tar.gz", hash = "sha256:70f99cdd2ef5f24aa160da4eb47f80933c8d69bce00383dc0eb60e8bd51663f5", size = 13991, upload-time = "2026-04-05T17:32:41.295Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/2a/21836581ec988b8c58cacac2bfb091bbb000b8fe682f62a2fa584674aa6b/gp_sphinx-0.0.1a1-py3-none-any.whl", hash = "sha256:6f0c73a1a13ba94bef7fb1c5368fe6e47dc4128ec948c27f08e834cdf41a2111", size = 14398, upload-time = "2026-04-05T17:32:31.292Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1033,12 +1061,9 @@ coverage = [ dev = [ { name = "codecov" }, { name = "coverage" }, - { name = "furo" }, { name = "gp-libs" }, - { name = "linkify-it-py" }, + { name = "gp-sphinx" }, { name = "mypy" }, - { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-mock" }, @@ -1046,38 +1071,15 @@ dev = [ { name = "pytest-watcher" }, { name = "pytest-xdist" }, { name = "ruff" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-inline-tabs" }, - { name = "sphinxext-opengraph" }, - { name = "sphinxext-rediraffe" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] docs = [ - { name = "furo" }, { name = "gp-libs" }, - { name = "linkify-it-py" }, - { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "gp-sphinx" }, { name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-inline-tabs" }, - { name = "sphinxext-opengraph" }, - { name = "sphinxext-rediraffe" }, ] lint = [ { name = "mypy" }, @@ -1108,11 +1110,9 @@ coverage = [ dev = [ { name = "codecov" }, { name = "coverage" }, - { name = "furo" }, { name = "gp-libs" }, - { name = "linkify-it-py" }, + { name = "gp-sphinx", specifier = "==0.0.1a1" }, { name = "mypy" }, - { name = "myst-parser" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-mock" }, @@ -1120,29 +1120,13 @@ dev = [ { name = "pytest-watcher" }, { name = "pytest-xdist" }, { name = "ruff" }, - { name = "sphinx", specifier = "<9" }, { name = "sphinx-autobuild" }, - { name = "sphinx-autodoc-typehints" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinx-inline-tabs" }, - { name = "sphinxext-opengraph" }, - { name = "sphinxext-rediraffe" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] docs = [ - { name = "furo" }, { name = "gp-libs" }, - { name = "linkify-it-py" }, - { name = "myst-parser" }, - { name = "sphinx", specifier = "<9" }, + { name = "gp-sphinx", specifier = "==0.0.1a1" }, { name = "sphinx-autobuild" }, - { name = "sphinx-autodoc-typehints" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinx-inline-tabs" }, - { name = "sphinxext-opengraph" }, - { name = "sphinxext-rediraffe" }, ] lint = [ { name = "mypy" }, @@ -2384,6 +2368,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350, upload-time = "2026-01-19T13:12:51.077Z" }, ] +[[package]] +name = "sphinx-fonts" +version = "0.0.1a1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/17/c7bdfd74248812b5d7df452d65474817ba96d41ebd67862022938c914465/sphinx_fonts-0.0.1a1.tar.gz", hash = "sha256:2c4ae152636649d88151a1421293b7b147bab36d97ef7aa3e85ce52ce7984dad", size = 5628, upload-time = "2026-04-05T17:32:46.905Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/dd/595ac1e9f72c7bc9b19bc9cc2e5c3d429c4d20b9a344674d23b75269906f/sphinx_fonts-0.0.1a1-py3-none-any.whl", hash = "sha256:6b45590254b912fb1b19e08c1ab6c3ce42eb1e1d07333183005d1fd54bb92b6f", size = 4348, upload-time = "2026-04-05T17:32:38.579Z" }, +] + +[[package]] +name = "sphinx-gptheme" +version = "0.0.1a1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "furo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/8d/2bbde808fcc5aadb2e9cdb4c5ae0713ad88f3f57bfbdcfc6f0a4eae82bb2/sphinx_gptheme-0.0.1a1.tar.gz", hash = "sha256:d4b64b6dd6f8c213300820e1300ba075c56428946f4a903d1258440c0a9094d5", size = 14566, upload-time = "2026-04-05T17:32:47.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/18/85b1d4550501d7f4a91d75a2ad39e6883e988e4217272e216e5a86b80a49/sphinx_gptheme-0.0.1a1-py3-none-any.whl", hash = "sha256:52a752136bda4641d001d8f32f59f3b492a631fe19cec116ba14c316351ba00d", size = 15624, upload-time = "2026-04-05T17:32:39.9Z" }, +] + [[package]] name = "sphinx-inline-tabs" version = "2025.12.21.14"