Pages in the catalogue
How a complete HTML document joins the catalogue beside screens and use-case flows.
Delivery Status
Pages use file-derived paths and complete-document rendering. The removed content previews plan implements page-only historical capture and delivery.
Purpose And Boundary
Every browsable document belongs to the same catalogue as screens and use cases. A page’s path derives from its file under the path contract, so its folder is the directory that holds it and its own slug is the leaf. Whole-document rendering remains supported independently of navigation.
A page is one complete authored HTML document, such as a printable document
or an existing multi-state reference page, registered with definePage. A
Markdown file is not a page; it is a document that
Mokly renders itself. A page does not require invented mobile/desktop
variants. Screens continue to own their real viewport and color-scheme views;
use-case steps continue to reference screens only. This contract adds no PDF
parser, browser scripting privilege, or page comparison engine. A page
callback always returns HTML, including printable documents.
Public Authoring
The root package exports definePage and its public input and definition
types. The input is:
interface PageInput extends EntryInput {
render: () => string;
tags?: readonly string[];
}PageDefinition adds kind: "page" and the same private definition brand and
module attribution as other definitions. Common metadata (slug, path,
movedFrom, title, description, dependencies, relatedDocs, optional
rationale) follows EntryInput. The slug defaults to the module’s file name
under the entry module contract; the document is
written at <path>/index.html. Page tags use the existing optional, unique
kebab-case tag contract.
// specs/documents/account-statement.mockup.tsx
import { definePage } from "@mokly/mokly";
import { source } from "./statement.source.js";
export default definePage({
title: "Account statement",
description: "The printable account statement.",
render: source,
relatedDocs: [],
dependencies: ["specs/documents/statement.source.tsx"],
});The example assumes an existing source(): string export and, under the
default specs root, generates static/mokly-generated/documents/account-statement/index.html
for the path documents/account-statement. A page named index.mockup.tsx,
or declaring slug: "index", is its folder’s own page under the
folder row rules.
Pages apply the general unknown-field rule to every undeclared input key,
including keys with an undefined value. Untyped JavaScript receives the same
validation as typed authoring. A use-case screenPath cannot name a page. Duplicate paths and
grammar violations fail registry validation under the
path contract.
Build And Output
Render screen views first, then complete pages, with deterministic catalogue ordering within each kind. This preserves existing consumer document helpers that collect a shared stylesheet after screen rendering.
The compiler calls each page callback once per compilation, synchronously, after registry validation. Non-functions, promises, non-string return values, throws, and incomplete HTML fail with the page path and source location before any output changes. Callbacks must be deterministic and return complete HTML; they must not write output themselves.
A page generates exactly one file at mockupsDir/mokly-generated/<path>/index.html under the
artifact path contract. The screen renderer does
not wrap it, inject stylesheets, or generate extra variants. The consumer
continues to own the document’s styles, responsive markup, and render context.
Pages are one light document regardless of the catalogue color-scheme setting.
Registry imports, page callbacks, imported document modules, and screen rendering share the existing consumer bundle and React runtime. Imported sources participate in watched rebuilds. Declared dependencies retain their metadata and evidence role; an input edit alone does not add a page whose document, rendered resources, and reviewable metadata remain unchanged.
The complete output passes the shared child-control adapter, logical-link and
fragment validation, final metadata checks,
HTML/CSS/resource validation, and Review-ignore validation. The existing
transaction replaces the entire mokly-generated/ tree with rollback and
symlink confinement. Authored files outside it remain untouched. Identity-derived
page paths remain collision-checked. The plain generated marker conveys no
ownership; the validated manifest and pending output set bind each document.
Manifest And Runtime Model
New builds write schema v9 at mokly-generated/mokly-manifest.json:
interface ManifestPage extends ManifestEntryBase {
kind: "page";
tags?: readonly string[];
}ManifestEntry includes pages, documents, screens, use cases and
components, and its base kind union includes page. All existing common
fields remain, including path and required declaredDependencies. No entry
carries a file name: readers derive <path>/index.html from the path. Pages
have no views, viewport arrays, callbacks, or screen-only fields in the
manifest. Preserve deterministic entry sorting, dependency normalization, and
serialization conventions.
sourceFiles follows the source-protection contract:
the complete config/consumer authoring graph, validated against current inputs.
Reserved source basenames stay protected even when unimported. Serving, resource
validation, Review, and publication share that policy.
Current and baseline readers accept the same v9 shape under the
compatibility contract. Catalogue lookup,
hierarchy, navigation, breadcrumbs, details, search, route targets, and static
publication consume one validated entry model. Page leaves use
entry:<path>; folder identities follow the
folder contract. A page that is its folder’s own page is
named by the author through its slug.
Browse And Navigation
A page appears once at its path, using the existing page icon. The heading uses its title; breadcrumbs use the resolved titles of its folders; the path chip, the shared search rule, tag picker, details, and home counts include pages. Details show authored description, rationale, dependencies, and related docs.
Reuse the complete-document frame, responsive shell, expansion control, ownership authentication, and script-free sandbox. Do not add device chrome around chrome already authored in the document. Hide controls implying screen viewport variants, page color variants, or page comparisons; remember the user’s screen choices when navigating back to a screen. Mobile drawer and desktop navigation show the same path-derived folders.
/view/<path>/ and /static/mokly-generated/<path>/index.html resolve a page with the
existing GET/HEAD behavior. MockLink and mockLink accept its path. Their
portable target is its single generated file with the validated optional
anchor; Browse opens the canonical page and reveals its ancestor folders.
Page-to-screen logical links resolve to the desktop/light view; a page has
no per-viewport render context. Screen-to-page links target the same document
from every screen viewport and scheme. Use-case links still resolve through
their first screen.
Validate page anchors against the final single document, including after final rendering. Preserve the existing fragment grammar, duplicate query rejection, invalid-anchor behavior, safe URL handling, link-owner authentication, and closure-limited authored HTML. Served and published pages must handle direct URLs, in-frame navigation, Back/Forward, and fragment restoration identically.
Changes, Watch, And Publishing
Pages participate in the All/Changes filter wherever review is enabled. Compare stable page metadata, the generated document, and its rendered local resources against the Git branch point. Apply the shared material-change rules, including paired ignore regions; source/dependency changes alone do not affect membership. Moving a page to another folder changes its path, so the move contract pairs it with its baseline and marks it moved rather than removed and added; pages and documents are the kinds it may pair by similarity. Renaming a folder title changes no path and marks no page. Moving unrelated source composition without changing a page’s inputs does not mark every page in that module changed. Regression coverage must distinguish metadata change attribution from tree position.
Screen comparison generation and use-case impact propagation retain their screen-only boundary. Adding page support must not make those paths assume every non-use-case entry has screen views. Pages expose Current only and never fabricate comparisons; the only historical capture for a page is its removed page preview. The catalogue-change contract owns the shared typed impact/removal snapshot, path-keyed removal, and flat removed-page rows in Changes. Baseline folder titles stay in details even when every ancestor is deleted; no historical folder tree is synthesized.
Watch rebuilds imported sources, recomputes page impact before notification, and restores disclosures by the keys defined in the disclosure persistence contract. Moving a file updates navigation and breadcrumbs after reload; the runtime owns obsolete-key handling. Active sections and ancestors open through the existing reveal logic.
Static publishing includes each page’s shell page, generated document and resources, validated anchor navigation, metadata, search/filter behavior, and the current hierarchy. The publication option defaults to the current catalogue; only an explicit opt-in includes Changes, removed registered-page state, and screen comparison artifacts. Removed pages are absent from ordinary publication. Preserve transactional publication and generate no page comparisons. Local development retains its Git-aware Changes behavior.
Acceptance
Authoring, schema, build, links, server, browser, watcher, comparison-regression, and packed-consumer tests cover normal pages, obsolete-config rejection, and transactional replacement of the generated tree. Use a shared folder containing a screen, page, and use case; a top-level page; a page that is its folder’s own page; and a folder that holds pages and components. Verify output determinism and every existing screen safety boundary.