Skip to content
ReferencePages in the catalogue

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:

ts
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.

tsx
// 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:

ts
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.