Document Structure
A PaperDocument is a tree: Document contains Slides, each slide contains Nodes. The format is the canonical input for both the self-hosted @paperjsx/core engine and the hosted V2 API.
PaperDocument
├── meta: { title, author }
├── slideSize: { width, height } (default 960x540 px)
├── theme?: ThemeConfig
├── template?: Buffer (self-hosted only)
└── slides: PaperSlide[]
└── children: PaperNode[]
Minimal Working Example
{
"type": "Document",
"meta": { "title": "Hello World" },
"slides": [
{
"type": "Slide",
"children": [
{
"type": "Text",
"style": {
"position": "absolute",
"top": 200, "left": 80, "width": 800,
"fontSize": 32, "fontWeight": "bold",
"textAlign": "center"
},
"content": "Hello, CmdCal"
}
]
}
]
}
Node Types
CmdCal defines 9 node types. Every node accepts a style property using FlexStyle for layout.
Text
Renders a text box. Supports plain strings or rich text runs with per-run styling.
{
"type": "Text",
"style": { "position": "absolute", "top": 100, "left": 60, "width": 400, "fontSize": 18 },
"content": "Plain string"
}
For rich text, use content as an array of TextRun objects, or use paragraphs for multi-paragraph control with bullets, alignment, and indentation:
{
"type": "Text",
"paragraphs": [
{ "runs": [{ "text": "Bold intro.", "style": { "fontWeight": "bold" } }] },
{ "runs": [{ "text": "Second paragraph." }], "spaceBefore": 8 }
]
}
View
A shape container. Supports children, preset shapes via shapeType, and custom geometry.
{
"type": "View",
"style": { "position": "absolute", "left": 60, "top": 100, "width": 200, "height": 120, "backgroundColor": "#2563EB" },
"shapeType": "roundRect",
"children": [
{ "type": "Text", "style": { "fontSize": 14, "color": "#FFFFFF", "padding": 12 }, "content": "Card" }
]
}
Image
Embeds raster or SVG images. Supports base64 data URLs and HTTPS URLs.
Key properties: src (required), svgSrc (native SVG embed with PNG fallback in src), crop ({ left, top, right, bottom } as percentages), borderRadius, imageEffects.
Table
Renders a native PPTX table. Defined via tableData containing columns (pixel widths), rows (arrays of cells), and optional style for banding and borders.
Chart
Renders a native OOXML chart. Defined via chartData with chartType (one of: bar, line, area, pie, doughnut, scatter, bubble, radar, combo, waterfall, stock, funnel, treemap, sunburst, histogram, boxWhisker), categories, series, axis configuration, and data labels.
Group
A logical grouping of child nodes. Does not render a visible shape but applies a shared transform and supports locks for edit protection.
Connector
A line connecting two points or shapes. Defined via connectorType (straight, elbow, curved), start/end coordinates, and optional arrow configs.
Video and Audio
Embed media files. Both accept src (URL or data URI), mimeType, and playback options (autoPlay, loop, showControls, volume).
FlexStyle Layout System
All nodes use FlexStyle for positioning. The engine uses Yoga (flexbox) for layout computation.
interface FlexStyle {
// Positioning
position?: "relative" | "absolute";
top?: number; right?: number; bottom?: number; left?: number;
// Dimensions
width?: number | `${number}%`;
height?: number | `${number}%`;
minWidth?: number | `${number}%`;
maxWidth?: number | `${number}%`;
// Flex container
flexDirection?: "row" | "column";
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around";
alignItems?: "flex-start" | "flex-end" | "center" | "stretch";
flexWrap?: "nowrap" | "wrap" | "wrap-reverse";
gap?: number;
// Flex item
flexGrow?: number;
flexShrink?: number;
flexBasis?: number | `${number}%`;
// Spacing
padding?: number; margin?: number; // also paddingTop/Right/Bottom/Left
// Visual
backgroundColor?: ColorValue;
fill?: Fill;
borderWidth?: number; borderColor?: ColorValue;
opacity?: number; rotation?: number;
}
Color Formats
The ColorValue type accepts three formats:
- Hex string:
"#2563EB"(6-digit, no alpha) - Scheme token:
"accent1","dk1","lt1", etc. (resolved from the document theme) - Color modifier object: Applies tint, shade, or luminance transforms to a scheme color
{ "scheme": "accent1", "tint": 40, "lumMod": 75 }
Available modifier fields: tint (0-100), shade (0-100), lumMod, lumOff, satMod, satOff, hueMod, hueOff, comp, inv, gray.
Validation with Zod Schemas
The @paperjsx/core package exports Zod schemas for compile-time and runtime validation:
import { PaperDocumentSchema, PaperSlideSchema, PaperNodeSchema } from "@paperjsx/core/engine";
const result = PaperDocumentSchema.safeParse(myJson);
if (!result.success) {
console.error(result.error.issues);
}
These schemas enforce structural constraints like maximum array lengths (1000 nodes per slide, 16384 data points per chart series, 100 columns per table) and valid enum values for all string-union fields.
Hosted API: PresentationSpec
If you use the hosted V2 API, you can submit either a raw PaperDocument or a higher-level PresentationSpec with semantic slide types:
{
"version": "2.0",
"title": "Quarterly Review",
"accentColor": "#2563EB",
"slides": [
{ "slideType": "title-body", "title": "Q4 Results", "body": ["Revenue up 23%", "Margin expanded 400bp"] },
{ "slideType": "kpi-grid", "title": "Key Metrics", "items": [
{ "label": "ARR", "value": "$4.2M", "trend": "up" },
{ "label": "NRR", "value": "118%", "trend": "up" }
]}
]
}
Available slide types: title-body, kpi-grid, comparison-table, market-map, timeline, org-chart, waterfall, tombstone-grid.
The protocol compiler converts PresentationSpec into a PaperDocument internally before rendering.