Architecture
A map of the source tree grouped by responsibility, not by import order — useful context if you're modifying the package itself rather than just consuming it.
Editor (src/Designer.tsx + src/components/)
Designer.tsx owns selection state, the tab bar (Fields/Data/Style/
Filter/Page), clipboard (copy/paste), keyboard shortcuts, and every
mutation on Template/Binding[] (add/remove/reorder schemas, update a
binding, resize page bands…). It renders two children:
PageCanvas.tsx— the actual page: one<Rnd>(react-rnd) per schema for drag/resize, the header/footer/margin bands drawn in red, the grid, marquee (box) selection, zoom controls. Delegates what a field actually looks like toFieldBox/(one small component perschema.type).- The side panel —
FieldList.tsx,Toolbar.tsx, and, once a field is selected,PropertyPanel.tsx— a thin dispatcher to onePropertyPanel<Type>.tsxper schema type, each split into a "Data" and a "Style" tab.BindingEditor.tsx(the generic path/array/ section/chart/kpi binding editor) andPropertyPanelFields.tsx(shared X/Y/width/height inputs) are reused across several of them.
Selection, editing, and binding all live in the same React tree — no module bridge, no imperative API between the canvas and the panel.
Bindings and templates (src/bindings/, src/tableColumns.ts)
bindings.ts is pure logic over strings/plain objects, with no
third-party dependency:
resolveToken/renderTemplate— evaluate a{token}/{FUNCTION(...)}template against the real JSON document. This is what turns aTextSchema.contentor aKpiSchema.title/subtitleinto the string that actually gets drawn. Formatting insideDATE/CURRENCYis intentionally independent of the Designer's own UI language (localeprop) — it's part of the generated report's content, written by whoever authors the template, not the tool's chrome.buildInputs— turns the whole JSON document +Binding[]into a flatRecord<schemaName, string>(or a stringified 2D array, for tables) that the canvas preview andgenerate.tsboth read from.resolveChartItems/aggregateChartItems— resolve a chart'sBindingagainst the real array, applyfilters, group the tail into "Other" pasttopN.resolveKpiValue— resolve akpi-typeBindinginto a single aggregated number (sum/count/avg/min/max).describeBinding/describeBindingShort— short human-readable summaries used only in the editor UI.
tableColumns.ts holds the pure functions that keep a TableSchema's
head/content/footer/columnStyles in sync with its Binding
(array) when a column is added/removed/reordered/reformatted from the
panel.
PDF generation (src/pdf/)
generate.ts is the entry point (generatePdf(template, data, bindings, options?)) — pure JS, no DOM, safe to run in Node. For each
schema it resolves the value via buildInputs/resolveToken (or
resolveKpiValue for a KPI with a kpi binding) and delegates the
actual drawing to a per-type module:
drawTable.ts— header/body/footer rows, per-column style overrides, pagination when a table doesn't fit on one page.drawSection.ts— repeats the group of member fields once per item of the bound array, growing/paginating with the rest of the body.drawChart.ts— pie/donut/bar, legend placement (font size configurable), color palette (src/chartColors.ts).drawKpi.ts— the colored card + Material Symbols icon path (src/materialIcons.ts), configurable font sizes/icon size/corner radius (defaults insrc/kpiFormat.ts).
Supporting modules: pagination.ts (splitting the body across pages
against headerHeight/footerHeight/marginLeft/marginRight — see
src/zones.ts for how the editor classifies a field into header/footer/
margin/body by position alone), fontUtils.ts (embedding a custom TTF
via fontkit, normalizeFontBytes), backgroundImage.ts (turning an
uploaded PDF/PNG/JPEG into the page's background PNG), color.ts,
resolvers.ts, and pdfWorker.ts (wiring up pdf.js's worker for
PdfPreview, browser-only).
Only downloadPdf, Designer, PdfPreview*, and the UI components
touch the DOM. Everything else under src/pdf/, src/bindings/, and
src/types/ is safe to import in a Node backend — see
Backend integration and
Server-only usage.
UI language (src/i18n/)
The Designer's own UI text (buttons, tabs, warnings, placeholders)
comes from a small dictionary — en.ts (canonical, default) and
pt-BR.ts (typed against it, so a missing key is a compile error, not
a silent blank string). I18nProvider/useT/useLocale (React
context) wire the <Designer locale="en" | "pt-BR"> prop through to
every component; a component used standalone, without <Designer> on
top, still renders correct (English) text via the context's default
value. This only covers the editor's chrome — it never touches how
{DATE(...)}/{CURRENCY(...)} format the generated report's own
content.
Two entry points (src/index.ts vs. src/server.ts)
src/index.ts exports the full public API — React components
(Designer, PdfPreview, PdfPreviewModal, the UI kit) plus
generation/bindings/types. src/server.ts mirrors the non-React subset
only — same generation/bindings/types, zero references to react
anywhere in its compiled output — built as a separate bundle (dist/ server.{js,cjs,d.ts}) exposed via the json-pdf-designer/server
subpath. Keeping them as two hand-maintained lists (rather than one
generated from the other) means an export accidentally added to only
one of them is easy to spot in review — see the comment at the top of
src/index.ts.