types
Module setup
IMPORTS
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
CONSTANTS
EXPORT_DECORATORS = ('app.function', 'app.class_definition')
rename
def rename(
name: str, # original function/class name
renames: dict, # prefix substitution map from config
) -> str:
"""Apply prefix substitutions to a name. Dunder prefixes wrap both sides."""
Source
def rename(
name: str, # original function/class name
renames: dict, # prefix substitution map from config
) -> str: # renamed identifier
"Apply prefix substitutions to a name. Dunder prefixes wrap both sides."
for prefix, replacement in renames.items():
if not name.startswith(prefix): continue
stem = name[len(prefix):]
if replacement.startswith('__'): return replacement + stem + '__'
return replacement + stem
return name
Config
class Config(
nbs: str, # notebook source directory
out: str, # package output directory
docs: str, # documentation output directory
root: str, # project root
init: str, # setup block added to package __init__
skip_prefixes: tuple, # filename prefixes to ignore
renames: dict, # name prefix substitutions
application: str | None, # entry point e.g. "module:obj" or "module:obj:runner"
):
"""Project configuration from pyproject.toml [tool.marimo-dev]."""
Methods
def app_parts(
) -> tuple[str, str, str | None] | None:
"""Parse application string into (module, object, optional runner)."""
Source
@dataclass(frozen=True)
class Config:
"Project configuration from pyproject.toml [tool.marimo-dev]."
nbs: str = 'notebooks' # notebook source directory
out: str = 'src' # package output directory
docs: str = 'docs' # documentation output directory
root: str = '.' # project root
init: str = '_init.py' # setup block added to package __init__
skip_prefixes: tuple = ('XX_', 'test_') # filename prefixes to ignore
renames: dict = field(default_factory=dict) # name prefix substitutions
application: str|None = None # entry point e.g. "module:obj" or "module:obj:runner"
@property
def app_parts(
self, # Config instance
) -> tuple[str, str, str|None]|None: # (module, object, runner) or None
"Parse application string into (module, object, optional runner)."
if not self.application: return None
parts = self.application.split(':')
if len(parts) < 2: return None
return (parts[0], parts[1], parts[2] if len(parts) > 2 else None)
Param
class Param(
name: str, # parameter name
anno: str, # type annotation
default: str, # default value
doc: str,
):
"""A function parameter with optional inline documentation."""
Source
@dataclass
class Param:
"A function parameter with optional inline documentation."
name: str # parameter name
anno: str = '' # type annotation
default: str = '' # default value
doc: str = ''
Return
class Return(
anno: str, # return type
doc: str,
):
"""A return annotation with optional inline documentation."""
Source
@dataclass
class Return:
"A return annotation with optional inline documentation."
anno: str # return type
doc: str = ''
Method
class Method(
name: str, # method name
doc: str, # docstring
params: list[Param],
ret: Return | None,
):
"""A class method with parameters and return info."""
Source
@dataclass
class Method:
"A class method with parameters and return info."
name: str # method name
doc: str = '' # docstring
params: list[Param] = field(default_factory=list)
ret: Return|None = None
Import
class Import(
src: str,
):
"""An import statement from a setup cell."""
Source
@dataclass
class Import:
"An import statement from a setup cell."
src: str
Const
class Const(
name: str, # variable name
src: str,
):
"""A constant assignment from a setup cell."""
Source
@dataclass
class Const:
"A constant assignment from a setup cell."
name: str # variable name
src: str
Setup
class Setup(
src: str,
):
"""Arbitrary setup code that is not an import or constant."""
Source
@dataclass
class Setup:
"Arbitrary setup code that is not an import or constant."
src: str
ExportKind
class ExportKind(
):
"""Classification of an exported definition."""
Source
class ExportKind(Enum):
"Classification of an exported definition."
FUNC = 'func'
ASYNC = 'async'
CLASS = 'class'
Export
class Export(
name: str, # original name in notebook
final_name: str, # name after renames (set by parser)
public: bool, # part of public API (set by parser)
kind: ExportKind, # func | async | class
src: str, # source WITH decorators (original)
clean_src: str, # source WITHOUT marimo decorators
doc: str, # docstring
params: list[Param],
methods: list[Method],
ret: Return | None,
lineno: int,
):
"""A decorated function or class marked for export."""
Source
@dataclass
class Export:
"A decorated function or class marked for export."
name: str # original name in notebook
final_name: str = '' # name after renames (set by parser)
public: bool = True # part of public API (set by parser)
kind: ExportKind = ExportKind.FUNC # func | async | class
src: str = '' # source WITH decorators (original)
clean_src: str = '' # source WITHOUT marimo decorators
doc: str = '' # docstring
params: list[Param] = field(default_factory=list)
methods: list[Method]= field(default_factory=list)
ret: Return|None = None
lineno: int = 0
ParsedFile
class ParsedFile(
imports: list[Import],
consts: list[Const],
setup: list[Setup],
exports: list[Export],
):
"""A parsed notebook file mostly ergonomic class for handeling returns more robustly"""
Source
@dataclass
class ParsedFile:
"A parsed notebook file mostly ergonomic class for handeling returns more robustly"
imports: list[Import]
consts: list[Const]
setup: list[Setup]
exports: list[Export]
Module
class Module(
name: str, # e.g. 'core' (prefix stripped)
nb_stem: str, # e.g. 'a_core' (original filename stem)
imports: list[Import],
consts: list[Const],
setup: list[Setup],
exports: list[Export],
):
"""A parsed notebook file containing imports, setup, and exports."""
Methods
def has_exports(
) -> bool:
"""True if module contains any exported definitions."""
def public_exports(
) -> list[Export]:
"""Exports that are part of the public API."""
def documented_exports(
) -> list[Export]:
"""Exports that should appear in docs."""
Source
@dataclass
class Module:
"A parsed notebook file containing imports, setup, and exports."
name: str # e.g. 'core' (prefix stripped)
nb_stem: str = '' # e.g. 'a_core' (original filename stem)
imports: list[Import] = field(default_factory=list)
consts: list[Const] = field(default_factory=list)
setup: list[Setup] = field(default_factory=list)
exports: list[Export] = field(default_factory=list)
@property
def has_exports(self) -> bool:
"True if module contains any exported definitions."
return len(self.exports) > 0
@property
def public_exports(
self, # Module instance
) -> list[Export]: # exports visible in __init__.py
"Exports that are part of the public API."
return [e for e in self.exports if e.public]
@property
def documented_exports(
self, # Module instance
) -> list[Export]: # exports included in documentation
"Exports that should appear in docs."
return [e for e in self.exports if e.public]
Meta
class Meta(
name: str,
version: str,
desc: str,
license: str,
author: str,
urls: dict,
):
"""Project metadata from pyproject.toml [project]."""
Methods
def repo_url(
) -> str:
"""Repository URL from project.urls."""
def pkg_name(
) -> str:
"""Python package name (hyphens replaced with underscores)."""
Source
@dataclass
class Meta:
"Project metadata from pyproject.toml [project]."
name: str = ''
version: str = '0.0.0'
desc: str = ''
license: str = ''
author: str = ''
urls: dict = field(default_factory=dict)
@property
def repo_url(self) -> str:
"Repository URL from project.urls."
return self.urls.get('Repository', '')
@property
def pkg_name(self) -> str:
"Python package name (hyphens replaced with underscores)."
return self.name.replace('-', '_')
Project
class Project(
meta: Meta,
config: Config,
init_extras: ParsedFile,
modules: list[Module],
):
"""A complete parsed marimo-dev project."""
Methods
def mod_names(
) -> list[str]:
"""All module names in build order."""
def nonempty_modules(
) -> list[Module]:
"""Modules that contain at least one export."""
Source
@dataclass
class Project:
"A complete parsed marimo-dev project."
meta: Meta
config: Config
init_extras: ParsedFile = field(default_factory=lambda: ParsedFile([], [], [], []))
modules: list[Module] = field(default_factory=list)
@property
def mod_names(
self, # Project instance
) -> list[str]: # list of module names
"All module names in build order."
return [m.name for m in self.modules]
@property
def nonempty_modules(
self, # Project instance
) -> list[Module]: # modules that have exports
"Modules that contain at least one export."
return [m for m in self.modules if m.has_exports]
parse
Module setup
IMPORTS
from pathlib import Path
import ast, re, tomllib
from a_types import Config, Meta, Project, Module, Import, Const, Setup, Export, ParsedFile, Param, Method, Return, ExportKind, EXPORT_DECORATORS, rename
read_config
def read_config(
root: str = '.', # project root containing pyproject.toml
) -> Config:
"""Read config once from pyproject.toml [tool.marimo-dev]."""
Source
def read_config(
root: str = '.', # project root containing pyproject.toml
) -> Config: # parsed configuration
"Read config once from pyproject.toml [tool.marimo-dev]."
try:
with open(Path(root) / 'pyproject.toml', 'rb') as f:
cfg = tomllib.load(f).get('tool', {}).get('marimo-dev', {})
if 'skip_prefixes' in cfg: cfg['skip_prefixes'] = tuple(cfg['skip_prefixes'])
return Config(**{k: v for k, v in cfg.items() if k in Config.__dataclass_fields__})
except FileNotFoundError:
return Config()
read_project
def read_project(
root: str = '.', # project root containing pyproject.toml and notebooks
) -> Project:
"""Read an entire marimo-dev project into a Project.
Single entry point. Call once, get everything."""
Source
def read_project(
root: str = '.', # project root containing pyproject.toml and notebooks
) -> Project: # complete parsed project
"""Read an entire marimo-dev project into a Project.
Single entry point. Call once, get everything.
"""
cfg = read_config(root)
meta = internal_read_meta(root)
nbs = Path(root) / cfg.nbs
init_extras, modules = ParsedFile([], [], [], []), []
for f in sorted(nbs.glob('*.py')):
if f.name == cfg.init:
init_extras = _parse_file(f, cfg)
continue
name = _module_name(f, cfg)
if name is None: continue
parsed = _parse_file(f, cfg)
modules.append(Module(
name = name,
nb_stem = f.stem,
imports = parsed.imports,
consts = parsed.consts,
setup = parsed.setup,
exports = parsed.exports,
))
return Project(meta=meta, config=cfg, init_extras=init_extras, modules=modules)
build_pkg
Module setup
IMPORTS
from pathlib import Path
import re, shutil, sys
from a_types import Project, Module
build
def build(
proj: Project, # complete parsed project
) -> str:
"""Build a Python package from a parsed Project."""
Source
def build(
proj: Project, # complete parsed project
) -> str: # path to built package directory
"Build a Python package from a parsed Project."
cfg, meta = proj.config, proj.meta
pkg = Path(cfg.root) / cfg.out / meta.pkg_name
if pkg.exists(): shutil.rmtree(pkg)
pkg.mkdir(parents=True, exist_ok=True)
mod_names = proj.mod_names
for mod in proj.modules:
if mod.name != 'index' and mod.has_exports:
internal_write_module(pkg / f'{mod.name}.py', mod, mod_names)
internal_write_init(pkg / '__init__.py', proj)
if cfg.app_parts:
internal_write_main(pkg / '__main__.py', cfg.app_parts)
return str(pkg)
bundle
def bundle(
proj: Project, # complete parsed project
name: str | None = None, # output filename (None for default)
) -> str:
"""Bundle all modules into a single Python file with PEP 723 deps."""
Source
def bundle(
proj: Project, # complete parsed project
name: str | None = None, # output filename (None for default)
) -> str: # path to bundled file
"Bundle all modules into a single Python file with PEP 723 deps."
cfg, meta = proj.config, proj.meta
mod_names = set(proj.mod_names)
# Collect external imports, deduplicate
seen_imports: set[str] = set()
external_imports: list[str] = []
dep_modules: set[str] = set()
for mod in proj.modules:
for imp in mod.imports:
is_local = False
if imp.src.startswith('from '):
parts = imp.src.split()
if len(parts) >= 2:
root_mod = re.sub(r'^[a-z]_', '', parts[1]).split('.')[0]
if root_mod in mod_names: is_local = True
elif parts[1].split('.')[0] not in sys.stdlib_module_names:
dep_modules.add(parts[1].split('.')[0])
elif imp.src.startswith('import '):
parts = imp.src.split()
if len(parts) >= 2:
root_mod = parts[1].split('.')[0]
if root_mod not in sys.stdlib_module_names:
dep_modules.add(root_mod)
if not is_local and imp.src not in seen_imports:
seen_imports.add(imp.src)
external_imports.append(imp.src)
# PEP 723 header
deps_str = ', '.join(f'"{d}"' for d in sorted(dep_modules))
header = f'# /// script\n# dependencies = [{deps_str}]\n# ///'
imports = '\n'.join(external_imports)
consts = '\n'.join(c.src for m in proj.modules for c in m.consts)
setup = '\n'.join(s.src for m in proj.modules for s in m.setup)
# Apply renames to exports and fix cross-references
exports = '\n\n'.join(
internal_apply_renames(e.clean_src, e.name, e.final_name)
for m in proj.modules for e in m.exports
)
rename_map = {
e.name: e.final_name
for m in proj.modules for e in m.exports
if e.final_name != e.name
}
for old, new in rename_map.items():
exports = re.sub(rf'\b{re.escape(old)}\b', new, exports)
sections = [header, imports, consts, setup, exports]
if cfg.app_parts:
sections.append(internal_entry_point_src(cfg.app_parts))
content = '\n\n'.join(p for p in sections if p.strip())
if name:
out = Path(cfg.root) / name
else:
out = Path(cfg.root) / cfg.out / meta.pkg_name / '__init__.py'
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(content)
return str(out)
build_docs
Module setup
IMPORTS
from pathlib import Path
from a_types import Project
render_llms
def render_llms(
proj: Project, # complete parsed project
base_url: str = '', # base URL for links
) -> str:
"""Render llms.txt — module index with export names."""
Source
def render_llms(
proj: Project, # complete parsed project
base_url: str = '', # base URL for links
) -> str: # llms.txt content
"Render llms.txt — module index with export names."
lines = [f"# {proj.meta.name}\n", f"> {proj.meta.desc}\n"]
for mod in proj.nonempty_modules:
names = ', '.join(e.final_name for e in mod.documented_exports)
if names:
lines.append(f"- [{mod.name}](/{mod.name}): {names}")
lines.append(f"\n- [llms-full.txt](/llms-full.txt): Complete source code")
return '\n'.join(lines) + '\n'
render_llms_full
def render_llms_full(
proj: Project, # complete parsed project
) -> str:
"""Render llms-full.txt — complete cleaned source."""
Source
def render_llms_full(
proj: Project, # complete parsed project
) -> str: # llms-full.txt content
"Render llms-full.txt — complete cleaned source."
parts = [f"# {proj.meta.name}\n\n> {proj.meta.desc}\n"]
for mod in proj.nonempty_modules:
documented = mod.documented_exports
if not documented: continue
parts.append(f"## {mod.name}\n")
for exp in documented:
parts.append(exp.clean_src)
return '\n\n'.join(parts) + '\n'
build_docs
def build_docs(
proj: Project, # complete parsed project
) -> str:
"""Write all documentation files."""
Source
def build_docs(
proj: Project, # complete parsed project
) -> str: # status message
"Write all documentation files."
docs = Path(proj.config.root) / proj.config.docs
docs.mkdir(parents=True, exist_ok=True)
(docs / 'llms.txt').write_text(render_llms(proj))
(docs / 'llms-full.txt').write_text(render_llms_full(proj))
return f"Wrote docs to {docs}"
publish
Module setup
IMPORTS
from pathlib import Path
import configparser, shutil, subprocess
from a_types import Project
from c_build_pkg import build
CONSTANTS
PYPI_URLS = {'testpypi': 'https://test.pypi.org/legacy/', 'pypi': 'https://upload.pypi.org/legacy/'}
TOKEN_URLS = {'testpypi': 'https://test.pypi.org/manage/account/', 'pypi': 'https://pypi.org/manage/account/'}
SETUP
"""
marimo-dev publish.
Build distribution and upload to PyPI.
Reads credentials from ~/.pypirc, calls uv build + uv publish.
Public API:
publish(project, test) -> None
"""
publish
def publish(
proj: Project, # complete parsed project
test: bool = True, # True for TestPyPI, False for PyPI
):
"""Build package and publish to PyPI."""
Source
def publish(
proj: Project, # complete parsed project
test: bool = True, # True for TestPyPI, False for PyPI
):
"Build package and publish to PyPI."
section = 'testpypi' if test else 'pypi'
target = 'TestPyPI' if test else 'PyPI'
creds = internal_read_pypirc(section)
if not creds: return
username, password = creds
preview = password[:9] + '...' if password.startswith('pypi-') else '****'
print(f"Authenticated as {username} ({preview})")
print("Building package from notebooks...")
build(proj)
shutil.rmtree('dist', ignore_errors=True)
print("Building distribution...")
subprocess.run(['uv', 'build'], check=True)
print(f"Publishing to {target}...")
try:
subprocess.run([
'uv', 'publish',
'--publish-url', PYPI_URLS[section],
'--username', username,
'--password', password,
], capture_output=True, text=True, check=True)
print(f"Published to {target}!")
except subprocess.CalledProcessError as e:
if '403' in (e.stderr or ''):
print(f"Auth failed. Token may be expired. Regenerate at {TOKEN_URLS[section]}")
else:
print(f"Publish failed: {e.stderr}")
cli
Module setup
IMPORTS
import sys, shutil
from pathlib import Path
from b_parse import read_project
from c_build_pkg import build, bundle
from d_build_docs import build_docs
from e_publish import publish
from g_build_docs_html import build_docs_html
tidy
def tidy(
):
"""Remove cache and temporary files."""
Source
def tidy():
"Remove cache and temporary files."
for pattern in ('__pycache__', '__marimo__', '.pytest_cache'):
for p in Path('.').rglob(pattern): shutil.rmtree(p, ignore_errors=True)
for p in Path('.').rglob('*.pyc'): p.unlink(missing_ok=True)
print("Cleaned cache files")
nuke
def nuke(
):
"""Remove all build artifacts and cache files."""
Source
def nuke():
"Remove all build artifacts and cache files."
tidy()
for d in ('dist', 'docs', 'src', 'temp'):
shutil.rmtree(d, ignore_errors=True)
print("Nuked build artifacts")
main
def main(
):
"""Entry point for the md command."""
Source
def main():
"Entry point for the md command."
if len(sys.argv) < 2:
print("Usage: md [build|docs|bundle|publish|tidy|nuke]")
sys.exit(1)
cmd = sys.argv[1]
if cmd == 'tidy': tidy(); return
if cmd == 'nuke': nuke(); return
# Everything else needs the project
proj = read_project()
if cmd == 'build':
pkg = build(proj)
build_docs(proj)
print(f"Built package at: {pkg}")
elif cmd == 'docs':
print(build_docs(proj))
print(build_docs_html(proj))
elif cmd == 'bundle':
name = sys.argv[2] if len(sys.argv) > 2 else None
print(bundle(proj, name=name))
elif cmd == 'publish':
test = '--test' in sys.argv or '-t' in sys.argv
target = "TestPyPI" if test else "PyPI"
if input(f"Publish to {target}? [y/N] ").lower() != 'y':
print("Aborted"); sys.exit(0)
publish(proj, test=test)
else:
print(f"Unknown command: {cmd}")
sys.exit(1)
build_docs_html
Module setup
IMPORTS
import json
import html_tags as h
from a_types import Project, Module, Export, Param, Return, ExportKind
SETUP
"""HTML documentation renderer for marimo-dev projects.
Renders a Project into a single-page HTML doc with Datastar-driven tab
switching. The parse step has already extracted everything needed — this
module is pure transformation from typed data to tag tree.
Layout: app-style grid
header → project name + version + description
nav → module tabs (drive the $current signal)
main → the active module's exports (signature + docstring + source)
aside → on-this-page anchors for the active module
"""
signature_text
def signature_text(
exp, # Export dataclass
):
"""Build a docments-style signature reconstruction from an Export."""
Source
def signature_text(
exp, # Export dataclass
): # full signature as plain text (ready for a <pre><code>)
"Build a docments-style signature reconstruction from an Export."
kw = 'async def' if exp.kind == ExportKind.ASYNC else \
'class' if exp.kind == ExportKind.CLASS else 'def'
lines = [f"{kw} {exp.final_name}("]
for p in exp.params:
lines.append(internal_fmt_param(p))
lines.append(internal_fmt_return(exp.ret))
if exp.doc:
# Render docstring inside the signature block so it highlights as Python
lines.append(f' """{exp.doc}"""')
return '\n'.join(lines)
method_signature_text
def method_signature_text(
m, # Method dataclass
):
"""Build a docments-style signature for a method (used inside classes)."""
Source
def method_signature_text(
m, # Method dataclass
): # signature reconstruction, indented as a class method
"Build a docments-style signature for a method (used inside classes)."
lines = [f"def {m.name}("]
for p in m.params:
lines.append(internal_fmt_param(p))
lines.append(internal_fmt_return(m.ret))
if m.doc:
lines.append(f' """{m.doc}"""')
return '\n'.join(lines)
render_export
def render_export(
mod, # parent Module (for anchor id)
exp, # Export to render
):
"""Render one export: heading, signature block, methods (if class), full source disclosure."""
Source
def render_export(
mod, # parent Module (for anchor id)
exp, # Export to render
): # a <section> tag for this export
"Render one export: heading, signature block, methods (if class), full source disclosure."
parts = [
h.h3(id=_export_id(mod, exp))(exp.final_name),
]
# If the final name differs from the original, show that
if exp.final_name != exp.name:
parts.append(h.small({"style": "--contrast: 0.6"})(
"originally ", h.code(exp.name),
))
# Signature + docstring as one highlighted code block
parts.append(h.pre()(h.code({"class": "python"})(signature_text(exp))))
# For classes: render each method as a nested signature block
if exp.methods:
parts.append(h.h4({"style": "--contrast: 0.7"})("Methods"))
for m in exp.methods:
if m.name.startswith('_') and m.name != '__init__':
continue # skip private methods except __init__
parts.append(h.pre()(h.code({"class": "python"})(method_signature_text(m))))
# Full source, collapsed by default
parts.append(h.details(
h.summary({"style": "--contrast: 0.7; cursor: pointer"})("Source"),
h.pre()(h.code({"class": "python"})(exp.clean_src)),
))
return h.section({"class": "surface stack"})(*parts)
render_module_setup
def render_module_setup(
mod, # Module containing imports/consts/setup lists
):
"""Render the module's imports / consts / setup inside a collapsed <details>."""
Source
# ── Module-setup block (imports / consts / setup) ────────────────────────────
def render_module_setup(
mod, # Module containing imports/consts/setup lists
): # a <details> tag if there's setup content, else None
"Render the module's imports / consts / setup inside a collapsed <details>."
has_any = mod.imports or mod.consts or mod.setup
if not has_any: return None
blocks = []
if mod.imports:
src = '\n'.join(i.src for i in mod.imports)
blocks.append(h.div(
h.small({"style": "--contrast: 0.6"})("IMPORTS"),
h.pre()(h.code({"class": "python"})(src)),
))
if mod.consts:
src = '\n'.join(c.src for c in mod.consts)
blocks.append(h.div(
h.small({"style": "--contrast: 0.6"})("CONSTANTS"),
h.pre()(h.code({"class": "python"})(src)),
))
if mod.setup:
src = '\n'.join(s.src for s in mod.setup)
blocks.append(h.div(
h.small({"style": "--contrast: 0.6"})("SETUP"),
h.pre()(h.code({"class": "python"})(src)),
))
return h.details({"class": "surface stack"},
h.summary({"style": "--contrast: 0.7; cursor: pointer"})("Module setup"),
*blocks,
)
render_module_panel
def render_module_panel(
mod, # Module to render
):
"""Render one module's content as a panel (hidden unless $current matches)."""
Source
# ── Module rendering (one tab panel per module) ──────────────────────────────
def render_module_panel(
mod, # Module to render
): # a <div> panel that shows/hides based on $current
"Render one module's content as a panel (hidden unless $current matches)."
children = [
h.h2({"id": mod.name})(mod.name),
]
setup_block = render_module_setup(mod)
if setup_block is not None:
children.append(setup_block)
for exp in mod.public_exports:
children.append(render_export(mod, exp))
return h.div(
{"data-show": f"$current === '{mod.name}'",
"class": "stack"},
*children,
)
render_tabs
def render_tabs(
proj, # Project
):
"""Render the tab strip — each button flips $current to that module name."""
Source
# ── Navigation & sidebar ─────────────────────────────────────────────────────
def render_tabs(
proj, # Project
): # nav element with a button per module
"Render the tab strip — each button flips $current to that module name."
modules = proj.nonempty_modules
buttons = [
h.button(
{"class": "btn",
"data-on:click": f"$current = '{m.name}'",
"data-class": f"{{active: $current === '{m.name}'}}"},
m.name,
)
for m in modules
]
return h.div({"class": "stack"})(*buttons)
render_sidebar
def render_sidebar(
proj, # Project
):
"""Render the 'on this page' sidebar, scoped to the active module."""
Source
def render_sidebar(
proj, # Project
): # aside content — per-module on-this-page lists (only active one visible)
"Render the 'on this page' sidebar, scoped to the active module."
lists = []
for mod in proj.nonempty_modules:
if not mod.public_exports: continue
items = [
h.li(h.a({
"href": f"#{_export_id(mod, exp)}",
"data-on:click": f"$current = '{mod.name}'",
})(exp.final_name))
for exp in mod.public_exports
]
lists.append(h.div(
{"data-show": f"$current === '{mod.name}'"},
h.small({"style": "--contrast: 0.6"})("ON THIS PAGE"),
h.ul({"role": "list", "class": "stack", "style": "--space: -1"},
*items),
))
return h.div({"class": "stack"})(*lists)
render_header
def render_header(
proj, # Project
):
"""Render the top-of-page project header."""
Source
# ── Header ───────────────────────────────────────────────────────────────────
def render_header(
proj, # Project
): # header content with name, version, description, optional repo link
"Render the top-of-page project header."
meta = proj.meta
title_bits = [meta.name or 'Untitled']
if meta.version:
title_bits.append(h.small({"style": "--contrast: 0.6"})(f"v{meta.version}"))
children = [
h.div({"class": "cluster", "style": "justify-content: flex-start"},
*title_bits),
]
if meta.desc:
children.append(h.p(meta.desc))
if meta.repo_url:
children.append(h.p(h.a({"href": meta.repo_url})(meta.repo_url)))
return h.div({"class": "stack"})(*children)
render_page
def render_page(
proj, # the complete parsed Project
):
"""Assemble the full HTML page for a Project."""
Source
# ── Full page ────────────────────────────────────────────────────────────────
def render_page(
proj, # the complete parsed Project
): # a Safe string of the complete HTML document
"Assemble the full HTML page for a Project."
modules = proj.nonempty_modules
if not modules:
return h.html_doc(h.head(h.title("No modules")), h.body(h.p("Empty project")))
# Initial signal: show the first module
init_current = modules[0].name
# On load: if the URL hash contains a module_export anchor, switch to that module.
# Expression parses '#utils_foo' → 'utils' and sets $current if it's a valid module.
module_names_js = json.dumps([m.name for m in modules])
on_load = (
f"const mods = {module_names_js}; "
"const hash = location.hash.slice(1); "
"if (!hash) return; "
# If hash is a plain module name, use it. Otherwise strip to first '_' segment.
"const exact = mods.includes(hash) ? hash : null; "
"const prefix = hash.includes('_') ? hash.split('_')[0] : null; "
"const match = exact || (mods.includes(prefix) ? prefix : null); "
"if (match) $current = match;"
)
body_content = h.body(
{"class": "app",
"data-signals": json.dumps({"current": init_current}),
"data-init": on_load},
h.header({"id": "header", "style": "padding: var(--s)"})(
render_header(proj),
),
h.nav({"id": "nav", "style": "padding: var(--s)"})(
render_tabs(proj),
),
h.main({"id": "main", "class": "surface", "style": "padding: var(--s)"},
*[render_module_panel(m) for m in modules],
),
h.aside({"id": "aside", "style": "padding: var(--s)"})(
render_sidebar(proj),
),
)
head_content = h.head(
h.meta(charset="utf-8"),
h.meta(name="viewport", content="width=device-width, initial-scale=1"),
h.title(f"{proj.meta.name} docs" if proj.meta.name else "Documentation"),
h.Favicon("📚"),
h.Color_type_css(),
h.Pointer(),
h.Highlight(),
h.Datastar(),
)
return h.html_doc(head_content, body_content)
build_docs_html
def build_docs_html(
proj, # complete parsed Project
path = None, # output path, defaults to {root}/{docs}/index.html
):
"""Write index.html alongside the existing llms.* files."""
Source
def build_docs_html(
proj, # complete parsed Project
path=None, # output path, defaults to {root}/{docs}/index.html
): # status message
"Write index.html alongside the existing llms.* files."
from pathlib import Path
docs = Path(proj.config.root) / proj.config.docs
docs.mkdir(parents=True, exist_ok=True)
out = Path(path) if path else docs / 'index.html'
out.write_text(str(render_page(proj)))
return f"Wrote HTML docs to {out}"