Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/css/mkdocstrings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Documentation layout styles for MkDocs Material.
Defines spacing, typography, and table formatting
to improve readability and consistency across docs.
*/

/* Add vertical spacing between documentation objects */
.md-typeset .doc .doc-object { margin: 1.1rem 0; }

/* Allow function/method signatures to wrap across lines */
.md-typeset .doc .sig { white-space: normal; word-break: break-word; }

/* Adjust table cell padding and align text to the top */
.md-typeset .doc .table th,
.md-typeset .doc .table td {
padding: .5rem .75rem;
vertical-align: top;
}

/* Apply alternating background color to table rows for readability */
.md-typeset .doc .table tr:nth-child(odd) td {
background: var(--md-code-bg-color);
}
146 changes: 146 additions & 0 deletions docs/css/theme-variants.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
Documentation theme styles for MkDocs Material.
Defines heading sizes, documentation object layout,
code signature blocks, and table formatting.
Includes multiple color/spacing schemes (brand, compact, comfy)
for different presentation preferences.
*/

/* Style level-1 headings */
.md-typeset h1 {
font-size: 1.4rem;
color: var(--md-default-fg-color--light);
font-weight: 800;
letter-spacing: .01em;
margin-top: 1.2rem;
margin-bottom: .6rem;
}

/* Style level-2 headings */
.md-typeset h2 {
font-size: 1rem;
color: var(--md-default-fg-color--light);
font-weight: 700;
letter-spacing: .02em;
margin-top: .8rem;
margin-bottom: .4rem;
}

/* Style level-3 headings */
.md-typeset h3 {
font-size: 0.9rem;
color: var(--md-default-fg-color--light);
font-weight: 600;
letter-spacing: .01em;
margin-top: .6rem;
margin-bottom: .3rem;
}

/* Card-like container for documentation objects */
.md-typeset .doc .doc-object {
margin: 1rem 0 1.25rem;
padding: 1rem 1.25rem;
border: 1px solid var(--md-default-fg-color--lighter);
border-radius: .75rem;
background: var(--md-default-bg-color);
box-shadow: 0 1px 0 var(--md-default-fg-color--lighter);
}

/* Heading inside a documentation object */
.md-typeset .doc .doc-object > .doc-heading {
font-weight: 700;
margin-bottom: .5rem;
}

/* Code signature block inside documentation */
.md-typeset .doc .sig {
display: block;
white-space: pre-wrap;
word-break: break-word;
background: var(--md-code-bg-color);
padding: .5rem .75rem;
border-radius: .5rem;
line-height: 1.45;
}

/* Section titles inside documentation */
.md-typeset .doc .doc-section-title {
font-weight: 800;
text-transform: uppercase;
font-size: .78rem;
letter-spacing: .04em;
color: var(--md-default-fg-color--light);
margin-top: .9rem;
border-top: 1px solid var(--md-default-fg-color--lighter);
padding-top: .5rem;
}

/* Table base styles */
.md-typeset .doc .table table {
width: 100%;
border-collapse: collapse;
}
.md-typeset .doc .table thead th {
font-weight: 700;
border-bottom: 1px solid var(--md-default-fg-color--lighter);
}
.md-typeset .doc .table th,
.md-typeset .doc .table td {
padding: .45rem .6rem;
vertical-align: top;
}
/* Alternating row background */
.md-typeset .doc .table tr:nth-child(odd) td {
background: var(--md-code-bg-color);
}

/* ============================= */
/* BRAND color scheme */
/* ============================= */
[data-md-color-scheme="brand"] {
--md-primary-fg-color: #6e56cf;
--md-accent-fg-color: #00c2a8;
--md-code-bg-color: rgba(110, 86, 207, .08);
}
[data-md-color-scheme="brand"] .md-typeset .doc .doc-object {
border-color: rgba(110, 86, 207, .35);
box-shadow: 0 1px 0 rgba(110, 86, 207, .25);
}

/* ============================= */
/* COMPACT color scheme */
/* ============================= */
[data-md-color-scheme="compact"] .md-typeset {
font-size: 0.94rem;
line-height: 1.55;
}
[data-md-color-scheme="compact"] .md-typeset .doc .doc-object {
margin: .75rem 0 1rem;
padding: .75rem .9rem;
}
[data-md-color-scheme="compact"] .md-typeset .doc .table th,
[data-md-color-scheme="compact"] .md-typeset .doc .table td {
padding: .35rem .5rem;
}
[data-md-color-scheme="compact"] .md-typeset .sig {
line-height: 1.35;
}

