Release v0.72.0 — Editor UI parity
Date: 2026-04-17
Tag: v0.72.0
Summary
Four structural authoring features that were already supported by the schema and renderers are now reachable from the editor’s properties panel. Users previously had to hand-edit template JSON to access them. All four now have first-class UI with full test coverage.
This release also fixes a product bug where the Letter and Invoice starter packs were silently producing A4-paper templates, and repairs accumulated test-vs-code drift across six untouched test files (the editor suite had been red at 72/80 files despite the code being correct).
What shipped
Chart combo type and data labels (ChartNodeProps)
'combo'added to the chart-type picker — bar series plus overlaid line series on one panel, with an optional secondary y-axis.showDataLabelscheckbox anddataLabelPositionselect (outside/inside/center/auto) exposed for every chart type that actually honours them:bar,horizontalBar,stackedBar,groupedBar, andcombo. The set matches both the generic schema and the combo renderer’s runtime contract (packages/chart-renderer/src/combo.ts).- Secondary-axis editor (label / min / max) with an invalid-number guard: non-finite numeric drafts normalise to
undefinedbefore the object is built, so a half-typed value like"abc"in the Min field cannot silently yield{}and enable the secondary scale with defaults. - Per-field dirty refs isolate Min and Max drafts from Label. An invalid own-blur keeps the draft and inline warning visible and does not commit. A sibling Label commit preserves any previously-committed Min/Max values rather than dropping them when the sibling draft is invalid.
TOC page numbers and leader style (TocNodePropsInline)
showPageNumberscheckbox andleaderStyleselect (dots/dashes/none).- Helper copy updated to name the PDF-only stamping behaviour: HTML export and on-screen preview leave the page-number slot blank; the PDF renderer stamps actual numbers post-render via
@pulp-engine/pdf-transform#stampTocPageNumbers. - The control never commits
undefinedonce touched —undefinedand'dots'are semantically identical (both render dot leaders per the model default), so forcing a distinct “unset” option would be ceremony with no observable effect.
PPTX slide layout (RenderConfigEditor)
- Tri-state
Default/Preset/Customradio. Default commits{ pptxLayout: undefined }so the field drops from the serialized template (PPTX renderer falls back to its ownLAYOUT_WIDEdefault). - Presets cover
LAYOUT_WIDE(13.333 × 7.5 in),LAYOUT_16x9(10 × 5.625 in), andLAYOUT_4x3(10 × 7.5 in). - Custom mode uses raw string width/height drafts so mid-typed values do not fight a number input; parsing happens at blur with bounds enforcement (positive, ≤ 100 inches per the schema limit). Invalid values show an inline error and do not commit.
- Drafts for both preset and custom modes survive round trips through Default.
Table custom header rows (TableHeaderRowsEditor, new lazy-loaded panel)
- Structural authoring for prepended
<thead>rows: content (inline HTML / Handlebars), colspan, horizontal alignment, row add/remove/move, cell add/remove. Existing JSON-authored row and cellstyleobjects round-trip untouched — style editing itself is deferred to a follow-up so this release can ship the structural surface. - Live per-row colspan-sum chip — green when the row’s cell colspans total
columns.length, red warning otherwise with the exact mismatch text. Inline nested-<table>rejection mirrors the schema’ssuperRefineguard at write time. - Zero-columns guard: when the table has no columns yet, the panel renders a disabled empty state rather than offering an add-row button that would produce schema-invalid rows.
repeatHeaderwarning: whenrepeatHeaderis off and any header rows are configured, an inline warning surfaces that those rows will not render (HTML/DOCX emit<thead>only whenrepeatHeader !== false).- Row identity is tracked via synthetic UUIDs maintained in lock-step with
headerRowsby every local structural handler, routed through a singlecommitHeaderRowshelper. Local moves preserve in-flight drafts across reorders even when rows share identical colspan signatures (a common case because the default new-row shape is a single cell spanning all columns). - External-mutation reset uses
useLayoutEffect(notuseEffect) so the row-id reseed runs synchronously before paint. Undo, redo, node switches, and out-of-band writes therefore tear down and remount every cell editor before the user sees a frame — no stale draft can ever attach to the wrong logical row, not even in a same-length same-shape undo-of-move.
Other fixes
- Starter-pack Letter paper regression:
FINANCIAL_RENDER_CONFIGandLETTER_RENDER_CONFIGhad both drifted topaperSize: 'A4'. The Letter and Invoice starter packs now correctly default to Letter paper as their names imply. RENDER_PREVIEW_RESERVED_SLOTSforwarding inRENDER_MODE=child-process— previously the env var was only read in the API process; the worker spawned byChildProcessRenderDispatchergot a fixed allow-list and silently ignored it. The documentedRENDER_PREVIEW_RESERVED_SLOTS=0rollback path in release-v0.65.0.md was non-functional in this mode. Added positive and no-leakage regression tests.RENDER_MODE=socketcontroller startup from pnpm-deployed bundles — strict===in the entry-point guard comparedprocess.argv[1]againstfileURLToPath(import.meta.url), but pnpm symlink resolution made the two paths diverge.realpathSync(process.argv[1])now normalises both sides.
Editor test-suite drift cleanup (test-only)
Six files in the editor test suite had drifted out of sync with their production counterparts — no production behaviour changed. Repaired in a dedicated commit so main goes from 72/80 files and 1149/1169 tests green to 80/80 files and 1174/1174 tests green:
LoginGate.test.tsx/login-identity.test.tsx:setStoredTokenassertions updated for the C.0 Stage 2tenantIdparameter (6th argument).login-identity.test.tsx:getStoredScopeadded to the auth module mock soEditorShellrenders under test.EditorHeader.test.tsx/EditorShell.lazy.test.tsx: History assertions now target the top-level toolbar button (aria-label="Version history") instead of the removed dropdown menu item.PreviewPanel.test.tsx: 7 regex matches updated frompulp-engine apitopulp engine api(post-rename copy alignment).ChartNodeView.test.tsx: node-type chip assertion case-fixed fromcharttoChart.
Docs + website sync
- Website showcase (
apps/website/src/pages/showcase.astro),docs/editor-guide.md, anddocs/evaluator-guide.mdnow say the new-template dialog offers 25 built-in starting points (Blank pinned at the top plus 24 tabbed packs across 4 categories), superseding the older “30+ starter packs” and “23 starter packs” phrasings. - Editor screenshot refreshed (
apps/website/public/screenshots/editor.png).
Breaking changes
None. All editor UI additions are additive; templates authored before this release open unchanged, and every new field committed by the new UI is also accepted by the pre-existing schema.
Residual notes
- Row and cell
styleediting for custom header rows is not yet exposed. JSON-authored styles are preserved on round-trip, but adding the style editor toTableHeaderRowsEditoris deferred to a follow-up so it can share a reusable style primitive with the rest of the panel. - The editor’s ad-hoc Claude-agent permission list (
.claude/settings.json) accumulated a handful of exploratory bash rules during authoring. Left uncommitted; not part of this release.