Manifest ReferenceChapters & Pages

Chapters & Pages

Chapter, Page, and PageLayout — how PanelWave organizes panels into chapters, optional print-style page layouts, and panel placements.

chapters is a required top-level array (min 1). Each Chapter owns a set of panels, the flow graph that connects them, and — optionally — Pages that arrange those panels spatially for page-view formats (print, big-screen spreads).

Panels always live in the chapter's panels map; pages only place them. A chapter without pages is still fully readable panel-by-panel via the graph.

Chapter

PropertyTypeRequiredDescription
idIdentifierYesChapter ID (referenced by paywall rules with scope: "chapter")
panelsobject (ID → Panel), min 1 entryYesAll panels of the chapter, keyed by panel ID (keys are validated as Identifiers)
graphGraphYesEntry panel and directed edges defining reading flow
titleLocalizedString—Chapter title
pagesPage[] (unique)—Page layouts for page-view formats
sequenceAudioTracksSequenceAudioTrack[] (unique)—Audio tracks spanning multiple panels within the chapter — see Audio

Page

PropertyTypeRequiredDescription
idIdentifierYesPage ID
layoutPageLayoutYesFormat and panel placements — see below
titleLocalizedString—Page title
readingOrderIdentifier[] (unique)—Panel IDs in reading order on this page
visualobject—Page appearance — see below
transitionsobject—in / out, each a Transition applied when entering/leaving the page

Page.visual

PropertyTypeConstraintsDescription
background_colorColorHexhex colorPage background color
background_imageIdentifier | Uri—Background image (asset catalog ID or URI)
background_textureIdentifier | Uri—Background texture (asset catalog ID or URI)
marginsobjecttop/right/bottom/left: integer 0–100, default 0Page margins in pixels

PageLayout

PropertyTypeRequiredDescription
formatOutputFormatYesWhich output format this layout targets (e.g. bigscreen-landscape, a4-portrait)
placementsPlacement[] (min 1)YesWhere each panel sits on the page — see below
canvasSizeobject—width/height (integers ≥ 100). Reference canvas for the CMS editor UI only — not used for player rendering
gridHelperobject—CMS editor grid: cols (1–100, default 12), rows (1–100, default 8), visible (default true), snapEnabled (default true), snapDistance (0.001–0.1, default 0.01, normalized units)

Placements

Each entry in placements positions one panel using normalized page coordinates:

PropertyTypeRequiredConstraintsDescription
panelIdIdentifierYes—Key into the chapter's panels map
xnumberYes0–1Left edge (0 = page left, 1 = page right)
ynumberYes0–1Top edge (0 = page top, 1 = page bottom)
wnumberYes0.001–2.0Width as a fraction of page width (> 1 allows bleed/overflow)
hnumberYes0.001–2.0Height as a fraction of page height (> 1 allows overflow)
zinteger—0–9999, default 0Stacking order for overlapping panels (higher = on top)
rnumber—−180–180, default 0Rotation in degrees, clockwise
originobject—x/y: 0–1, default 0.5Transform origin for rotation (0.5/0.5 = center)
vx, vynumber—0–1Visible-area position relative to the panel — crop window origin. If omitted, the full panel is visible
vw, vhnumber—0.001–1Visible-area size relative to the panel

The vx/vy/vw/vh quartet lets a page show only a crop of a panel (e.g. a page spread showing the top half, while panel view shows everything).

Example

Adapted from the bundled minimal sample — one page with three side-by-side panels:

{
  "chapters": [
    {
      "id": "ch-1",
      "title": { "en-US": "Chapter One" },
      "pages": [
        {
          "id": "pg-1",
          "title": { "en-US": "Page 1" },
          "layout": {
            "format": "bigscreen-landscape",
            "canvasSize": { "width": 3840, "height": 2160 },
            "gridHelper": { "cols": 12, "rows": 8, "visible": true, "snapEnabled": true, "snapDistance": 0.01 },
            "placements": [
              { "panelId": "p1", "x": 0.0,    "y": 0.0, "w": 0.3333, "h": 1.0, "z": 0, "r": 0 },
              { "panelId": "p2", "x": 0.3333, "y": 0.0, "w": 0.3333, "h": 1.0, "z": 0, "r": 0 },
              { "panelId": "p3", "x": 0.6667, "y": 0.0, "w": 0.3333, "h": 1.0, "z": 0, "r": 0 }
            ]
          },
          "readingOrder": ["p1", "p2", "p3"],
          "visual": { "background_color": "#111111" },
          "transitions": {
            "in": { "type": "fade", "durationMs": 300 }
          }
        }
      ],
      "panels": {
        "p1": { "layers": [ { "kind": "image", "id": "ly-p1", "assetId": "img-p1", "z": 0 } ] },
        "p2": { "layers": [ { "kind": "image", "id": "ly-p2", "assetId": "img-p2", "z": 0 } ] },
        "p3": { "layers": [ { "kind": "image", "id": "ly-p3", "assetId": "img-p3", "z": 0 } ] }
      },
      "graph": {
        "entry": "p1",
        "edges": [
          { "from": "p1", "to": "p2" },
          { "from": "p2", "to": "p3" }
        ]
      }
    }
  ]
}

Page view vs. panel view

Whether readers see whole pages or single panels depends on the output format's FormatPreset (pageView / panelView) and per-panel formatViews overrides (Panels). A typical setup enables page view for bigscreen-landscape and print formats while mobile stays panel-by-panel using each panel's minimalFocusRect.

Schema validation does not verify that placements[].panelId and readingOrder[] reference existing panel keys, or that graph.entry exists — check references in your pipeline. See Validation.