"""Data model persisted in the kanta database. The site structure is a tree of Nodes. Every node is a menu label with a configurable title and slug (its key in the parent's ``children``); the URL path is the chain of slugs from the top level. ``content`` is the node's Markdown page, or None for a pure category label, whose URL renders a placeholder page while nav links point at its first child. """ from datetime import UTC, datetime import msgspec class Node(msgspec.Struct, omit_defaults=True): """One item of the site hierarchy. Siblings are ordered by the fractional ``order`` key (never list positions): a moved item takes a fresh key relative to its new siblings, all other items keep theirs. The front page is whichever top-level node has slug "" (URL "/") — an item parallel to the other main-level pages, not their parent, so it cannot have children. Renaming it away leaves no front page ("/" redirects to the first nav item); any childless top-level node can take the empty slug. """ title: str = "" order: float = 0 #: Markdown source of the node's page; None = pure category label #: (its URL renders a placeholder page). content: str | None = None #: Raw HTML for the header banner (img, styled div, canvas+script...), #: rendered after the banner design's artwork so author code always #: wins over the design's own styles. #: Empty inherits the nearest ancestor's banner, front page last. banner: str = "" #: Banner design: a theme folder name (its banner.css/banner.svg), #: "" = explicitly no design, None = inherit (nearest ancestor, front #: page last, then the active theme's own design). banner_design: str | None = None published: bool = True children: dict[str, "Node"] = {} created: datetime = msgspec.field( default_factory=lambda: datetime.now(UTC), ) modified: datetime = msgspec.field( default_factory=lambda: datetime.now(UTC), ) class Page(msgspec.Struct, omit_defaults=True): """Legacy flat page record, from before the tree model. Kept only so old databases still decode; app.py migrates any entries into ``Data.menu`` on startup and clears this. """ title: str markdown: str published: bool = True order: float = 0 banner: str = "" created: datetime = msgspec.field( default_factory=lambda: datetime.now(UTC), ) modified: datetime = msgspec.field( default_factory=lambda: datetime.now(UTC), ) class Data(msgspec.Struct): """Root object of the kanta database. Owned and edited in place by us.""" #: Top-level menu items by slug; "" is the front page. menu: dict[str, Node] = {} #: Content-addressed file store: name (blake3 hash prefix + extension) #: -> bytes, served immutable at "/_f/{name}". Absolute URLs that stay #: valid when pages move. files: dict[str, bytes] = {} #: Bumped on every structure/content write, so page ETags (which embed #: it) invalidate cached copies when navigation-affecting changes happen. version: int = 0 #: Site name shown in the header and