/* ============================= */
/* COMFY color scheme */
/* ============================= */
[data-md-color-scheme="comfy"] .md-typeset {
font-size: 1.05rem;
line-height: 1.7;
}
[data-md-color-scheme="comfy"] .md-typeset .doc .doc-object {
margin: 1.2rem 0 1.5rem;
padding: 1.1rem 1.35rem;
}
[data-md-color-scheme="comfy"] .md-typeset .doc .table th,
[data-md-color-scheme="comfy"] .md-typeset .doc .table td {
padding: .55rem .75rem;
}
[data-md-color-scheme="comfy"] .md-typeset .sig {
line-height: 1.55;
}
55 changes: 55 additions & 0 deletions docs/gen_ref_pages/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from pathlib import Path

# Global configuration flags and constants
INCLUDE_PRIVATE = False # Whether to include private packages (names starting with "_")
SOURCE_DIR = Path("src") # Root source directory to search for packages

# Mapping from source subdirectories > human-readable section titles
SECTION_TITLE_MAP: dict[str, str] = {}

# Explicit ordering for sections when displaying documentation or indexes
SECTION_ORDER: dict[str, int] = {}

# File extensions that should be recognized as linkable images
LINKABLE_IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".ico", ".gif"}


def _is_pkg_dir(path: Path) -> bool:
"""
Check whether a given path points to a valid Python package directory.

A valid package directory must:
1. Be a directory.
2. Contain an __init__.py file.

:param path: Filesystem path to check.
:return: True if the path is a Python package directory, False otherwise.
"""
return path.is_dir() and (path / "__init__.py").exists()


def find_package_dir(include_private: bool = INCLUDE_PRIVATE) -> tuple[Path, str]:
"""
Locate the first valid Python package directory under SOURCE_DIR.

- Private packages (names starting with "_") can be excluded unless explicitly allowed.
- If multiple candidates exist, the package with the lexicographically smallest name is chosen.

:param include_private: Whether to allow private packages (default: global INCLUDE_PRIVATE).
:return: Tuple (package_path, package_name).
:raises SystemExit: If no valid package directory is found.
"""
# Gather all package directories under SOURCE_DIR
candidates = [
package_path
for package_path in SOURCE_DIR.iterdir()
if _is_pkg_dir(package_path) and (include_private or not package_path.name.startswith("_"))
]

# No valid package found > stop execution with an error message
if not candidates:
raise SystemExit("No package found in src/. Make sure you have something like " "src/yourpkg/__init__.py")

# Pick the lexicographically smallest directory (deterministic choice)
package_dir = min(candidates, key=lambda package_path: package_path.name)
return package_dir, package_dir.name
50 changes: 50 additions & 0 deletions docs/gen_ref_pages/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from dataclasses import dataclass, field

# Record structure:
# - tuple: arbitrary metadata (e.g., file info)
# - tuple[str, ...]: path parts (e.g., ("pkg", "subpkg"))
# - str: identifier/name
# - bool: status flag
Record = tuple[tuple, tuple[str, ...], str, bool]


@dataclass
class Context:
"""
Holds the in-memory representation of a package hierarchy. (tree structure)

Tracks:
- Created folder paths.
- Relationships between parent folders → child directories/modules.
- Records associated with discovered entities.
"""

# Set of folder paths already created (each as a tuple of path parts).
created_folders: set[tuple[str, ...]] = field(default_factory=set)

# Map: parent folder > set of child directories.
children_directories: dict[tuple[str, ...], set[tuple[str, ...]]] = field(default_factory=dict)

# Map: parent folder > list of child modules.
children_modules: dict[tuple[str, ...], list[tuple[str, ...]]] = field(default_factory=dict)

# Collected records for all discovered items (see Record type alias).
records: list[Record] = field(default_factory=list)

def ensure_folder(self, parts: list[str]) -> None:
"""
Ensure that a folder path is registered in the context.

If the folder path is not yet known:
- Add it to created_folders.
- Initialize its entry in children_directories and children_modules.

:param parts: Path components for the folder (e.g., ["pkg", "subpkg"]).
:return: None
"""
key = tuple(parts)

if key not in self.created_folders:
self.created_folders.add(key)
self.children_directories.setdefault(key, set())
self.children_modules.setdefault(key, [])
Loading