Pulp Engine Visual Editor — Guide
The visual editor (apps/editor) is a browser-based tool for building and editing
Pulp Engine template definitions without writing JSON by hand. It runs alongside the
existing API and uses the same render pipeline for previews.
The editor is an operator-facing tool intended for internal template authors — access is gated by operator-held credentials, designed for internal team use within your own deployment.
Deployment modes
| Context | Editor URL | API URL |
|---|---|---|
| Bundled deployment (Docker image) | http://[host]:3000/editor/ |
http://[host]:3000 |
Local development (pnpm dev from source) |
http://localhost:5174 |
http://localhost:3000 |
Bundled deployment: The editor is included in the Docker image and served by the API at /editor/. No separate hosting is required. The editor makes same-origin requests to the API — no CORS configuration needed for the editor itself. For live preview, PREVIEW_ROUTES_ENABLED=true must be set on the API. See docs/deployment-guide.md § Visual Editor for details.
Local development: The editor runs as a separate Vite dev server on port 5174. The API defaults to port 3000. Both start together with pnpm dev from the repo root.
Port conflict with Docker Desktop: Docker Desktop on Windows may bind port 3000. If you run Docker Desktop alongside the dev server, set
PORTto any free port in the root.envand createapps/editor/.env.development.localwithVITE_API_URL=http://127.0.0.1:<same-port>to match. The.env.development.localfile is gitignored — it is a per-machine override and is not committed.
Start the editor in local development:
pnpm --filter @pulp-engine/editor dev
# → http://localhost:5174
Authentication:
When the API has credentials configured, the editor shows a “Sign in” screen on first load. The login form adapts to the credential mode configured on the API server.
Named-user mode (v0.23.0+, EDITOR_USERS_JSON set on the API):
Each team member has a personal key. Enter your personal key in the API Key field and click Sign in. No actor/identity field is shown — your identity is server-verified from the user registry. After login, your display name (or user ID) appears as an identity pill in the editor toolbar.
Shared-key mode (default, no EDITOR_USERS_JSON):
Enter the value of API_KEY_EDITOR (or API_KEY_ADMIN) in the API Key field. Optionally enter a display name or identifier in the “Your name or identifier” field — this operator-supplied actor label is embedded in the session token and appears in server-side audit logs alongside template and asset write events. This is a caller-asserted label, not a verified identity — it is suitable for audit traceability within a trusted team.
OIDC / SSO mode (server has OIDC_DISCOVERY_URL configured):
The login form shows a Sign in with <provider> button above the API-key input (the provider name comes from OIDC_PROVIDER_NAME, default SSO). Clicking it redirects to the identity provider’s login page (PKCE auth-code flow). On successful callback the editor receives a session token the same way the key-based path does and marks the session as OIDC-authenticated so silent refresh uses the OIDC exchange endpoint instead of the editor-token mint endpoint. Key-based login continues to work alongside OIDC — the form shows both affordances. See oidc-guide.md for server configuration.
Common to both modes:
- The editor exchanges the submitted key for a short-lived session token stored in
sessionStorage(cleared on tab close). NoVITE_API_KEYor frontend environment variable is needed. API_KEY_EDITORgrants template management, asset management, and preview access — everything the editor needs — without exposing production render endpoints to the browser.- In local dev with no server credentials configured, the login gate is bypassed automatically.
- Session token lifetime defaults to 8 hours and is configurable via
EDITOR_TOKEN_TTL_MINUTESon the API server (range: 5–1440 minutes). When the session expires or is invalidated by an operator, the editor returns to the login form and shows a “Your session has ended” notice — re-enter the key to continue. - If
API_KEY_EDITORis rotated on the server, outstanding session tokens are immediately invalidated. To rotate without invalidating outstanding sessions, useAPI_KEY_EDITOR_PREVIOUS— see the operator runbook for the near-zero-downtime procedure. - An operator can also invalidate all outstanding sessions without key rotation by setting
EDITOR_TOKEN_ISSUED_AFTER(v0.19.0+) — see the runbook for the procedure. - In named-user mode, a single user’s sessions can be revoked by setting
tokenIssuedAfteron their registry entry — see deployment-guide.md § Named-User Mode for per-user revocation. - If the API is unreachable at startup, the editor shows an “API unavailable” error with a Retry button instead of the login form. This prevents confusing API outages with authentication failures.
See docs/api-guide.md § Editor auth for the full auth endpoint reference.
The API must also be running for load-from-API, save, version history, and preview:
pnpm --filter @pulp-engine/api dev
# → http://localhost:3000
Or start both together from the repo root:
pnpm dev
What the editor can do today
| Capability | Status |
|---|---|
| Build templates visually (all 28 content node types including grids, shapes, checkboxes, combs, KPI, layout slots, pivotTable, barcode and templateRef) | ✓ |
| Edit node properties in a right-side panel | ✓ |
| Outline panel — navigate and reorder nodes | ✓ |
| Undo / redo (100-level history) | ✓ |
| Drag-and-drop reordering within and across parent containers | ✓ |
| Keyboard reorder and cross-container Move to — preserves IDs, one undo step | ✓ |
| Accessibility advice, light/dark axe gate and Windows High Contrast support | ✓ |
| Human-readable drag/reorder announcements for screen readers | ✓ |
| Inline Handlebars syntax warnings | ✓ |
| Unsaved-change protection (confirm before discard) | ✓ |
| Dirty-state indicator (• dot in header) | ✓ |
| Template source badge (API / local / history copy) | ✓ |
| Load a template from the API (by key) | ✓ |
| Save / update a template to the API | ✓ |
| Version history — browse, open, visual compare, and restore past versions | ✓ |
Version labels — promote specific versions with named labels (e.g. stable, draft) |
✓ |
Scheduled delivery — cron-driven render jobs to email / S3 / webhook targets (requires SCHEDULE_ENABLED; Postgres or SQL Server) |
✓ |
| Find / replace across the template tree | ✓ |
| Schema explorer — scope-aware fields, drag-to-bind, insert and copy | ✓ |
| Autosave drafts — local-storage safety net for in-progress edits | ✓ |
Embed mode — editor runs inside a customer application via <pulp-engine-editor> custom element |
✓ |
| Plugin-registered custom nodes — render, edit, and validate plugin-supplied node types | ✓ |
| Load a template from a local JSON file or paste | ✓ |
| Export the current template as a JSON file | ✓ |
| Preview rendered HTML in-editor (requires API) | ✓ |
| Download rendered PDF (requires API) | ✓ |
| inputSchema editor — lossless constraints, inference/import and JSON editing | ✓ |
| fieldMappings editor — structured source → target panel with jexl expression field | ✓ |
| Document tab — page margins, header/footer HTML authoring | ✓ |
| Header/footer snippet buttons (confirm-before-replace guard) | ✓ |
| Navigable validation panel — errors and warnings with node jump | ✓ |
| Editor session token authentication (login gate + 8-hour HMAC token) | ✓ |
| Handlebars / expression validation | ✓ (syntax errors, unknown helpers, missing helper arguments, invalid date format literals, unknown binding paths, jexl expression syntax, required field checks — full evaluation at render time) |
| Multi-template tabs — multiple templates open simultaneously | ✓ |
| Duplicate / Save As — copy current template to a new key | ✓ |
Command menu — keyboard-driven quick actions (Ctrl+K / Cmd+K) |
✓ |
| Image asset management — upload, browse, and delete image files | ✓ |
| Paper size and orientation — configurable per template in Document tab | ✓ |
| richText inline canvas editing — double-click (or Enter) to edit on canvas | ✓ |
| richText Visual editor — Tiptap toolbar with formatting, colour palette, lists, links | ✓ |
| richText accessibility — WCAG-compliant toolbar, roving tabindex, aria-pressed, focus rings | ✓ |
| Empty-state affordances on heading, text, table, and richText nodes | ✓ |
| Per-user named credentials and identity pill (v0.23.0) | ✓ |
OIDC / SSO login (when server has OIDC_DISCOVERY_URL configured) |
✓ |
AI template generation from a natural-language prompt (requires ANTHROPIC_API_KEY on server) |
✓ |
| Collapsible property groups — expandable/collapsible sections in the properties panel | ✓ |
| Per-node comments — attach editorial notes to any node (500-char limit) | ✓ |
| Insert from data ? tables, lists, cards, charts and pivots from schema or sample arrays | ✓ |
| System clipboard, save shortcut and canvas arrow navigation | ✓ |
| Node templates / favorites — save and reuse common node configurations | ✓ |
| Template linting hints with dismissals — best-practice suggestions with per-template dismissal | ✓ |
| Visual expression builder — point-and-click JEXL expression editor with live preview | ✓ |
| Handlebars playground — interactive expression testing with helper reference and history | ✓ |
Header at a glance
Pulp Engine [• My Template] [v1.0.4] [API] ↩ ↪ History Preview New Load Sample Open Load JSON Duplicate… Alice Smith Update*
──────────────────────────────────────────────────────────────────────────────────────────────────────
[ My Template × ] [ Invoice × ] [ + ] ← tab bar
-
Identity pill (named-user mode only) — shows the logged-in user’s display name (or user ID if no display name is set). Hovering shows “Signed in as …”. Not shown in shared-key mode or when no actor is stored.
-
• dot after the template name — unsaved changes exist
-
Version badge — the version currently loaded (updates to server version after save)
-
Source badge — shows where the template came from:
API(blue) — loaded from the API; Save willPUTto update itlocal(grey) — created from scratch, a file, or Load Sample; Save willPOSTto create ithistory copy(amber) — opened from version history; treated as a local copy until saved
-
Save button — labelled Update when in API mode, Publish when in local/history mode
Language
Use Language next to the theme selector to choose English, Deutsch, Français
or العربية. German, French and Arabic are Beta while native-language review
is pending. The choice is saved in pulp-engine-editor-locale. With no saved
choice, the editor follows the first supported language in navigator.languages;
regional tags such as fr-CA match French, and unsupported languages use English.
The language applies to editor controls, dialogs, validation, dates and relative times. Arabic mirrors the editor chrome, menus, outline arrow keys and preview splitter. Document content keeps its authored language and direction; an ordinary canvas page and preview remain LTR. Changing the UI language does not translate template text, schema titles, starter content or plugin labels, or change the document’s formatting locale.
An embedding host can set locale="fr" on either custom element and change it
live. A host-selected editor locale hides the Language menu unless
locale-selectable="true". See embed localisation
for the SDK and runtime protocol.
Contributing translations
apps/editor/src/i18n/en.ts owns the typed message keys. Add every new message
to locales/de.ts, locales/fr.ts and locales/ar.ts with the same parameters.
Use useT() in React or translate() with a useLocale() subscription when a
component reads translated module-level labels. Use UiText for sentences that
contain authored values or inline React markup, so translators can reorder the
whole sentence. Never split a sentence into translated fragments or build
plurals with an English suffix.
Keep {name} interpolation parameters and {{Handlebars}} examples intact.
Translate surrounding explanations, but preserve field paths, enum identifiers,
URLs, CSS syntax and other executable examples. Use Intl plural forms with an
other fallback; Arabic supports zero, one, two, few, many and other.
Route UI dates, numbers and relative times through the i18n formatters.
Diagnostics use message codes and parameters while retaining stable English
message values for callers. Unknown API and plugin detail remains as supplied.
For local error and status state, use useUiMessage with a uiMessage descriptor
so a displayed message follows runtime language changes. Its setter stays stable
across language changes. UiMessageError retains an English Error.message while
getUiMessage exposes its translated UI descriptor. Keep enum values and field
identifiers in ordinary state; they are not display messages.
To add a language, extend the locale types, supported-language list, native name,
lazy loader and pre-iframe host catalogue. Add its catalogue to
scripts/check-i18n.mjs, and provide the cron-description vendor locale if
available. Register the language’s direction in directionFor and verify menus,
outline navigation, splitter movement and document direction independently.
Run the editor lint and type checks, then inspect the UI with ?locale=en-XA
(accented and padded) and ?locale=ar-XB (RTL pseudo). Pseudo-locales are available
in development and through an explicit test query. The catalogue checker checks
keys, interpolation and Handlebars examples; the JSX guard prevents new literal
chrome. Use logical CSS and Tailwind properties. A physical CSS exception must
have an immediately preceding i18n-physical: comment explaining its authored
coordinate system. New non-English locales should remain Beta until reviewed by
native speakers.
Loading a template
From the API
- Click Open in the header.
- The picker fetches the template list from
GET /templatesand displays each template’s name and current version. - Click Open next to the template you want.
- The editor loads it in API mode — the source badge shows
API.
If the template list fails to load, a Retry button appears inline. The Cancel button is always available.
If you have unsaved changes, a confirmation prompt appears before the current template is replaced.
From a local file or JSON paste
- Click Load JSON in the header.
- Either click Open JSON file… and select a
.jsonfile, or paste aTemplateDefinitionJSON object directly into the text area. - Click Load.
The editor validates the top-level structure before loading (key, name, version, inputSchema, document must all be present). Detailed Handlebars or data-path errors are only caught at render time.
This template is in local mode — the source badge shows local.
Load Sample
Click Load Sample to load a built-in loan-approval-letter skeleton. Useful for
exploring the editor without a database connection. Loads in local mode.
New template
Click New to open the new-template dialog. Fill in a key, name, and version (semver), then choose a starting point from the 28 default public starter packs (Blank plus 27 tabbed packs).
Blank is pinned at the top as the default — an empty document with one section. When the server has AI template generation enabled (see below), a second pinned card ✨ Generate with AI sits next to Blank. Below the pinned row, packs are organised into 4 visible tabs: Documents, Financial, HR, Operations. Underlying pack categories include commercial, financial, hr, and operations (the Documents tab surfaces the commercial category), with Blank in the general category.
| Tab | Packs |
|---|---|
| Documents | Letter, Proposal, Meeting Minutes, Report, Certificate, Contract, NDA, Event Agenda, Product Sheet |
| Financial | Invoice (Recommended), Quote, Receipt, Credit Note, Expense Report, Timesheet, Statement, Stock Report |
| HR | Offer Letter, Pay Stub, Performance Review |
| Operations | Packing Slip, Purchase Order, Inventory Report, Sales Pivot (Single Dimension), Sales Pivot (Multi-Level + Subtotals), Component Showcase, Page Chrome |
Each pack card shows a short description and a bestFor label explaining the intended use case. The Invoice pack carries a Recommended badge. Selecting a pack auto-fills the template name from the pack’s default name.
Themed vs Plain
Once a non-Blank pack is selected, a Style toggle appears with two options (defaulting to Themed):
- Themed — packs ship with a per-category palette (deep blue for Financial/Operations, teal for Documents/Commercial, indigo for HR, graphite for general correspondence). h1 headings, accent dividers, themed table header rows, alternating body row tints, and accent footer/total rows all pick up the category palette.
- Plain — strips theme colours/backgrounds while preserving structural styles (
textAlign,fontWeight,fontSize, margins, paper size). Useful when you want to apply your own brand styling from scratch.
The toggle runs as a post-process pass over the generated template (applyPlainTheme(def)) — packs are authored once with theming baked in, and Plain mode walks the document tree dropping color, backgroundColor, borderColor / borderTop / borderBottom from style objects and the table row-style sets (headerRowStyle, bodyRowStyle, alternateRowStyle, footerRowStyle, totalRowStyle, tableStyle). Layout properties survive so the document keeps its structure.
The two new Sales Pivot packs in the Operations tab demonstrate the pivot table v1 (single row dimension, grand totals only) and v2 (multi-level row dimensions with subtotals) capabilities — useful as both feature showcases and starting points for hierarchical reports.
The editor creates the template definition from the selected pack and loads it in local mode.
All four entry points will prompt you to confirm before discarding unsaved changes.
Generate with AI (opt-in)
When the server has ANTHROPIC_API_KEY configured, the new-template dialog shows a ✨ Generate with AI card next to Blank. Click it and the category-tabs grid is replaced with a prompt panel:
- Type a natural-language description of the template you want — for example, “a one-page invoice with a header, a 3-column line-item table, and a total row at the bottom”. Prompts must be between 10 and 4000 characters; the counter under the textarea turns red if you go over.
- Click Generate. The button shows a spinner and a Cancel button appears next to it. Generation typically takes 20–40 seconds (the editor waits up to 2 minutes before timing out).
- You’ll know it worked when the four metadata fields below the panel auto-populate from the generated template’s
key/name/version/description, and the Create button (previously disabled) becomes enabled. There is no separate confirmation banner — the form’s state transition is the success signal. You can edit any of the auto-populated fields; your edits override the AI’s choices when you click Create. - Click Create as usual. The generated template loads into a fresh editor tab as an unsaved draft, ready for review and editing. The editor automatically seeds synthesized preview data (matching the template’s
inputSchema) into the sample-data panel, so your first preview/publish attempt passes without manual JSON entry. The synthesized values are valid but plain — strings show"Sample Text", numbers show0, dates show2025-01-15. Open the Preview panel’s data tab to replace them with realistic values for a better preview. Save the template through the normal Save flow.
You can Cancel at any point during generation, or close the dialog (Escape, click-outside, or the X) — the in-flight request is aborted cleanly. If generation fails, an inline error banner explains why and the Generate button re-enables for a retry. Common errors:
| Error | Meaning |
|---|---|
| “You’ve hit the generation rate limit. Wait a minute and try again.” | The server caps generations per credential (default 5/min). Wait and retry. |
| “The AI couldn’t produce a valid template. Try rephrasing your prompt to be more specific.” | The model produced output that failed schema validation across all retry attempts. Usually means the prompt is too vague or asks for an unsupported shape. |
| “Generation took longer than 2 minutes. Try a shorter or simpler prompt.” | Hit the 2-minute editor timeout. Long prompts and complex layouts are more likely to time out. |
| “AI generation isn’t enabled on this server. Refresh the page — capabilities may be out of date.” | The server’s ANTHROPIC_API_KEY was unset after your editor loaded. Refreshing fetches the current capabilities. |
The AI card is invisible when the server has not enabled the feature — there is no greyed-out fallback. The editor checks GET /capabilities on first mount; if you enable the feature on the server while the editor is already open, you need to refresh to see the card. Token usage and per-call cost details are not shown in the editor — operators can query GET /audit-events?event=template_generation for those, which is the system of record for AI generation cost.
Saving a template
Publish-readiness check
Before any save or publish is allowed, the editor runs a two-phase check:
- Static validation — checks Handlebars syntax, JEXL expression syntax, data path validity, link safety, field mapping coverage, and missing static asset references. Issues with
errorseverity block publish;warning-severity issues are advisory. - Render validation — calls
POST /render/validatewith the current template definition and sample data. This is a publish-readiness validation check that verifies the template is structurally valid and renderable. It never returns HTML or PDF output.
POST /render/validate is always available in production — it does not require PREVIEW_ROUTES_ENABLED. Operators do not need to enable preview routes just to publish templates.
If the API is unreachable (network failure) or returns an authentication, rate-limit, or server error, publish is blocked. Restore API connectivity and try again. The render check must complete successfully before publish is allowed.
Publish gate outcomes
The publish gate produces one of six outcomes:
| Outcome | Meaning | User action |
|---|---|---|
| All checks passed | Static validation and render preflight both succeeded | Click Publish / Update |
| Blocking issues | Static checks found errors (bad syntax, missing assets, etc.) | Fix the listed issues |
| Sample data must be a valid JSON object | The sample data in the Preview panel is not a JSON object (malformed, array, null, or other non-object value) — preflight cannot run | Open Preview, fix the sample data, then try again |
| Asset list could not be loaded | The API could not be reached while checking for referenced assets | Restore API connectivity and try again |
| Template failed to render | Static checks passed; server returned a classified render or validation error | Fix the template expression or data mapping |
| Validation check could not be completed | Network failure, authentication error, rate limit, or server error prevented the check | Restore connectivity or check server health, then retry |
API-loaded template (Update)
If the template was loaded via Open (from the API), clicking Update sends a
PUT /templates/:key request. The editor automatically includes the loaded version as
an If-Match precondition header, so concurrent writes from other users are detected
rather than silently overwritten. The API auto-bumps the patch version (e.g. 1.0.2 → 1.0.3)
and returns the new version. A success flash shows Updated to v1.0.3.
The save resets the undo history — treat it as an intentional save boundary.
Local / sample / file-loaded template (Publish)
If the template was loaded from a file, Load Sample, or created from scratch, clicking
Publish sends a POST /templates request to create a new database record.
After a successful create, the template switches to API mode and subsequent saves
will use PUT.
History copy (Publish)
Opening a past version from the version history creates a local copy (source badge:
history copy). Clicking Publish will POST it as a new template unless you first
change the key to a new unique value, or discard it and use the main template instead.
If the key already exists on the server, publish will fail. Change the key, or open the existing live template explicitly if your intent is to modify that template.
Save feedback
| Outcome | Flash message |
|---|---|
| Successful update | Updated to v1.0.X |
| Successful create | Created v1.0.0 (or whatever version was set) |
| Key conflict on create | A template with this key already exists. Change the key or open the existing template instead. |
| Concurrent write conflict | Automatic merge feedback, or Resolve the conflicting changes before updating. |
| Network error | Error text from the API or fetch exception |
Save conflict recovery
When another editor saves first, Update fetches the common base and latest server version and merges non-overlapping changes automatically. It validates the merged template before retrying, with at most three retries. The feedback identifies intervening authors and offers View changes. Edits made during the request remain local and retain the newly saved version for the next Update.
If both editors change the same property, Resolve save conflict lists each conflict. Choose Keep mine or Keep theirs, individually or in bulk. Preview merged result opens the existing diff viewer. Apply only leaves an undoable edit; Apply & Update also opens the normal publish gate. Overwriting with your whole template or discarding local edits requires confirmation. If the base version is unavailable, whole-template actions remain available. A remotely deleted template offers Save as new?.
Collaboration: presence and locks
Saved API templates show active editors in the header. Presence refreshes every
20 seconds while visible and 45 seconds while hidden; sleeping or disconnected
browsers disappear after the server TTL (90 seconds by default). Local templates,
sandbox embeds and unsupported servers do not send heartbeats. Embedded hosts can
set features.collaboration=false and receive pulp-engine:save-conflict alongside
the existing save-error event.
A banner reports editing peers and newer server versions. Reload is available when there are no unsaved changes; Merge now preserves local edits. Another tab of the same browser is announced through BroadcastChannel when supported.
Lock for editing? accepts an optional note; Release lock releases your own
lock. The holder’s heartbeat renews it for five idle minutes, up to eight hours.
Closing the tab releases it best effort; expiry handles interrupted departures.
Locks are advisory: others can choose Edit anyway and save. Administrators can
Break lock. CLI push skips locked templates unless --force is explicit.
Preview panel
The preview panel renders the current template and sample data as HTML or PDF using the Pulp Engine API. It requires PREVIEW_ROUTES_ENABLED=true on the API and a working Chromium installation (for PDF). The panel checks preview availability on open and fails closed if the service cannot be confirmed available. If the check fails with a recoverable error, a Retry button appears in the panel so you can re-check without closing and reopening.
Spreadsheet downloads
Choose CSV in the modal, or Download > CSV table data in the docked preview, to open the export dialog. Pick a table/pivot, All tables (.zip) or All tables in one file. Choose delimiter, UTF-8 BOM, headers, totals, banners, quoting and line endings. Raw values offer a decimal separator; formatted values follow the render locale. The editor starts with BOM enabled for Excel and remembers choices per template when browser storage is available. Template CSV defaults pre-fill the form. Stacked files contain separate table blocks; use ZIP for separate rectangular files.
Table and pivot properties have a collapsed Spreadsheet export group for sheet/ZIP entry names, frozen headers/columns, AutoFilter and exclusion. Table options also include Caption; footer formula generation can be disabled when a footer is shown. Render config > Advanced has Spreadsheet (XLSX) and CSV defaults. XLSX Report layout includes captions, banners and enabled totals; Data layout starts at the column headers. Excel-native typing supports sorting and calculations and preserves identifiers. Data layout plus Text typing restores legacy cell values.
Pages view and PDF proof
Proof PDF opens a paged viewer in either the docked panel or modal. Use Prev/Next, the page-number input, thumbnails, or PgUp/PgDn/Home/End while the viewer has focus. Fit scales to the panel; 100% uses document size. Thumbnails hide below 480px panel width. Pages render as they become visible at the screen’s pixel density. If the viewer or worker cannot load, a notice and the browser PDF viewer provide download/open controls.
Turn on Pages view in the canvas toolbar to see PDF-derived page boundaries and the page count for the current sample data. It is off by default and requires an available preview service. Automatic maps wait three seconds after edits, run at most once every ten seconds, and keep one mapping request in flight. Refresh pages requests a new map immediately. Explicit PDF proofs also refresh the map. Old boundaries dim and show their capture time until refreshed.
A selected block and its outline row show its page or occurrence range. Click its page link or a boundary pill to open the proof there. Clicking a PDF region selects the closest block above that position and scrolls it into view on the canvas; its anchor band is highlighted in the PDF. HTML preview also supports click-to-select when no text is selected. Repeater links show all captured occurrences, but canvas pills attach to the first occurrence. Nodes inside template references are not mapped; links stop after 5,000 occurrences with a notice. Overflow hints flag absolute widths beyond the printable area; fluid percentage widths are excluded.
Preview panel states
| Panel shows | Meaning | What to do |
|---|---|---|
| “Checking preview availability…” | Startup check in flight | Wait — the check is fast and cached on the backend |
| “Preview unavailable” (routes disabled) | PREVIEW_ROUTES_ENABLED is not set or is false on this server |
Enable preview routes or contact your administrator |
| “Preview unavailable” (rendering engine) | Chromium failed to launch at API startup | Check API logs for Chromium/Puppeteer errors; verify your deployment environment |
| “Preview service unreachable” + Retry | The status check could not connect to the API at all (network failure) | Check that the Pulp Engine API is running and accessible, then click Retry |
| “Preview check failed (HTTP N)” + Retry | The status endpoint returned an HTTP error (401, 403, 429, 500, etc.) | Check authentication credentials, rate limits, or server health, then click Retry |
| “Preview check failed” (unexpected response) + Retry | The status endpoint returned a 200 response the editor could not interpret | Check that the Pulp Engine API version is compatible with this editor version, then click Retry |
| Idle guidance — “Click Render HTML…” | Preview available (or older backend without status endpoint) | Click Render HTML or Proof PDF |
| Render error banner | Preview available but the render itself failed | See the error kind and message; fix the template or data, then retry |
Note: On older API backends that predate GET /render/preview/status (which always returns 404), the panel shows idle guidance and lets you attempt a render. If the API is reachable and the render endpoints work, rendering will succeed. If the backend is genuinely unreachable, the render attempt will fail with an “API unavailable” error.
Capability freshness: The preview availability check is re-run automatically when you return to an editor tab and the last check is more than 5 minutes old — no manual Retry required. This covers transient failures (service_unreachable, HTTP errors, unexpected responses) and successful states (detecting if the service has since gone down). Operator-level states (routes disabled, rendering engine unavailable) are not re-checked automatically — they require a server-side fix regardless.
Canvas: design and data modes
Design shows editable blocks and binding tokens. Fonts, colors, borders, spacing, alignment and dimensions update as you type in Properties. Paper text now defaults to 12pt Arial, black, with a 1.5 line height; headings use HTML sizes. Editor controls stay compact. Position and z-index are omitted so blocks remain selectable and draggable.
Choose Data beside the zoom controls to resolve the active scenario’s preview JSON and field mappings. Repeaters use the same aliases, parent scope and row variables as the renderers. Tables show formatted values, header/footer rows and alternate-row styles. Conditions show their active branch; Show hidden content reveals the other branch dimmed. Invalid JSON or mappings show an error banner.
Click a block to select it and edit its properties. All repeated instances highlight and update together. Double-click returns to Design and starts inline editing for Text or Heading. Starting a palette or field drag also returns to Design. The mode persists across reloads; the hidden-content toggle is temporary.
Data mode displays at most 25 items per repeater/table and 2,000 projected blocks, with notices when data is truncated. Uploaded image assets use authenticated access; external images show a placeholder. Text, including triple-brace bindings, stays literal text. Charts and barcodes use their current repeater scope. Pivot tables, template references and generated contents remain schematic.
The canvas approximates HTML/PDF output. It does not show custom CSS, remote font imports, page headers/footers or pagination, and does not reproduce Office layout. Use the rendered Preview for final output checks.
Working with multiple blocks
Ctrl/Cmd-click toggles blocks in a selection; Shift-click selects a sibling range. Properties shows counts by type; click a type to narrow the selection. Shared styles appear normally, differing values show Mixed, and mixed alignment has no active button. The panel reports how many selected blocks support style editing, including plugin opt-outs. Changes apply to those blocks together; clearing a shared field removes that property. Repeated typing in one field coalesces into one undo step.
Copy style uses the first selected block. Paste style replaces styles and pagination hints on the selection in one undo step. Table-to-table and pivot-to-pivot pastes also copy their table/row styles; other types receive only common styles. Reset style removes common styles and pagination hints. Click Format painter to apply the copied style to the next block; double-click to keep painting until Escape. Painting preserves the original selection.
Duplicate (Ctrl/Cmd+D) clones each selected top-level block immediately after its original and selects the clones. Selected descendants of selected ancestors are included only through their ancestor’s clone.
Wrap in groups adjacent siblings in a Container, Columns, Conditional or Repeater. Selections spanning parents/branches, structural nodes or unavailable embed types are refused with a reason. Columns creates one column per block for 2-6 blocks; otherwise all blocks go in the first of two columns. Properties opens for configuring the new wrapper. Unwrap restores children in order; alternate-branch content prevents unwrapping, and a repeater asks for confirmation because its binding scope changes. Wrapping, unwrapping and duplicating each create one undo step.
These actions also appear in block menus and the command menu. Shortcuts:
| Shortcut | Action |
|---|---|
| Ctrl/Cmd+D | Duplicate selection |
| Ctrl/Cmd+Alt+C / V | Copy / paste style |
| Ctrl/Cmd+Alt+G | Wrap in container |
| Ctrl/Cmd+Alt+Shift+G | Unwrap |
Shortcuts yield to inputs, contenteditable surfaces, open dialogs and AltGr typing.
Version history
Version history is available when an API-backed template is open.
- Click History in the toolbar (only visible for API-backed templates), or use
Ctrl+K→ Version History. - The history modal lists all saved versions newest-first. The currently loaded version
is highlighted with a
currentbadge. - Each past version has three actions:
- Compare — opens a text diff view showing what changed between that version and the current one
- Open as copy — opens the version as a local draft in a new tab. The source badge shows
history copy. Publish will create a new template with a different key. - Restore to this version — creates a new HEAD version from this version (admin scope required). Label pointers keep their existing versions. Unpinned renders follow the default label when configured, otherwise HEAD. A confirmation dialog appears first; the restore runs only after you confirm. The current tab reloads automatically. Editor-scoped credentials will receive a permission error.
Rows show change notes, with Edit note for the author or an admin. Admins can promote a row. Tick any two versions (including current), or the pinned Working copy (unsaved) row, then Compare selected (2). Selection is limited to two and comparisons run older → newer. The header shows both notes and a Changes in range list fetched across history pages. Label shortcuts offer prod ↔ current and staging ↔ prod when those labels exist. Restore is offered only when comparing against HEAD/working copy; either stored side can be opened as a copy.
Visual Compare
From the Compare view, click Visual Compare in the footer to open a full-screen side-by-side comparison. Structure shows the template canvases; Rendered output runs actual HTML previews. The left pane shows the past version, the right pane shows the current version. Structural changes are highlighted:
| Highlight | Meaning |
|---|---|
| Green left border | Node added in the current version |
| Red left border | Node removed (present in past version only) |
| Orange left border | Node modified (properties changed) |
| Blue left border + dashed outline | Node moved to a different location |
The left panel lists all changes grouped by kind. Click any entry to scroll both canvases to that node. Toggle Sync scroll to keep both panes aligned while scrolling manually.
Rendered output defaults to the same current preview data on both sides. Choose a saved scenario or each version’s stored sample. Changed text blocks have red (before) or green (after) outlines; highlighting stops above 4000 blocks per side. Scroll remains synchronized. Errors are shown per side. Pixel diff (PDF) loads PDF.js on demand and shows red changed pixels, per-page changed/unchanged badges and a page-count mismatch. It is bounded to 40 pages and 16 million raster pixels per side. Rendered comparison requires a healthy preview renderer.
These are previews using today’s nested templates and assets, not a reproduction of earlier production output. Deleted assets can fail and unpinned children use today’s HEAD. Different stored samples can introduce data differences. Use shared data to inspect template changes.
Which action to use
| Goal | Action |
|---|---|
| Roll back HEAD to a past version | Restore to this version (admin only); promote separately when a default live label is configured |
| Fork a past version to a new template key | Open as copy, then change the key, then Publish |
| Inspect or preview a past version without changing anything | Open as copy or Compare |
Drag-and-drop reordering
Nodes can be reordered and moved across containers by dragging.
- Sections — a
⠿handle is always visible at the top-left of each section card. Grab it and drag up or down to reorder sections within the document. - Content nodes (text, heading, table, container, conditional, repeater) — a faint
⠿handle is always visible at the left edge of each node. It brightens on hover or when the node is selected. Drag it to reorder within the same parent or move it to a different container, column, conditional branch, or repeater branch. A dashed indigo outline highlights the drop target as you drag.
Empty containers show a Drop here zone during any active drag, making it easy to populate containers that have no children yet.
The ↑/↓ buttons in the action bar remain available for keyboard-friendly reordering.
Keyboard reordering
Focus a canvas block and press Alt+Up/Down to move it within its current parent while keeping focus. Arrow keys without Alt navigate rendered blocks.
All drag handles are keyboard-accessible. When a drag handle has focus:
- Press Space to pick up the node.
- Press Arrow Up / Arrow Down to move the node within its parent container.
- Press Space again to drop it at the new position.
- Press Escape to cancel and restore the original position.
A live-region announcement is spoken on pick-up, move, and drop, naming the node so screen readers can describe the action (e.g. “Heading: Loan summary. Position 2 of 4. Use arrow keys to move, Space to drop, Escape to cancel.”).
While a node is being keyboard-dragged, pointer-drag on other nodes is suppressed. Drag handles on richText nodes in inline-edit mode have their tabIndex removed so they do not interrupt the text-editing keyboard flow.
Handlebars warnings
Content fields that accept Handlebars expressions (text body, heading text, image src, table column headers and footers) show inline warnings as you type. Warnings are advisory — they never block save or publish.
| Warning type | Example trigger | Message |
|---|---|---|
| Syntax error | {{unclosed |
Parser error message |
| Unknown helper | {{currancy total}} or (gtee total 100) |
Offers likely names and the Expression reference |
| Missing argument | {{eq total}} |
Shows the helper signature; bare {{currency}} reads the data field |
sum needs two args |
{{sum items}} |
sum requires 2 arguments: {{sum arrayField "fieldName"}} |
| Invalid date token | {{formatDate field "YYYY-MM-DD"}} |
Use yyyy for year and dd for day. Date patterns and presets are supported. |
| Unknown binding path | {{unknownField}} |
Checked against the selected node’s item/alias and document scope when the schema is populated; unknown scopes suppress advisory warnings |
The Helpers picker searches the shared catalogue, grouped by category, and inserts a complete example. The playground shows the same descriptions and examples. See the Expression reference for every signature, example output, null behavior and comparison rule.
The Fields picker includes a Report group: @render.now, @render.today, template metadata, label, format, locale, zone, request ID and preview state. These globals remain available inside loops and subtemplates. Unknown report-field names produce advisory warnings. Date fields insert a formatted date expression.
The condition builder groups comparison, text, list and presence operators. List operators use editable pills (Enter or comma adds an item); between has two bounds. Aa controls case-insensitive matching, and Compare as opts into string, number or date coercion. Ordered comparisons infer number/date coercion from the input schema when edited. Existing conditions retain their original strict semantics. Relative date values such as @today-30d use the render clock. The live result uses the same evaluator as rendering.
The format check for date only fires when the format argument is a string literal.
If it is a variable reference (e.g. {{date field fmt}}), no warning is shown.
conditionExpression fields (jexl escape hatch on conditional nodes) are syntax-checked
using the jexl compiler. Invalid expressions appear in the validation panel.
Full Handlebars evaluation (context resolution, runtime helper output) still happens at render time via the API.
Escaping and raw HTML
{{value}} interpolations are HTML-escaped at render time, so data values are safe to drop
into text and heading content. {{{value}}} (triple-stash) emits the value without
escaping. Use triple-stash only for markup you control as the template author — never to
interpolate render data that originates from a lower-trust upstream source, because that
would pass unescaped HTML straight into the rendered document.
Collapsible property groups
The properties panel organises node settings into collapsible groups. Each group has a header that can be clicked to expand or collapse its contents. Groups include Style, Pagination, Expression override, and others depending on the node type.
- Groups default to open unless the section is secondary (e.g. expression overrides default to collapsed).
- Open/closed state resets when a different node is selected.
- Group headers may include a badge showing a count (e.g. “Columns (3)”).
Per-node comments
Any node can have a free-text comment attached for editorial notes, reminders, or review annotations. Comments are stored in node.meta.comment and do not affect rendering.
Properties panel: An expandable comment section appears at the top of the properties panel for the selected node. Type a comment (up to 500 characters), and it saves automatically on blur. A character counter shows current/max length. Click the clear button to remove the comment.
Canvas: Nodes with a comment show a * indicator on the node badge. Click the indicator to open a popover showing the comment text.
Node templates / favorites
Commonly used node configurations can be saved as reusable block templates. This is useful for standardised content blocks that appear across multiple templates (e.g. a disclaimer section, a standard table layout).
- Save the current node configuration as a block template via the canvas action menu.
- Browse and insert saved templates from the block palette.
- Manage saved templates through the template picker dialog.
Block templates are stored locally and are available across all templates in the editor session.
Insert from data
Open From data in the palette’s Content group or search for From data with Ctrl/Cmd+K. Empty tables offer the same action, and dropping an array field opens the wizard with its insertion point and array already chosen.
- Choose an array from the input schema or adapted sample data. The list shows its source and sample item count. Inside a repeater, item-relative arrays are available too.
- Choose Table, List, Cards, Chart or Pivot. Unsupported layouts explain why they are disabled. A pivot needs two category fields and a numeric measure.
- Include and reorder fields, edit labels and formats, choose totals and currency, and optionally add a heading. The structural preview shows up to three sample rows.
Money-like numeric names default to currency and totals; quantities use whole numbers. Currency uses the last choice, then the template locale’s region, then USD. Review these editable guesses before inserting. Inference reads at most 50 items. When mappings change data paths, prefer arrays found in the adapted sample; schema-only arrays may have been remapped.
Insert layout inserts the heading and main block in one undo step and selects the main block. Existing tables also offer Add columns from data (N) for unused fields; an untouched Column 1 placeholder is replaced. Generated blocks use the existing renderer support for each format.
Template linting hints
In addition to errors and warnings, the editor surfaces hints — best-practice suggestions that do not block publish. Hints appear in the validation panel alongside errors and warnings, with a distinct visual treatment.
| Hint rule | Trigger | Suggestion |
|---|---|---|
heading-level-skip |
H2 → H4 jump | Use sequential heading levels |
wide-table |
Table with > 8 columns | Consider splitting into multiple tables |
unlabeled-section |
Section without a label | Add a label for outline navigation |
image-without-alt |
Non-decorative image without alt text | Describe the image or mark it decorative |
chart-without-alt |
Non-decorative chart without alt text | Add its meaning; an automatic English data summary is the fallback |
image-alt-filename |
Filename or generic image/picture/photo/logo label | Describe what matters in the image |
heading-empty |
Blank heading | Add meaningful text or remove it |
table-without-headers |
All column headers blank | Name the columns |
table-without-caption |
Table/pivot without a caption or immediately preceding heading | Add context for the table |
low-contrast |
Resolved text colours below 4.5:1, or 3:1 for large text | Check foreground and background; changed colours rearm dismissed advice |
document-language-mismatch |
Non-English formatting language differs from metadata language | Set the document’s actual language |
empty-else-branch |
Conditional with empty else | Remove the else branch if unused |
deep-nesting |
Nesting > 4 levels deep | Simplify the document structure |
unused-schema-field |
Schema field not referenced in document or mappings | Remove unused fields or add references |
Dismissing hints: Each hint can be dismissed per template. Dismissed hints are stored in localStorage and do not reappear until the underlying condition changes (e.g. the node is modified). Click the dismiss button next to any hint in the validation panel.
Conditional formatting and visibility
Select a table and expand Conditional formatting, between Columns and Header rows. Choose Whole row or a column. Use Negative ? red, Greater than, Equals text or Traffic lights, then edit the condition and text/background colours. Bold, Italic and Strike are portable across styled exports. Rules run in order; move them with the buttons and use Stop if true to prevent later matches overriding the result. Changes participate in normal undo/redo.
The sample pill evaluates the first displayed row of adapted preview data.
Column rules expose $value and $index; suggestions also include item fields.
A missing or invalid sample is shown instead of a misleading true/false result.
Toggle Expression for JEXL; syntax errors are shown immediately. Rule paths
also appear in validation, hints and find/rename-path.
Columns can enable a Data bar, Colour scale or Icon set. Colours must be hex; blank numeric anchors use the data range and median. Percent icon thresholds mean percent of the range. In pivot properties, the group follows Measures: select a measure and explicitly include totals when desired. Regular cells are the default. A sample pivot pill evaluates the first leaf aggregate.
Every block has Conditional style & visibility. Style-editable blocks show style rules; all blocks can enable Show only when. Rules use that block’s scope, including repeater items. The canvas shows an fx chip; HTML/PDF preview is the authoritative visual proof. CSV carries no styles. XLSX uses native visuals only for numeric cells; PowerPoint retains values without bars. DOCX uses nested bars and coloured glyphs. Pair colour with text or icons and review Office documents in the intended viewer before release.
Conditional logic builder
Conditional nodes use a visual boolean rule builder in the properties panel. Non-developers can compose multi-clause conditions — e.g. show this section if customer.country == "US" and amount > 1000 — without writing code.
Structure. The builder edits a tree of clauses:
- A leaf clause is a single
path / operator / valuerow (eq,ne,gt,gte,lt,lte,exists,notExists). Paths autocomplete frominputSchema. - A group clause combines child clauses with
ANDorOR, optionally wrapped inNOT. Groups can nest arbitrarily deep.
Controls per group. Toggle AND/OR, check NOT to negate, and use Add condition / Add group / ✕ to edit the tree.
Live evaluation. Each leaf and group shows a pill (true / false / —) evaluated against the current preview data. The panel is fail-safe: incomplete clauses (empty path, missing value) render — and never throw while editing.
Equivalent expression. A read-only JEXL preview appears under the tree (e.g. customer.country == "US" && amount > 1000), helping users learn the underlying syntax. It is informational only — the structured tree is the source of truth at render time.
Storage & back-compat. Existing single-clause templates load and save byte-identically (single-leaf AND groups collapse to a bare StructuredCondition on save). No migration is required.
Expression override. The conditionExpression (JEXL) override is still available in a collapsible section. When set, it takes precedence over the visual tree and the tree renders read-only with a “Clear override to edit visually” hint.
Visual JEXL expression builder
The conditionExpression override offers its own visual JEXL pattern builder as an alternative to writing raw JEXL syntax.
Supported patterns:
| Pattern | Example | Description |
|---|---|---|
| Comparison | status == "approved" |
Field compared to a value (==, !=, >, >=, <, <=) |
| Arithmetic | price * quantity |
Field combined with another value (+, -, *, /) |
| Concatenation | firstName + " " + lastName |
Multiple field/literal segments joined |
| Ternary | active ? "Yes" : "No" |
Conditional value selection |
Mode switching: The builder auto-detects the pattern from existing JEXL text. You can toggle between visual and raw modes at any time. Expressions that do not match a supported pattern are shown in raw mode only.
Live preview: The builder shows the evaluated result of the expression against the current sample data from the Preview panel.
Schema awareness: The field selectors suggest available fields from inputSchema.
Handlebars playground
The Handlebars playground is an interactive testing environment for authoring and debugging Handlebars expressions. Open it from the editor toolbar or via Ctrl+K → Handlebars Playground.
- Expression input: Type any Handlebars expression (e.g.
{{currency amount}},{{#if active}}Yes{{/if}}). - Live evaluation: The expression is evaluated against the current preview data (with field mappings applied) after a 200 ms debounce. Results or syntax errors are shown immediately.
- Helper reference: A collapsible panel lists all available helpers with their argument counts and usage examples.
- Expression history: The last 10 expressions are saved in
localStorage. Click any history entry to re-run it. - Auto-population: When opened with a text or heading node selected, the playground pre-fills with that node’s content.
The playground uses the same Handlebars engine as the properties panel — it does not call the API. Full server-side evaluation (context resolution, runtime helper output) happens at render time.
Validation
The editor runs structural and syntax checks as you edit and shows an issue badge in the header:
- Red badge — one or more errors (e.g. empty required fields, invalid Handlebars syntax, invalid jexl expression in
conditionExpressionor field mapping) - Yellow badge — warnings only
- Green dot — no issues
Click the badge to open the issues panel — a dropdown listing all errors and warnings. Click any row to jump directly to the relevant node in the canvas.
Checks cover:
- Empty content fields (text, heading, image
src, tabledataPath) - Handlebars syntax errors in any content string
- Unknown Handlebars helper names in text, heading, image, and table header/footer fields
- Helper calls with too few arguments, and
datehelper with an unrecognised format literal - Table column
formatfield set to an unrecognised helper name - jexl syntax errors in
conditionExpressionand field mappingexpressionfields - Missing required node properties
- richText: invalid colour format in a
TextLeaf(must be#rgb,#rrggbb,rgb(),rgba(),hsl(), orhsla()) — flagged as an error - richText: link with no
href, or anhrefusing an unsafe protocol — flagged as an error - richText: Handlebars syntax errors in
TextLeaf.textcontent — flagged as an error
Full Handlebars evaluation (context resolution, runtime helper output) still happens at render time via the API.
Undo / redo
- Undo:
Ctrl+Z/Cmd+Z(or the ↩ button in the header) - Redo:
Ctrl+Shift+Z/Cmd+Shift+Z/Ctrl+Y(or the ↪ button)
History tracks all node edits, additions, deletions, moves, reorders and selection actions. Bulk style typing coalesces by selection and changed properties; paste style, duplicate and wrap/unwrap each undo together. It is reset when:
- A new template is loaded (Open, Load Sample, Load JSON, New, or opening a history version)
- A template is saved (the save version sync triggers a reload into the store)
History is in-memory only — it does not persist across page refreshes.
Multi-template tabs
Multiple templates can be open simultaneously. Each template occupies its own tab in the tab bar displayed below the main header. Each tab maintains independent state: dirty indicator, undo/redo history, source badge, and version.
Opening a tab: Any load action — New, Open, Load Sample, Load JSON, or opening a version from history — opens the template in a new tab (or replaces the current tab if it is unmodified and in local mode).
Switching tabs:
Click any tab in the tab bar. You can also use Ctrl+K → Switch Tab group, which
lists all open tabs with the current one marked and disabled.
Closing a tab:
Click the ✕ on the tab. If the tab has unsaved changes, a confirmation dialog appears
before discarding. You can also close the active tab via Ctrl+K → Close Active Tab.
Duplicate / Save As
Duplicate… opens a Save As dialog that creates a copy of the current template under a new key and name. The copy opens in a new tab in local mode — it is not saved to the API until you click Publish.
Access it via the Duplicate… button in the header or via Ctrl+K → Duplicate Template.
The button is disabled when no template is open.
This is useful for branching a template (e.g. creating a loan-approval-letter-v2 while
keeping the original intact) or for seeding a new template from an existing structure.
Command menu
Press Ctrl+K (Windows/Linux) or Cmd+K (Mac) to open the command menu. The shortcut
works from text inputs and textareas; a content-editable field keeps its own link shortcut.
The command menu is organised into groups:
| Group | Commands |
|---|---|
| File | New Template, Open Template, Load Sample, Load JSON, Export JSON, Duplicate Template, Close Active Tab |
| Template | Preview, Publish / Update Template, Version History |
| Insert block | From data wizard and individual blocks |
| Selection | Duplicate blocks, Copy/Paste/Reset style, Wrap in Container/Columns/Conditional/Repeater, Unwrap |
| View | Design/Data canvas, zoom and panel controls |
| Navigate | Properties Tab, Schema Tab, Mappings Tab, Document Tab, Blocks Panel, Outline Panel |
| Switch Tab | Lists all open tabs by name; current tab is marked and disabled (only shown when 2+ tabs are open) |
The Publish / Update label and the Version History item reflect the current template’s source — they are disabled when no template is open or when the template is not API-backed (for Version History).
Accessibility
Use Check accessibility in Ctrl/Cmd+K to open the Issues popover with Accessibility (n) selected. The toggle exposes its pressed state to assistive technology. If no undismissed matches exist, the header announces “No accessibility issues found”. These checks are advisory and do not block publication.
Charts have an Accessibility properties group for alt text, a long description, decoration, and an optional hidden HTML data table. Alt text and descriptions accept Handlebars. Images have a decorative checkbox. Set language and title in Document metadata. See Accessible output for per-format support and the current PDF/UA limitations.
The checks cover headings, descriptions, table context, language and possible contrast problems in text, rich text and table styles. They cannot interpret a chart’s meaning, verify reading order, or account for custom CSS, theme CSS and data-bound colours. Unknown colour expressions are skipped. Check the final document with assistive technology; these hints do not certify accessibility.
Open Move to… in a block’s menu or Move selection to… in Ctrl/Cmd+K. Use Up/Down, Home/End and Left/Right to navigate the destination tree, choose At start or At end, then press Enter. Conditional else and repeater empty branches are destinations when present. Invalid targets are disabled, including a block’s descendants and Sticky Headers outside section roots. Moves retain node IDs and selection order, require one Undo, restore focus to the moved block, and announce their destination.
Windows High Contrast uses system colours for selection, focus, drop lines, targets, binding pills and status controls. Colour swatches retain their authored colours. Paper follows system colours in this mode. The automated axe gate covers the start screen, selected chart, Document settings, command menu, Issues, shortcuts, template picker and Move to dialog in light and dark themes. It runs in full push-gated E2E CI. Embed surfaces and the rendered-preview iframe are not covered by this gate.
Keyboard shortcuts
Use Ctrl on Windows/Linux and Cmd on Mac. Ctrl/Cmd+S commits the active property field and opens the same validation/publish gate as the header button. With a dialog open it suppresses browser saving without opening another gate. The playground and an unchanged server template show a status message instead.
| Shortcut | Action |
|---|---|
| Ctrl/Cmd+D | Duplicate selected roots, including sections, in one undo step |
| Ctrl/Cmd+A | Select siblings of the selection anchor on the canvas or outline; with no anchor, select sections |
| Up / Down; Home / End | Focus and select previous/next or first/last rendered canvas block |
| Left / Right | Focus parent or first rendered child; structural columns map to their Columns block |
| Shift+Up / Down | Extend sibling selection from its anchor |
| Alt+Up / Down | Move the focused block within its parent and keep focus |
| ContextMenu / Shift+F10 | Open the focused node’s action menu |
| F2 / Enter | Edit inline text; F2 on a section focuses its label in Properties |
| Ctrl/Cmd+C / X / V | Copy, cut or paste blocks through the system clipboard |
| Ctrl/Cmd+Z; Ctrl/Cmd+Shift+Z or Ctrl+Y | Undo; redo |
The shortcuts dialog (Ctrl/Cmd+/) lists all editor bindings, including find, style copying and wrapping. Drag handles retain Space/Arrow/Space keyboard dragging; Alt+Left/Right retains browser navigation.
Copied blocks use a versioned, schema-validated JSON envelope capped at 2 MB. They can travel between editor windows and survive reload in browsers that dispatch native clipboard events. Paste regenerates IDs and respects the embed host’s allowed blocks. Section envelopes insert at document level after the anchor’s section. External plain text becomes blank-line-separated text blocks, capped at 50 paragraphs in one undo step. Pasted PNG/JPEG/WebP/GIF images upload as assets before insertion; this is unavailable in the playground or when images are blocked.
Inputs, selects, content-editable text, dialogs and selected text retain native clipboard behavior. When a browser omits clipboard events (notably some Safari cases), a memory fallback supports operations within the current editor. Cross-window clipboard use then requires a browser that emits those events. Assets copied from another server or tenant may need to be uploaded there too.
Document tab
The Document tab in the right-side panel (alongside Properties, Schema, and Mappings) contains template-level rendering settings that apply to every page of the PDF output.
Paper size and orientation
| Setting | Options | Default |
|---|---|---|
| Paper size | ISO, North American and envelope presets, or Custom | A4 |
| Orientation | Portrait, Landscape | Portrait |
Changing these settings affects PDF, DOCX and browser-printed HTML — HTML preview renders without page boundaries.
The render API also accepts per-request options.paperSize and options.orientation overrides that take precedence over the template setting. See docs/api-guide.md.
Label sheets
Select a repeater, open Layout, and choose Label sheet. Pick Avery 5160, 5163, 5167 or 5371 (Letter), or L7160, L7163 or L7651 (A4), then Apply page setup. This sets the paper and margins; selecting stock alone preserves your page settings and displays a mismatch warning. The Address labels starter pack provides a ready-made 5160 sheet with sample recipients.
Design one cell in the canvas frame. Custom sheets accept columns, rows, cell
dimensions, horizontal/vertical gaps and safe-area padding. Skip leaves used
positions blank on the first sheet; copies repeats each item. Both accept an
integer or a Handlebars expression resolved against the repeater’s parent data.
Fill across rows or down columns. Cell scope includes @labelIndex (1-based
printed item position), @sheet (1-based) and @copy (1-based), while @index
continues to identify the source item. Copies are bounded to 1,000 per item and
one repeater to 100,000 expanded positions. Skip is capped at one sheet minus one
position. Empty arrays use the existing empty branch without producing a sheet.
PDF/HTML use exact CSS grids, with a fresh page for every label sheet and clipping enabled by default. Grid mode flows cards across pages without a fixed sheet capacity. DOCX uses fixed-width tables and exact row heights; inspect the result in your Word/printer combination. PPTX falls back to flow and reports a warning. CSV/XLSX continue to extract tables. Enable outlines for a proof, print at Actual size / 100%, and test on plain paper against the physical stock.
Overflow and auto-fit
Text and heading nodes offer Overflow modes Wrap, Clip, Ellipsis and Shrink to fit. Specify a height/max-height or a line limit. Ellipsis defaults to one line; shrink without a height or line limit fits one line to the available width. The minimum shrink size defaults to 6pt; use a readable limit for your label or certificate. Inline text with a fit becomes an inline block.
PDF measures the loaded fonts in print layout, including percentage-width columns.
HTML uses CSS clipping/ellipsis and an estimate for shrink when an absolute width
and authored font size are available. DOCX/PPTX use a font-agnostic estimate and
truncate overflow; positioned PowerPoint text also retains native shrink fitting.
Text that cannot fit at the minimum is clipped; PDF logs its count and PPTX reports
an overflow warning. The PDF measurement pass processes at most 2,000 shrink nodes.
Rich text and individual table cells do not yet have a typed fit field.
The dismissible hints text-fit-unbounded and fixed-box-no-fit flag missing box
limits and fixed-height/positioned text without overflow controls.
Barcodes and payment QR codes
Barcode formats are grouped by use: 2D (QR, Data Matrix, PDF417, Aztec), retail
(EAN-13/8, UPC-A/E), logistics (ITF-14, GS1-128, GS1 Data Matrix/QR), industrial
(Code 128/39/93, Codabar), postal (USPS IMb, Royal Mail, Australia Post), and Swiss
QR. GS1 values use application identifiers, for example
(01)09506000134352(17)271231. Postal checks validate structure; they do not certify
a carrier’s tracking/routing allocation.
Choose a Payload builder for EPC SEPA payments, Swiss QR-bills, vCard 3.0 or Wi-Fi. Builder fields accept Handlebars and replace the raw Value. Invalid payloads show a placeholder with a warning. Appearance includes bars/background/text colours, rotation and quiet-zone padding. Preserve strong contrast and quiet zones when printing; verify dense codes with the scanner used in production.
EPC uses UTF-8, IBAN validation and ECC M. Swiss QR-bills use structured addresses, CH/LI IBAN validation and QRR/SCOR checks. The builder also rejects EUR with QR-IBAN, following SIX v2.4’s November 2026 rule. A Swiss symbol must measure 46×46mm excluding its quiet zone; SVG box dimensions include encoder padding, so account for that padding when sizing the printed symbol. Use black on white. This builder creates the payload and symbol; the complete receipt/payment-slip layout is a separate template design. See the SIX implementation guidelines and EPC069-12.
Select the paper size and orientation for the rendered PDF. These map to renderConfig.paperSize and renderConfig.orientation.
Page margins
Top and bottom margin inputs accept CSS length values (e.g. 20mm, 1.5cm). The full
margin set (top, right, bottom, left) is stored in renderConfig.margin. Setting top or
bottom here merges with the existing left/right values.
Set top/bottom margins to ≥ 16mm when using a page header or footer — they render inside the margin area and will overlap body content if the margin is too small. An advisory warning appears automatically when the margin is detected to be under 15mm.
Page header and footer
Use the header and footer bands in the canvas margins, or open Document / Header & Footer. Each visual band has left, center and right slots. Click a slot to edit text, insert Page #, Pages, Date, Title or schema fields, or select an image. Apply slot commits the complete edit as one undo step. The variant selector chooses default, first, even or last; More options… opens the controlled Header & Footer tab for fonts, rules and variant settings.
Bindings appear as token chips in Design mode and as evaluated sample values in
Data mode. Page fields use the latest proof count. Header/footer contents stay
inside the configured margins. Legacy pageHeader/pageFooter HTML is read-only
on the canvas: Convert to visual keeps text and page tokens from both bands
and drops HTML layout/styles (undo restores it); Edit HTML opens the advanced
editor. HTML preview shows page chrome once at the top and bottom. Proof PDF
shows its actual printed position and repeated page variants.
Constraints:
- Inline styles only — body CSS and
customCssdo not apply inside headers/footers - Puppeteer sets
font-size: 0by default; set it explicitly in your div - Span classes injected by Puppeteer:
pageNumber,totalPages,date,title,url
Snippet buttons offer common starting points (page number, page X of Y, date, title + page). Clicking a snippet when the textarea already has content triggers a confirmation prompt before replacing. Snippets are undo-tracked.
Security note: pageHeader and pageFooter HTML is allowlist-sanitized at render
time before passing to Puppeteer. Obvious script, event-handler, and external-resource
vectors are stripped automatically (see sanitizeHeaderFooter in pdf-renderer). The
allowlist covers the elements and CSS properties used by the built-in snippets and common
hand-authored headers/footers; content using only supported elements does not require
special precautions. Residual sanitizer and Chromium risk is not zero — do not rely on
this as a complete security boundary for content from fully untrusted sources. Both
fields are capped at 10,000 characters.
Rendered-page security posture
The main rendered HTML document (body content, customCss, font imports) benefits from
additional browser-layer hardening:
- Script execution blocked at browser level: every rendered page includes a
Content-Security-Policymeta tag withscript-src 'none'; object-src 'none', blocking JavaScript execution even if a future sanitizer bypass allows hostile content through. Note this CSP applies to the main document only — Puppeteer’sheaderTemplate/footerTemplatecontexts operate separately. - Network requests intercepted: all outbound requests from the Chromium renderer are subject to SSRF guards blocking private IPv4, RFC 1918, link-local (IPv4 and IPv6 fe80::/10), IPv4-mapped IPv6 (::ffff:), and ULA IPv6 ranges. Font URLs additionally undergo application-layer validation before being emitted into CSS.
These are defences for trusted-team use. Pulp Engine is not designed for environments where template authors are fully untrusted or adversarial.
RichText node editing
richText nodes can be edited in two ways: directly on the canvas (inline editing) or in the Properties panel. Both surfaces use the same Tiptap-based editor.
Inline canvas editing
Double-click any richText node on the canvas to open the editor inline without leaving the document view. Press Enter with the node selected to do the same.
- The editor appears inside the node bounds at full canvas width.
- All toolbar operations (formatting, colour, lists, links, line breaks) work exactly as in the Properties panel.
- Press Escape to close the inline editor and return focus to the node wrapper.
- The Properties panel shows a status notice (“editing inline on canvas”) when inline editing is active for the current node, and the Visual / JSON tabs remain fully accessible.
- The inline editor exits automatically when a different node is selected.
Visual mode
A Tiptap-based editor with a formatting toolbar. Supported operations:
| Toolbar item | Effect |
|---|---|
| B Bold | Toggles bold mark on selected text |
| I Italic | Toggles italic mark on selected text |
| U Underline | Toggles underline mark on selected text |
| Colour swatch | Opens a compact colour palette; click a preset or “No color” to apply/remove the color mark |
| Bullet list | Toggles an unordered list |
| Numbered list | Toggles an ordered list |
| Link | Opens an inline popover to set or edit a link URL |
| Shift+Enter | Inserts a hard line break (lineBreakInline) |
Colour palette: The toolbar shows a compact 3-column palette of 8 preset colours
(Black, Dark gray, Red, Amber, Green, Blue, Purple, Pink) plus a “No color” option to
remove the mark. Clicking a swatch emits a #rrggbb hex value. Existing rgb(),
rgba(), hsl(), or hsla() colour values are preserved on round-trip. Named colours
(e.g. red) are not supported and will be flagged as an error by the validator.
Typography: Strike, superscript/subscript, highlight, font size and family are available beside bold/italic/underline. Block type selects Paragraph, Heading 1-4 or Quote. Alignment, indent/outdent and paragraph spacing apply to the current block. Inline editing exposes later controls through More.
Nested lists: Lists support four levels. Tab indents and Shift+Tab outdents; indenting at level four does nothing. Numbering supports decimal, lower/upper letters, lower/upper Roman, legal numbering and an explicit start in JSON. DOCX and PPTX default to decimal, lower letters, lower Roman, then decimal. HTML preserves old one/two-level list output; new three/four-level lists use those defaults. Set numbering explicitly for matching shorter lists. Legal numbering is native in PDF/DOCX; PPTX uses separate arabic numbers per level and warns.
Field chips: Insert field adds a marked chip at the caret. Click a chip to edit its expression or choose a scoped field; schema fields can also be dragged onto the editor. JSON stores ordinary text such as Dear {{customer.name}},. Bold and other marks apply across chips and the eventual bound values. Block expressions and triple-stash remain text for precise editing in JSON.
Binding formatted text
Choose From data field, select a string field and choose Markdown, HTML or Plain text. The canvas shows the source badge; Data mode previews the converted sample value. Visual/JSON now edit Fallback content. Missing, null and blank values use the fallback, or render nothing with When empty: Hide. Object values use the fallback and report an issue. Static removes the source.
Markdown supports CommonMark plus strikethrough: paragraphs, headings, quotes, four-level lists, links, code and line breaks. HTML supports the corresponding native structure and safe inline marks. Active elements and their contents are removed, event attributes are discarded, and unsafe links/styles are omitted. Images and tables degrade to text with issues. GFM tables/task lists are outside this subset.
Payload expressions such as {{secret}} print literally; they never execute as template code. Conversion is capped at 200,000 characters and 2,000 blocks. PDF/DOCX preserve native content; PPTX keeps native text/bullets but warns about unsupported hyperlinks and legal numbering. CSV/XLSX omit rich-text nodes.
Toolbar accessibility: The toolbar is a single role="toolbar" region with roving
tabindex navigation. Tab moves focus into the toolbar; Arrow Left / Right navigate
between items; Home / End jump to the first / last item. Format toggle buttons have
aria-pressed reflecting their active state. Tab moves focus out of the toolbar.
Link popover
Clicking the link toolbar button opens an inline popover:
- URL input — pre-filled with the existing
hrefwhen editing a link; blank when creating a new one - Save — applies the link; a blank URL removes the link; bare domains (e.g.
example.com) are automatically prefixed withhttps:// - Remove — removes the link and returns the text to unstyled
- Cancel — discards changes
Accepted protocols: https://, http://, mailto:, tel:. The popover shows a
warning (but still allows saving) when an unsafe protocol such as javascript: or
ftp:// is entered. The server renderer and validator also reject unsafe protocols.
Bare domains without a protocol are normalised to https:// on save.
JSON mode
A raw JSON textarea that accepts the full RichTextBlock[] content array. Use this mode for:
- Authoring or editing Handlebars expressions precisely (e.g.
{{fieldName}}in text leaves) - Constructing content structures that are difficult to express in Visual mode
- Pasting content from an external source
The rich-opening node in loan-approval-letter is a working reference — it demonstrates a paragraph with Handlebars, bold and colour marks, a nested list (four levels are supported), two links, and a line break. Open it in JSON mode to see the full RichTextBlock[] shape.
The textarea shows an inline parse-error message if the JSON is malformed. Changes sync to the template on blur. A schema-hint toggle is available below the textarea.
JSON mode is the lossless fallback — switching between Visual and JSON tabs round-trips content through the AST, which may normalise whitespace or merge adjacent text leaves with identical marks.
Tab mode persistence
The active tab (Visual or JSON) is remembered in the editor store. Switching between nodes preserves the last-used tab so the workflow is not interrupted by repeated tab switching.
Image assets
The editor integrates with the API’s asset storage to manage image files used in templates.
Uploading assets
Assets are managed through the API and are accessible to all templates. To upload:
- The editor fetches the asset list from
GET /assetson load and refreshes it after any upload or delete. - Upload a file via
POST /assets/upload(multipart/form-data). The editor exposes this through the image node property panel. - Accepted types: PNG, JPEG, GIF, WebP and sanitized SVG. Maximum file size: 10 MB; SVG has a separate 2 MiB input limit. The server removes active SVG content and rejects DTD/entities.
Using assets in image nodes
Once uploaded, an asset’s URL (e.g. /assets/uuid-logo.png) can be used directly as an image node src value. The src field accepts any Handlebars string — you can use a static path, a data-bound variable, or a mix.
Managing the library
The Library tab filters by folders and tag chips and shows Used by N. Its menu offers Rename / edit details, Replace file, Copy name reference, Version history and Delete. Details edits the display name, reference name, default alt text, tags and folder, and lists current/label-pinned usage and dynamic-reference warnings. Restore creates a new version. Replace keeps every template URL stable and refreshes thumbnails. Uploads use the selected folder. Deleting an in-use asset lists affected templates and requires Delete anyway to override.
Picking an asset fills an empty node alt text from its default. Copy name reference produces {{asset 'acme-logo'}}; Expression also offers an asset-by-name insertion. The image properties add a fallback picker, Hide when no image, and focal presets/custom position for cover, none and scale-down. WebP shows an Office-output warning. Canvas metadata indicates fallback and empty-image hiding.
Broken reference validation
The editor compares image src values that start with /assets/ against the loaded asset list. If the referenced file is not found in the list, an error is shown in the validation panel. This catches stale references when an asset has been deleted.
Missing static asset references block publish. Upload the missing file or update the image
srcbefore publishing. The check covers static/assets/paths, fallbacks and literal{{asset 'name'}}references; data-bound references (e.g.{{logo.url}}) are not checked and do not block publish.
Asset access in private mode
When the API is configured with ASSET_ACCESS_MODE=private, asset images cannot be loaded via a direct <img src> tag (the browser cannot send X-Editor-Token headers on image requests). The editor handles this automatically:
- Each asset tile in the image picker and each thumbnail in the properties panel fetches the binary via the Fetch API with the
X-Editor-Tokenheader. - A temporary blob URL is created and passed to the
<img>element. The blob URL is revoked when the component unmounts or the asset URL changes. - In dev mode with no credentials (auth disabled), asset URLs are used directly without authenticated fetch.
Known limitations and rough edges
| Limitation | Notes |
|---|---|
| Canvas fidelity | Approximate HTML/PDF only; custom CSS, remote font imports, page header/footer and pagination require rendered Preview. Office layouts differ. |
| Data canvas limits | 25 items per repeater/table, 2,000 projected blocks; external images are placeholders. |
| Save resets undo history | Treated as a save boundary; intended |
| Helper validation is advisory only | Unknown helpers, missing args, and invalid format literals are warned but not blocked. Complex helper expressions (e.g. nested sub-expressions) are not parsed for argument checks to avoid false positives. |
| Binding path validation requires a populated schema | Unknown-path warnings are suppressed until inputSchema has at least one property defined |
| Asset broken-ref check blocks publish | /assets/ path errors block publish but do not block save. Data-bound src values are not checked. |
| Asset storage defaults to local filesystem | Assets are stored on the API host by default (ASSET_BINARY_STORE=filesystem). Set ASSET_BINARY_STORE=s3 for S3-compatible object storage — eliminates the shared-volume requirement for multi-instance deployments. See deployment-guide.md § Object Storage. |
| richText expressions | Simple expressions appear as editable field chips; JSON remains available for block expressions and precise AST editing |
| richText nested lists: four levels | Tab/indent stops before any descendant would exceed four levels |
| richText link popover: partial URL validation | The popover warns on unsafe protocols and normalises bare domains to https://; the renderer and validator enforce safe protocols at render time |
| richText Visual mode: local undo history clears when global undo changes node content | When global Ctrl+Z restores a richText node to an earlier version (because the edit was made outside the Visual editor), Tiptap’s local undo history starts fresh from the restored content. This is intentional — the document baseline changed. Use global Ctrl+Z / Ctrl+Shift+Z to navigate document-level history; local Ctrl+Z inside Visual mode undoes fine-grained formatting changes within the current editing session. |
| Keyboard drag/reorder | Space+Arrow reorders within a parent. Move to… provides keyboard moves across containers and branches while preserving IDs. |
Document pagination and properties
In Document > Page, turn on Smart pagination to keep headings with the following block, keep table rows together, and keep figures together. Each behavior can be disabled separately; widows and orphans accept 1-10 lines. The policy affects printed HTML/PDF and DOCX. Word supports widow/orphan control as an on/off setting. PPTX, CSV and XLSX do not use this page-flow policy.
New templates (blank, starter pack or AI) enable this policy. Opening an existing template does not change it. Templates with paginated content and no policy show a dismissible hint. Per-node pagination hints override document defaults; set Break after = auto to allow a heading to end a page, or Break inside = auto to allow a figure or table row to split.
In Document > Advanced, document properties include title, author, subject,
comma-separated keywords and language. Title defaults to the template name.
Title, author, subject and keywords accept {{bindings}}. Language must be a
valid tag such as en-NZ. Generate PDF bookmarks from headings adds a PDF
outline. DOCX downloads show a warning notice when unsupported images or layout
features reduce fidelity; detailed warnings are recorded in server logs.
Locale, numeric settings and preview overrides
Advanced document settings include formatting language, number locale, default currency, ISO/fixed currency digits, time zone, exact decimal arithmetic and rounding mode. Table and pivot formats share a picker with format codes, date pattern chips and a live sample. Invalid literal patterns are advisory warnings.
The preview toolbar’s Preview as control applies a locale/time zone to the
current session without editing the saved definition. Clear it to return to the
template settings. Re-render PDF proofs after changing an override.
See Number and date formatting for all codes, tokens,
rounding modes and the sumRounded footer reconciliation recipe.
Fonts tab
Open document properties and choose Fonts. Set the default family, size, line height and colour under Default text. Under Brand fonts, upload TTF, OTF, WOFF or WOFF2, or register an existing font asset. The editor reads the font’s family, weight and style; each face has a sample, editable alias, weight/style selectors and a remove action. Check the licence when the embedding flag is restricted. An alias differing from the font’s internal family may cause Word to substitute it.
The family picker groups template, server and generic fonts and accepts a free-text stack. Uploaded fonts load with editor authentication for the canvas; the HTML preview receives embedded font bytes. Missing or non-font registry assets block publishing; unknown family names produce warnings. Image pickers show images only. Remote font imports (needs internet) remains in Advanced for external CSS imports.
PDF and HTML embed all four formats. DOCX embeds regular TTF/OTF only and synthesizes other faces. PPTX records font names and warns that recipients need fonts installed. Default text settings inherit unless a node or variant supplies its own style.
Binding data
Open Schema > Browse to drag a field onto a canvas insertion point, or use its + button to insert at the current selection. A scalar creates bound text (date fields use the date helper), an array of objects creates a table with up to eight typed scalar columns, a primitive array creates an inline list, and an object creates a container with up to twelve labelled scalar fields. Each insert is one undo step. Embed node restrictions also apply to generated children.
The Fields picker in text, heading and barcode properties, and Insert field in rich text, groups This item, Outer items, Document, Row and Report fields. Table column keys suggest the table’s item fields; collection inputs suggest collections available in the current scope. Copy uses the same scope rules. A field hidden by a nearer item is disabled. An unresolved repeater path suppresses advisory unknown-field warnings until its schema is available.
Inside a repeater over items, use {{description}}, or {{line.description}}
with item alias line. items[].description is schema notation, not an executable
path: expressions containing it show an error and block publish. Dragging that
item field outside the matching repeater is refused and announced. Dropping onto
existing text creates a new block; use the Fields picker to append to an expression.
Repeater and table scope
Without an alias, object-item fields are merged over the parent scope. With
Item alias (optional), only the alias is added, so {{line.qty}} can coexist
with {{order.number}}. Aliases have 1–64 ASCII letters, digits or underscores,
start with a letter/underscore, and cannot use reserved names (including this,
root, constructor, prototype and __proto__). Invalid drafts show an inline
message and do not commit.
| Variable | Meaning |
|---|---|
@index / @number |
Zero-based index / one-based row number |
@first / @last |
First / last item flags |
@count |
Number of items in this loop |
@item |
The current item, including primitive values |
@parent.number |
Enclosing loop’s one-based number |
Nested repeaters can print {{@parent.number}}.{{@number}} as 1.1, 1.2, 2.1.
Inline {{#each tags}}{{@item}}{{#unless @last}}, {{/unless}}{{/each}} has the same
row variables and preserves Handlebars block parameters and object iteration.
A table column key @number prints row numbers, and @item lists primitive items;
XLSX keeps numeric row numbers as numbers. These bindings work in every output.
@root remains the merged node scope. A templateRef applying field mappings starts
a fresh frame because data adaptation uses structured cloning.
Input schema
Schema > Edit preserves root metadata, constraints, unknown keywords and
primitive array items. Expand Constraints to edit title, typed JSON default,
allowed-value chips, string format/length/pattern/multiline, numeric min/max and
multiple-of, array item bounds and nullable. The array Items selector supports
object, string, number, integer and boolean. Defaults are schema metadata: renders
do not automatically fill absent payload fields. Multiline emits x-ui: textarea;
the form embed consumes the
supported standard constraints and UI hints.
Custom schema shapes such as $ref, oneOf, tuples and unsupported unions remain
read-only in the structured view. Schema > JSON edits the complete object and
commits valid drafts on blur; invalid drafts remain visible with an error. Custom
string formats and unmodelled keywords are retained.
Infer from preview data, or Create schema from this data in preview’s menu, proposes a draft-07 schema. Inference samples at most 1,000 array items and ten levels; it distinguishes integers, common date/date-time/email/HTTP URI strings, nullable values and incompatible types. Only properties present and non-null in every object of an array with at least two objects become required. Single objects add no required fields. Review inferred formats, especially IDs that look like dates.
Import JSON Schema accepts a JSON object schema. Both workflows show added, changed and removed paths: Merge adds missing properties while retaining all existing types and constraints, Replace applies the proposal, and Cancel changes nothing. Each accepted change is one undo step. Invalid JSON or imports show an inline error. Preview sample generation honours examples, nullable types and numeric minimums.
Chart axes, references and captions
Select a chart and open Axes to enable or remove axes. Configure value-axis bounds, tick count, linear/log scale and gridlines; category axes support rotation, wrapping and intervals. Scatter exposes two value axes. Reference lines can use a fixed value, a field in the current chart scope, or mean/median/min/max; combo references can use the secondary axis. Numeric drafts commit on blur/Enter and reject invalid bounds.
Labels & format controls number/currency/percent/compact formats, decimals or currency code, prefix/suffix, category dates and pie/donut slice labels. Combo charts have an independent secondary format. Title, subtitle and footnote support field/helper insertion. Design mode shows binding chips; Data mode resolves captions in nested scopes. Labels and gridlines update in the live preview.
Sort, filter and calculated fields
Select a table, repeater, chart or pivot and open Sort & filter. Add a condition group or a JEXL filter; a nonblank expression takes precedence over the group. Paths refer to each item, with parent fields also available. Add up to five sort keys, choose direction, type and null placement, and reorder the keys to set priority. Distinct keeps the first row for each selected key (blank means the whole row). Offset and limit apply last. The preview shows the visible row count against the source count.
Supported charts also offer Top N, ranking by the value field unless another field is selected. Retained rows use the sort controls; an optional Others aggregate is pinned last. Top N replaces offset and limit. Pivots support filtering before aggregation.
In Field mappings, open the advanced controls and choose Applies to for the whole document or each object in an array. Per-item targets such as lines[].total always read adapted data. Enable Use results of earlier mappings for dependent document calculations, add a display label, and use Move up/down to order dependencies. Preview evaluates earlier mappings first and shows the first three per-item results. Computed targets appear in binding suggestions.
The visual JEXL builder’s Function pattern offers transforms grouped by category, with signatures and examples from the runtime catalogue. Add literal or field arguments. Complex function calls remain available in Raw mode. Unknown functions and forward references to later adapted mappings are reported by validation.
Table designer
Select a table to edit its Columns group. Cards support pointer drag or keyboard reorder: focus Move, press Space, use arrows, then Space to drop. Up/Down buttons provide another route. Reorder is one undo step and focus follows the moved card. Grouped header spans follow positions, so inspect their labels after reordering.
The key picker suggests fields from the table’s array, including tables nested in repeaters and item aliases. Append missing adds schema fields without duplicating keys; Replace all from schema replaces the list. Scalar fields through depth two become columns. Schema titles become headers, numeric fields align right, price/amount/total fields use currency, dates use date format and booleans use checkboxes.
Widths need units, for example 30mm or 25%. Invalid drafts are flagged and not saved. Equalise widths assigns equal percentages; Clear widths returns to automatic sizing. Each card also offers alignment, formats, footer text and cell/header styles.
Choose field, expression (JEXL) or template (Handlebars inline HTML) as the value source. Cell templates can combine row fields and use @value, @index, @rowNumber, @first and @last; their field picker uses row scope. Show as selects text, image, barcode, link, checkbox or sparkline and reveals relevant options. Type changes clear incompatible options. Show column when tests the enclosing table scope once; use this for customer-dependent columns. Overflow supports wrap, nowrap and estimated ellipsis. Issues report malformed templates/expressions and ellipsis without an explicit width.
Table style offers minimal, lined, grid, zebra, boxed and accent presets, applied in one undo step, plus clear, grid colour, layout, caption and header/body/alternate/footer style controls. Pivots have the same panel with total-row styles. Grouped header rows have individual style disclosures. Show header row is independent of Repeat header on each page; repeat is disabled while hidden.
The design canvas shows the first five sample rows with styles, widths, caption, footer and a remaining-item count. It shows raw values while formatting loads and one token row without sample data. Pivots show a styled three-row skeleton. This remains an approximate canvas; PDF preview verifies pagination. Word supports native cell images/barcodes/links; PowerPoint degrades image/barcode cells to text and reports warnings.
Page chrome and numbering
Open Document / Header & Footer / Structured for letterheads and page bands. Enable different first, odd/even or last pages, select a variant, then add text or an uploaded PNG/JPEG to a left, center or right slot. Text supports field chips and Page, Pages and Section pages tokens. Choose font, size, colour, bold/italic, separator rule and band height. Hide suppresses a band; Inherit band restores fallback. Last-page variants cannot change margin height. Canvas margin previews open this tab when clicked. HTML preview shows the first header and last footer; use Proof PDF for actual pagination.
Advanced HTML retains the legacy editor. Convert asks before keeping plain text and page tokens in center slots and discarding rich HTML layout. Switching back clears structured bands after confirmation. All changes use document history and support undo/redo. A margin warning indicates less than 15mm of space. Image slots use the asset library and accept local image assets only.
The Page tab controls the document’s number format and start-at. Select a section for Continue, Restart at or Don’t count (cover), an inherited/explicit format and the optional Show header & footer control on excluded sections. Numbering changes start a new page; canvas badges identify the policy. The report starter excludes its cover, uses Roman front matter and restarts the body at 1. Last variants and section numbering buffer PDF output. DOCX supports default/first/even bands; PowerPoint supports document numbering.
Browsing and organising templates
Open Template uses a two-pane library with All, Recent, Favourites, Unfiled, folders and Trash. Search runs on the server after a 250 ms pause. Sort by name, key or creation/update time, choose tags (all selected tags must match), or select Mine for templates assigned to your named session. Folder views include descendants. Shared sessions without a named actor cannot use Mine.
Star a row to save it to Favourites. Opening a template records it in the last 15 Recent items. These lists stay in this browser and are separate for each tenant and actor. Blocked browser storage does not prevent opening templates.
Use the folder menu to create a subfolder, rename it or delete an empty folder. Row menus open Details or move a template to a folder. Admins can move templates to trash. Trash offers confirmed Restore and Delete permanently actions; permanent deletion removes all stored versions and cannot be undone.
Click the header name or choose More actions > Template details. In Document, edit name and description and select Apply document changes. These edits are undoable and mark the tab dirty; Update or Publish saves the next version. For API templates, Organisation lets you choose a folder, edit comma-separated tags and assign an owner. Save organisation applies immediately without creating a version or changing document history. Details also shows key, version, timestamps and labels.
Admins can choose More actions > Move to trash to discard unsaved changes, trash the stored template and close its tab. The confirmation explains that stored versions remain restorable.
Group a table and paginate records
- Select a table and open Grouping between Columns and Header rows.
- Choose Add group level, enter an item field such as
region, and add a second level such asrep. Up/Down changes the hierarchy; four levels is the maximum. Label paths, date buckets, key sorting, aggregate ranking and a group limit are optional. - Edit each level’s Group header rows and Group footer rows. A banner
such as
{{@group.label}} ({{@group.count}})can span all columns. The shared row editor checks spans and rejects nested tables. - Set Aggregate on numeric columns, enable Subtotal on the desired levels, and enable the table footer for a grand total. A custom footer expression takes precedence over its column’s automatic grand total.
- Use Keep together or New page between groups for pagination. Only the first level can Repeat group header. Download a PDF to check the final page breaks. Include group labels in CSV/XLSX columns adds flat labels ahead of the exported detail columns.
The design canvas shows the group hierarchy and subtotal bands. The data canvas shows grouped details and totals using the same scopes as rendering. Edits, including group hierarchy and pagination, support Undo/Redo.
For one statement per customer, select a repeater, enable Group items, and
choose a grouping field. Its branch becomes a group template. Bind a nested
table/repeater to @group.items; text can use {{@group.label}} and
{{@group.parent.key}} in nested groups. Pagination per group (or per
item) offers page/odd-page breaks, keep together, numbering restart and a count
of leading children to repeat. The canvas draws a divider after those children.
Numbering restart is available only when a page break is selected. In ordinary
Pagination hints, Next even page and Next odd page may add a blank PDF
page to reach the required parity.
Styles and brand kits
Choose Styles in document properties to edit document styling. Brand kit selects a stored kit and its current, labelled or pinned revision; Refresh kit reloads its latest values. The template library opens on Documents; choose Brand kits or All kinds to edit stored kits. New template > Brand kit creates a kit with a specimen showing the default text, headings, caption and table.
Theme controls edit local colour and font-family tokens, default font, heading font/scale and chart palette. Registered fonts remain available. Inherited kit values are shown alongside local values. Advanced JSON supports font faces and the complete theme schema; invalid drafts display an error and are not saved. Local values take precedence over the selected kit.
Named styles support create, edit, duplicate, delete, inheritance and table presets. Usage counts and Select nodes using find affected nodes. Deleting a used style warns before proceeding. Select a node to choose a named style under Text style, or apply one to several selected nodes. Update style from selection moves inline properties into a local named style in one undo step. Reset controls remove individual inline overrides; inherited values appear as placeholders. Unknown references appear in validation.
Colour pickers show theme swatches. Selecting one writes $token; the chip also shows its resolved colour. Starter packs now store their category colours as tokens. Plain changes the token palette to neutral colours, so the same document structure remains editable. Table presets use available accent, soft and alternate-row tokens. The canvas resolves theme styles and chart palettes; use rendered preview to verify pagination.
A live kit label can change every dependent document on its next render. Use a pinned version for a stable approved document. Rich-text run colours, sparkline colours, kit chaining and tenant-wide default kits are not part of this version.
Chart colours, series and KPI cards
Select a chart and open Colours & legend to choose docking, palette presets (including colour-blind-safe Okabe-Ito), individual series colours, category/series mappings, row colour paths or conditional colours. Heatmaps and waterfalls expose their semantic colours. Chart pickers accept hex and rgb/rgba values and reject unsupported CSS colour names; rgba becomes opaque. Reset to theme removes the node palette. Document theme Chart defaults JSON edits the new palette, negativeColor and legendPosition defaults; the legacy palette remains supported.
Series & aggregation switches between a single value, wide columns, or distinct values from a series field. Long format selects sum automatically. Choose series ordering, maximum series (remaining values become Other), missing cells and aggregation. In aggregated charts, Sort & filter sorts categories or totals and Top-N selects categories; Others is recomputed from the original rows.
Add KPI Card from the content palette, child menu or command menu (search KPI, metric or scorecard). Set its value path and optional format helper, comparison, direction, status bands/rules, trend and footnote. The design/data canvas uses the same calculation and formatting as exports. Changes support undo/redo. Blank value paths are validation errors, and overlapping bands produce a warning. Use table conditional-format icon sets for indicators inside cells.
Gauge properties include bands, a numeric or bound target, and units. Colour rules and band labels can convey business status alongside the visual colour.
Data handling and missing bindings
In Schema → Edit → Data handling, enable Fill missing values from schema defaults, choose whether null or empty values count as missing, and optionally Convert types. Coercion can turn null into zero or empty text; use nullable schema types when null must remain null. Schema defaults are retained when editing fields. These settings support Undo and Redo.
Mappings have Use default when beside their default value. A table column’s Text when empty controls the visual placeholder; CSV and XLSX exports keep empty cells. Hide when empty offers automatic content/data detection for supported blocks or a scoped field path. Sections ignore suppression. The rendered preview and exports show the resulting layout.
The docked HTML preview shows a missing-bindings badge. Open it to see paths, breadcrumbs, occurrence counts and spelling suggestions. Select focuses the affected block and opens Properties. Diagnostics inside referenced templates identify the template to open. Older servers that omit the warning-count header show no badge.
Document → Advanced → Binding checks → Fail renders on missing bindings sets the template’s strict default. The publish gate groups missing bindings and offers a block selection action. Ordinary warnings allow publishing; strict binding errors block it. Fix the data or field path and run the check again.
Statement aggregates
The Helpers picker, playground and inline warnings share the same catalogue. Aggregate field arguments accept nested row paths; an own key containing a literal dot takes priority. Missing numeric values are ignored. Financial sums use decimal arithmetic.
| Helper | Example | Result or rule |
|---|---|---|
sum, avg, min, max |
{{sum lines "price.net"}} |
Numeric aggregate; existing empty-array behavior is preserved |
count |
{{count lines "sku"}} |
Existing truthy-field count; without a field, array length |
join |
{{join lines "meta.name" ", "}} |
Join field values |
groupBy |
{{#groupBy lines "meta.region"}}...{{/groupBy}} |
Group by the nested field |
distinctCount |
{{distinctCount lines "sku"}} |
Distinct non-null values, separated by type (1 differs from "1") |
median |
{{median lines "days"}} |
Numeric median; zero when empty |
sumIf |
{{sumIf invoices "total" "status" "overdue"}} |
Sum matching rows |
countIf |
{{countIf lines "qty" 0 op="gt"}} |
Count matching rows |
avgIf |
{{avgIf invoices "total" "status" "overdue"}} |
Numeric average of matching rows; zero when empty |
weightedAvg |
{{weightedAvg lines "price" "qty"}} |
Sum of value times weight / sum of valid paired weights; zero for a zero denominator |
runningSum |
{{runningSum lines "amount" @index}} |
Inclusive prefix sum; uses the supplied array’s order |
percent |
{{percent ratio 1}} |
Formats ratio 0.25 as 25.0% |
percentOf |
{{percentOf part whole 1}} |
Inline math returns a number on the 0-100 scale |
Conditional aggregates accept every structured condition operator via op=, defaulting to eq. Unknown operators fail with an expression error naming the helper. Inside a group band, @group.items provides group scope and the parent collection remains available for report totals.
Running and percent columns
Select a table column’s Value: Running total or Percent of total. The optional value field is a row-relative path, including when the table uses an item alias. With no field, the column’s expression or key supplies the raw value. Running functions are sum, count of non-null values, average, minimum and maximum. Opening balance paths resolve against the enclosing table scope and apply to sums. They are reapplied after every reset.
Running values follow the filtered, sorted and grouped displayed rows. Reset when field changes compares consecutive rows; Reset per group uses the innermost group. Tables inside repeaters restart independently. Percent scope is the whole table or innermost group; missing numerators and zero denominators produce blank values. Percent columns default to the percent formatter at one decimal place. A custom cell template sees the result through @value.
{
"key": "balance", "header": "Balance", "format": "number", "formatArgs": [2],
"running": { "fn": "sum", "field": "amount", "openingPath": "account.opening" }
}
These values agree across HTML, PDF, DOCX, PPTX, CSV and XLSX. XLSX stores percentage ratios as numbers with a percent number format. A column can use only one of running, percent and sparkline.
Pivot show-as
Pivot measures support Distinct count and Median. Pivot count includes every non-null value, including text, zero and false. Distinct counts merge sets across subtotals and grand totals; median uses the underlying numeric observations.
For sum or count, Show as offers value, percent of row, percent of column, percent of grand total and running total down the displayed leaf rows. Percent modes return ratios and default to percent formatting. Hidden total columns still supply denominators. Running subtotal rows show the running value at the close of their group. Other aggregation functions disable Show as and clear incompatible settings.
Long tables across pages
The table’s Pagination group controls header repetition, the final totals row, row protection and continuation bands. First page only retains the header, including extra header rows. Legacy repeatHeader:false now has this meaning. None removes it. New tables place footer totals After last row; older tables keep their saved behavior. The repeated-total hint highlights an aggregate footer that would print on every page.
Keep rows together overrides the document setting. Page totals force it on. Minimum rows at break (2-5) protects the first and last rows with best-effort break-avoid rules; it is not a general widow/orphan counter. A row taller than a whole printable page cannot be kept intact.
Enable Page totals (PDF) and select transaction-value columns. Each printed fragment gets its exact row sum. Carry balance forward adds brought/carried rows and an optional closing label. Opening balances come from the selected column’s running configuration, or a running column whose value field matches that transaction column. Select transaction amounts to make carries match a running balance; summing a balance column instead sums those balances. Continued header text appears after the first fragment and continued footer text before the last. These two texts support table-scope bindings.
| Output | Support |
|---|---|
| Header/footer placement, row protection, page totals, carries and continuation text | |
| DOCX | Header repetition/omission, final footer, cantSplit and paragraph keepNext; no page subtotals or continuation stamps |
| HTML | Header/footer CSS and break hints; browser preview has no PDF page calculations |
| PPTX | Table values; no printed-page flow; continuation/page-total options produce a render warning |
| XLSX / CSV | Table values; printed pagination and continuation bands do not apply |
Continuation bands reserve height even where their first/last-page text is blank. PDF streaming buffers the PDF when stamping is needed, then streams the completed bytes. Templates without these options retain their normal streaming path. Page totals cannot be combined with repeating root group headers or a repeated grand-total footer. A missing printed-row mapping fails the render rather than returning partial totals. Stamped labels use an inlined TTF/OTF report font when available, otherwise Helvetica; use an embeddable report font for non-Latin labels and currency symbols. See verification and performance.
Expression editor
Open Content on a Text, Heading or Barcode block to edit bindings. Table header and footer cells use the same editor in single-line mode. Type {{ for fields and helpers, or press Ctrl+Space. Use arrows to choose, Enter to accept and Esc to close completion. Field completion adds closing braces. Helpers show their signature, active argument, summary and example; hover a binding to inspect its sample value. Aggregate field arguments offer quoted item fields, and format arguments offer supported patterns.
The Result line uses the adapted preview data and the selected repeater item. Local results debounce by 200 ms; plugin expressions debounce by 400 ms and show evaluated on server. Previous/Next item changes the sampled item. Empty bindings appear as amber warnings and Issues hints with spelling suggestions. Hints inspect at most 50 sample scopes. Results are plain text, capped at 300 characters.
Fields, Helpers, Insert and Format… remain available. Block helper insertion wraps selected text. Browse all helpers opens the playground reference. Plain text restores the textarea and saves that preference on this browser. Canvas inline editing remains available.
A failed preview shows its node path and, when available, line/column. Go to block selects a local block. Suggested replacements change exact binding tokens in one undo step. Errors inside referenced templates retain their location but cannot navigate to a block in the current document.
Getting started checklist
The canvas checklist tracks four live steps: define schema fields, bind a block, render a successful preview and save the template to the server. Show me opens the relevant existing control. Collapse or dismiss it; reopen it through Getting started in the command menu. It hides after all four steps are complete. Dismissal and the first successful preview are remembered on this browser.
Contextual question-mark links open documentation in a new tab. Set VITE_DOCS_BASE_URL at build time to point to your documentation host; set it to an empty string to hide links. Embeds default to no help links or onboarding. Hosts can opt in with features.help, features.onboarding and docsBaseUrl, or the custom-element attributes help="true", onboarding="true" and docs-base-url. Empty URLs hide links, including in embeds.
Data schema
The Schema tab defines available fields and powers field suggestions. Nested array item fields are suggested in their repeater scope. A schema describes the expected shape; sample data supplies the concrete values used in the Result line. Schema defaults follow the template’s existing data-handling settings.
Field mappings
Mappings adapt caller data before preview or rendering. Ordered mappings can use earlier adapted results, including plugin JEXL functions on the server. Expression preview sends raw sample data and mappings to the server whenever a required plugin function cannot run locally.
Data scope in repeaters and tables
Repeater children use item fields directly, merged with the enclosing scope. An item alias places those fields under the alias instead. Root fields remain available unless shadowed. @index, @number, @first, @last, @count and nested @parent.number follow the renderer’s scope frames. Grouped repeaters expose @group. An empty branch uses the enclosing scope.
Table headers and footers evaluate once in the enclosing scope. Use an aggregate over the table’s real data path, for example {{sum invoice.lines "amount"}}. Body cells use row scope. The editor’s sample builder shares the renderer’s repeater iteration code, including filtering, grouping and aliases.
Conditional blocks
Use the visual expression builder or raw JEXL to control visibility. Server plugin functions are available in conditions and mappings when their metadata is advertised by the connected server. Their previews are labelled as server evaluations.
Page headers and footers
Configure running page chrome in the Document tab. Header/footer bindings and page tokens use the existing print-rendering rules. The help link opens this section; see Page chrome and numbering above for region layouts and pagination controls.
PDF archival and security settings
Document > Advanced offers PDF/A Off, 2b and 3b, plus PDF security when the server has qpdf. Choose print quality and allowed reader actions, and optionally enter a raw-payload password path such as employee.pdfPassword. The editor never saves literal passwords in templates. PDF/A and security controls disable each other. Preview PDFs remain readable; production downloads and scheduled delivery apply protection. Permission restrictions depend on the reader.
Saving, promoting and recovering drafts
The publish gate includes Describe this change, required when the server requires notes. A network retry reuses the note. When a default render label is configured, success shows the saved version and whether it is live. Admins can promote to staging or confirm promotion to the live label, using the label’s last-read version as an If-Match guard. Editor scope gets Ask an admin to promote and Copy promote command. Header chips show live/not live and staging for the loaded version. A failed label refresh never blocks editing.
Named users with server drafts enabled autosave active API-backed edits after 30 seconds without typing. Local crash recovery continues every 10 seconds for all dirty tabs. On reopening, recovery offers the newest server/local draft, warning when its base version differs from HEAD. Successful save deletes the server draft. Teammates’ drafts appear in history as Open read-only copy: this opens an isolated history copy for review and cannot overwrite the original through Update. It can be forked under a new key like other history copies. Shared actor-less API keys keep local crash recovery only.
Author scheduled reports (Wave 31)
Administrators can open Schedules from the header, choose New schedule, and set a name and saved API template. The command menu also offers Schedule this template? and Schedules for this template for the current API template. Editor-scope sessions do not show these commands. Current version, a label or an exact saved version can be selected; label and version pins are mutually exclusive.
Choose an hourly, daily, weekdays, weekly, monthly or quarterly recurrence, or enter a custom cron expression. Monthly supports a specific day or the last day. Choose the schedule’s IANA time zone; the next five fires show schedule and local times. Invalid or sub-minute expressions show the server’s validation error.
Use static JSON, template sample data, or a URL data source with headers and a timeout. Test fetch displays returned keys/preview or the SSRF/network error. Saved secret/header inputs appear blank with an unchanged placeholder; enter a replacement when changing destinations. Choose any combination of PDF, Word, Excel, CSV, PowerPoint (when enabled) and HTML. CSV/Excel offer a table selector; multiple formats can be bundled in ZIP.
Add email, S3 or webhook deliveries. Unconfigured providers are disabled with an explanation; existing targets remain visible. Email accepts comma-separated To/CC recipients. S3 uses the server’s configured region. Webhook signing secrets can be generated locally. Save before Send test, then supply one to five override email recipients. The test sends only to those addresses, with a [TEST] subject, and does not create a scheduled execution. Editing any field disables test send until saved. SMTP and an enabled schedule engine are required for testing.
The row Actions menu provides edit, disabled duplicate and delete with confirmation. Deleting also removes execution history. The current-template entry point offers This template / All filters. A header badge refreshes the last-24-hours failure count every minute while the page is visible. The form warns when target-order changes could orphan pending delivery retries. If the engine is disabled, an amber notice explains that saved schedules will not fire.
Schedule parameters, conditions and email content (Wave 32)
The schedule form’s Parameters section defines typed fixed values or relative dates. For date/range values, choose a preset and its week/fiscal-year options; previews show values for today and the next scheduled fire in the selected time zone. Parameters are visible in execution history and should not contain passwords.
Choose Always, When a list has rows, or When an expression is true in Deliver. A skipped run appears in history with its reason and resolved parameters. Run now lets you select parameter overrides, an optional local as-of time, and delivery even when the condition fails; the ordinary Trigger now button keeps its existing behavior.
Token menus beneath delivery fields insert run, parameter and sample-data values and show a text preview. Email supports BCC, reply-to, a display name, plain text, HTML with a sandboxed preview, or an HTML body template. Auto mode chooses an attachment or a signed download link using the configured size threshold. S3 offers disposition and metadata tokens. The data source form supports GET/POST, query fields, tokenized headers, a JSON request body and an array-response key. Inserting a body token adds a new JSON field that you can rename. Save before sending a test email.
Admin console
Open Admin console from the account menu of a standalone editor signed in as an admin, or visit /editor/admin/ directly. The menu opens a new tab and copies the same-origin editor session when the browser permits it. Sign in again if no session is available. Personal keys and SSO require an admin role. Shared editor sessions cannot administer the server; use Sign in with admin key instead. Console API-key sessions are separate from editor sessions and expire without refresh.
The navigation reflects available server features. Users require named-user identity mode. Delivery failures, usage and database API-key management require their corresponding database stores. Tenant management requires multi-tenant mode and a super-admin credential. A super-admin selects a tenant in the header before opening audit, delivery, usage or API-key data; user creation accepts the selected or explicitly entered tenant. The user list remains global for super-admins.
- Users: create a personal key (shown once), copy it securely, rename users, change their role, revoke sessions, delete users, or reload the registry. OIDC accounts are provisioned through SSO and can then be administered here. An empty registry displays a sign-in warning.
- Audit trail: filter by event, operation, actor, resource and ISO timestamp bounds. Filters remain in the URL hash. Open a timestamp for event details, export filtered CSV, or purge matching events after typing
PURGE. User governance events use the canonicaluser_adminevent introduced in Wave 34, includingrevoke_sessionsfor a single-field cutoff update. - Delivery failures: choose schedule or batch, filter pending failures by source ID, replay, or abandon after confirmation. A changed schedule or expired artifact includes recovery guidance. A failed replay remains pending.
- Usage: inspect rollups over a default 30-day half-open UTC window, change grouping, or download CSV.
- System: view readiness (including degraded 503 responses), licence expiry, server modes and format capabilities. Licence expiry at 30 days or fewer displays a renewal warning.
- API keys: create, edit, rotate with immediate old-key revocation, or revoke database keys. New secrets are shown once.
- Tenants: super-admins create slugs, edit names and JSON metadata, archive and unarchive. Archiving blocks writes while preserving readable data.
Tables paginate in groups of 50. Sort headers sort the displayed page; use filters to narrow large server-side lists. Sign out clears both local editor and console credentials.
Access and custom roles
The editor loads effective permissions and hides actions that are unavailable. Viewers see View only. Publishers can promote labels and restore versions where granted. Schedulers can manage their own schedules. The API checks each action even while the editor’s 10-second permission cache is refreshing.
Users with role:admin can open Manage access from a folder menu or template
details to view inherited grants and add/remove direct user, group or API-key
grants. The admin console’s Roles page edits custom roles through action
checkboxes; built-in roles cannot be changed. See Access control.
Report options and sub-reports (Wave 37)
Use Params, between Schema and Mappings, to add, duplicate, reorder or remove
parameter declarations. Name/type/visibility, required and multiple choices,
default literals, relative dates or expressions, dependencies, constraints and
allowed values are editable. Import enum choices from the input schema or test a
URL/plugin option source. Defaults and dependencies follow declaration order.
Each committed edit supports undo. The content editor’s Parameters picker
inserts @params expressions (formatted dates or from/to ranges); condition
builders suggest $params paths. The validator flags malformed declarations and
warns about undeclared references.
Preview’s Report options panel keeps values separately from payload JSON, persisted per tenant and template. Changing values does not dirty the template. Dependent options refetch after 300 ms; valid selections remain and invalid children clear. Relative presets remain presets until the server resolves them. The schedule editor shares the prompt panel for templates with declarations; legacy schedules keep their earlier parameter editor.
Insert Sub-report from Blocks / Structure, the command menu, or child-add.
Choose a template from the searchable paged picker, then Latest, Label or Exact
version. Confirming commits the key and pin in one undo step. Manual key entry
remains available for file-mode templates. The mapping grid loads the pinned
input schema, marks required fields and shows mapped, passes-through or missing
status. Map from a parent field, expression or constant; extra paths remain under
Other mappings. Enable Validate against the sub-report’s input schema to run
its own mappings and schema. The grid shows its post-mapping schema, so targets
with their own mappings need compatible raw inputs. The canvas shows @label
and marks URL-backed sub-reports in repeaters per row.
See the API parameter and sub-report contract for value shapes, fiscal/relative dates, source failures and fan-out limits.
Shared Components (Wave 38)
Components above My Blocks lists reusable templates from the connected API. Search, click to insert, or drag onto the canvas. The default pin is prod when available, otherwise latest. The picker can include ordinary sub-reports. Choose Latest, Label or Exact version; Latest changes when the component is saved.
Select a block and choose Save as shared component… in its actions menu. Enter a name, key and description. The editor copies content with fresh IDs, infers a schema from bound parent fields, creates the component and promotes 1.0.0 to prod. Optionally replace the source in one undo step. A failed promotion can be retried without creating a duplicate. My Blocks offers Share with team.
The inspector lists props and offers Auto-map by name; raw mappings are under Advanced. Edit component opens another tab. Detach (copy content here) copies the pinned content with new IDs, and requires no data path, mappings or data source. Parent page settings remain in control.
Inline previews are collapsible and read-only; nested references remain chips. The 30-second cache is scoped to API, tenant and credential, and invalidates on component save. Document’s Use as reusable component switch changes kind.
Document, publish and label panels show consumers and their pin impact. Impact fetch failures do not block saving; truncated counts are lower bounds. Publishing checks missing references and warns about unmapped required props. The server guards deletion of pinned labels unless an administrator explicitly forces it.
Master layouts
In Document ? Template type, choose Layout to author shared chrome. Add a Slot block from the palette, give it a unique name, and choose whether it is required or restricts block types. A slot placed directly in a section must be that section’s only block. Use the Locked settings checklist to protect shared document settings.
For a child document, choose Choose layout in the Document tab. Search the layout catalogue and select Latest, Label or Version; a layout with a prod label defaults to that label. Latest follows every save immediately. Label pins change only after promotion.
Inherited canvas content is inert and cannot be selected, edited or dragged. Its lock badge identifies the layout and version; Open layout opens the source in another tab. Child sections remain editable inside their slot. Use Add section to ? or the section’s Slot field to assign content. Unknown assignments remain visible in Unplaced sections.
Document settings show inherited values. Enable Override to supply a child value; locked fields are disabled. Complex settings use JSON values. Detach from layout copies the merged result into the child, gives inherited nodes fresh IDs and can be undone. Remove layout keeps just the child’s content.
The layout save dialog shows its dependents and offers Validate dependents against the current draft and their stored sample data. Review failures and incomplete scans before saving or promoting. A child layout’s fonts and page geometry come from the merged settings.
Shared preview datasets and publish checks
API templates save named scenarios on the server so teammates can use the same inputs. Local, unsaved and sandbox templates retain browser-only scenarios. Preview headers provide a quick switcher; switching with edited data asks before replacing it. The data picker shows tags and a warning on schema-invalid samples.
Use Save as, Update and Dataset details to share data and set title, description, tags, default and Negative case. Updates protect the loaded revision. If a teammate saves first, choose Reload to load their data or Overwrite to retry your edits against the displayed latest revision. A new concurrent edit still requires review. The migration banner’s Share with team action uploads browser scenarios, assigns safe unique names, and removes browser copies only after every upload succeeds. It appears after a local template’s first server save too.
Form | JSON edit the same object. Unsupported schemas or invalid JSON disable Form. Generate offers Typical, Minimal, Empty, Long text, 500 rows and Unicode & RTL. Array constraints may reduce the requested count. Upload JSON/CSV accepts an object or RFC 4180 CSV; select an array-of-objects property for CSV and its item schema controls numeric and boolean coercion. Replacement actions protect unsaved data.
Dataset details also provide Checks: page range, literal text that must/must not appear, maximum PDF KB and no missing bindings. These checks are saved with the dataset. Use synthetic test data and avoid production PII.
After current-data preflight, the publish gate checks up to ten shared datasets, default first then most recently updated. It skips negative cases and warns on schema-stale cases. A failing dataset blocks publication and offers Open dataset. When the server advertises templateTests and any selected dataset has checks, the gate runs those checks against the unsaved template. Otherwise it uses format-aware validation. Truncation is shown explicitly. Dataset revision changes invalidate the verdict; publication rechecks the set before confirming. Label promotion on the server remains independent of these editor/CI checks.
Payload contracts and promotion
The Issues panel checks bindings in rich text, pivot tables, chart fields, conditions and mappings, including repeater scopes. Analysis loads on demand; computed expressions remain best effort. Schema Explorer’s ?Used by N? badge selects nodes using a field. ?Infer from preview data? derives a Draft-07 schema from all array elements and lets you review it before applying.
Labels show None, Additive or Breaking with the changes for the selected revision. Breaking promotions require ?Promote anyway?; an enforced server contract gate still rejects them. Variant comparisons use the control label. If the check fails, resolve the error before saving. Template trash/delete confirmations list dependent templates so you can assess impact before proceeding.
Advanced page setup
Document > Page groups paper presets into ISO, North American and Envelopes. Custom reveals width and height in mm or inches; dimensions must be between 10 and 1500 mm. Orientation applies to custom paper too. Margins offers Mirror margins (duplex), Inside/Outside labels and a binding Gutter. Advanced offers PDF-only bleed and crop marks.
Select a section and enable Own page setup to choose inherited or explicit paper/orientation, partial margins, page-start parity and inherited/hidden/custom header and footer. Custom chrome opens its own dialog with default, first and even variants. First means the first page of the section. Advanced legacy HTML chrome must be converted to structured chrome before section overrides are available.
The section and the following section begin on new pages when layouts differ. Continue if same layout keeps matching adjacent sections together. The canvas shows separate physical frames and section-break labels, with one drag/reorder list across all frames. Fit zoom uses the widest frame; A3 and landscape paper are no longer clamped. PDF page-boundary overlays are shown for a single frame; use the PDF proof for final pagination across mixed frames.
Search commands for Insert landscape section to add an appendix with one undo step. Page settings, custom dimensions and section overrides are saved with the template and participate in undo/redo. Gutter shading and a dashed bleed outline are editing aids and are not exported.
Overlays
Open Document > Overlays to add up to three watermarks. Choose Text or Image, insert a schema field into template text, and set font, size, weight, colour, opacity, rotation, placement, layer and page selection. Only when opens the condition editor; selected template labels restrict a mark to those resolved render labels. Leave labels unselected for every label.
Background chooses a PNG/JPEG, its fit and page selector. Stationery chooses first-page and continuation PDFs and an under/over layer. Both support conditions. Cover and appendix lists accept up to five PDFs each and can be reordered. The asset picker filters PDF and raster choices; PDF thumbnails show a PDF badge. Select stored assets or enter their /assets/... path.
Text fields commit on blur; selections commit immediately, and settings participate in undo/redo and template saves. The canvas shows non-interactive watermarks, faded backgrounds and a stationery badge. Conditional marks stay visible with a conditional chip while authoring. Use the PDF proof for actual pagination, conditional evaluation and the finished composition; cover/appendix pages are not canvas frames.
One document per customer
In a schedule, expand Send one document per… and enable the separate-document option. Choose the array path, a stable customer key and email recipient paths. Combine records with report data or assign an item name, optionally filter with the expression builder, and set limits, concurrency and the failure threshold. Preview recipients shows the first three resolved recipients and subjects before any document is sent. Each email needs static recipients or a recipient path.
The schedule detail shows its burst path and key. Execution delivery counts summarize sent/failed records; Items opens the paged customer table, and Failed only filters failures for diagnosis. An interrupted send can be repeated during recovery; use customer keys and check execution history before manually triggering another run.
Translation authoring and right-to-left documents
Open Translations from the command menu or the Document language controls. The lazy grid shows language columns and virtual rows; search keys or use Missing only, add keys/languages and rename keys together with literal t calls. Edit plain ICU messages and leave the cell to validate. Export/import UTF-8 CSV or plain-text XLIFF 1.2. Imports replace the catalogue after validation; export first when retaining messages. Quoted empty CSV cells preserve intentional empty messages, while blank cells represent missing translations.
Text and Heading properties offer Make translatable for plain text and simple bindings. It creates a stable key/message and replaces the content with a t call in one undo step. Complex helper/block expressions remain manual. The linter flags unknown keys, missing supported-language values and unused keys. Save the template to publish the catalogue as a version; the version diff shows translation changes separately.
Document settings expose direction (auto/ltr/rtl), authored/default/supported languages and an optional customer language path. Node Language and direction controls override inheritance. RTL canvases retain LTR editor controls and node action bars; text alignment offers logical Start/End with mirrored icons. Rich-text editing follows the selected node’s direction. Preview as offers supported-language choices and existing locale/time-zone overrides; preview choices do not modify the template. Install fonts covering Arabic, Hebrew and other required scripts. See the format matrix.
Links and page fields
Select a block and open Link & anchor. Text, headings, images and barcodes can link to a web address or a place in the document. Web addresses and tooltips accept fields; use {{urlEncode id}} when inserting a value into a URL path. The target picker lists headings and named anchors. Picking a heading creates a unique anchor and sets the link in one undo step. Every block can have an Anchor name: start with a letter, then use letters, numbers or underscores, up to 40 characters. Inside a repeater, bind a unique value, for example row_{{id}}. Duplicate static anchors block publication.
Table column cell settings include Link URL and Link tooltip, evaluated against each row. The rich-text link popover includes In this document. Unknown static link and page-reference targets appear as warnings. The destination is controlled by the template and bound data; review external domains used by your payloads.
Use Insert > Page fields in a content editor for {{@pageNumber}}, {{@totalPages}} or a reference such as {{pageRef "terms"}}. HTML previews show placeholder numbers; PDFs resolve the printed page. Missing targets show ??. PDF page fields can require two or three layout passes and buffer streaming output. DOCX cross-references need Word to update fields. PPTX, CSV and XLSX omit body page fields. PowerPoint internal links work when the target has already been placed; forward links report a warning.
Grids and merged cells
Insert Grid from Structure for bordered key/value panels, approval forms and totals boxes. The default is three rows and two columns. Select a cell using its R1C1 button, then use Merge right, Merge down or Split cell in Properties. Merges concatenate the absorbed content. Adjacent cells must have matching spans along their shared edge; merge each row first to combine a rectangular block. Add or delete rows and columns in Grid properties. Insertions through a span extend it; deleting its anchor preserves the remaining span and content. Deleting a cell clears its contents. Structural rows and cells cannot be dragged out of the grid.
Drop content into a cell or use its add menu. Each cell supports style and vertical alignment. Column widths accept physical units, percentages or auto. A row height is a minimum. Repeat headers form a contiguous prefix and cannot contain nested tables or grids. Page breaks and sticky headers are forbidden inside cells. Grids support 63 columns and 500 rows. All operations use ordinary undo and save.
For data tables, Repeated values offers Show, Hide duplicates and Merge cells. Runs reset at changes in an earlier column with duplicate handling enabled, and at group boundaries. Templated cells compare their rendered plain text. Long printed listings generally work better with Hide duplicates: Chromium can split a merged cell over pages, leaving its text on the first fragment. Page totals and continuation bands require Hide duplicates or Show. Sparklines always show each row.
Shapes and form controls
The Shapes & forms palette supplies rectangles, rounded rectangles, ellipses, lines/arrows, checkboxes and character combs. Shape dimensions use mm, cm, in, pt or px; lines may have one zero dimension. Set fill, stroke, dash, rotation, vertical flip and arrow ends in Properties. Colours must be RGB values; dimensions are bounded at 10,000 px.
A checkbox uses the condition builder or an expression evaluated against preview data. A nonblank expression overrides the condition. Labels support Handlebars. Choose box, tick, cross or radio and set the physical size. PDF/HTML use the same accessible SVG as the canvas, without checkbox font dependencies. Word box/tick/cross controls remain toggleable; radio is an image. PowerPoint uses a checkbox image and label; checkboxes nested inside native grid cells become plain-text checkbox symbols.
A comb has 1–64 cells, one Unicode code point per box. It uppercases before truncation when enabled, truncates from the end, then pads on the left or right. 12-345 in nine right-aligned cells has three leading empty boxes. Word and PowerPoint keep combs as native tables.
See format support for export limitations.
Office previews and fidelity
Use More in Preview to view DOCX, XLSX, PPTX or CSV inline. Word is an approximate view in an isolated shadow root; embedded fonts are disabled. Spreadsheet previews show the exported workbook, worksheet tabs, merges and number-format tooltips, bounded to 500 rows × 50 columns per sheet. PowerPoint shows recorded layout boxes; text shaping can differ in PowerPoint. Downloads remain available.
Office views refresh manually and show a stale notice after edits. Auto-refresh is optional, waits 2.5 seconds and runs only while the preview is visible. The modal offers Compare with HTML. Fidelity warnings select the affected block and outline it on the canvas. Target formats in page settings control inspector badges and publish checks; absent targets, table-free templates skip CSV/XLSX checks. Warnings do not block publishing.
AI assistance while editing
Use Ask AI in the header or command menu, or Ctrl/Cmd+I outside rich text. Describe the change, choose the whole template or selected subtrees, and optionally include preview data. Review changes opens the visual diff. Accept changes applies one undoable change; Reject discards it. If the document changed while AI worked, run the request again.
Text and heading menus offer Translate (a BCP-47 locale, such as de-DE) and Rewrite with instruction. Submit the language/instruction before streaming begins. Translation produces a translated copy of the text; use multilingual string tables for reusable translations (FA-89).
Generate with AI > Start from my data accepts a JSON object. Objects containing type, properties or $schema are treated as JSON Schema; other objects are sample data. Conditional and field-mapping expression editors offer Describe in words with an explanation, preview and explicit Insert expression action. In preview data, Generate > Realistic data (AI) creates one replacement or multiple named scenarios. Replacing custom data uses the existing confirmation flow.
AI controls appear only when the server reports the matching capability and your permissions allow AI use.
Import a document
Choose Import document on the welcome screen or in New Template. Upload/drop HTML, DOCX, PDF or a raster image. HTML and DOCX work without AI; the AI modes are disabled when unavailable. Optionally provide data/schema and an import hint. Convert, compare the source with the editable draft, and read the warnings. DOCX sources are offered as downloads for comparison in Word.
Choose Create to open a local draft in a new tab with its sample data ready for preview. Importing and reviewing do not save a server template. Publish when you are satisfied. HTML source previews run in a sandbox with network access blocked. Dense or unusual layouts may need manual correction.
Large-template navigation and JSON source
In Outline, double-click a label or press F2 to name a block (up to 80 characters). Names appear in the outline, breadcrumbs, inspector and canvas accessible labels. Sections use their existing label; other names are stored in meta.name. Names and comments remain available on locked blocks.
Use the row lock button or block menu to lock a subtree. Ctrl/Cmd+Shift+L toggles the selected blocks; the menu is the fallback when a browser reserves that shortcut. Descendants inherit locks. Editing, deletion, cut, movement, style changes and replacement skip locked blocks, and an ancestor containing a locked block cannot be deleted or moved. Duplicate/copy produces an unlocked copy. These are authoring safeguards inside the editor; API and SDK clients can still modify those nodes.
Hide/Show in Outline collapses a container on the canvas without changing rendered output or marking the tab dirty. This browser setting is saved by tenant and template. Selecting a hidden descendant expands its ancestors. The outline filter matches names, types and bound data paths, includes matching ancestors, and temporarily expands them. Press / in the tree to focus the filter and Escape to clear it. Filtering disables drag reordering.
Use Source in the header, Toggle source view (JSON) in the command menu, or View JSON in a block menu. Choose the whole template or selected block. Format changes indentation; Revert reloads the current template. Apply (Ctrl/Cmd+Enter) checks strict JSON, the template schema, unique IDs, block placement, embedded constraints and authoring locks. Problems link to their source positions. Warnings remain advisory. A successful Apply creates one template undo step and preserves unchanged node identities. Deleting a property in selected-block JSON removes it from that block.
JSON typing uses its own local undo history. Header Undo affects the template after Apply. Unapplied drafts survive design/source and tab switches in this session, but are not saved to the server. If the template changes while a draft is open, Reload discards the draft; Keep my draft retains it for a three-way merge on Apply. Conflicting changes require explicit confirmation. Server template keys cannot be renamed in source; local key changes use the normal local-template storage migration.
Source view is enabled in the standalone editor. Embeds opt in with source-view="true", createEditor({ sourceView: true, ... }) or setConfig({ sourceView: true }). Host read-only and property locks also apply to source editing.
For local profiling, load ?fixture=large (1,000 nodes) or ?fixture=large&nodes=2500. Run node scripts/large-template-perf.mjs from apps/editor, with EDITOR_URL pointing to the dev server. Optional API_URL and EDITOR_API_KEY authenticate a local test server; PLAYWRIGHT_EXECUTABLE_PATH selects an installed Chromium. Timings are logged; PERF_BUDGET=1 enables assertions. This timing script is intentionally outside CI.
Build a matrix report
- Start with Sales Pivot (Matrix), or add a Pivot Table and choose its array data path.
- Set row fields, then use Column Dimensions to add up to three column levels. For dates, open Grouping & ranking and choose year, quarter, month, ISO week, day or a fiscal bucket. Fiscal start month is 1-12.
- Give base measures explicit keys such as
revenueandprofit. Use label as key generates an editable key; keys must be unique identifiers. Select sum, count, average, min, max, distinct count or median. - Add a Calculated Measure named Margin with key
margin, expressionrevenue ? profit / revenue : null, and percent format. Expressions see base keys and earlier calculated keys; the panel shows syntax errors inline. - Enable Show column subtotals and Show dimension labels under Totals & Display. Subtotal labels apply to both axes.
- Optionally rank siblings by a measure, or set Top N and its base rank-by measure. Others keeps the remainder and always appears last. Use Show as for parent percentages, running totals across or changes from the previous column.
- Preview, save and download. Matrix changes use ordinary Undo/Redo and survive JSON/API reload. Use landscape/larger paper for many columns; XLSX retains numeric values and merged headers.
Icons and extended charts
Choose Icon from Content to add a bundled symbol. The searchable picker matches names and tags (for example, email finds mail). Enable Bind to data for a Handlebars name, add value-to-icon and value-to-colour mappings, and choose a fallback for unknown values. Set size, colour and stroke width; add alternative text for a meaningful image or leave it unset for decoration. The optional label supports bindings and appears beside the glyph. In a table column, enable Display as icon, configure its map, and optionally show the value beside it. PowerPoint tables and spreadsheet exports retain value text/data.
The chart selector groups 24 types into Basic, Comparison, Distribution, Financial and Project. Each new type exposes its own bindings and options. Gantt accepts ISO dates or epoch milliseconds; a missing end is a milestone. Bullet and Gantt grow automatically with their row counts unless a fixed height is set.
Running values and notes
Select a node and open Running values beside Pagination hints. Add up to four named plain-text or Handlebars expressions. Repeater definitions run for each item; table group levels offer the same controls for group scope. Structured header/footer text offers an Insert running value menu with first, last and start policies. The editor warns when chrome refers to a name with no definition. Preview shows first values; download the PDF to check pagination and carry-forward behavior.
In a rich-text visual editor, choose Insert footnote, enter a body, choose Footnote or Endnote, and save. A numbered superscript pill preserves the body and kind in template JSON. Click a pill to edit it; unchanged formatted bodies retain their formatting. Typing a new body creates plain text with optional Handlebars bindings. Insertions support local and global undo/redo. The compact inline toolbar exposes this control under More. Handlebars content fields also offer Footnote and Endnote snippets.
Document settings have a Notes group for list placement, separate numbering formats, document/section restarts and headings. Clear a heading to hide it. Word uses its own native note numbering and placement; PDF uses linked lists rather than page-bottom footnotes. Search and path rename include note bodies and running expressions. The older footnote text-size variant is now labelled Small print.
Hyphenation and flowing columns
Document > Advanced > Hyphenation enables bundled offline language patterns, selects a language, sets minimum word length (4-20, default 6), and optionally hyphenates capitals. The default language comes from the document, metadata or render locale, falling back to US English. Unknown languages warn once and remain unchanged. Node Typography offers Hyphenation (inherit, auto, manual, none) and Text wrap (wrap, balance, pretty). Explicit node languages select their own available pack. Heading/code/SVG subtrees default to none; an explicit auto style enables them. Script and stylesheet contents are always preserved.
Container and Section properties offer Flow columns (text continues column to column): one to six columns, gap, divider and final-page balancing. The existing Columns block keeps independent side-by-side cells; flow columns fill down one column and continue into the next. Set Span all columns on a direct child for a full-width heading. Put breakInside: avoid on repeater cards to keep each card together, and keep unbreakable content shorter than a printed column. The canvas keeps its vertical sorting and shows a column-count chip; preview/download shows the real layout.
Word uses native hyphenation and continuous column sections. It always balances, uses a plain divider, and degrades nested flow regions or flow inside fixed Columns/grid/label cells to one column. Per-node hyphenation suppression and minimum-word length apply to HTML/PDF, not Word. PowerPoint uses one column with an unsupported-style warning and ignores hyphenation. Spreadsheet exports do not receive soft hyphens. See the print verification and performance limits.
PDF forms (Wave 65)
Choose Form Field under Integrations to add text, multiline, checkbox, radio, dropdown, date or signature fields. Properties include a bound name and default value, label and tooltip, options, required/read-only flags, dimensions and font size. Data-path suggestions insert Handlebars expressions. Give repeated fields a name containing {{[@index]}}; duplicate static names share one PDF value and raise an advisory warning. The Signature block also has an option to add a standard fillable signature field.
Choose New template > From PDF form, then select or upload an original PDF. The canvas becomes a field mapping table with field names, types, pages and options. Enter a data path or switch to a Handlebars expression. Checkbox bindings may also use a Checked when expression. Infer inputSchema from fields creates safe field1, field2, etc. input keys and matching bindings, including boolean/choice types and required fields. It replaces the current schema and mappings; undo restores them. Flatten filled fields produces a completed static document. Use the existing PDF Preview to inspect the filled authority form. Replace PDF form keeps mappings so changed/missing field names can be diagnosed by dry-run. Form-backed templates support PDF output only.
Importing CSV, XML and Excel data
Open the sample-data menu and choose Import CSV / XML / Excel…. Choose a file and mapping options, then Preview import to inspect up to 20 rows. Apply import uses the existing replacement confirmation for unsaved data. Select Infer input schema after applying to review the inferred schema. Import is unavailable in sandbox mode or while network actions are disabled. URL sources have a Response format selector and format-options editor.
When SQL sources are enabled, choose SQL query, select a shared connection and enter one parameterized query. The parameter grid detects :name placeholders and supports paths, JSON values and templates with optional types. Set maximum rows and click Run to inspect up to 50 rows, then infer the schema at the source target path. Root SQL sources select /rows by default.
Review requests, threaded comments and label schedules
Open More actions > Labels for promotion controls. Protected labels display a lock and use Request promotion with a saved version, note and optional test-run ID. For a future release, enter a local effective time and IANA time zone; the default is your browser’s zone. Unprotected labels also offer Schedule promotion to users with label promotion permission. Show label timeline loads the selected label’s history, pending moves and cancellation buttons. Times display in the selected zone. The API rejects invalid or ambiguous daylight-saving times.
More actions > Reviews (n) shows the pending count and opens the lazy review dialog. Filter by status, inspect the request and decisions, and use Compare to open both saved versions in the visual diff viewer. Named users with approval permission can approve or reject; rejection needs a comment. Requesters and admins can cancel a pending request. API-key and shared-key sessions cannot approve.
The Comments tab beside Blocks and Outline stores review threads for the current saved version. Start a template-wide thread or enable Selected node only, reply, resolve/reopen, and enter comma-separated user IDs for mentions. Authors can edit their text; authors and admins can soft-delete. A node’s note popover links to its review thread count. The existing node note is still part of the versioned template. Review comments are stored separately and do not dirty the template or create versions. Local templates must first be saved to the server.
E-signature anchors and signers
In Render config > Advanced > E-signature signers, declare each role with the email path, name path and optional routing order. These paths read the adapted render data. An optional email subject supports bindings.
Select a Signature block to choose a declared Signer Role or enter a custom role, and set Required. An undeclared role is marked invalid until it is declared or supplied by the envelope request. Anchors and roles support bindings; repeaters should produce a unique anchor for every rendered field. Hidden labels remain hidden by default in PDF, HTML and DOCX.
Sending an envelope requires a configured e-signature connection and the API feature flag. The first provider is DocuSign, and sending supports PDF only. DOCX interpolates anchors for downstream workflows; spreadsheet, CSV and presentation exports do not create signature tabs. See E-signature API setup.
Report portal and guided forms
Administrators publish reports from Labels > Portal, choosing an approved label, category, summary, icon, formats and subscriptions. Users open /portal, run permitted reports and manage their own retained outputs and email subscriptions. Users administration includes optional email.
The input-schema editor’s Form disclosure configures widgets, steps, help, placeholders, enum labels, visibility and lookups. Root steps control interview order. Advanced JSON preserves translations and unsupported annotations. See portal guide and interviews.
Usage analytics
Open Admin console > Usage with an account that has usage:read (admin or
auditor by default) and PostgreSQL or SQL Server storage. The navigation entry
is hidden when the server does not advertise analytics or the account lacks
permission. Plain editor accounts and file storage do not show the dashboard.
Choose 7, 30 or 90 days, or custom UTC dates. The end date is exclusive, and the range is clamped to the server’s maximum (normally 92 days). Production is the default source; preview/test sources are separate. Optional mode and actor filters apply to every chart, statistic, template row and CSV export.
Tiles show successful renders, failure rate, exact p50/p95 successful durations and pages. Daily lines include zero-activity days; charts compare formats and modes, including scheduled runs. Error codes identify common failures. Sort the per-template table across all matching templates, or choose Unused in range to identify active documents with no attempts in the selected window. A template with only failures is still used. Last rendered in this table refers to the selected range. Export CSV uses the current source/range/mode/actor filters and includes successes and failures; external billing exports still default to successes unless explicitly requested otherwise.
Super-admins can select All tenants for global consumption totals and exact durations. Select a tenant in the console header to use the full template dashboard and CSV export. Tenant identifiers appear in this explicit global view only.
The template picker also shows the last successful render and rolling 30-day successful count, or Never rendered, whenever database analytics are available.
AI Narrative block (Wave 77)
When the server enables narratives, Content includes AI narrative. Set a static instruction, add named facts with the variable picker, and provide fallback text. The instruction is not a binding. Choose tone, language, word limit, variant, cache days, number verification and disclosure. Facts and instructions are sent to the configured provider; the provider/region note identifies it.
Preview narrative uses the current sample data and shows text, evaluated facts, numeric verification and token usage. Regenerate bypasses cache and consumes tokens. Canvas shows fallback with sample bindings until a preview succeeds; the preview is kept only in the current browser session and invalidates when the node or sample changes. Rendered editor previews only use cached results and never generate automatically. A warning explains why narratives inside repeaters or subreports always use fallback. The palette entry disappears when the capability is disabled; existing blocks remain editable and keep their fallback.
A monthly-budget error has a budget-specific message in generation and rewrite dialogs. Increasing a rate limit does not increase the AI token budget; an operator must review tenant AI policy.
Positioned designer
Choose Positioned for fixed geometry. The properties panel preserves each length’s unit, stages numeric edits until blur or Enter, and exposes the anchor, rotation and sibling layer order. Layout & spacing → Layering also exposes Position and Z-index for ordinary nodes.
Container is the default anchor. Page and Every page are available only for direct section children and measure from the physical paper edge. Page starts at the section’s origin; start later sections on a new page. Every page repeats throughout the document. The canvas displays a selectable pin in the outline’s flow order and an overlay on each section page group. Its page groups represent section starts, not a live simulation of every PDF page.
Use Rulers to show physical mm or inch ticks and margin markers. Rulers scale with canvas zoom. Snap to grid defaults to 1 mm; grid size, ruler visibility, units and snapping persist in this browser. Drag the box’s label to move it; select it to reveal eight resize handles. Alt bypasses snapping; Shift preserves the resize aspect ratio. Margin, centre and sibling edge guides help placement. The drag is staged locally and commits one undo step on release. Escape, pointer cancellation or an intervening document edit cancels it.
Arrow keys nudge a selected positioned box by 1 mm, Shift by 5 mm and Alt by
0.1 mm. Repeated nudges within 800 ms share an undo step. Inputs, dialogs and
an active sortable drag keep their own keyboard behavior. Select sibling boxes
with the same anchor to align edges or centres; three or more can distribute
horizontal or vertical gaps. Each operation has one undo step and respects locks.
Relative %, em and rem dimensions require confirmation before conversion
to fixed mm on the first pointer drag. Alignment requires physical units.
Chromium clips positioned content in print margins, including absolute page anchors. The outside-printable-area diagnostic is therefore an error. For full bleed, use zero page margins and put ordinary flow padding in a section or container. Rotation can extend the box beyond its unrotated bounds; the diagnostic checks those expanded bounds too. See the format-specific limitations.
Office workspace
With Office authoring enabled, New Template offers Upload Word / Excel / PowerPoint file. Choose a file, review its tag contract, then create it. The picker shows an Office format badge and opens a separate workspace with tags, schema inference, sample data, source replacement, labels and version history. Save before previewing. PDF preview requires the converter; otherwise download the filled native file. Edit layout in Office and replace the source to save a new version. See Office templates.
Import a legacy report
Use Open or import template to select JSON, RDL/RDLC, JRXML, RptToXml XML or HTML. Available legacy formats come from server capabilities. Choose the HTML dialect or XML layout, review the filtered fidelity summary, then select Load. The draft opens in a new tab with sample data; imported assets are uploaded into the current tenant. Approximations and placeholders appear as navigable Issues-panel hints and warnings. See the migration guide.