@broadpaper/renderer 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @broadpaper/renderer
2
+
3
+ [![npm](https://img.shields.io/npm/v/@broadpaper/renderer?color=171511&label=npm)](https://www.npmjs.com/package/@broadpaper/renderer)
4
+ [![Licence: commercial](https://img.shields.io/badge/licence-commercial-171511)](https://www.broadpaper.com/legal/terms/)
5
+ [![Free to evaluate](https://img.shields.io/badge/evaluate-free%2C%20no%20trial%20clock-4a453c)](https://www.broadpaper.com/docs/guide/licensing/)
6
+
7
+ DOM rendering, measurement and page composition for BroadPaper reports.
8
+
9
+ Part of [**BroadPaper**](https://www.broadpaper.com/developers/) — an embeddable visual report designer and PDF renderer.
10
+ Your users design the report in your application; BroadPaper owns the layout, the branding,
11
+ the pagination and the output.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @broadpaper/renderer
17
+ ```
18
+
19
+ Turns a resolved document into pages in a browser: rendering virtual nodes to the DOM,
20
+ measuring them, and composing the result into paginated output. `layoutDocument` and
21
+ `layoutDocumentAsync` are the entry points, and `createReportViewer` is the read-only viewer
22
+ for showing a finished report without the designer around it.
23
+
24
+ BroadPaper owns pagination — not the browser, and not the PDF engine. Sections are measured
25
+ as continuous galleys and a pure paginator decides the breaks, so the same document breaks
26
+ in the same places wherever it is drawn.
27
+
28
+ ## Licensing
29
+
30
+ BroadPaper is **free to evaluate, with no trial clock and nothing behind a feature gate**.
31
+ Install it, use every feature and evaluate it against your real data for as long as you like.
32
+ Production use needs a licence.
33
+
34
+ In the software, the only difference without a licence is that generated PDFs carry an
35
+ evaluation mark, and a licence removes it. Contractually, the licence is also what permits
36
+ production use. No seats, no activation, no machine binding, no telemetry, and no call home at
37
+ any point.
38
+
39
+ - [How licensing works](https://www.broadpaper.com/docs/guide/licensing/)
40
+ - [Pricing](https://www.broadpaper.com/developers/pricing/)
41
+ - [Licence agreement](https://www.broadpaper.com/legal/terms/)
42
+
43
+ ## Documentation
44
+
45
+ - [The read-only viewer](https://www.broadpaper.com/docs/guide/viewer)
46
+ - [Themes and branding](https://www.broadpaper.com/docs/guide/themes)
47
+ - [White labelling](https://www.broadpaper.com/docs/guide/white-labelling)
48
+ - [Full documentation](https://www.broadpaper.com/docs/)
49
+
50
+ ## Support
51
+
52
+ Paid licences include email support at [support@broadpaper.com](mailto:support@broadpaper.com).
53
+
54
+ ---
55
+
56
+ Copyright © 2026 Codingbadger Creative Limited. See [LICENSE](./LICENSE) in this package, and
57
+ [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md) for the components it ships with.
package/dist/index.d.ts CHANGED
@@ -20,9 +20,19 @@ declare class DomMeasurer implements Measurer {
20
20
  private readonly host;
21
21
  private readonly cache;
22
22
  private opts;
23
+ /** What the measurements since the last `settled()` found still on its way. */
24
+ private arriving;
23
25
  constructor(doc: Document, container?: HTMLElement);
24
26
  setOptions(opts: RenderTreeOptions): void;
25
27
  measure(sections: ResolvedNode[], width: number): GeometryMap;
28
+ /**
29
+ * Resolves once everything the measurements since the last call found still
30
+ * arriving has arrived — or failed to, which is final too — with whether there
31
+ * was anything. When it is true, a layout taken before it is short and should be
32
+ * taken again; the sections involved were not cached, so the next measurement
33
+ * reads them afresh.
34
+ */
35
+ settled(): Promise<boolean>;
26
36
  clearCache(): void;
27
37
  destroy(): void;
28
38
  }
package/dist/index.js CHANGED
@@ -63,6 +63,8 @@ var DomMeasurer = class {
63
63
  host;
64
64
  cache = /* @__PURE__ */ new Map();
65
65
  opts = null;
66
+ /** What the measurements since the last `settled()` found still on its way. */
67
+ arriving = [];
66
68
  setOptions(opts) {
67
69
  this.opts = opts;
68
70
  this.host.classList.toggle("bp-doc--design", opts.mode === "design");
@@ -88,9 +90,17 @@ var DomMeasurer = class {
88
90
  }
89
91
  if (pending.length === 0) return out;
90
92
  for (const p of pending) this.host.appendChild(p.el);
91
- for (const p of pending) {
92
- const geom = readSectionGeometry(p.el, p.section, this.opts);
93
- this.cache.set(p.key, geom);
93
+ const read = pending.map((p) => ({ p, geom: readSectionGeometry(p.el, p.section, this.opts) }));
94
+ const fonts = this.doc.fonts;
95
+ const faces = [];
96
+ if (fonts && typeof fonts[Symbol.iterator] === "function") {
97
+ for (const face of fonts) if (face.status === "loading") faces.push(face.loaded);
98
+ }
99
+ this.arriving.push(...faces);
100
+ for (const { p, geom } of read) {
101
+ const images = [...p.el.querySelectorAll("img")].filter((img) => !img.complete).map(decoded);
102
+ this.arriving.push(...images);
103
+ if (!faces.length && !images.length) this.cache.set(p.key, geom);
94
104
  out[p.section.key] = geom;
95
105
  }
96
106
  for (const p of pending) p.el.remove();
@@ -100,14 +110,36 @@ var DomMeasurer = class {
100
110
  }
101
111
  return out;
102
112
  }
113
+ /**
114
+ * Resolves once everything the measurements since the last call found still
115
+ * arriving has arrived — or failed to, which is final too — with whether there
116
+ * was anything. When it is true, a layout taken before it is short and should be
117
+ * taken again; the sections involved were not cached, so the next measurement
118
+ * reads them afresh.
119
+ */
120
+ async settled() {
121
+ const waiting = this.arriving;
122
+ this.arriving = [];
123
+ if (!waiting.length) return false;
124
+ await Promise.all(waiting.map((p) => p.catch(() => void 0)));
125
+ return true;
126
+ }
103
127
  clearCache() {
104
128
  this.cache.clear();
105
129
  }
106
130
  destroy() {
107
131
  this.host.remove();
108
132
  this.cache.clear();
133
+ this.arriving = [];
109
134
  }
110
135
  };
136
+ function decoded(img) {
137
+ if (typeof img.decode === "function") return img.decode().catch(() => void 0);
138
+ return new Promise((resolve) => {
139
+ img.addEventListener("load", resolve, { once: true });
140
+ img.addEventListener("error", resolve, { once: true });
141
+ });
142
+ }
111
143
  function readSectionGeometry(root, section, opts) {
112
144
  const base = root.getBoundingClientRect();
113
145
  const nodes = {};
@@ -523,7 +555,11 @@ function createReportPreview(container, options) {
523
555
  ensureStyle(doc, "bp-theme-fonts", theme ? fontFaceCss({ ...theme, id: theme.id ?? "t", name: theme.name ?? "", typography: theme.typography ?? { headingFont: "", bodyFont: "" }, colors: theme.colors ?? {} }) : "");
524
556
  await waitForFonts(doc, (theme?.fonts ?? []).map((f) => f.family));
525
557
  if (destroyed) return api.result;
526
- const result = layoutDocument(current, measurer);
558
+ let result = layoutDocument(current, measurer);
559
+ for (let pass = 0; pass < 3 && await measurer.settled(); pass++) {
560
+ if (destroyed) return api.result ?? result;
561
+ result = layoutDocument(current, measurer);
562
+ }
527
563
  api.result = result;
528
564
  const gap = current.pageGap ?? 24;
529
565
  const zoom = current.zoom ?? 1;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dom.ts","../src/measurer.ts","../src/layout.ts","../src/preview.ts","../src/styles.ts","../src/chart-interactions.ts","../src/client.ts","../src/viewer.ts"],"sourcesContent":["import { classNames, styleString, type VNode, type VProps } from \"@broadpaper/core\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/** Creates a DOM node from a VNode. Attribute values are set with setAttribute (never innerHTML). */\nexport function vnodeToDom(v: VNode | string, doc: Document = document): Node {\n if (typeof v === \"string\") return doc.createTextNode(v);\n const el = v.ns === \"svg\" ? doc.createElementNS(SVG_NS, v.tag) : doc.createElement(v.tag);\n applyProps(el, v.props);\n for (const c of v.children) el.appendChild(vnodeToDom(c, doc));\n return el;\n}\n\nfunction applyProps(el: Element, props: VProps) {\n for (const [k, v] of Object.entries(props)) {\n if (v === undefined || v === null || v === false) continue;\n if (k === \"class\") {\n const c = classNames(v as VProps[\"class\"]);\n if (c) el.setAttribute(\"class\", c);\n } else if (k === \"style\") {\n const s = styleString(v as VProps[\"style\"]);\n if (s) el.setAttribute(\"style\", s);\n } else if (v === true) el.setAttribute(k, \"\");\n else el.setAttribute(k, String(v));\n }\n}\n\n/** Replaces the children of `container` with the given VNodes. */\nexport function mountVNodes(container: HTMLElement, vnodes: Array<VNode | string>): void {\n const doc = container.ownerDocument;\n const frag = doc.createDocumentFragment();\n for (const v of vnodes) frag.appendChild(vnodeToDom(v, doc));\n container.replaceChildren(frag);\n}\n\n/** Injects (or updates) a style element with the given id. */\nexport function ensureStyle(doc: Document, id: string, css: string): HTMLStyleElement {\n let el = doc.getElementById(id) as HTMLStyleElement | null;\n if (!el) {\n el = doc.createElement(\"style\");\n el.id = id;\n doc.head.appendChild(el);\n }\n if (el.textContent !== css) el.textContent = css;\n return el;\n}\n","import { renderNode, type GeometryMap, type Measurer, type NodeGeometry, type RenderTreeOptions, type ResolvedNode, type SectionGeometry, type TableRowGeometry } from \"@broadpaper/core\";\nimport { vnodeToDom } from \"./dom.js\";\n\n/** FNV-1a 32-bit hash; fast and good enough for cache keys. */\nexport function hashString(s: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36) + s.length.toString(36);\n}\n\nexport function sectionHash(section: ResolvedNode, width: number, mode: string): string {\n return hashString(JSON.stringify(section)) + \":\" + Math.round(width * 100) + \":\" + mode;\n}\n\n/**\n * Measures resolved sections by laying them out as continuous galleys in a\n * hidden container and reading geometry from the DOM. Results are cached by\n * section content hash so unchanged sections are never re-measured.\n */\nexport class DomMeasurer implements Measurer {\n private readonly host: HTMLElement;\n private readonly cache = new Map<string, SectionGeometry>();\n private opts: RenderTreeOptions | null = null;\n\n constructor(private readonly doc: Document, container?: HTMLElement) {\n this.host = doc.createElement(\"div\");\n this.host.className = \"bp-doc bp-galley\";\n this.host.setAttribute(\"aria-hidden\", \"true\");\n (container ?? doc.body).appendChild(this.host);\n }\n\n setOptions(opts: RenderTreeOptions): void {\n this.opts = opts;\n this.host.classList.toggle(\"bp-doc--design\", opts.mode === \"design\");\n }\n\n measure(sections: ResolvedNode[], width: number): GeometryMap {\n if (!this.opts) throw new Error(\"DomMeasurer.setOptions() must be called before measure()\");\n const out: GeometryMap = {};\n const pending: Array<{ section: ResolvedNode; key: string; el: HTMLElement }> = [];\n for (const section of sections) {\n const key = sectionHash(section, width, this.opts.mode);\n const hit = this.cache.get(key);\n if (hit) {\n out[section.key] = hit;\n continue;\n }\n const el = this.doc.createElement(\"div\");\n el.className = \"bp-galley__section\";\n el.style.width = `${width}px`;\n el.style.position = \"relative\";\n const v = renderNode(section, { ...this.opts, width }, width);\n if (v) el.appendChild(vnodeToDom(v, this.doc));\n pending.push({ section, key, el });\n }\n if (pending.length === 0) return out;\n\n // Single layout pass: attach all, then read.\n for (const p of pending) this.host.appendChild(p.el);\n for (const p of pending) {\n const geom = readSectionGeometry(p.el, p.section, this.opts);\n this.cache.set(p.key, geom);\n out[p.section.key] = geom;\n }\n for (const p of pending) p.el.remove();\n if (this.cache.size > 400) {\n // Simple eviction: drop the oldest half.\n const keys = [...this.cache.keys()].slice(0, 200);\n for (const k of keys) this.cache.delete(k);\n }\n return out;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n destroy(): void {\n this.host.remove();\n this.cache.clear();\n }\n}\n\nfunction readSectionGeometry(root: HTMLElement, section: ResolvedNode, opts: RenderTreeOptions): SectionGeometry {\n const base = root.getBoundingClientRect();\n const nodes: Record<string, NodeGeometry> = {};\n const byKey = new Map<string, ResolvedNode>();\n const collect = (n: ResolvedNode) => {\n byKey.set(n.key, n);\n for (const c of n.children) collect(c);\n };\n collect(section);\n\n const elements = root.querySelectorAll<HTMLElement>(\"[data-key]\");\n for (const el of elements) {\n const key = el.getAttribute(\"data-key\")!;\n if (nodes[key]) continue; // first occurrence wins\n const node = byKey.get(key);\n const r = el.getBoundingClientRect();\n const g: NodeGeometry = { top: r.top - base.top, height: r.height, left: r.left - base.left, width: r.width };\n if (node) {\n const def = opts.registry.get(node.type);\n const measure = def?.measure ?? (node.flow.split === \"lines\" ? \"text\" : node.flow.split === \"rows\" ? \"table\" : \"none\");\n if (measure === \"text\") g.lines = readLineBoxes(el, base.top, g.top + g.height);\n else if (measure === \"table\") g.rows = readTableRows(el, base.top);\n }\n nodes[key] = g;\n }\n const sectionRect = root.firstElementChild?.getBoundingClientRect();\n const height = sectionRect ? sectionRect.height : base.height;\n return { height, width: base.width, nodes };\n}\n\n/** Returns the y coordinate (relative to `baseTop`) of the bottom of each line box. */\nexport function readLineBoxes(el: HTMLElement, baseTop: number, nodeBottom: number): number[] {\n const container = (el.querySelector(\"[data-part='text']\") as HTMLElement | null) ?? el;\n const doc = el.ownerDocument;\n const rects: Array<{ top: number; bottom: number }> = [];\n const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {\n acceptNode: (n) => {\n if (n.nodeType === Node.TEXT_NODE) return (n.textContent ?? \"\").trim().length ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n const tag = (n as Element).tagName;\n if (tag === \"BR\" || tag === \"IMG\" || tag === \"SVG\") return NodeFilter.FILTER_ACCEPT;\n const e = n as HTMLElement;\n if (e.classList?.contains(\"bp-p--empty\")) return NodeFilter.FILTER_ACCEPT;\n return NodeFilter.FILTER_SKIP;\n }\n });\n let cur: Node | null = walker.nextNode();\n while (cur) {\n if (cur.nodeType === Node.TEXT_NODE) {\n const range = doc.createRange();\n range.selectNodeContents(cur);\n for (const r of range.getClientRects()) if (r.height > 0 && r.width > 0) rects.push({ top: r.top - baseTop, bottom: r.bottom - baseTop });\n } else {\n const r = (cur as Element).getBoundingClientRect();\n if (r.height > 0) rects.push({ top: r.top - baseTop, bottom: r.bottom - baseTop });\n }\n cur = walker.nextNode();\n }\n if (rects.length === 0) return [];\n rects.sort((a, b) => a.top - b.top || a.bottom - b.bottom);\n const lines: Array<{ top: number; bottom: number }> = [];\n for (const r of rects) {\n const last = lines[lines.length - 1];\n if (last && r.top < last.bottom - 0.5) {\n last.top = Math.min(last.top, r.top);\n last.bottom = Math.max(last.bottom, r.bottom);\n } else lines.push({ ...r });\n }\n const out: number[] = [];\n for (let i = 0; i < lines.length - 1; i++) out.push(round2((lines[i]!.bottom + lines[i + 1]!.top) / 2));\n out.push(round2(nodeBottom));\n return out;\n}\n\nexport function readTableRows(el: HTMLElement, baseTop: number): TableRowGeometry[] {\n const table = el.querySelector(\"table.bp-table\");\n if (!table) return [];\n const rows: TableRowGeometry[] = [];\n const read = (selector: string, kind: TableRowGeometry[\"kind\"]) => {\n for (const tr of table.querySelectorAll<HTMLElement>(`:scope > ${selector} > tr`)) {\n const r = tr.getBoundingClientRect();\n rows.push({ top: round2(r.top - baseTop), height: round2(r.height), kind });\n }\n };\n read(\"thead\", \"header\");\n read(\"tbody\", \"body\");\n read(\"tfoot\", \"footer\");\n return rows;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import {\n computePageGeometry,\n mergeTheme,\n resolveValue,\n paginate,\n resolveTemplate,\n renderNode,\n h,\n walkVNodes,\n type BlockRegistry,\n type DataSource,\n type GeometryMap,\n type Measurer,\n type PagedDocument,\n type PageGeometry,\n type ReportData,\n type ReportTemplate,\n type RenderMode,\n type RenderTreeOptions,\n type ResolvedDocument,\n type ResolvedNode,\n type SectionGeometry,\n type TableRowGeometry,\n type Theme,\n type VNode,\n type VProps,\n type FormatContext,\n type LayoutWarning\n} from \"@broadpaper/core\";\n\nexport interface LayoutInput {\n template: ReportTemplate;\n registry: BlockRegistry;\n theme?: Partial<Theme>;\n dataSources?: DataSource[];\n data?: ReportData;\n sampleData?: ReportData;\n mode?: RenderMode;\n now?: Date;\n locale?: string;\n currency?: string;\n timeZone?: string;\n designIterations?: number;\n allowStyleOverrides?: boolean;\n /** Minimum pages to show (design canvas). */\n minPages?: number;\n /** Editor hook to decorate node wrappers. */\n decorate?: RenderTreeOptions[\"decorate\"];\n}\n\nexport interface LayoutResult {\n resolved: ResolvedDocument;\n paged: PagedDocument;\n geometry: GeometryMap;\n regionGeometry: GeometryMap;\n page: PageGeometry;\n theme: Theme;\n format: FormatContext;\n mode: RenderMode;\n warnings: Array<{ source: \"resolve\" | \"layout\"; message: string; nodeId?: string; page?: number; code?: LayoutWarning[\"code\"] }>;\n /** Map of resolved node key → node, across all regions. */\n nodesByKey: Map<string, ResolvedNode>;\n /** Renders one page to a VNode. */\n renderPage(index: number): VNode;\n /** Renders every page. */\n renderPages(): VNode[];\n}\n\n/**\n * A measurer whose results arrive asynchronously.\n *\n * The DOM measurer answers immediately because the browser has already done\n * the layout; an engine-backed measurer has to run the engine first. Both\n * drive the identical pipeline — only the awaiting differs.\n */\nexport interface AsyncMeasurer {\n measure(sections: ResolvedNode[], width: number): Promise<GeometryMap>;\n setOptions?(opts: RenderTreeOptions): void;\n}\n\ninterface Prepared {\n input: LayoutInput;\n mode: RenderMode;\n theme: Theme;\n template: ReportTemplate;\n page: PageGeometry;\n format: FormatContext;\n resolved: ResolvedDocument;\n baseOpts: RenderTreeOptions;\n regionSections: ResolvedNode[];\n}\n\nfunction indexNodes(doc: ResolvedDocument): Map<string, ResolvedNode> {\n const map = new Map<string, ResolvedNode>();\n const visit = (n: ResolvedNode) => {\n map.set(n.key, n);\n n.children.forEach(visit);\n };\n for (const s of doc.body) visit(s);\n for (const s of doc.header.default) visit(s);\n for (const s of doc.header.firstPage ?? []) visit(s);\n for (const s of doc.footer.default) visit(s);\n for (const s of doc.footer.firstPage ?? []) visit(s);\n return map;\n}\n\n/**\n * The full pipeline: resolve → measure → paginate. Returns a result that can\n * render individual pages on demand (the editor virtualises them).\n */\n/**\n * Everything up to measurement: resolving the template, page geometry and the\n * render options. Split out so the synchronous and asynchronous entry points\n * share one pipeline and can never drift apart.\n */\nfunction prepareLayout(input: LayoutInput): Prepared {\n const mode: RenderMode = input.mode ?? \"preview\";\n const theme = mergeTheme(input.theme);\n const template = input.template;\n const page = computePageGeometry(template.page);\n const now = input.now ?? new Date();\n const format: FormatContext = {\n locale: input.locale ?? template.settings?.locale ?? \"en-GB\",\n currency: input.currency ?? template.settings?.currency ?? \"GBP\",\n timeZone: input.timeZone ?? template.settings?.timeZone,\n now\n };\n\n const resolved = resolveTemplate({\n template,\n registry: input.registry,\n theme,\n dataSources: input.dataSources,\n data: input.data,\n sampleData: input.sampleData,\n mode,\n now,\n locale: format.locale,\n currency: format.currency,\n timeZone: format.timeZone,\n designIterations: input.designIterations,\n allowStyleOverrides: input.allowStyleOverrides\n });\n\n const baseOpts: RenderTreeOptions = {\n registry: input.registry,\n theme,\n styles: template.styles,\n mode,\n page: { number: 1, total: 1 },\n format,\n width: page.contentWidth,\n decorate: input.decorate\n };\n\n const regionSections = [...resolved.header.default, ...(resolved.header.firstPage ?? []), ...resolved.footer.default, ...(resolved.footer.firstPage ?? [])];\n return { input, mode, theme, template, page, format, resolved, baseOpts, regionSections };\n}\n\n/** Completes a layout once its geometry has been measured, however that happened. */\nfunction finishLayout(p: Prepared, geometry: GeometryMap, regionGeometry: GeometryMap): LayoutResult {\n const { input, mode, theme, template, page, format, resolved, baseOpts } = p;\n\n const paged = paginate({ document: resolved, geometry, page, minPages: input.minPages ?? template.page.minPages ?? 1, regionGeometry });\n const nodesByKey = indexNodes(resolved);\n\n const warnings: LayoutResult[\"warnings\"] = [\n ...resolved.warnings.map((w) => ({ source: \"resolve\" as const, message: w.message, nodeId: w.nodeId })),\n ...paged.warnings.map((w) => ({ source: \"layout\" as const, message: w.message, nodeId: w.nodeId, page: w.page, code: w.code }))\n ];\n\n const renderPage = (index: number): VNode => {\n const p = paged.pages[index];\n if (!p) throw new Error(`Page ${index} does not exist`);\n const opts: RenderTreeOptions = { ...baseOpts, page: { number: p.number, total: paged.totalPages } };\n return composePage(p.index, p.number, paged.totalPages, p.first, resolved, paged, page, opts, nodesByKey, regionGeometry, theme, template.page.background, geometry);\n };\n\n return {\n resolved,\n paged,\n geometry,\n regionGeometry,\n page,\n theme,\n format,\n mode,\n warnings,\n nodesByKey,\n renderPage,\n renderPages: () => paged.pages.map((_, i) => renderPage(i))\n };\n}\n\n/** Lays out a document with a measurer that answers immediately. */\nexport function layoutDocument(input: LayoutInput, measurer: Measurer & { setOptions?(opts: RenderTreeOptions): void }): LayoutResult {\n const p = prepareLayout(input);\n measurer.setOptions?.(p.baseOpts);\n const geometry = measurer.measure(p.resolved.body, p.page.contentWidth);\n const regionGeometry = p.regionSections.length ? measurer.measure(p.regionSections, p.page.contentWidth) : {};\n return finishLayout(p, geometry, regionGeometry);\n}\n\n/**\n * Lays out a document with a measurer that answers asynchronously.\n *\n * This is what lets the editor page a document exactly as the PDF backend\n * will: the same engine measures both, so a break in the canvas is a break in\n * the file. Everything after measurement — pagination, composition, warnings —\n * is the code the synchronous path runs.\n */\nexport async function layoutDocumentAsync(input: LayoutInput, measurer: AsyncMeasurer): Promise<LayoutResult> {\n const p = prepareLayout(input);\n measurer.setOptions?.(p.baseOpts);\n const geometry = await measurer.measure(p.resolved.body, p.page.contentWidth);\n const regionGeometry = p.regionSections.length ? await measurer.measure(p.regionSections, p.page.contentWidth) : {};\n return finishLayout(p, geometry, regionGeometry);\n}\n\n/**\n * Windowed rendering: nodes entirely outside the fragment's clip window are\n * replaced by empty boxes of identical size, and table bodies keep only the rows\n * that intersect the window (leading rows become one spacer row). Layout is\n * unchanged; per-page DOM cost becomes proportional to what is visible.\n */\nexport function windowSection(root: VNode, geom: SectionGeometry | undefined, from: number, to: number): VNode {\n if (!geom) return root;\n const visit = (v: VNode): VNode => {\n const key = v.props[\"data-key\"];\n if (typeof key === \"string\") {\n const g = geom.nodes[key];\n if (g) {\n const bottom = g.top + g.height;\n if (bottom <= from - 0.01 || g.top >= to + 0.01) {\n const existing = v.props.style;\n const style = typeof existing === \"string\" ? `${existing};height:${g.height}px;overflow:hidden` : { ...((existing as Record<string, string | number | undefined>) ?? {}), height: `${g.height}px`, overflow: \"hidden\" };\n return { ...v, props: { ...v.props, style, \"data-culled\": \"\" }, children: [] };\n }\n if (g.rows && g.rows.length) return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : windowTable(c, g.rows!, from, to))) };\n }\n }\n return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : visit(c))) };\n };\n return visit(root);\n}\n\nfunction windowTable(v: VNode, rows: TableRowGeometry[], from: number, to: number): VNode {\n if (v.tag === \"table\") {\n const body = rows.filter((r) => r.kind === \"body\");\n return {\n ...v,\n children: v.children.map((c) => {\n if (typeof c === \"string\" || c.tag !== \"tbody\") return c;\n const trs = c.children.filter((x): x is VNode => typeof x !== \"string\" && x.tag === \"tr\");\n if (trs.length !== body.length) return c;\n let leading = 0;\n let trailing = 0;\n const kept: VNode[] = [];\n for (let i = 0; i < trs.length; i++) {\n const r = body[i]!;\n const bottom = r.top + r.height;\n if (bottom <= from + 0.01) leading += r.height;\n else if (r.top >= to - 0.01) trailing += r.height;\n else kept.push(trs[i]!);\n }\n const spacer = (height: number) => h(\"tr\", { class: \"bp-table__spacer\" }, h(\"td\", { colspan: \"100\", style: { padding: \"0\", border: \"0\", height: `${height}px` } }));\n return { ...c, children: [...(leading > 0 ? [spacer(leading)] : []), ...kept, ...(trailing > 0 ? [spacer(trailing)] : [])] };\n })\n };\n }\n return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : windowTable(c, rows, from, to))) };\n}\n\n/** Renders only the header part of a table node (for repeated headers). */\nfunction renderTableHeader(node: ResolvedNode, opts: RenderTreeOptions, width: number): VNode | null {\n const full = renderNode(node, opts, width);\n if (!full) return null;\n walkVNodes(full, (v) => {\n if (v.tag === \"table\") {\n v.children = v.children.filter((c) => typeof c === \"string\" || c.tag === \"colgroup\" || c.tag === \"thead\");\n return false;\n }\n return true;\n });\n // Strip wrapper margins so the header sits flush at the fragment top.\n const props = { ...full.props } as VProps;\n delete props.style;\n full.props = { ...props, class: [String(props.class ?? \"\"), \"bp-repeat-header__node\"] };\n return full;\n}\n\nexport function composePage(\n index: number,\n number: number,\n total: number,\n first: boolean,\n resolved: ResolvedDocument,\n paged: PagedDocument,\n page: PageGeometry,\n opts: RenderTreeOptions,\n nodesByKey: Map<string, ResolvedNode>,\n regionGeometry: GeometryMap,\n theme: Theme,\n pageBackground?: string,\n geometry?: GeometryMap\n): VNode {\n const p = paged.pages[index]!;\n const pageStyle: Record<string, string> = {\n width: `${page.width}px`,\n height: `${page.height}px`,\n background: resolveValue(pageBackground, theme, \"#ffffff\")\n };\n\n const region = (sections: ResolvedNode[] | undefined, top: number, height: number, name: \"header\" | \"footer\", align: \"top\" | \"bottom\") => {\n if (!sections || sections.length === 0 || height <= 0) return null;\n const total = sections.reduce((a, s) => a + (regionGeometry[s.key]?.height ?? 0), 0);\n const offset = align === \"bottom\" ? Math.max(0, height - total) : 0;\n return h(\n \"div\",\n { class: [\"bp-page__region\", `bp-page__${name}`], \"data-region\": name, style: { top: `${top}px`, left: `${page.margins.left}px`, width: `${page.contentWidth}px`, height: `${height}px` } },\n h(\"div\", { class: \"bp-page__region-inner\", style: { position: \"absolute\", top: `${offset}px`, left: \"0\", width: \"100%\" } }, sections.map((s) => renderNode(s, opts, page.contentWidth)))\n );\n };\n\n const headerSections = first && resolved.header.firstPage ? resolved.header.firstPage : resolved.header.default;\n const footerSections = first && resolved.footer.firstPage ? resolved.footer.firstPage : resolved.footer.default;\n\n const windowed = (section: ResolvedNode, from: number, to: number): VNode | null => {\n const v = renderNode(section, opts, page.contentWidth);\n if (!v) return null;\n return windowSection(v, geometry?.[section.key], from, to);\n };\n\n const fragments = p.fragments.map((f) => {\n const section = nodesByKey.get(f.sectionKey);\n if (!section) return null;\n const extra = f.repeatHeaders.reduce((a, r) => a + r.height, 0);\n const headers = f.repeatHeaders.map((r) => {\n const t = nodesByKey.get(r.tableKey);\n const v = t ? renderTableHeader(t, opts, r.width) : null;\n return h(\"div\", { class: \"bp-repeat-header\", \"data-table\": r.tableKey, style: { left: `${r.left}px`, width: `${r.width}px`, height: `${r.height}px` } }, v);\n });\n return h(\n \"div\",\n { class: [\"bp-fragment\", f.continued && \"bp-fragment--continued\", f.continues && \"bp-fragment--continues\"], \"data-section\": f.sectionKey, \"data-from\": String(f.from), \"data-to\": String(f.to), style: { top: `${f.y}px`, height: `${f.height}px` } },\n headers,\n h(\"div\", { class: \"bp-fragment__clip\", style: { top: `${extra}px`, height: `${f.to - f.from}px` } }, h(\"div\", { class: \"bp-fragment__content\", style: { \"margin-top\": `${-f.from}px`, width: `${page.contentWidth}px` } }, windowed(section, f.from, f.to)))\n );\n });\n\n return h(\n \"div\",\n { class: [\"bp-page\", first && \"bp-page--first\"], \"data-page\": String(number), \"data-page-index\": String(index), style: pageStyle, \"aria-label\": `Page ${number} of ${total}` },\n region(headerSections, page.headerTop, page.headerHeight, \"header\", \"top\"),\n h(\"div\", { class: \"bp-page__body\", \"data-region\": \"body\", style: { top: `${page.contentTop}px`, left: `${page.margins.left}px`, width: `${page.contentWidth}px`, height: `${page.contentHeight}px` } }, fragments),\n region(footerSections, page.footerTop, page.footerHeight, \"footer\", \"bottom\")\n );\n}\n","import { h, toHtml, escapeHtml, type VNode } from \"@broadpaper/core\";\nimport { layoutDocument, type LayoutInput, type LayoutResult } from \"./layout.js\";\nimport { DomMeasurer } from \"./measurer.js\";\nimport { mountVNodes, ensureStyle } from \"./dom.js\";\nimport { DOCUMENT_CSS, fontFaceCss } from \"./styles.js\";\n\nexport interface PreviewOptions extends LayoutInput {\n /** Gap between pages in px. Default 24. */\n pageGap?: number;\n /** Scale factor. Default 1. */\n zoom?: number;\n /** Additional CSS class on the root. */\n className?: string;\n}\n\nexport interface ReportPreview {\n /** Re-renders with new input (template, data, theme, ...). */\n update(input: Partial<PreviewOptions>): Promise<LayoutResult>;\n /** The last layout result. */\n result: LayoutResult | null;\n /** Scroll to a page (1-based). */\n goToPage(number: number): void;\n destroy(): void;\n}\n\n/** Waits for web fonts so measurement is stable. Resolves immediately when unsupported. */\nexport async function waitForFonts(doc: Document, families: string[] = []): Promise<void> {\n const fonts = (doc as Document & { fonts?: FontFaceSet }).fonts;\n if (!fonts) return;\n try {\n await Promise.all(families.map((f) => fonts.load(`12px \"${f}\"`).catch(() => undefined)));\n await fonts.ready;\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Mounts a live, paginated preview into a host element. This is the same\n * pipeline the editor canvas and the PDF service use.\n */\nexport function createReportPreview(container: HTMLElement, options: PreviewOptions): ReportPreview {\n const doc = container.ownerDocument;\n ensureStyle(doc, \"bp-document-css\", DOCUMENT_CSS);\n const measurer = new DomMeasurer(doc);\n let current: PreviewOptions = options;\n let destroyed = false;\n\n const root = doc.createElement(\"div\");\n root.className = `bp-doc bp-preview ${options.className ?? \"\"}`.trim();\n container.appendChild(root);\n\n const api: ReportPreview = {\n result: null,\n async update(input) {\n current = { ...current, ...input };\n const theme = current.theme;\n ensureStyle(doc, \"bp-theme-fonts\", theme ? fontFaceCss({ ...theme, id: theme.id ?? \"t\", name: theme.name ?? \"\", typography: theme.typography ?? { headingFont: \"\", bodyFont: \"\" }, colors: theme.colors ?? ({} as never) }) : \"\");\n await waitForFonts(doc, (theme?.fonts ?? []).map((f) => f.family));\n if (destroyed) return api.result!;\n const result = layoutDocument(current, measurer);\n api.result = result;\n const gap = current.pageGap ?? 24;\n const zoom = current.zoom ?? 1;\n root.classList.toggle(\"bp-doc--design\", result.mode === \"design\");\n const pages = result.renderPages().map((p, i) => h(\"div\", { class: \"bp-preview__page\", style: { \"margin-bottom\": i < result.paged.totalPages - 1 ? `${gap}px` : \"0\", \"box-shadow\": \"0 1px 3px rgba(16,24,40,0.12), 0 8px 24px rgba(16,24,40,0.08)\" } }, p));\n const inner: VNode = h(\"div\", { class: \"bp-preview__pages\", style: { transform: zoom !== 1 ? `scale(${zoom})` : undefined, \"transform-origin\": \"top center\", width: `${result.page.width}px`, margin: \"0 auto\" } }, pages);\n mountVNodes(root, [inner]);\n return result;\n },\n goToPage(number) {\n const el = root.querySelector<HTMLElement>(`[data-page=\"${number}\"]`);\n el?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n },\n destroy() {\n destroyed = true;\n measurer.destroy();\n root.remove();\n }\n };\n void api.update({});\n return api;\n}\n\nexport interface HtmlDocumentOptions {\n title?: string;\n /** Extra CSS appended after the document stylesheet. */\n extraCss?: string;\n /** Include the design-mode class (rarely wanted for PDFs). */\n design?: boolean;\n /** Document language for the html element. */\n lang?: string;\n /** PDF metadata. */\n author?: string;\n subject?: string;\n keywords?: string[];\n}\n\n/**\n * Serialises a laid-out document to a standalone HTML page: one fixed-size box\n * per page with `@page` sized to match. This is what the PDF service prints.\n */\nexport function documentToHtml(result: LayoutResult, opts: HtmlDocumentOptions = {}): string {\n const pages = result.renderPages();\n const { width, height } = result.page;\n const fonts = fontFaceCss(result.theme);\n const css = `${fonts}\\n${DOCUMENT_CSS}\\n@page{size:${width}px ${height}px;margin:0;}\\nhtml,body{margin:0;padding:0;background:#fff;}\\n.bp-page{break-after:page;page-break-after:always;}\\n.bp-page:last-child{break-after:auto;page-break-after:auto;}\\n${opts.extraCss ?? \"\"}`;\n const meta = [\n `<meta charset=\"utf-8\">`,\n `<meta name=\"viewport\" content=\"width=${width}\">`,\n `<meta name=\"generator\" content=\"BroadPaper\">`,\n opts.author ? `<meta name=\"author\" content=\"${escapeHtml(opts.author)}\">` : \"\",\n opts.subject ? `<meta name=\"description\" content=\"${escapeHtml(opts.subject)}\">` : \"\",\n opts.keywords?.length ? `<meta name=\"keywords\" content=\"${escapeHtml(opts.keywords.join(\", \"))}\">` : \"\"\n ].join(\"\\n\");\n const body = pages.map((p) => toHtml(p)).join(\"\\n\");\n return `<!doctype html>\\n<html lang=\"${escapeHtml(opts.lang ?? \"en\")}\">\\n<head>\\n${meta}\\n<title>${escapeHtml(opts.title ?? \"Report\")}</title>\\n<style>${css}</style>\\n</head>\\n<body class=\"bp-doc${opts.design ? \" bp-doc--design\" : \"\"}\" data-pages=\"${result.paged.totalPages}\">\\n${body}\\n</body>\\n</html>`;\n}\n","import type { Theme } from \"@broadpaper/core\";\n\n/**\n * Document stylesheet shared by the editor canvas, the preview and the PDF\n * service. Everything is scoped under `.bp-doc` so it never leaks into a host\n * application. Block-specific visual styling is inline (resolved from the theme);\n * this sheet provides structure, print rules and design-mode affordances.\n */\nexport const DOCUMENT_CSS = `\n.bp-doc, .bp-doc * { box-sizing: border-box; }\n.bp-doc { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; font-variant-numeric: tabular-nums; }\n.bp-doc p { margin: 0; }\n.bp-doc h1, .bp-doc h2, .bp-doc h3, .bp-doc h4 { margin: 0; font-weight: inherit; }\n.bp-doc img { max-width: 100%; }\n.bp-doc a { color: inherit; }\n.bp-doc table { border-spacing: 0; }\n.bp-doc ul.bp-list, .bp-doc ol.bp-list { margin: 0; padding-left: 1.4em; }\n.bp-doc .bp-list li { margin: 0.15em 0; }\n.bp-doc .bp-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; background: rgba(0,0,0,0.05); padding: 0 3px; border-radius: 3px; }\n.bp-doc .bp-p--empty { min-height: 1em; }\n\n.bp-page { position: relative; overflow: hidden; background: #fff; color: #1a1d21; }\n.bp-page__region { position: absolute; overflow: hidden; }\n.bp-page__body { position: absolute; overflow: hidden; }\n.bp-fragment { position: absolute; left: 0; width: 100%; overflow: hidden; }\n.bp-fragment__clip { position: absolute; left: 0; right: 0; overflow: hidden; }\n.bp-repeat-header { position: absolute; top: 0; overflow: hidden; }\n.bp-repeat-header .bp-table { table-layout: fixed; }\n\n.bp-stack { display: flex; flex-direction: column; }\n.bp-stack > .bp-node { min-width: 0; }\n.bp-node--hidden > * { opacity: 0.4; }\n.bp-node--placeholder { opacity: 0.85; }\n.bp-empty-section, .bp-empty-column { display: none; }\n.bp-galley { position: absolute; left: -100000px; top: 0; visibility: hidden; pointer-events: none; }\n\n.bp-table { width: 100%; border-collapse: collapse; }\n.bp-table td, .bp-table th { word-wrap: break-word; overflow-wrap: anywhere; }\n.bp-chart__svg text { user-select: none; }\n.bp-page-break--print { height: 0; }\n\n/* --- design mode affordances (only when .bp-doc--design) --- */\n.bp-doc--design .bp-token { display: inline; padding: 0 5px; margin: 0 1px; border-radius: 4px; background: rgba(79, 111, 165, 0.14); color: #2f4f7f; font-size: 0.92em; font-weight: 500; white-space: nowrap; box-decoration-break: clone; -webkit-box-decoration-break: clone; }\n.bp-doc--design .bp-token--lg { font-size: 0.65em; vertical-align: middle; }\n.bp-doc--design .bp-token__value { opacity: 0.65; margin-left: 4px; font-weight: 400; }\n.bp-doc--design .bp-cell--bound { text-decoration: underline dotted rgba(79,111,165,0.45); text-underline-offset: 3px; text-decoration-thickness: 1px; }\n.bp-doc--design .bp-placeholder-text { color: #9aa1ab; font-style: italic; }\n.bp-doc--design .bp-empty-section { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 96px; border: 1.5px dashed #cfd5dd; border-radius: 8px; color: #6b7280; font-size: 12px; font-family: Inter, system-ui, sans-serif; background: #fafbfc; }\n.bp-doc--design .bp-empty-section__title { font-weight: 600; color: #4b5563; }\n.bp-doc--design .bp-empty-column { display: flex; align-items: center; justify-content: center; min-height: 56px; border: 1.5px dashed #d4d9e0; border-radius: 6px; color: #9aa1ab; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-spacer--design { background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(0,0,0,0.04) 6px 8px); border-radius: 3px; }\n.bp-doc--design .bp-page-break { display: flex; align-items: center; gap: 8px; margin: 2px 0; color: #b4541f; font-size: 9.5px; font-family: Inter, system-ui, sans-serif; text-transform: uppercase; letter-spacing: 0.06em; }\n.bp-doc--design .bp-page-break::before, .bp-doc--design .bp-page-break::after { content: \"\"; flex: 1; border-top: 2px dashed rgba(180, 84, 31, 0.5); }\n.bp-doc--design .bp-page-break__label { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: 999px; font-weight: 600; background: rgba(180, 84, 31, 0.12); white-space: nowrap; }\n\n/* Structural blocks draw nothing of their own, so design mode outlines them.\n The label is absolutely positioned: naming a box must not resize it. */\n.bp-doc--design .bp-container,\n.bp-doc--design .bp-keep-together { position: relative; outline: 1px dashed rgba(79, 111, 165, 0.32); outline-offset: 1px; border-radius: 3px; }\n.bp-doc--design .bp-container::before,\n.bp-doc--design .bp-keep-together::before {\n content: \"Container\"; position: absolute; top: -6px; left: 8px; z-index: 1; padding: 0 4px;\n background: #fff; color: #7c8697; font-family: Inter, system-ui, sans-serif; font-size: 8.5px;\n font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; pointer-events: none;\n}\n.bp-doc--design .bp-keep-together::before { content: \"Keep together\"; }\n.bp-doc--design .bp-repeater__badge, .bp-doc--design .bp-conditional__badge, .bp-doc--design .bp-table__badge, .bp-doc--design .bp-chart__badge { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 4px; padding: 1px 7px; border-radius: 999px; font-size: 9.5px; font-weight: 600; letter-spacing: 0.02em; font-family: Inter, system-ui, sans-serif; background: rgba(79,111,165,0.12); color: #2f4f7f; user-select: none; }\n.bp-doc--design .bp-conditional--false .bp-conditional__badge { background: rgba(199,125,10,0.14); color: #8a5a06; }\n.bp-doc--design .bp-conditional--false > .bp-stack { opacity: 0.45; }\n.bp-doc--design .bp-repeater { border-left: 2px dashed rgba(79,111,165,0.35); padding-left: 8px; }\n.bp-doc--design .bp-conditional { border-left: 2px dashed rgba(199,125,10,0.45); padding-left: 8px; }\n.bp-doc--design .bp-image-placeholder { display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #f1f3f6, #e6e9ee); color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; border: 1px dashed #cfd5dd; }\n.bp-doc--design .bp-logo-placeholder { display: inline-flex; align-items: center; padding: 0 12px; border: 1px dashed #cfd5dd; border-radius: 4px; color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-node--locked { outline: 1px dashed rgba(0,0,0,0.15); }\n\n@media print {\n .bp-page { break-after: page; page-break-after: always; box-shadow: none !important; margin: 0 !important; }\n .bp-page:last-child { break-after: auto; page-break-after: auto; }\n}\n`;\n\n/** Builds @font-face rules for a theme's fonts. */\nexport function fontFaceCss(theme: Theme): string {\n return (theme.fonts ?? [])\n .map((f) => {\n const src = (Array.isArray(f.src) ? f.src : [f.src]).map((u) => `url(\"${u.replace(/\"/g, \"%22\")}\")`).join(\", \");\n return `@font-face{font-family:\"${f.family.replace(/\"/g, \"\")}\";src:${src};font-weight:${f.weight ?? 400};font-style:${f.style ?? \"normal\"};font-display:${f.display ?? \"block\"};}`;\n })\n .join(\"\\n\");\n}\n","/**\n * Charts you can point at, without changing what gets printed.\n *\n * The obvious way to get interactive charts is to draw them a second time with\n * a charting library on screen. That gives you two chart implementations that\n * drift, and a screen that no longer matches the file — which is the one thing\n * this product is for. So the drawing does not change at all: the same SVG the\n * PDF contains is the SVG the browser shows, and this adds a hover, a focus\n * ring and a tooltip on top of the marks that are already there.\n *\n * The chart block records what each mark stands for as it draws\n * (`data-bp-chart` on the wrapper, `data-bp-hit=\"<index>\"` on each mark), so\n * nothing here re-derives any geometry: it reads the real element's own box.\n * The PDF translator strips those attributes, so print output is unchanged.\n *\n * Everything it adds is inert: no layout is read or written that could affect\n * pagination, and `destroy()` puts the DOM back exactly as it found it.\n */\n\n/** One mark a reader can point at. Mirrors `ChartHit` in the blocks package. */\ninterface Hit {\n categoryIndex: number;\n category: string;\n seriesIndex: number;\n series: string;\n value: number | null;\n formatted: string;\n color: string;\n share?: number;\n}\n\ninterface ChartPayload {\n type: string;\n title: string;\n hits: Hit[];\n}\n\nexport interface ChartInteractionOptions {\n /**\n * Builds the tooltip's content. Return a string (shown as text) or an\n * element. The default names the category, the series and the value.\n */\n tooltip?(hit: Hit, context: { type: string; title: string; siblings: Hit[] }): string | HTMLElement;\n /** Called when a mark is clicked or activated from the keyboard. */\n onSelect?(hit: Hit, event: Event): void;\n /**\n * Replaces the whole thing for one chart. Return `false` to leave that chart\n * alone — the escape hatch for a host that would rather mount its own\n * charting library over the box. The SVG underneath is still what prints.\n */\n onChart?(element: HTMLElement, payload: ChartPayload): boolean | void;\n /** Where the tooltip is appended. Defaults to the document body. */\n portal?: HTMLElement;\n}\n\nconst STYLE_ID = \"bp-chart-interactions\";\n\nconst CSS = `\n.bp-chart [data-bp-hit] { cursor: default; transition: opacity 120ms ease, filter 120ms ease; }\n.bp-chart--active [data-bp-hit] { opacity: 0.45; }\n.bp-chart--active [data-bp-hit][data-bp-on] { opacity: 1; filter: brightness(1.06); }\n.bp-chart [data-bp-hit]:focus-visible { outline: none; }\n.bp-chart [data-bp-hit][data-bp-focus] { stroke: #111827; stroke-width: 2; paint-order: stroke; }\n.bp-chart-tip {\n position: fixed; z-index: 2147483000; pointer-events: none;\n max-width: 260px; padding: 6px 9px; border-radius: 6px;\n background: #111827; color: #fff;\n font: 12px/1.45 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);\n opacity: 0; transform: translateY(2px); transition: opacity 90ms ease, transform 90ms ease;\n}\n.bp-chart-tip[data-bp-shown] { opacity: 1; transform: none; }\n.bp-chart-tip__row { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }\n.bp-chart-tip__row + .bp-chart-tip__row { margin-top: 2px; }\n.bp-chart-tip__swatch { width: 8px; height: 8px; border-radius: 2px; flex: none; align-self: center; }\n.bp-chart-tip__label { color: #cbd5e1; }\n.bp-chart-tip__value { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }\n.bp-chart-tip__head { font-weight: 600; margin-bottom: 3px; }\n@media (prefers-reduced-motion: reduce) {\n .bp-chart-tip, .bp-chart [data-bp-hit] { transition: none; }\n}\n@media print { .bp-chart-tip { display: none !important; } }\n`;\n\nfunction ensureStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID)) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\n/** The default tooltip: every series at the category the reader is pointing at. */\nfunction defaultTooltip(hit: Hit, ctx: { type: string; title: string; siblings: Hit[] }, doc: Document): HTMLElement {\n const box = doc.createElement(\"div\");\n const head = doc.createElement(\"div\");\n head.className = \"bp-chart-tip__head\";\n head.textContent = hit.category || ctx.title;\n box.appendChild(head);\n\n // A pie has one series, so naming it twice would just repeat the category.\n const rows = ctx.type === \"pie\" || ctx.type === \"donut\" ? [hit] : ctx.siblings;\n for (const r of rows) {\n const row = doc.createElement(\"div\");\n row.className = \"bp-chart-tip__row\";\n\n const swatch = doc.createElement(\"span\");\n swatch.className = \"bp-chart-tip__swatch\";\n swatch.style.background = r.color;\n row.appendChild(swatch);\n\n const label = doc.createElement(\"span\");\n label.className = \"bp-chart-tip__label\";\n label.textContent = ctx.type === \"pie\" || ctx.type === \"donut\" ? (r.share !== undefined ? `${Math.round(r.share * 1000) / 10}%` : \"\") : r.series;\n row.appendChild(label);\n\n const value = doc.createElement(\"span\");\n value.className = \"bp-chart-tip__value\";\n // A null is a gap in the data, not a zero, and saying \"0\" would be a lie.\n value.textContent = r.value === null ? \"—\" : r.formatted;\n if (r === hit && rows.length > 1) value.style.textDecoration = \"underline\";\n row.appendChild(value);\n\n box.appendChild(row);\n }\n return box;\n}\n\n/**\n * Makes every chart under `root` respond to a pointer and to the keyboard.\n *\n * Safe to call again after the DOM is replaced — each call returns its own\n * teardown, and calling it twice on the same nodes does not double up.\n */\nexport function enableChartInteractions(root: HTMLElement, options: ChartInteractionOptions = {}): () => void {\n const doc = root.ownerDocument;\n if (!doc?.defaultView) return () => undefined;\n ensureStyle(doc);\n\n const portal = options.portal ?? doc.body;\n const tip = doc.createElement(\"div\");\n tip.className = \"bp-chart-tip\";\n tip.setAttribute(\"role\", \"tooltip\");\n tip.setAttribute(\"aria-hidden\", \"true\");\n\n let tipMounted = false;\n const cleanups: Array<() => void> = [];\n\n const hideTip = () => {\n tip.removeAttribute(\"data-bp-shown\");\n tip.setAttribute(\"aria-hidden\", \"true\");\n };\n\n const showTip = (content: string | HTMLElement, anchor: DOMRect) => {\n if (!tipMounted) {\n portal.appendChild(tip);\n tipMounted = true;\n }\n tip.replaceChildren(typeof content === \"string\" ? doc.createTextNode(content) : content);\n tip.setAttribute(\"aria-hidden\", \"false\");\n tip.setAttribute(\"data-bp-shown\", \"\");\n // Measured after the content is in, and clamped to the viewport so a mark\n // near an edge does not push the tooltip off the screen.\n const box = tip.getBoundingClientRect();\n const view = doc.defaultView!;\n const gap = 10;\n let left = anchor.left + anchor.width / 2 - box.width / 2;\n let top = anchor.top - box.height - gap;\n if (top < 4) top = anchor.bottom + gap;\n left = Math.max(4, Math.min(left, view.innerWidth - box.width - 4));\n top = Math.max(4, Math.min(top, view.innerHeight - box.height - 4));\n tip.style.left = `${Math.round(left)}px`;\n tip.style.top = `${Math.round(top)}px`;\n };\n\n for (const chart of Array.from(root.querySelectorAll<HTMLElement>(\".bp-chart[data-bp-chart]\"))) {\n if (chart.dataset.bpInteractive === \"1\") continue;\n\n let payload: ChartPayload;\n try {\n payload = JSON.parse(chart.dataset.bpChart ?? \"\") as ChartPayload;\n } catch {\n continue;\n }\n if (!Array.isArray(payload.hits) || payload.hits.length === 0) continue;\n if (options.onChart?.(chart, payload) === false) continue;\n\n chart.dataset.bpInteractive = \"1\";\n const marks = Array.from(chart.querySelectorAll<SVGElement>(\"[data-bp-hit]\"));\n if (marks.length === 0) continue;\n\n // Everything at the same category, which is what a reader comparing two\n // lines actually wants to see.\n const byCategory = new Map<number, Hit[]>();\n for (const hit of payload.hits) {\n const list = byCategory.get(hit.categoryIndex) ?? [];\n list.push(hit);\n byCategory.set(hit.categoryIndex, list);\n }\n\n const activate = (mark: SVGElement, hit: Hit, focus: boolean) => {\n chart.classList.add(\"bp-chart--active\");\n const siblings = byCategory.get(hit.categoryIndex) ?? [hit];\n for (const m of marks) {\n const h = payload.hits[Number(m.getAttribute(\"data-bp-hit\"))];\n if (h && siblings.includes(h)) m.setAttribute(\"data-bp-on\", \"\");\n else m.removeAttribute(\"data-bp-on\");\n m.removeAttribute(\"data-bp-focus\");\n }\n if (focus) mark.setAttribute(\"data-bp-focus\", \"\");\n const content = options.tooltip?.(hit, { type: payload.type, title: payload.title, siblings }) ?? defaultTooltip(hit, { type: payload.type, title: payload.title, siblings }, doc);\n showTip(content, mark.getBoundingClientRect());\n };\n\n const deactivate = () => {\n chart.classList.remove(\"bp-chart--active\");\n for (const m of marks) {\n m.removeAttribute(\"data-bp-on\");\n m.removeAttribute(\"data-bp-focus\");\n }\n hideTip();\n };\n\n for (const mark of marks) {\n const hit = payload.hits[Number(mark.getAttribute(\"data-bp-hit\"))];\n if (!hit) continue;\n\n // Reachable by keyboard and announced, because a chart nobody can tab to\n // is a picture of some numbers.\n mark.setAttribute(\"tabindex\", \"0\");\n mark.setAttribute(\"role\", \"img\");\n const share = hit.share !== undefined ? `, ${Math.round(hit.share * 1000) / 10}%` : \"\";\n mark.setAttribute(\"aria-label\", `${hit.category}${hit.series ? `, ${hit.series}` : \"\"}: ${hit.value === null ? \"no value\" : hit.formatted}${share}`);\n\n const onEnter = () => activate(mark, hit, false);\n const onFocus = () => activate(mark, hit, true);\n const onKey = (e: Event) => {\n const key = (e as KeyboardEvent).key;\n if (key === \"Enter\" || key === \" \") {\n e.preventDefault();\n options.onSelect?.(hit, e);\n } else if (key === \"Escape\") deactivate();\n };\n const onClick = (e: Event) => options.onSelect?.(hit, e);\n\n mark.addEventListener(\"pointerenter\", onEnter);\n mark.addEventListener(\"focus\", onFocus);\n mark.addEventListener(\"keydown\", onKey);\n if (options.onSelect) mark.addEventListener(\"click\", onClick);\n cleanups.push(() => {\n mark.removeEventListener(\"pointerenter\", onEnter);\n mark.removeEventListener(\"focus\", onFocus);\n mark.removeEventListener(\"keydown\", onKey);\n mark.removeEventListener(\"click\", onClick);\n mark.removeAttribute(\"tabindex\");\n mark.removeAttribute(\"role\");\n mark.removeAttribute(\"aria-label\");\n mark.removeAttribute(\"data-bp-on\");\n mark.removeAttribute(\"data-bp-focus\");\n });\n }\n\n chart.addEventListener(\"pointerleave\", deactivate);\n chart.addEventListener(\"focusout\", (e) => {\n // Moving between two marks of the same chart is not leaving it.\n if (!chart.contains((e as FocusEvent).relatedTarget as Node | null)) deactivate();\n });\n cleanups.push(() => {\n chart.classList.remove(\"bp-chart--active\");\n delete chart.dataset.bpInteractive;\n });\n }\n\n // Scrolling moves the mark out from under a tooltip pinned to the viewport.\n const onScroll = () => hideTip();\n doc.addEventListener(\"scroll\", onScroll, true);\n cleanups.push(() => doc.removeEventListener(\"scroll\", onScroll, true));\n\n return () => {\n for (const c of cleanups.splice(0)) c();\n tip.remove();\n tipMounted = false;\n };\n}\n","/**\n * Browser-side export helpers: a zero-infrastructure \"Print / Save as PDF\"\n * fallback and a tiny client for the render service.\n *\n * They live here rather than in `@broadpaper/pdf` because they are pure browser\n * code with no Chromium in them, and a designer that imported them from the\n * Chromium backend package made every browser application install\n * `playwright-core` — 14 MB of driver it can never run. `@broadpaper/pdf/client`\n * re-exports this module, so the old import path still works.\n */\nimport type { DataSource, ReportData, ReportTemplate, Theme } from \"@broadpaper/core\";\nimport { resolveWatermark, watermarkCss } from \"@broadpaper/license\";\nimport { documentToHtml } from \"./preview.js\";\nimport type { LayoutResult } from \"./layout.js\";\n\nexport interface PrintOptions {\n title?: string;\n /** Called after the print dialog closes (best effort). */\n onDone?: () => void;\n /** A licence for this print only, overriding whatever `configure` was given. */\n license?: string | null;\n}\n\n/**\n * Opens the laid-out document in a hidden iframe and triggers the browser's\n * print dialog. Because the pages are fixed boxes, the output matches the\n * server-rendered PDF (subject to the browser's own PDF writer).\n *\n * \"Save as PDF\" in that dialog produces a PDF as surely as either backend does,\n * so it carries the evaluation mark on the same terms. The licence check is\n * what makes this asynchronous; the returned promise settles when the dialog\n * has been opened, and ignoring it is fine.\n */\nexport async function printReport(result: LayoutResult, opts: PrintOptions = {}): Promise<void> {\n const mark = await resolveWatermark(opts.license !== undefined ? { license: opts.license } : {});\n const html = documentToHtml(result, { title: opts.title, ...(mark ? { extraCss: watermarkCss(mark) } : {}) });\n const frame = document.createElement(\"iframe\");\n frame.setAttribute(\"aria-hidden\", \"true\");\n frame.style.position = \"fixed\";\n frame.style.right = \"0\";\n frame.style.bottom = \"0\";\n frame.style.width = \"0\";\n frame.style.height = \"0\";\n frame.style.border = \"0\";\n document.body.appendChild(frame);\n const doc = frame.contentDocument!;\n doc.open();\n doc.write(html);\n doc.close();\n const win = frame.contentWindow!;\n const cleanup = () => {\n setTimeout(() => frame.remove(), 1000);\n opts.onDone?.();\n };\n win.addEventListener(\"afterprint\", cleanup, { once: true });\n const go = () => {\n win.focus();\n win.print();\n };\n const fonts = (doc as Document & { fonts?: FontFaceSet }).fonts;\n if (fonts?.ready) fonts.ready.then(() => setTimeout(go, 50)).catch(go);\n else setTimeout(go, 100);\n}\n\nexport interface PdfClientOptions {\n /** Base URL of the PDF service, e.g. \"https://pdf.internal.example.com\". */\n baseUrl: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nexport interface PdfRequest {\n template: ReportTemplate;\n data?: ReportData;\n theme?: Partial<Theme>;\n dataSources?: DataSource[];\n now?: Date;\n locale?: string;\n currency?: string;\n timeZone?: string;\n metadata?: { title?: string; author?: string; subject?: string; keywords?: string[] };\n network?: { allowedHosts?: string[] };\n}\n\n/** Minimal client for the BroadPaper PDF service. */\nexport function createPdfClient(opts: PdfClientOptions) {\n const f = opts.fetch ?? fetch;\n return {\n async render(req: PdfRequest): Promise<{ blob: Blob; pages: number; warnings: number }> {\n const res = await f(`${opts.baseUrl.replace(/\\/$/, \"\")}/render`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...(opts.token ? { authorization: `Bearer ${opts.token}` } : {}) },\n body: JSON.stringify({ ...req, now: req.now?.toISOString() })\n });\n if (!res.ok) {\n let message = `PDF service responded ${res.status}`;\n try {\n const j = (await res.json()) as { error?: string };\n if (j.error) message = j.error;\n } catch {\n /* ignore */\n }\n throw new Error(message);\n }\n return { blob: await res.blob(), pages: Number(res.headers.get(\"x-broadpaper-pages\") ?? 0), warnings: Number(res.headers.get(\"x-broadpaper-warnings\") ?? 0) };\n },\n async health(): Promise<boolean> {\n try {\n const res = await f(`${opts.baseUrl.replace(/\\/$/, \"\")}/health`);\n return res.ok;\n } catch {\n return false;\n }\n }\n };\n}\n\n/** Triggers a download of a Blob in the browser. */\nexport function downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => URL.revokeObjectURL(url), 5000);\n}\n","/**\n * The report as a web page: read-only, page-shaped, and interactive where a PDF\n * cannot be.\n *\n * This is the other half of the product. A saved template plus data is a file,\n * and it is also something a person should be able to *read* — in a tab, on a\n * phone, shared by link — without downloading anything. The drawing is\n * identical either way: the same paginator decides the breaks, the same blocks\n * draw the marks, the same SVG the PDF contains is the SVG the browser shows.\n * What differs is only what a screen can do that paper cannot, which is why\n * chart interactions are on by default here and stripped on the way to the file.\n *\n * It is deliberately a thin layer over `createReportPreview`. Everything about\n * measurement, pagination and rendering is that pipeline's job and must stay\n * there, or the viewer and the file start to disagree — which is the one thing\n * this product exists to prevent. What is added here is presentation and\n * navigation: paper on a ground, a fit-to-width zoom that responds to the\n * element's own width, page navigation, and an optional toolbar.\n */\nimport { enableChartInteractions, type ChartInteractionOptions } from \"./chart-interactions.js\";\nimport { createReportPreview, type PreviewOptions, type ReportPreview } from \"./preview.js\";\nimport { ensureStyle } from \"./dom.js\";\nimport type { LayoutResult } from \"./layout.js\";\n\n/** How the viewer sizes the page against the space it has. */\nexport type ViewerFit = \"width\" | \"page\" | \"actual\";\n\nexport interface ReportViewerOptions extends Omit<PreviewOptions, \"mode\" | \"zoom\"> {\n /**\n * How to size pages. `\"width\"` (the default) fits the page to the element,\n * never enlarging past 100% — a report blown up to 180% on a wide monitor\n * looks like a mistake, and the text is already sized for print. `\"page\"`\n * fits a whole page in view; `\"actual\"` pins it to 100%.\n */\n fit?: ViewerFit;\n /** A fixed zoom, which overrides `fit`. 1 is actual size. */\n zoom?: number;\n /** Gap between pages, in CSS px at 100%. Default 32. */\n pageGap?: number;\n /**\n * The toolbar: page position, zoom and print. `false` hides it, for a host\n * that would rather draw its own chrome around `goToPage` and `setZoom`.\n * Default true.\n */\n toolbar?: boolean;\n /**\n * Pointing at a chart mark shows what it stands for. Default true — it is\n * most of the reason to read a report on a screen rather than on paper.\n * `false`, or options passed straight to `enableChartInteractions`.\n */\n interactions?: boolean | ChartInteractionOptions;\n /** Extra class on the viewer root. */\n className?: string;\n /** Accessible label for the scroll region. Default \"Report\". */\n label?: string;\n /** Called whenever the visible page changes. */\n onPageChange?(page: number): void;\n /** Called after each layout, with the result. */\n onLayout?(result: LayoutResult): void;\n /**\n * What the toolbar's print button does. The default opens the browser's\n * print dialog on the page as laid out, which produces a file matching this\n * view. Supply your own to hand the reader a real PDF instead.\n */\n onPrint?(result: LayoutResult): void;\n}\n\nexport interface ReportViewer {\n /** Re-renders with new input. Resolves with the new layout. */\n update(input: Partial<ReportViewerOptions>): Promise<LayoutResult>;\n /** The last layout result, or null before the first render. */\n readonly result: LayoutResult | null;\n /** The page currently in view, 1-based. */\n readonly page: number;\n /** The number of pages, or 0 before the first render. */\n readonly pageCount: number;\n /** Scrolls to a page, 1-based and clamped. */\n goToPage(page: number): void;\n /** Sets an explicit zoom, or returns to a fit mode. */\n setZoom(zoom: number | ViewerFit): void;\n /** The zoom actually in use, resolved from the fit mode. */\n readonly zoom: number;\n /** Opens the print dialog (or calls `onPrint`). */\n print(): void;\n destroy(): void;\n}\n\n/**\n * Viewer chrome, kept out of `DOCUMENT_CSS` on purpose.\n *\n * That stylesheet is shared with the PDF path — the Chromium backend prints the\n * very markup it styles — so a rule added there for a scrollbar or a toolbar is\n * a rule that can reach the file. These are scoped under `.bp-viewer`, which\n * wraps the document rather than being part of it, and none of it is inside\n * `.bp-page`.\n */\nconst VIEWER_CSS = `\n.bp-viewer { display: flex; flex-direction: column; width: 100%; height: 100%; min-height: 0; background: #f1f2f4; color: #1a1d21; font: 13px/1.4 system-ui, -apple-system, \"Segoe UI\", sans-serif; }\n.bp-viewer *, .bp-viewer *::before, .bp-viewer *::after { box-sizing: border-box; }\n.bp-viewer__scroll { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 24px 16px; scroll-behavior: smooth; }\n.bp-viewer__stage { margin: 0 auto; transform-origin: top left; }\n.bp-viewer .bp-preview__page { box-shadow: 0 1px 3px rgba(16,24,40,0.14), 0 10px 28px rgba(16,24,40,0.10); }\n\n.bp-viewer__bar { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #fff; border-bottom: 1px solid #e2e5ea; }\n.bp-viewer__spacer { flex: 1 1 auto; }\n.bp-viewer__btn { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 8px; border: 1px solid #e2e5ea; border-radius: 6px; background: #fff; color: inherit; font: inherit; cursor: pointer; }\n.bp-viewer__btn:hover:not(:disabled) { background: #f5f6f8; }\n.bp-viewer__btn:disabled { opacity: 0.4; cursor: default; }\n.bp-viewer__btn:focus-visible, .bp-viewer__field:focus-visible { outline: 2px solid #4a6fa5; outline-offset: 1px; }\n.bp-viewer__count { font-variant-numeric: tabular-nums; white-space: nowrap; }\n.bp-viewer__field { width: 3.5em; height: 28px; padding: 0 6px; border: 1px solid #e2e5ea; border-radius: 6px; font: inherit; text-align: right; font-variant-numeric: tabular-nums; }\n\n/* The reader's own print of the view. The page boxes are already exact, so all\n this has to do is get the ground and the chrome out of the way. */\n@media print {\n .bp-viewer { background: #fff; height: auto; }\n .bp-viewer__bar { display: none; }\n .bp-viewer__scroll { overflow: visible; padding: 0; }\n .bp-viewer__stage { transform: none !important; width: auto !important; }\n .bp-viewer .bp-preview__page { box-shadow: none; margin: 0 !important; break-after: page; page-break-after: always; }\n .bp-viewer .bp-preview__page:last-child { break-after: auto; page-break-after: auto; }\n}\n\n@media (prefers-color-scheme: dark) {\n .bp-viewer { background: #22252a; color: #e8eaed; }\n .bp-viewer__bar { background: #2b2f36; border-bottom-color: #3a3f47; }\n .bp-viewer__btn, .bp-viewer__field { background: #2b2f36; border-color: #3a3f47; color: inherit; }\n .bp-viewer__btn:hover:not(:disabled) { background: #343941; }\n}\n`;\n\n/** Clamps to a range, tolerating a NaN low bound of an empty document. */\nconst clamp = (n: number, lo: number, hi: number) => (n < lo ? lo : n > hi ? hi : n);\n\nfunction button(doc: Document, label: string, title: string): HTMLButtonElement {\n const b = doc.createElement(\"button\");\n b.type = \"button\";\n b.className = \"bp-viewer__btn\";\n b.textContent = label;\n b.title = title;\n b.setAttribute(\"aria-label\", title);\n return b;\n}\n\n/**\n * Mounts a read-only, page-shaped view of a template into an element.\n *\n * The container is expected to have a height — the viewer fills it and scrolls\n * inside itself, so that the toolbar stays put and `fit: \"width\"` has something\n * to measure against. A container with no height collapses to the toolbar.\n */\nexport function createReportViewer(container: HTMLElement, options: ReportViewerOptions): ReportViewer {\n const doc = container.ownerDocument;\n const win = doc.defaultView;\n ensureStyle(doc, \"bp-viewer-css\", VIEWER_CSS);\n\n let opts: ReportViewerOptions = { ...options };\n let destroyed = false;\n let page = 1;\n let zoom = 1;\n let stopInteractions: (() => void) | null = null;\n\n const root = doc.createElement(\"div\");\n root.className = `bp-viewer ${opts.className ?? \"\"}`.trim();\n\n const bar = doc.createElement(\"div\");\n bar.className = \"bp-viewer__bar\";\n bar.setAttribute(\"role\", \"toolbar\");\n bar.setAttribute(\"aria-label\", \"Report controls\");\n\n const prev = button(doc, \"‹\", \"Previous page\");\n const next = button(doc, \"›\", \"Next page\");\n const pageField = doc.createElement(\"input\");\n pageField.className = \"bp-viewer__field\";\n pageField.type = \"text\";\n pageField.inputMode = \"numeric\";\n pageField.setAttribute(\"aria-label\", \"Page number\");\n const count = doc.createElement(\"span\");\n count.className = \"bp-viewer__count\";\n const zoomOut = button(doc, \"−\", \"Zoom out\");\n const zoomIn = button(doc, \"+\", \"Zoom in\");\n const zoomLabel = doc.createElement(\"button\");\n zoomLabel.type = \"button\";\n zoomLabel.className = \"bp-viewer__btn bp-viewer__count\";\n zoomLabel.title = \"Fit to width\";\n const printBtn = button(doc, \"Print\", \"Print or save as PDF\");\n const spacer = doc.createElement(\"span\");\n spacer.className = \"bp-viewer__spacer\";\n\n bar.append(prev, pageField, count, next, spacer, zoomOut, zoomLabel, zoomIn, printBtn);\n\n const scroll = doc.createElement(\"div\");\n scroll.className = \"bp-viewer__scroll\";\n scroll.tabIndex = 0;\n scroll.setAttribute(\"role\", \"region\");\n scroll.setAttribute(\"aria-label\", opts.label ?? \"Report\");\n\n const stage = doc.createElement(\"div\");\n stage.className = \"bp-viewer__stage\";\n scroll.appendChild(stage);\n\n if (opts.toolbar !== false) root.appendChild(bar);\n root.appendChild(scroll);\n container.appendChild(root);\n\n // Read-only throughout: `mode: \"preview\"` is what resolves real data and drops\n // every design-mode affordance. A viewer showing dashed drop targets and\n // \"sample data\" badges would be showing the reader the editor's furniture.\n const preview: ReportPreview = createReportPreview(stage, { ...opts, mode: \"preview\", zoom: 1 });\n\n /** The zoom a fit mode works out to, given the space there actually is. */\n function fitZoom(): number {\n const result = preview.result;\n if (!result) return 1;\n const explicit = opts.zoom;\n if (typeof explicit === \"number\") return explicit;\n const fit = opts.fit ?? \"width\";\n if (fit === \"actual\") return 1;\n // The padding is the scroll box's own, and the page cannot use it.\n const styles = win?.getComputedStyle(scroll);\n const padX = styles ? parseFloat(styles.paddingLeft || \"0\") + parseFloat(styles.paddingRight || \"0\") : 32;\n const padY = styles ? parseFloat(styles.paddingTop || \"0\") + parseFloat(styles.paddingBottom || \"0\") : 48;\n const availableWidth = scroll.clientWidth - padX;\n if (!(availableWidth > 0)) return 1;\n const byWidth = availableWidth / result.page.width;\n if (fit === \"width\") {\n // Never past 100%: the type is already sized for paper, and enlarging it\n // on a wide monitor reads as a rendering fault rather than as a feature.\n return clamp(byWidth, 0.1, 1);\n }\n const availableHeight = scroll.clientHeight - padY;\n if (!(availableHeight > 0)) return clamp(byWidth, 0.1, 1);\n return clamp(Math.min(byWidth, availableHeight / result.page.height), 0.1, 1);\n }\n\n function applyZoom(): void {\n zoom = fitZoom();\n const result = preview.result;\n stage.style.transform = zoom === 1 ? \"\" : `scale(${zoom})`;\n if (result) {\n // A transform is painting, not layout: a scaled element still occupies\n // its unscaled box. Left alone, a page shrunk to fit still reserved its\n // full width and height, so the scroll box kept a page's worth of empty\n // space below the document and scrolled sideways past its right edge.\n //\n // The width is given as the scaled width and the origin is the top left\n // corner, so the box and the paint agree and `margin: 0 auto` still\n // centres what the reader sees. The children stay full width and hang\n // out of that box, which is exactly what the scale then pulls back in.\n const unscaled = stage.scrollHeight;\n stage.style.width = `${Math.round(result.page.width * zoom)}px`;\n stage.style.marginBottom = zoom < 1 ? `${-Math.round(unscaled * (1 - zoom))}px` : \"\";\n }\n zoomLabel.textContent = `${Math.round(zoom * 100)}%`;\n zoomLabel.setAttribute(\"aria-label\", `Zoom, ${Math.round(zoom * 100)} percent. Activate to fit to width.`);\n }\n\n /** The page whose top is nearest the top of the scroll box. */\n function visiblePage(): number {\n const pages = stage.querySelectorAll<HTMLElement>(\"[data-page]\");\n if (!pages.length) return 1;\n const top = scroll.getBoundingClientRect().top;\n let best = 1;\n let bestDistance = Infinity;\n for (const el of pages) {\n const box = el.getBoundingClientRect();\n // Bottom-of-page beats top-of-page: a page still half on screen is the\n // one being read, not the one whose first line has just appeared.\n const distance = box.bottom < top ? Infinity : Math.abs(box.top - top);\n if (distance < bestDistance) {\n bestDistance = distance;\n best = Number(el.getAttribute(\"data-page\")) || 1;\n }\n }\n return best;\n }\n\n function syncBar(): void {\n const total = preview.result?.paged.totalPages ?? 0;\n count.textContent = total ? `/ ${total}` : \"\";\n if (doc.activeElement !== pageField) pageField.value = total ? String(page) : \"\";\n pageField.disabled = total < 2;\n prev.disabled = page <= 1;\n next.disabled = total === 0 || page >= total;\n }\n\n function setPage(next_: number, scrollTo: boolean): void {\n const total = preview.result?.paged.totalPages ?? 0;\n const wanted = total ? clamp(Math.round(next_), 1, total) : 1;\n if (scrollTo) {\n const el = stage.querySelector<HTMLElement>(`[data-page=\"${wanted}\"]`);\n // Measured from rectangles rather than `offsetTop`, for two reasons: the\n // stage is scaled, and a rectangle is already in scaled coordinates while\n // `offsetTop` is not; and `offsetParent` is whatever positioned ancestor\n // the *host* happens to have, which is not ours to assume.\n if (el) scroll.scrollTop += el.getBoundingClientRect().top - scroll.getBoundingClientRect().top;\n }\n if (wanted === page) return;\n page = wanted;\n syncBar();\n opts.onPageChange?.(page);\n }\n\n const onScroll = () => {\n const seen = visiblePage();\n if (seen === page) return;\n page = seen;\n syncBar();\n opts.onPageChange?.(page);\n };\n scroll.addEventListener(\"scroll\", onScroll, { passive: true });\n\n const onKey = (e: KeyboardEvent) => {\n if (e.target === pageField) return;\n if (e.key === \"PageDown\") setPage(page + 1, true);\n else if (e.key === \"PageUp\") setPage(page - 1, true);\n else if (e.key === \"Home\") setPage(1, true);\n else if (e.key === \"End\") setPage(preview.result?.paged.totalPages ?? 1, true);\n else return;\n e.preventDefault();\n };\n scroll.addEventListener(\"keydown\", onKey);\n\n prev.addEventListener(\"click\", () => setPage(page - 1, true));\n next.addEventListener(\"click\", () => setPage(page + 1, true));\n pageField.addEventListener(\"change\", () => setPage(Number(pageField.value), true));\n zoomLabel.addEventListener(\"click\", () => api.setZoom(\"width\"));\n zoomIn.addEventListener(\"click\", () => api.setZoom(clamp(zoom + 0.1, 0.1, 4)));\n zoomOut.addEventListener(\"click\", () => api.setZoom(clamp(zoom - 0.1, 0.1, 4)));\n printBtn.addEventListener(\"click\", () => api.print());\n\n // Fit is against the element, never the window: the viewer is embedded, so a\n // narrow pane in a wide page and a phone are the same problem and the window\n // answers neither. Same reasoning as the designer's own narrow layout.\n const observer = win && \"ResizeObserver\" in win ? new win.ResizeObserver(() => applyZoom()) : null;\n observer?.observe(scroll);\n\n function afterRender(result: LayoutResult): void {\n stopInteractions?.();\n stopInteractions = null;\n if (opts.interactions !== false) {\n stopInteractions = enableChartInteractions(stage, typeof opts.interactions === \"object\" ? opts.interactions : {});\n }\n applyZoom();\n setPage(page, false);\n syncBar();\n opts.onLayout?.(result);\n }\n\n const api: ReportViewer = {\n result: null,\n page: 1,\n pageCount: 0,\n zoom: 1,\n async update(input) {\n opts = { ...opts, ...input };\n const result = await preview.update({ ...input, mode: \"preview\", zoom: 1 });\n if (destroyed) return result;\n afterRender(result);\n return result;\n },\n goToPage(n) {\n setPage(n, true);\n },\n setZoom(value) {\n if (typeof value === \"number\") opts = { ...opts, zoom: value };\n else opts = { ...opts, fit: value, zoom: undefined };\n applyZoom();\n },\n print() {\n const result = preview.result;\n if (!result) return;\n if (opts.onPrint) opts.onPrint(result);\n else win?.print();\n },\n destroy() {\n destroyed = true;\n observer?.disconnect();\n scroll.removeEventListener(\"scroll\", onScroll);\n scroll.removeEventListener(\"keydown\", onKey);\n stopInteractions?.();\n preview.destroy();\n root.remove();\n }\n };\n\n // `result`, `page`, `pageCount` and `zoom` read through to live state rather\n // than being copies a caller could find stale one frame after a scroll.\n Object.defineProperties(api, {\n result: { get: () => preview.result, enumerable: true },\n page: { get: () => page, enumerable: true },\n pageCount: { get: () => preview.result?.paged.totalPages ?? 0, enumerable: true },\n zoom: { get: () => zoom, enumerable: true }\n });\n\n syncBar();\n void api.update({});\n return api;\n}\n"],"mappings":"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","names":["h","renderNode","p","total","h","h","ensureStyle","h"]}
1
+ {"version":3,"sources":["../src/dom.ts","../src/measurer.ts","../src/layout.ts","../src/preview.ts","../src/styles.ts","../src/chart-interactions.ts","../src/client.ts","../src/viewer.ts"],"sourcesContent":["import { classNames, styleString, type VNode, type VProps } from \"@broadpaper/core\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/** Creates a DOM node from a VNode. Attribute values are set with setAttribute (never innerHTML). */\nexport function vnodeToDom(v: VNode | string, doc: Document = document): Node {\n if (typeof v === \"string\") return doc.createTextNode(v);\n const el = v.ns === \"svg\" ? doc.createElementNS(SVG_NS, v.tag) : doc.createElement(v.tag);\n applyProps(el, v.props);\n for (const c of v.children) el.appendChild(vnodeToDom(c, doc));\n return el;\n}\n\nfunction applyProps(el: Element, props: VProps) {\n for (const [k, v] of Object.entries(props)) {\n if (v === undefined || v === null || v === false) continue;\n if (k === \"class\") {\n const c = classNames(v as VProps[\"class\"]);\n if (c) el.setAttribute(\"class\", c);\n } else if (k === \"style\") {\n const s = styleString(v as VProps[\"style\"]);\n if (s) el.setAttribute(\"style\", s);\n } else if (v === true) el.setAttribute(k, \"\");\n else el.setAttribute(k, String(v));\n }\n}\n\n/** Replaces the children of `container` with the given VNodes. */\nexport function mountVNodes(container: HTMLElement, vnodes: Array<VNode | string>): void {\n const doc = container.ownerDocument;\n const frag = doc.createDocumentFragment();\n for (const v of vnodes) frag.appendChild(vnodeToDom(v, doc));\n container.replaceChildren(frag);\n}\n\n/** Injects (or updates) a style element with the given id. */\nexport function ensureStyle(doc: Document, id: string, css: string): HTMLStyleElement {\n let el = doc.getElementById(id) as HTMLStyleElement | null;\n if (!el) {\n el = doc.createElement(\"style\");\n el.id = id;\n doc.head.appendChild(el);\n }\n if (el.textContent !== css) el.textContent = css;\n return el;\n}\n","import { renderNode, type GeometryMap, type Measurer, type NodeGeometry, type RenderTreeOptions, type ResolvedNode, type SectionGeometry, type TableRowGeometry } from \"@broadpaper/core\";\nimport { vnodeToDom } from \"./dom.js\";\n\n/** FNV-1a 32-bit hash; fast and good enough for cache keys. */\nexport function hashString(s: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36) + s.length.toString(36);\n}\n\nexport function sectionHash(section: ResolvedNode, width: number, mode: string): string {\n return hashString(JSON.stringify(section)) + \":\" + Math.round(width * 100) + \":\" + mode;\n}\n\n/**\n * Measures resolved sections by laying them out as continuous galleys in a\n * hidden container and reading geometry from the DOM. Results are cached by\n * section content hash so unchanged sections are never re-measured.\n */\nexport class DomMeasurer implements Measurer {\n private readonly host: HTMLElement;\n private readonly cache = new Map<string, SectionGeometry>();\n private opts: RenderTreeOptions | null = null;\n /** What the measurements since the last `settled()` found still on its way. */\n private arriving: Array<Promise<unknown>> = [];\n\n constructor(private readonly doc: Document, container?: HTMLElement) {\n this.host = doc.createElement(\"div\");\n this.host.className = \"bp-doc bp-galley\";\n this.host.setAttribute(\"aria-hidden\", \"true\");\n (container ?? doc.body).appendChild(this.host);\n }\n\n setOptions(opts: RenderTreeOptions): void {\n this.opts = opts;\n this.host.classList.toggle(\"bp-doc--design\", opts.mode === \"design\");\n }\n\n measure(sections: ResolvedNode[], width: number): GeometryMap {\n if (!this.opts) throw new Error(\"DomMeasurer.setOptions() must be called before measure()\");\n const out: GeometryMap = {};\n const pending: Array<{ section: ResolvedNode; key: string; el: HTMLElement }> = [];\n for (const section of sections) {\n const key = sectionHash(section, width, this.opts.mode);\n const hit = this.cache.get(key);\n if (hit) {\n out[section.key] = hit;\n continue;\n }\n const el = this.doc.createElement(\"div\");\n el.className = \"bp-galley__section\";\n el.style.width = `${width}px`;\n el.style.position = \"relative\";\n const v = renderNode(section, { ...this.opts, width }, width);\n if (v) el.appendChild(vnodeToDom(v, this.doc));\n pending.push({ section, key, el });\n }\n if (pending.length === 0) return out;\n\n // Single layout pass: attach all, then read.\n for (const p of pending) this.host.appendChild(p.el);\n const read = pending.map((p) => ({ p, geom: readSectionGeometry(p.el, p.section, this.opts!) }));\n\n /*\n * Whether this was measured too early, asked straight after reading.\n *\n * An image with no stated height is 0px until it has decoded, and text is laid\n * out in a fallback face until the face it names has loaded — and reading the\n * geometry is what makes the browser start fetching both. So a document\n * measured the moment it is shown was paginated as shorter than it draws: an\n * imported invoice's logo measured nothing, its text a line short per block,\n * and the preview's second page ended ninety pixels above the last rows of the\n * table. Such a measurement is still returned, because something has to be\n * drawn now, but it is never cached, and `settled()` tells the caller when to\n * measure again. A face loading is a fact about the document rather than one\n * section, so it keeps the whole batch out of the cache.\n */\n const fonts = (this.doc as Document & { fonts?: FontFaceSet }).fonts;\n const faces: Array<Promise<unknown>> = [];\n if (fonts && typeof fonts[Symbol.iterator] === \"function\") for (const face of fonts) if (face.status === \"loading\") faces.push(face.loaded);\n this.arriving.push(...faces);\n for (const { p, geom } of read) {\n const images = [...p.el.querySelectorAll(\"img\")].filter((img) => !img.complete).map(decoded);\n this.arriving.push(...images);\n if (!faces.length && !images.length) this.cache.set(p.key, geom);\n out[p.section.key] = geom;\n }\n for (const p of pending) p.el.remove();\n if (this.cache.size > 400) {\n // Simple eviction: drop the oldest half.\n const keys = [...this.cache.keys()].slice(0, 200);\n for (const k of keys) this.cache.delete(k);\n }\n return out;\n }\n\n /**\n * Resolves once everything the measurements since the last call found still\n * arriving has arrived — or failed to, which is final too — with whether there\n * was anything. When it is true, a layout taken before it is short and should be\n * taken again; the sections involved were not cached, so the next measurement\n * reads them afresh.\n */\n async settled(): Promise<boolean> {\n const waiting = this.arriving;\n this.arriving = [];\n if (!waiting.length) return false;\n await Promise.all(waiting.map((p) => p.catch(() => undefined)));\n return true;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n destroy(): void {\n this.host.remove();\n this.cache.clear();\n this.arriving = [];\n }\n}\n\n/**\n * When an image has decoded, or failed to.\n *\n * `decode()` works on an element that is no longer in the document, which is what\n * a galley element is by the time anybody waits. A broken image counts as arrived:\n * its box is final, and it reports `complete`, so it is not waited for twice.\n *\n * The failure is caught here, where the promise is made, and not in `settled()`. A\n * caller that never asks — the Chromium backend lays out once — would otherwise leave\n * a blocked image's rejection unhandled, and the render service reports an unhandled\n * rejection as a failed document: a private-network image, refused by design, stopped\n * the whole PDF with \"The source image cannot be decoded\".\n */\nfunction decoded(img: HTMLImageElement): Promise<unknown> {\n if (typeof img.decode === \"function\") return img.decode().catch(() => undefined);\n return new Promise((resolve) => {\n img.addEventListener(\"load\", resolve, { once: true });\n img.addEventListener(\"error\", resolve, { once: true });\n });\n}\n\nfunction readSectionGeometry(root: HTMLElement, section: ResolvedNode, opts: RenderTreeOptions): SectionGeometry {\n const base = root.getBoundingClientRect();\n const nodes: Record<string, NodeGeometry> = {};\n const byKey = new Map<string, ResolvedNode>();\n const collect = (n: ResolvedNode) => {\n byKey.set(n.key, n);\n for (const c of n.children) collect(c);\n };\n collect(section);\n\n const elements = root.querySelectorAll<HTMLElement>(\"[data-key]\");\n for (const el of elements) {\n const key = el.getAttribute(\"data-key\")!;\n if (nodes[key]) continue; // first occurrence wins\n const node = byKey.get(key);\n const r = el.getBoundingClientRect();\n const g: NodeGeometry = { top: r.top - base.top, height: r.height, left: r.left - base.left, width: r.width };\n if (node) {\n const def = opts.registry.get(node.type);\n const measure = def?.measure ?? (node.flow.split === \"lines\" ? \"text\" : node.flow.split === \"rows\" ? \"table\" : \"none\");\n if (measure === \"text\") g.lines = readLineBoxes(el, base.top, g.top + g.height);\n else if (measure === \"table\") g.rows = readTableRows(el, base.top);\n }\n nodes[key] = g;\n }\n const sectionRect = root.firstElementChild?.getBoundingClientRect();\n const height = sectionRect ? sectionRect.height : base.height;\n return { height, width: base.width, nodes };\n}\n\n/** Returns the y coordinate (relative to `baseTop`) of the bottom of each line box. */\nexport function readLineBoxes(el: HTMLElement, baseTop: number, nodeBottom: number): number[] {\n const container = (el.querySelector(\"[data-part='text']\") as HTMLElement | null) ?? el;\n const doc = el.ownerDocument;\n const rects: Array<{ top: number; bottom: number }> = [];\n const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {\n acceptNode: (n) => {\n if (n.nodeType === Node.TEXT_NODE) return (n.textContent ?? \"\").trim().length ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n const tag = (n as Element).tagName;\n if (tag === \"BR\" || tag === \"IMG\" || tag === \"SVG\") return NodeFilter.FILTER_ACCEPT;\n const e = n as HTMLElement;\n if (e.classList?.contains(\"bp-p--empty\")) return NodeFilter.FILTER_ACCEPT;\n return NodeFilter.FILTER_SKIP;\n }\n });\n let cur: Node | null = walker.nextNode();\n while (cur) {\n if (cur.nodeType === Node.TEXT_NODE) {\n const range = doc.createRange();\n range.selectNodeContents(cur);\n for (const r of range.getClientRects()) if (r.height > 0 && r.width > 0) rects.push({ top: r.top - baseTop, bottom: r.bottom - baseTop });\n } else {\n const r = (cur as Element).getBoundingClientRect();\n if (r.height > 0) rects.push({ top: r.top - baseTop, bottom: r.bottom - baseTop });\n }\n cur = walker.nextNode();\n }\n if (rects.length === 0) return [];\n rects.sort((a, b) => a.top - b.top || a.bottom - b.bottom);\n const lines: Array<{ top: number; bottom: number }> = [];\n for (const r of rects) {\n const last = lines[lines.length - 1];\n if (last && r.top < last.bottom - 0.5) {\n last.top = Math.min(last.top, r.top);\n last.bottom = Math.max(last.bottom, r.bottom);\n } else lines.push({ ...r });\n }\n const out: number[] = [];\n for (let i = 0; i < lines.length - 1; i++) out.push(round2((lines[i]!.bottom + lines[i + 1]!.top) / 2));\n out.push(round2(nodeBottom));\n return out;\n}\n\nexport function readTableRows(el: HTMLElement, baseTop: number): TableRowGeometry[] {\n const table = el.querySelector(\"table.bp-table\");\n if (!table) return [];\n const rows: TableRowGeometry[] = [];\n const read = (selector: string, kind: TableRowGeometry[\"kind\"]) => {\n for (const tr of table.querySelectorAll<HTMLElement>(`:scope > ${selector} > tr`)) {\n const r = tr.getBoundingClientRect();\n rows.push({ top: round2(r.top - baseTop), height: round2(r.height), kind });\n }\n };\n read(\"thead\", \"header\");\n read(\"tbody\", \"body\");\n read(\"tfoot\", \"footer\");\n return rows;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import {\n computePageGeometry,\n mergeTheme,\n resolveValue,\n paginate,\n resolveTemplate,\n renderNode,\n h,\n walkVNodes,\n type BlockRegistry,\n type DataSource,\n type GeometryMap,\n type Measurer,\n type PagedDocument,\n type PageGeometry,\n type ReportData,\n type ReportTemplate,\n type RenderMode,\n type RenderTreeOptions,\n type ResolvedDocument,\n type ResolvedNode,\n type SectionGeometry,\n type TableRowGeometry,\n type Theme,\n type VNode,\n type VProps,\n type FormatContext,\n type LayoutWarning\n} from \"@broadpaper/core\";\n\nexport interface LayoutInput {\n template: ReportTemplate;\n registry: BlockRegistry;\n theme?: Partial<Theme>;\n dataSources?: DataSource[];\n data?: ReportData;\n sampleData?: ReportData;\n mode?: RenderMode;\n now?: Date;\n locale?: string;\n currency?: string;\n timeZone?: string;\n designIterations?: number;\n allowStyleOverrides?: boolean;\n /** Minimum pages to show (design canvas). */\n minPages?: number;\n /** Editor hook to decorate node wrappers. */\n decorate?: RenderTreeOptions[\"decorate\"];\n}\n\nexport interface LayoutResult {\n resolved: ResolvedDocument;\n paged: PagedDocument;\n geometry: GeometryMap;\n regionGeometry: GeometryMap;\n page: PageGeometry;\n theme: Theme;\n format: FormatContext;\n mode: RenderMode;\n warnings: Array<{ source: \"resolve\" | \"layout\"; message: string; nodeId?: string; page?: number; code?: LayoutWarning[\"code\"] }>;\n /** Map of resolved node key → node, across all regions. */\n nodesByKey: Map<string, ResolvedNode>;\n /** Renders one page to a VNode. */\n renderPage(index: number): VNode;\n /** Renders every page. */\n renderPages(): VNode[];\n}\n\n/**\n * A measurer whose results arrive asynchronously.\n *\n * The DOM measurer answers immediately because the browser has already done\n * the layout; an engine-backed measurer has to run the engine first. Both\n * drive the identical pipeline — only the awaiting differs.\n */\nexport interface AsyncMeasurer {\n measure(sections: ResolvedNode[], width: number): Promise<GeometryMap>;\n setOptions?(opts: RenderTreeOptions): void;\n}\n\ninterface Prepared {\n input: LayoutInput;\n mode: RenderMode;\n theme: Theme;\n template: ReportTemplate;\n page: PageGeometry;\n format: FormatContext;\n resolved: ResolvedDocument;\n baseOpts: RenderTreeOptions;\n regionSections: ResolvedNode[];\n}\n\nfunction indexNodes(doc: ResolvedDocument): Map<string, ResolvedNode> {\n const map = new Map<string, ResolvedNode>();\n const visit = (n: ResolvedNode) => {\n map.set(n.key, n);\n n.children.forEach(visit);\n };\n for (const s of doc.body) visit(s);\n for (const s of doc.header.default) visit(s);\n for (const s of doc.header.firstPage ?? []) visit(s);\n for (const s of doc.footer.default) visit(s);\n for (const s of doc.footer.firstPage ?? []) visit(s);\n return map;\n}\n\n/**\n * The full pipeline: resolve → measure → paginate. Returns a result that can\n * render individual pages on demand (the editor virtualises them).\n */\n/**\n * Everything up to measurement: resolving the template, page geometry and the\n * render options. Split out so the synchronous and asynchronous entry points\n * share one pipeline and can never drift apart.\n */\nfunction prepareLayout(input: LayoutInput): Prepared {\n const mode: RenderMode = input.mode ?? \"preview\";\n const theme = mergeTheme(input.theme);\n const template = input.template;\n const page = computePageGeometry(template.page);\n const now = input.now ?? new Date();\n const format: FormatContext = {\n locale: input.locale ?? template.settings?.locale ?? \"en-GB\",\n currency: input.currency ?? template.settings?.currency ?? \"GBP\",\n timeZone: input.timeZone ?? template.settings?.timeZone,\n now\n };\n\n const resolved = resolveTemplate({\n template,\n registry: input.registry,\n theme,\n dataSources: input.dataSources,\n data: input.data,\n sampleData: input.sampleData,\n mode,\n now,\n locale: format.locale,\n currency: format.currency,\n timeZone: format.timeZone,\n designIterations: input.designIterations,\n allowStyleOverrides: input.allowStyleOverrides\n });\n\n const baseOpts: RenderTreeOptions = {\n registry: input.registry,\n theme,\n styles: template.styles,\n mode,\n page: { number: 1, total: 1 },\n format,\n width: page.contentWidth,\n decorate: input.decorate\n };\n\n const regionSections = [...resolved.header.default, ...(resolved.header.firstPage ?? []), ...resolved.footer.default, ...(resolved.footer.firstPage ?? [])];\n return { input, mode, theme, template, page, format, resolved, baseOpts, regionSections };\n}\n\n/** Completes a layout once its geometry has been measured, however that happened. */\nfunction finishLayout(p: Prepared, geometry: GeometryMap, regionGeometry: GeometryMap): LayoutResult {\n const { input, mode, theme, template, page, format, resolved, baseOpts } = p;\n\n const paged = paginate({ document: resolved, geometry, page, minPages: input.minPages ?? template.page.minPages ?? 1, regionGeometry });\n const nodesByKey = indexNodes(resolved);\n\n const warnings: LayoutResult[\"warnings\"] = [\n ...resolved.warnings.map((w) => ({ source: \"resolve\" as const, message: w.message, nodeId: w.nodeId })),\n ...paged.warnings.map((w) => ({ source: \"layout\" as const, message: w.message, nodeId: w.nodeId, page: w.page, code: w.code }))\n ];\n\n const renderPage = (index: number): VNode => {\n const p = paged.pages[index];\n if (!p) throw new Error(`Page ${index} does not exist`);\n const opts: RenderTreeOptions = { ...baseOpts, page: { number: p.number, total: paged.totalPages } };\n return composePage(p.index, p.number, paged.totalPages, p.first, resolved, paged, page, opts, nodesByKey, regionGeometry, theme, template.page.background, geometry);\n };\n\n return {\n resolved,\n paged,\n geometry,\n regionGeometry,\n page,\n theme,\n format,\n mode,\n warnings,\n nodesByKey,\n renderPage,\n renderPages: () => paged.pages.map((_, i) => renderPage(i))\n };\n}\n\n/** Lays out a document with a measurer that answers immediately. */\nexport function layoutDocument(input: LayoutInput, measurer: Measurer & { setOptions?(opts: RenderTreeOptions): void }): LayoutResult {\n const p = prepareLayout(input);\n measurer.setOptions?.(p.baseOpts);\n const geometry = measurer.measure(p.resolved.body, p.page.contentWidth);\n const regionGeometry = p.regionSections.length ? measurer.measure(p.regionSections, p.page.contentWidth) : {};\n return finishLayout(p, geometry, regionGeometry);\n}\n\n/**\n * Lays out a document with a measurer that answers asynchronously.\n *\n * This is what lets the editor page a document exactly as the PDF backend\n * will: the same engine measures both, so a break in the canvas is a break in\n * the file. Everything after measurement — pagination, composition, warnings —\n * is the code the synchronous path runs.\n */\nexport async function layoutDocumentAsync(input: LayoutInput, measurer: AsyncMeasurer): Promise<LayoutResult> {\n const p = prepareLayout(input);\n measurer.setOptions?.(p.baseOpts);\n const geometry = await measurer.measure(p.resolved.body, p.page.contentWidth);\n const regionGeometry = p.regionSections.length ? await measurer.measure(p.regionSections, p.page.contentWidth) : {};\n return finishLayout(p, geometry, regionGeometry);\n}\n\n/**\n * Windowed rendering: nodes entirely outside the fragment's clip window are\n * replaced by empty boxes of identical size, and table bodies keep only the rows\n * that intersect the window (leading rows become one spacer row). Layout is\n * unchanged; per-page DOM cost becomes proportional to what is visible.\n */\nexport function windowSection(root: VNode, geom: SectionGeometry | undefined, from: number, to: number): VNode {\n if (!geom) return root;\n const visit = (v: VNode): VNode => {\n const key = v.props[\"data-key\"];\n if (typeof key === \"string\") {\n const g = geom.nodes[key];\n if (g) {\n const bottom = g.top + g.height;\n if (bottom <= from - 0.01 || g.top >= to + 0.01) {\n const existing = v.props.style;\n const style = typeof existing === \"string\" ? `${existing};height:${g.height}px;overflow:hidden` : { ...((existing as Record<string, string | number | undefined>) ?? {}), height: `${g.height}px`, overflow: \"hidden\" };\n return { ...v, props: { ...v.props, style, \"data-culled\": \"\" }, children: [] };\n }\n if (g.rows && g.rows.length) return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : windowTable(c, g.rows!, from, to))) };\n }\n }\n return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : visit(c))) };\n };\n return visit(root);\n}\n\nfunction windowTable(v: VNode, rows: TableRowGeometry[], from: number, to: number): VNode {\n if (v.tag === \"table\") {\n const body = rows.filter((r) => r.kind === \"body\");\n return {\n ...v,\n children: v.children.map((c) => {\n if (typeof c === \"string\" || c.tag !== \"tbody\") return c;\n const trs = c.children.filter((x): x is VNode => typeof x !== \"string\" && x.tag === \"tr\");\n if (trs.length !== body.length) return c;\n let leading = 0;\n let trailing = 0;\n const kept: VNode[] = [];\n for (let i = 0; i < trs.length; i++) {\n const r = body[i]!;\n const bottom = r.top + r.height;\n if (bottom <= from + 0.01) leading += r.height;\n else if (r.top >= to - 0.01) trailing += r.height;\n else kept.push(trs[i]!);\n }\n const spacer = (height: number) => h(\"tr\", { class: \"bp-table__spacer\" }, h(\"td\", { colspan: \"100\", style: { padding: \"0\", border: \"0\", height: `${height}px` } }));\n return { ...c, children: [...(leading > 0 ? [spacer(leading)] : []), ...kept, ...(trailing > 0 ? [spacer(trailing)] : [])] };\n })\n };\n }\n return { ...v, children: v.children.map((c) => (typeof c === \"string\" ? c : windowTable(c, rows, from, to))) };\n}\n\n/** Renders only the header part of a table node (for repeated headers). */\nfunction renderTableHeader(node: ResolvedNode, opts: RenderTreeOptions, width: number): VNode | null {\n const full = renderNode(node, opts, width);\n if (!full) return null;\n walkVNodes(full, (v) => {\n if (v.tag === \"table\") {\n v.children = v.children.filter((c) => typeof c === \"string\" || c.tag === \"colgroup\" || c.tag === \"thead\");\n return false;\n }\n return true;\n });\n // Strip wrapper margins so the header sits flush at the fragment top.\n const props = { ...full.props } as VProps;\n delete props.style;\n full.props = { ...props, class: [String(props.class ?? \"\"), \"bp-repeat-header__node\"] };\n return full;\n}\n\nexport function composePage(\n index: number,\n number: number,\n total: number,\n first: boolean,\n resolved: ResolvedDocument,\n paged: PagedDocument,\n page: PageGeometry,\n opts: RenderTreeOptions,\n nodesByKey: Map<string, ResolvedNode>,\n regionGeometry: GeometryMap,\n theme: Theme,\n pageBackground?: string,\n geometry?: GeometryMap\n): VNode {\n const p = paged.pages[index]!;\n const pageStyle: Record<string, string> = {\n width: `${page.width}px`,\n height: `${page.height}px`,\n background: resolveValue(pageBackground, theme, \"#ffffff\")\n };\n\n const region = (sections: ResolvedNode[] | undefined, top: number, height: number, name: \"header\" | \"footer\", align: \"top\" | \"bottom\") => {\n if (!sections || sections.length === 0 || height <= 0) return null;\n const total = sections.reduce((a, s) => a + (regionGeometry[s.key]?.height ?? 0), 0);\n const offset = align === \"bottom\" ? Math.max(0, height - total) : 0;\n return h(\n \"div\",\n { class: [\"bp-page__region\", `bp-page__${name}`], \"data-region\": name, style: { top: `${top}px`, left: `${page.margins.left}px`, width: `${page.contentWidth}px`, height: `${height}px` } },\n h(\"div\", { class: \"bp-page__region-inner\", style: { position: \"absolute\", top: `${offset}px`, left: \"0\", width: \"100%\" } }, sections.map((s) => renderNode(s, opts, page.contentWidth)))\n );\n };\n\n const headerSections = first && resolved.header.firstPage ? resolved.header.firstPage : resolved.header.default;\n const footerSections = first && resolved.footer.firstPage ? resolved.footer.firstPage : resolved.footer.default;\n\n const windowed = (section: ResolvedNode, from: number, to: number): VNode | null => {\n const v = renderNode(section, opts, page.contentWidth);\n if (!v) return null;\n return windowSection(v, geometry?.[section.key], from, to);\n };\n\n const fragments = p.fragments.map((f) => {\n const section = nodesByKey.get(f.sectionKey);\n if (!section) return null;\n const extra = f.repeatHeaders.reduce((a, r) => a + r.height, 0);\n const headers = f.repeatHeaders.map((r) => {\n const t = nodesByKey.get(r.tableKey);\n const v = t ? renderTableHeader(t, opts, r.width) : null;\n return h(\"div\", { class: \"bp-repeat-header\", \"data-table\": r.tableKey, style: { left: `${r.left}px`, width: `${r.width}px`, height: `${r.height}px` } }, v);\n });\n return h(\n \"div\",\n { class: [\"bp-fragment\", f.continued && \"bp-fragment--continued\", f.continues && \"bp-fragment--continues\"], \"data-section\": f.sectionKey, \"data-from\": String(f.from), \"data-to\": String(f.to), style: { top: `${f.y}px`, height: `${f.height}px` } },\n headers,\n h(\"div\", { class: \"bp-fragment__clip\", style: { top: `${extra}px`, height: `${f.to - f.from}px` } }, h(\"div\", { class: \"bp-fragment__content\", style: { \"margin-top\": `${-f.from}px`, width: `${page.contentWidth}px` } }, windowed(section, f.from, f.to)))\n );\n });\n\n return h(\n \"div\",\n { class: [\"bp-page\", first && \"bp-page--first\"], \"data-page\": String(number), \"data-page-index\": String(index), style: pageStyle, \"aria-label\": `Page ${number} of ${total}` },\n region(headerSections, page.headerTop, page.headerHeight, \"header\", \"top\"),\n h(\"div\", { class: \"bp-page__body\", \"data-region\": \"body\", style: { top: `${page.contentTop}px`, left: `${page.margins.left}px`, width: `${page.contentWidth}px`, height: `${page.contentHeight}px` } }, fragments),\n region(footerSections, page.footerTop, page.footerHeight, \"footer\", \"bottom\")\n );\n}\n","import { h, toHtml, escapeHtml, type VNode } from \"@broadpaper/core\";\nimport { layoutDocument, type LayoutInput, type LayoutResult } from \"./layout.js\";\nimport { DomMeasurer } from \"./measurer.js\";\nimport { mountVNodes, ensureStyle } from \"./dom.js\";\nimport { DOCUMENT_CSS, fontFaceCss } from \"./styles.js\";\n\nexport interface PreviewOptions extends LayoutInput {\n /** Gap between pages in px. Default 24. */\n pageGap?: number;\n /** Scale factor. Default 1. */\n zoom?: number;\n /** Additional CSS class on the root. */\n className?: string;\n}\n\nexport interface ReportPreview {\n /** Re-renders with new input (template, data, theme, ...). */\n update(input: Partial<PreviewOptions>): Promise<LayoutResult>;\n /** The last layout result. */\n result: LayoutResult | null;\n /** Scroll to a page (1-based). */\n goToPage(number: number): void;\n destroy(): void;\n}\n\n/** Waits for web fonts so measurement is stable. Resolves immediately when unsupported. */\nexport async function waitForFonts(doc: Document, families: string[] = []): Promise<void> {\n const fonts = (doc as Document & { fonts?: FontFaceSet }).fonts;\n if (!fonts) return;\n try {\n await Promise.all(families.map((f) => fonts.load(`12px \"${f}\"`).catch(() => undefined)));\n await fonts.ready;\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Mounts a live, paginated preview into a host element. This is the same\n * pipeline the editor canvas and the PDF service use.\n */\nexport function createReportPreview(container: HTMLElement, options: PreviewOptions): ReportPreview {\n const doc = container.ownerDocument;\n ensureStyle(doc, \"bp-document-css\", DOCUMENT_CSS);\n const measurer = new DomMeasurer(doc);\n let current: PreviewOptions = options;\n let destroyed = false;\n\n const root = doc.createElement(\"div\");\n root.className = `bp-doc bp-preview ${options.className ?? \"\"}`.trim();\n container.appendChild(root);\n\n const api: ReportPreview = {\n result: null,\n async update(input) {\n current = { ...current, ...input };\n const theme = current.theme;\n ensureStyle(doc, \"bp-theme-fonts\", theme ? fontFaceCss({ ...theme, id: theme.id ?? \"t\", name: theme.name ?? \"\", typography: theme.typography ?? { headingFont: \"\", bodyFont: \"\" }, colors: theme.colors ?? ({} as never) }) : \"\");\n await waitForFonts(doc, (theme?.fonts ?? []).map((f) => f.family));\n if (destroyed) return api.result!;\n let result = layoutDocument(current, measurer);\n // Measured while an image was decoding or a face was loading, so shorter than\n // it draws: the pages would end above their content and the last rows of a\n // table would never be shown. Laid out again once it has arrived — a bounded\n // number of times, because a pass can only find what the one before it drew.\n for (let pass = 0; pass < 3 && (await measurer.settled()); pass++) {\n if (destroyed) return api.result ?? result;\n result = layoutDocument(current, measurer);\n }\n api.result = result;\n const gap = current.pageGap ?? 24;\n const zoom = current.zoom ?? 1;\n root.classList.toggle(\"bp-doc--design\", result.mode === \"design\");\n const pages = result.renderPages().map((p, i) => h(\"div\", { class: \"bp-preview__page\", style: { \"margin-bottom\": i < result.paged.totalPages - 1 ? `${gap}px` : \"0\", \"box-shadow\": \"0 1px 3px rgba(16,24,40,0.12), 0 8px 24px rgba(16,24,40,0.08)\" } }, p));\n const inner: VNode = h(\"div\", { class: \"bp-preview__pages\", style: { transform: zoom !== 1 ? `scale(${zoom})` : undefined, \"transform-origin\": \"top center\", width: `${result.page.width}px`, margin: \"0 auto\" } }, pages);\n mountVNodes(root, [inner]);\n return result;\n },\n goToPage(number) {\n const el = root.querySelector<HTMLElement>(`[data-page=\"${number}\"]`);\n el?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n },\n destroy() {\n destroyed = true;\n measurer.destroy();\n root.remove();\n }\n };\n void api.update({});\n return api;\n}\n\nexport interface HtmlDocumentOptions {\n title?: string;\n /** Extra CSS appended after the document stylesheet. */\n extraCss?: string;\n /** Include the design-mode class (rarely wanted for PDFs). */\n design?: boolean;\n /** Document language for the html element. */\n lang?: string;\n /** PDF metadata. */\n author?: string;\n subject?: string;\n keywords?: string[];\n}\n\n/**\n * Serialises a laid-out document to a standalone HTML page: one fixed-size box\n * per page with `@page` sized to match. This is what the PDF service prints.\n */\nexport function documentToHtml(result: LayoutResult, opts: HtmlDocumentOptions = {}): string {\n const pages = result.renderPages();\n const { width, height } = result.page;\n const fonts = fontFaceCss(result.theme);\n const css = `${fonts}\\n${DOCUMENT_CSS}\\n@page{size:${width}px ${height}px;margin:0;}\\nhtml,body{margin:0;padding:0;background:#fff;}\\n.bp-page{break-after:page;page-break-after:always;}\\n.bp-page:last-child{break-after:auto;page-break-after:auto;}\\n${opts.extraCss ?? \"\"}`;\n const meta = [\n `<meta charset=\"utf-8\">`,\n `<meta name=\"viewport\" content=\"width=${width}\">`,\n `<meta name=\"generator\" content=\"BroadPaper\">`,\n opts.author ? `<meta name=\"author\" content=\"${escapeHtml(opts.author)}\">` : \"\",\n opts.subject ? `<meta name=\"description\" content=\"${escapeHtml(opts.subject)}\">` : \"\",\n opts.keywords?.length ? `<meta name=\"keywords\" content=\"${escapeHtml(opts.keywords.join(\", \"))}\">` : \"\"\n ].join(\"\\n\");\n const body = pages.map((p) => toHtml(p)).join(\"\\n\");\n return `<!doctype html>\\n<html lang=\"${escapeHtml(opts.lang ?? \"en\")}\">\\n<head>\\n${meta}\\n<title>${escapeHtml(opts.title ?? \"Report\")}</title>\\n<style>${css}</style>\\n</head>\\n<body class=\"bp-doc${opts.design ? \" bp-doc--design\" : \"\"}\" data-pages=\"${result.paged.totalPages}\">\\n${body}\\n</body>\\n</html>`;\n}\n","import type { Theme } from \"@broadpaper/core\";\n\n/**\n * Document stylesheet shared by the editor canvas, the preview and the PDF\n * service. Everything is scoped under `.bp-doc` so it never leaks into a host\n * application. Block-specific visual styling is inline (resolved from the theme);\n * this sheet provides structure, print rules and design-mode affordances.\n */\nexport const DOCUMENT_CSS = `\n.bp-doc, .bp-doc * { box-sizing: border-box; }\n.bp-doc { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; font-variant-numeric: tabular-nums; }\n.bp-doc p { margin: 0; }\n.bp-doc h1, .bp-doc h2, .bp-doc h3, .bp-doc h4 { margin: 0; font-weight: inherit; }\n.bp-doc img { max-width: 100%; }\n.bp-doc a { color: inherit; }\n.bp-doc table { border-spacing: 0; }\n.bp-doc ul.bp-list, .bp-doc ol.bp-list { margin: 0; padding-left: 1.4em; }\n.bp-doc .bp-list li { margin: 0.15em 0; }\n.bp-doc .bp-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; background: rgba(0,0,0,0.05); padding: 0 3px; border-radius: 3px; }\n.bp-doc .bp-p--empty { min-height: 1em; }\n\n.bp-page { position: relative; overflow: hidden; background: #fff; color: #1a1d21; }\n.bp-page__region { position: absolute; overflow: hidden; }\n.bp-page__body { position: absolute; overflow: hidden; }\n.bp-fragment { position: absolute; left: 0; width: 100%; overflow: hidden; }\n.bp-fragment__clip { position: absolute; left: 0; right: 0; overflow: hidden; }\n.bp-repeat-header { position: absolute; top: 0; overflow: hidden; }\n.bp-repeat-header .bp-table { table-layout: fixed; }\n\n.bp-stack { display: flex; flex-direction: column; }\n.bp-stack > .bp-node { min-width: 0; }\n.bp-node--hidden > * { opacity: 0.4; }\n.bp-node--placeholder { opacity: 0.85; }\n.bp-empty-section, .bp-empty-column { display: none; }\n.bp-galley { position: absolute; left: -100000px; top: 0; visibility: hidden; pointer-events: none; }\n\n.bp-table { width: 100%; border-collapse: collapse; }\n.bp-table td, .bp-table th { word-wrap: break-word; overflow-wrap: anywhere; }\n.bp-chart__svg text { user-select: none; }\n.bp-page-break--print { height: 0; }\n\n/* --- design mode affordances (only when .bp-doc--design) --- */\n.bp-doc--design .bp-token { display: inline; padding: 0 5px; margin: 0 1px; border-radius: 4px; background: rgba(79, 111, 165, 0.14); color: #2f4f7f; font-size: 0.92em; font-weight: 500; white-space: nowrap; box-decoration-break: clone; -webkit-box-decoration-break: clone; }\n.bp-doc--design .bp-token--lg { font-size: 0.65em; vertical-align: middle; }\n.bp-doc--design .bp-token__value { opacity: 0.65; margin-left: 4px; font-weight: 400; }\n.bp-doc--design .bp-cell--bound { text-decoration: underline dotted rgba(79,111,165,0.45); text-underline-offset: 3px; text-decoration-thickness: 1px; }\n.bp-doc--design .bp-placeholder-text { color: #9aa1ab; font-style: italic; }\n.bp-doc--design .bp-empty-section { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 96px; border: 1.5px dashed #cfd5dd; border-radius: 8px; color: #6b7280; font-size: 12px; font-family: Inter, system-ui, sans-serif; background: #fafbfc; }\n.bp-doc--design .bp-empty-section__title { font-weight: 600; color: #4b5563; }\n.bp-doc--design .bp-empty-column { display: flex; align-items: center; justify-content: center; min-height: 56px; border: 1.5px dashed #d4d9e0; border-radius: 6px; color: #9aa1ab; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-spacer--design { background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(0,0,0,0.04) 6px 8px); border-radius: 3px; }\n.bp-doc--design .bp-page-break { display: flex; align-items: center; gap: 8px; margin: 2px 0; color: #b4541f; font-size: 9.5px; font-family: Inter, system-ui, sans-serif; text-transform: uppercase; letter-spacing: 0.06em; }\n.bp-doc--design .bp-page-break::before, .bp-doc--design .bp-page-break::after { content: \"\"; flex: 1; border-top: 2px dashed rgba(180, 84, 31, 0.5); }\n.bp-doc--design .bp-page-break__label { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: 999px; font-weight: 600; background: rgba(180, 84, 31, 0.12); white-space: nowrap; }\n\n/* Structural blocks draw nothing of their own, so design mode outlines them.\n The label is absolutely positioned: naming a box must not resize it. */\n.bp-doc--design .bp-container,\n.bp-doc--design .bp-keep-together { position: relative; outline: 1px dashed rgba(79, 111, 165, 0.32); outline-offset: 1px; border-radius: 3px; }\n.bp-doc--design .bp-container::before,\n.bp-doc--design .bp-keep-together::before {\n content: \"Container\"; position: absolute; top: -6px; left: 8px; z-index: 1; padding: 0 4px;\n background: #fff; color: #7c8697; font-family: Inter, system-ui, sans-serif; font-size: 8.5px;\n font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; pointer-events: none;\n}\n.bp-doc--design .bp-keep-together::before { content: \"Keep together\"; }\n.bp-doc--design .bp-repeater__badge, .bp-doc--design .bp-conditional__badge, .bp-doc--design .bp-table__badge, .bp-doc--design .bp-chart__badge { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 4px; padding: 1px 7px; border-radius: 999px; font-size: 9.5px; font-weight: 600; letter-spacing: 0.02em; font-family: Inter, system-ui, sans-serif; background: rgba(79,111,165,0.12); color: #2f4f7f; user-select: none; }\n.bp-doc--design .bp-conditional--false .bp-conditional__badge { background: rgba(199,125,10,0.14); color: #8a5a06; }\n.bp-doc--design .bp-conditional--false > .bp-stack { opacity: 0.45; }\n.bp-doc--design .bp-repeater { border-left: 2px dashed rgba(79,111,165,0.35); padding-left: 8px; }\n.bp-doc--design .bp-conditional { border-left: 2px dashed rgba(199,125,10,0.45); padding-left: 8px; }\n.bp-doc--design .bp-image-placeholder { display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #f1f3f6, #e6e9ee); color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; border: 1px dashed #cfd5dd; }\n.bp-doc--design .bp-logo-placeholder { display: inline-flex; align-items: center; padding: 0 12px; border: 1px dashed #cfd5dd; border-radius: 4px; color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-node--locked { outline: 1px dashed rgba(0,0,0,0.15); }\n\n@media print {\n .bp-page { break-after: page; page-break-after: always; box-shadow: none !important; margin: 0 !important; }\n .bp-page:last-child { break-after: auto; page-break-after: auto; }\n}\n`;\n\n/** Builds @font-face rules for a theme's fonts. */\nexport function fontFaceCss(theme: Theme): string {\n return (theme.fonts ?? [])\n .map((f) => {\n const src = (Array.isArray(f.src) ? f.src : [f.src]).map((u) => `url(\"${u.replace(/\"/g, \"%22\")}\")`).join(\", \");\n return `@font-face{font-family:\"${f.family.replace(/\"/g, \"\")}\";src:${src};font-weight:${f.weight ?? 400};font-style:${f.style ?? \"normal\"};font-display:${f.display ?? \"block\"};}`;\n })\n .join(\"\\n\");\n}\n","/**\n * Charts you can point at, without changing what gets printed.\n *\n * The obvious way to get interactive charts is to draw them a second time with\n * a charting library on screen. That gives you two chart implementations that\n * drift, and a screen that no longer matches the file — which is the one thing\n * this product is for. So the drawing does not change at all: the same SVG the\n * PDF contains is the SVG the browser shows, and this adds a hover, a focus\n * ring and a tooltip on top of the marks that are already there.\n *\n * The chart block records what each mark stands for as it draws\n * (`data-bp-chart` on the wrapper, `data-bp-hit=\"<index>\"` on each mark), so\n * nothing here re-derives any geometry: it reads the real element's own box.\n * The PDF translator strips those attributes, so print output is unchanged.\n *\n * Everything it adds is inert: no layout is read or written that could affect\n * pagination, and `destroy()` puts the DOM back exactly as it found it.\n */\n\n/** One mark a reader can point at. Mirrors `ChartHit` in the blocks package. */\ninterface Hit {\n categoryIndex: number;\n category: string;\n seriesIndex: number;\n series: string;\n value: number | null;\n formatted: string;\n color: string;\n share?: number;\n}\n\ninterface ChartPayload {\n type: string;\n title: string;\n hits: Hit[];\n}\n\nexport interface ChartInteractionOptions {\n /**\n * Builds the tooltip's content. Return a string (shown as text) or an\n * element. The default names the category, the series and the value.\n */\n tooltip?(hit: Hit, context: { type: string; title: string; siblings: Hit[] }): string | HTMLElement;\n /** Called when a mark is clicked or activated from the keyboard. */\n onSelect?(hit: Hit, event: Event): void;\n /**\n * Replaces the whole thing for one chart. Return `false` to leave that chart\n * alone — the escape hatch for a host that would rather mount its own\n * charting library over the box. The SVG underneath is still what prints.\n */\n onChart?(element: HTMLElement, payload: ChartPayload): boolean | void;\n /** Where the tooltip is appended. Defaults to the document body. */\n portal?: HTMLElement;\n}\n\nconst STYLE_ID = \"bp-chart-interactions\";\n\nconst CSS = `\n.bp-chart [data-bp-hit] { cursor: default; transition: opacity 120ms ease, filter 120ms ease; }\n.bp-chart--active [data-bp-hit] { opacity: 0.45; }\n.bp-chart--active [data-bp-hit][data-bp-on] { opacity: 1; filter: brightness(1.06); }\n.bp-chart [data-bp-hit]:focus-visible { outline: none; }\n.bp-chart [data-bp-hit][data-bp-focus] { stroke: #111827; stroke-width: 2; paint-order: stroke; }\n.bp-chart-tip {\n position: fixed; z-index: 2147483000; pointer-events: none;\n max-width: 260px; padding: 6px 9px; border-radius: 6px;\n background: #111827; color: #fff;\n font: 12px/1.45 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);\n opacity: 0; transform: translateY(2px); transition: opacity 90ms ease, transform 90ms ease;\n}\n.bp-chart-tip[data-bp-shown] { opacity: 1; transform: none; }\n.bp-chart-tip__row { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }\n.bp-chart-tip__row + .bp-chart-tip__row { margin-top: 2px; }\n.bp-chart-tip__swatch { width: 8px; height: 8px; border-radius: 2px; flex: none; align-self: center; }\n.bp-chart-tip__label { color: #cbd5e1; }\n.bp-chart-tip__value { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }\n.bp-chart-tip__head { font-weight: 600; margin-bottom: 3px; }\n@media (prefers-reduced-motion: reduce) {\n .bp-chart-tip, .bp-chart [data-bp-hit] { transition: none; }\n}\n@media print { .bp-chart-tip { display: none !important; } }\n`;\n\nfunction ensureStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID)) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\n/** The default tooltip: every series at the category the reader is pointing at. */\nfunction defaultTooltip(hit: Hit, ctx: { type: string; title: string; siblings: Hit[] }, doc: Document): HTMLElement {\n const box = doc.createElement(\"div\");\n const head = doc.createElement(\"div\");\n head.className = \"bp-chart-tip__head\";\n head.textContent = hit.category || ctx.title;\n box.appendChild(head);\n\n // A pie has one series, so naming it twice would just repeat the category.\n const rows = ctx.type === \"pie\" || ctx.type === \"donut\" ? [hit] : ctx.siblings;\n for (const r of rows) {\n const row = doc.createElement(\"div\");\n row.className = \"bp-chart-tip__row\";\n\n const swatch = doc.createElement(\"span\");\n swatch.className = \"bp-chart-tip__swatch\";\n swatch.style.background = r.color;\n row.appendChild(swatch);\n\n const label = doc.createElement(\"span\");\n label.className = \"bp-chart-tip__label\";\n label.textContent = ctx.type === \"pie\" || ctx.type === \"donut\" ? (r.share !== undefined ? `${Math.round(r.share * 1000) / 10}%` : \"\") : r.series;\n row.appendChild(label);\n\n const value = doc.createElement(\"span\");\n value.className = \"bp-chart-tip__value\";\n // A null is a gap in the data, not a zero, and saying \"0\" would be a lie.\n value.textContent = r.value === null ? \"—\" : r.formatted;\n if (r === hit && rows.length > 1) value.style.textDecoration = \"underline\";\n row.appendChild(value);\n\n box.appendChild(row);\n }\n return box;\n}\n\n/**\n * Makes every chart under `root` respond to a pointer and to the keyboard.\n *\n * Safe to call again after the DOM is replaced — each call returns its own\n * teardown, and calling it twice on the same nodes does not double up.\n */\nexport function enableChartInteractions(root: HTMLElement, options: ChartInteractionOptions = {}): () => void {\n const doc = root.ownerDocument;\n if (!doc?.defaultView) return () => undefined;\n ensureStyle(doc);\n\n const portal = options.portal ?? doc.body;\n const tip = doc.createElement(\"div\");\n tip.className = \"bp-chart-tip\";\n tip.setAttribute(\"role\", \"tooltip\");\n tip.setAttribute(\"aria-hidden\", \"true\");\n\n let tipMounted = false;\n const cleanups: Array<() => void> = [];\n\n const hideTip = () => {\n tip.removeAttribute(\"data-bp-shown\");\n tip.setAttribute(\"aria-hidden\", \"true\");\n };\n\n const showTip = (content: string | HTMLElement, anchor: DOMRect) => {\n if (!tipMounted) {\n portal.appendChild(tip);\n tipMounted = true;\n }\n tip.replaceChildren(typeof content === \"string\" ? doc.createTextNode(content) : content);\n tip.setAttribute(\"aria-hidden\", \"false\");\n tip.setAttribute(\"data-bp-shown\", \"\");\n // Measured after the content is in, and clamped to the viewport so a mark\n // near an edge does not push the tooltip off the screen.\n const box = tip.getBoundingClientRect();\n const view = doc.defaultView!;\n const gap = 10;\n let left = anchor.left + anchor.width / 2 - box.width / 2;\n let top = anchor.top - box.height - gap;\n if (top < 4) top = anchor.bottom + gap;\n left = Math.max(4, Math.min(left, view.innerWidth - box.width - 4));\n top = Math.max(4, Math.min(top, view.innerHeight - box.height - 4));\n tip.style.left = `${Math.round(left)}px`;\n tip.style.top = `${Math.round(top)}px`;\n };\n\n for (const chart of Array.from(root.querySelectorAll<HTMLElement>(\".bp-chart[data-bp-chart]\"))) {\n if (chart.dataset.bpInteractive === \"1\") continue;\n\n let payload: ChartPayload;\n try {\n payload = JSON.parse(chart.dataset.bpChart ?? \"\") as ChartPayload;\n } catch {\n continue;\n }\n if (!Array.isArray(payload.hits) || payload.hits.length === 0) continue;\n if (options.onChart?.(chart, payload) === false) continue;\n\n chart.dataset.bpInteractive = \"1\";\n const marks = Array.from(chart.querySelectorAll<SVGElement>(\"[data-bp-hit]\"));\n if (marks.length === 0) continue;\n\n // Everything at the same category, which is what a reader comparing two\n // lines actually wants to see.\n const byCategory = new Map<number, Hit[]>();\n for (const hit of payload.hits) {\n const list = byCategory.get(hit.categoryIndex) ?? [];\n list.push(hit);\n byCategory.set(hit.categoryIndex, list);\n }\n\n const activate = (mark: SVGElement, hit: Hit, focus: boolean) => {\n chart.classList.add(\"bp-chart--active\");\n const siblings = byCategory.get(hit.categoryIndex) ?? [hit];\n for (const m of marks) {\n const h = payload.hits[Number(m.getAttribute(\"data-bp-hit\"))];\n if (h && siblings.includes(h)) m.setAttribute(\"data-bp-on\", \"\");\n else m.removeAttribute(\"data-bp-on\");\n m.removeAttribute(\"data-bp-focus\");\n }\n if (focus) mark.setAttribute(\"data-bp-focus\", \"\");\n const content = options.tooltip?.(hit, { type: payload.type, title: payload.title, siblings }) ?? defaultTooltip(hit, { type: payload.type, title: payload.title, siblings }, doc);\n showTip(content, mark.getBoundingClientRect());\n };\n\n const deactivate = () => {\n chart.classList.remove(\"bp-chart--active\");\n for (const m of marks) {\n m.removeAttribute(\"data-bp-on\");\n m.removeAttribute(\"data-bp-focus\");\n }\n hideTip();\n };\n\n for (const mark of marks) {\n const hit = payload.hits[Number(mark.getAttribute(\"data-bp-hit\"))];\n if (!hit) continue;\n\n // Reachable by keyboard and announced, because a chart nobody can tab to\n // is a picture of some numbers.\n mark.setAttribute(\"tabindex\", \"0\");\n mark.setAttribute(\"role\", \"img\");\n const share = hit.share !== undefined ? `, ${Math.round(hit.share * 1000) / 10}%` : \"\";\n mark.setAttribute(\"aria-label\", `${hit.category}${hit.series ? `, ${hit.series}` : \"\"}: ${hit.value === null ? \"no value\" : hit.formatted}${share}`);\n\n const onEnter = () => activate(mark, hit, false);\n const onFocus = () => activate(mark, hit, true);\n const onKey = (e: Event) => {\n const key = (e as KeyboardEvent).key;\n if (key === \"Enter\" || key === \" \") {\n e.preventDefault();\n options.onSelect?.(hit, e);\n } else if (key === \"Escape\") deactivate();\n };\n const onClick = (e: Event) => options.onSelect?.(hit, e);\n\n mark.addEventListener(\"pointerenter\", onEnter);\n mark.addEventListener(\"focus\", onFocus);\n mark.addEventListener(\"keydown\", onKey);\n if (options.onSelect) mark.addEventListener(\"click\", onClick);\n cleanups.push(() => {\n mark.removeEventListener(\"pointerenter\", onEnter);\n mark.removeEventListener(\"focus\", onFocus);\n mark.removeEventListener(\"keydown\", onKey);\n mark.removeEventListener(\"click\", onClick);\n mark.removeAttribute(\"tabindex\");\n mark.removeAttribute(\"role\");\n mark.removeAttribute(\"aria-label\");\n mark.removeAttribute(\"data-bp-on\");\n mark.removeAttribute(\"data-bp-focus\");\n });\n }\n\n chart.addEventListener(\"pointerleave\", deactivate);\n chart.addEventListener(\"focusout\", (e) => {\n // Moving between two marks of the same chart is not leaving it.\n if (!chart.contains((e as FocusEvent).relatedTarget as Node | null)) deactivate();\n });\n cleanups.push(() => {\n chart.classList.remove(\"bp-chart--active\");\n delete chart.dataset.bpInteractive;\n });\n }\n\n // Scrolling moves the mark out from under a tooltip pinned to the viewport.\n const onScroll = () => hideTip();\n doc.addEventListener(\"scroll\", onScroll, true);\n cleanups.push(() => doc.removeEventListener(\"scroll\", onScroll, true));\n\n return () => {\n for (const c of cleanups.splice(0)) c();\n tip.remove();\n tipMounted = false;\n };\n}\n","/**\n * Browser-side export helpers: a zero-infrastructure \"Print / Save as PDF\"\n * fallback and a tiny client for the render service.\n *\n * They live here rather than in `@broadpaper/pdf` because they are pure browser\n * code with no Chromium in them, and a designer that imported them from the\n * Chromium backend package made every browser application install\n * `playwright-core` — 14 MB of driver it can never run. `@broadpaper/pdf/client`\n * re-exports this module, so the old import path still works.\n */\nimport type { DataSource, ReportData, ReportTemplate, Theme } from \"@broadpaper/core\";\nimport { resolveWatermark, watermarkCss } from \"@broadpaper/license\";\nimport { documentToHtml } from \"./preview.js\";\nimport type { LayoutResult } from \"./layout.js\";\n\nexport interface PrintOptions {\n title?: string;\n /** Called after the print dialog closes (best effort). */\n onDone?: () => void;\n /** A licence for this print only, overriding whatever `configure` was given. */\n license?: string | null;\n}\n\n/**\n * Opens the laid-out document in a hidden iframe and triggers the browser's\n * print dialog. Because the pages are fixed boxes, the output matches the\n * server-rendered PDF (subject to the browser's own PDF writer).\n *\n * \"Save as PDF\" in that dialog produces a PDF as surely as either backend does,\n * so it carries the evaluation mark on the same terms. The licence check is\n * what makes this asynchronous; the returned promise settles when the dialog\n * has been opened, and ignoring it is fine.\n */\nexport async function printReport(result: LayoutResult, opts: PrintOptions = {}): Promise<void> {\n const mark = await resolveWatermark(opts.license !== undefined ? { license: opts.license } : {});\n const html = documentToHtml(result, { title: opts.title, ...(mark ? { extraCss: watermarkCss(mark) } : {}) });\n const frame = document.createElement(\"iframe\");\n frame.setAttribute(\"aria-hidden\", \"true\");\n frame.style.position = \"fixed\";\n frame.style.right = \"0\";\n frame.style.bottom = \"0\";\n frame.style.width = \"0\";\n frame.style.height = \"0\";\n frame.style.border = \"0\";\n document.body.appendChild(frame);\n const doc = frame.contentDocument!;\n doc.open();\n doc.write(html);\n doc.close();\n const win = frame.contentWindow!;\n const cleanup = () => {\n setTimeout(() => frame.remove(), 1000);\n opts.onDone?.();\n };\n win.addEventListener(\"afterprint\", cleanup, { once: true });\n const go = () => {\n win.focus();\n win.print();\n };\n const fonts = (doc as Document & { fonts?: FontFaceSet }).fonts;\n if (fonts?.ready) fonts.ready.then(() => setTimeout(go, 50)).catch(go);\n else setTimeout(go, 100);\n}\n\nexport interface PdfClientOptions {\n /** Base URL of the PDF service, e.g. \"https://pdf.internal.example.com\". */\n baseUrl: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nexport interface PdfRequest {\n template: ReportTemplate;\n data?: ReportData;\n theme?: Partial<Theme>;\n dataSources?: DataSource[];\n now?: Date;\n locale?: string;\n currency?: string;\n timeZone?: string;\n metadata?: { title?: string; author?: string; subject?: string; keywords?: string[] };\n network?: { allowedHosts?: string[] };\n}\n\n/** Minimal client for the BroadPaper PDF service. */\nexport function createPdfClient(opts: PdfClientOptions) {\n const f = opts.fetch ?? fetch;\n return {\n async render(req: PdfRequest): Promise<{ blob: Blob; pages: number; warnings: number }> {\n const res = await f(`${opts.baseUrl.replace(/\\/$/, \"\")}/render`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...(opts.token ? { authorization: `Bearer ${opts.token}` } : {}) },\n body: JSON.stringify({ ...req, now: req.now?.toISOString() })\n });\n if (!res.ok) {\n let message = `PDF service responded ${res.status}`;\n try {\n const j = (await res.json()) as { error?: string };\n if (j.error) message = j.error;\n } catch {\n /* ignore */\n }\n throw new Error(message);\n }\n return { blob: await res.blob(), pages: Number(res.headers.get(\"x-broadpaper-pages\") ?? 0), warnings: Number(res.headers.get(\"x-broadpaper-warnings\") ?? 0) };\n },\n async health(): Promise<boolean> {\n try {\n const res = await f(`${opts.baseUrl.replace(/\\/$/, \"\")}/health`);\n return res.ok;\n } catch {\n return false;\n }\n }\n };\n}\n\n/** Triggers a download of a Blob in the browser. */\nexport function downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => URL.revokeObjectURL(url), 5000);\n}\n","/**\n * The report as a web page: read-only, page-shaped, and interactive where a PDF\n * cannot be.\n *\n * This is the other half of the product. A saved template plus data is a file,\n * and it is also something a person should be able to *read* — in a tab, on a\n * phone, shared by link — without downloading anything. The drawing is\n * identical either way: the same paginator decides the breaks, the same blocks\n * draw the marks, the same SVG the PDF contains is the SVG the browser shows.\n * What differs is only what a screen can do that paper cannot, which is why\n * chart interactions are on by default here and stripped on the way to the file.\n *\n * It is deliberately a thin layer over `createReportPreview`. Everything about\n * measurement, pagination and rendering is that pipeline's job and must stay\n * there, or the viewer and the file start to disagree — which is the one thing\n * this product exists to prevent. What is added here is presentation and\n * navigation: paper on a ground, a fit-to-width zoom that responds to the\n * element's own width, page navigation, and an optional toolbar.\n */\nimport { enableChartInteractions, type ChartInteractionOptions } from \"./chart-interactions.js\";\nimport { createReportPreview, type PreviewOptions, type ReportPreview } from \"./preview.js\";\nimport { ensureStyle } from \"./dom.js\";\nimport type { LayoutResult } from \"./layout.js\";\n\n/** How the viewer sizes the page against the space it has. */\nexport type ViewerFit = \"width\" | \"page\" | \"actual\";\n\nexport interface ReportViewerOptions extends Omit<PreviewOptions, \"mode\" | \"zoom\"> {\n /**\n * How to size pages. `\"width\"` (the default) fits the page to the element,\n * never enlarging past 100% — a report blown up to 180% on a wide monitor\n * looks like a mistake, and the text is already sized for print. `\"page\"`\n * fits a whole page in view; `\"actual\"` pins it to 100%.\n */\n fit?: ViewerFit;\n /** A fixed zoom, which overrides `fit`. 1 is actual size. */\n zoom?: number;\n /** Gap between pages, in CSS px at 100%. Default 32. */\n pageGap?: number;\n /**\n * The toolbar: page position, zoom and print. `false` hides it, for a host\n * that would rather draw its own chrome around `goToPage` and `setZoom`.\n * Default true.\n */\n toolbar?: boolean;\n /**\n * Pointing at a chart mark shows what it stands for. Default true — it is\n * most of the reason to read a report on a screen rather than on paper.\n * `false`, or options passed straight to `enableChartInteractions`.\n */\n interactions?: boolean | ChartInteractionOptions;\n /** Extra class on the viewer root. */\n className?: string;\n /** Accessible label for the scroll region. Default \"Report\". */\n label?: string;\n /** Called whenever the visible page changes. */\n onPageChange?(page: number): void;\n /** Called after each layout, with the result. */\n onLayout?(result: LayoutResult): void;\n /**\n * What the toolbar's print button does. The default opens the browser's\n * print dialog on the page as laid out, which produces a file matching this\n * view. Supply your own to hand the reader a real PDF instead.\n */\n onPrint?(result: LayoutResult): void;\n}\n\nexport interface ReportViewer {\n /** Re-renders with new input. Resolves with the new layout. */\n update(input: Partial<ReportViewerOptions>): Promise<LayoutResult>;\n /** The last layout result, or null before the first render. */\n readonly result: LayoutResult | null;\n /** The page currently in view, 1-based. */\n readonly page: number;\n /** The number of pages, or 0 before the first render. */\n readonly pageCount: number;\n /** Scrolls to a page, 1-based and clamped. */\n goToPage(page: number): void;\n /** Sets an explicit zoom, or returns to a fit mode. */\n setZoom(zoom: number | ViewerFit): void;\n /** The zoom actually in use, resolved from the fit mode. */\n readonly zoom: number;\n /** Opens the print dialog (or calls `onPrint`). */\n print(): void;\n destroy(): void;\n}\n\n/**\n * Viewer chrome, kept out of `DOCUMENT_CSS` on purpose.\n *\n * That stylesheet is shared with the PDF path — the Chromium backend prints the\n * very markup it styles — so a rule added there for a scrollbar or a toolbar is\n * a rule that can reach the file. These are scoped under `.bp-viewer`, which\n * wraps the document rather than being part of it, and none of it is inside\n * `.bp-page`.\n */\nconst VIEWER_CSS = `\n.bp-viewer { display: flex; flex-direction: column; width: 100%; height: 100%; min-height: 0; background: #f1f2f4; color: #1a1d21; font: 13px/1.4 system-ui, -apple-system, \"Segoe UI\", sans-serif; }\n.bp-viewer *, .bp-viewer *::before, .bp-viewer *::after { box-sizing: border-box; }\n.bp-viewer__scroll { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 24px 16px; scroll-behavior: smooth; }\n.bp-viewer__stage { margin: 0 auto; transform-origin: top left; }\n.bp-viewer .bp-preview__page { box-shadow: 0 1px 3px rgba(16,24,40,0.14), 0 10px 28px rgba(16,24,40,0.10); }\n\n.bp-viewer__bar { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #fff; border-bottom: 1px solid #e2e5ea; }\n.bp-viewer__spacer { flex: 1 1 auto; }\n.bp-viewer__btn { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 8px; border: 1px solid #e2e5ea; border-radius: 6px; background: #fff; color: inherit; font: inherit; cursor: pointer; }\n.bp-viewer__btn:hover:not(:disabled) { background: #f5f6f8; }\n.bp-viewer__btn:disabled { opacity: 0.4; cursor: default; }\n.bp-viewer__btn:focus-visible, .bp-viewer__field:focus-visible { outline: 2px solid #4a6fa5; outline-offset: 1px; }\n.bp-viewer__count { font-variant-numeric: tabular-nums; white-space: nowrap; }\n.bp-viewer__field { width: 3.5em; height: 28px; padding: 0 6px; border: 1px solid #e2e5ea; border-radius: 6px; font: inherit; text-align: right; font-variant-numeric: tabular-nums; }\n\n/* The reader's own print of the view. The page boxes are already exact, so all\n this has to do is get the ground and the chrome out of the way. */\n@media print {\n .bp-viewer { background: #fff; height: auto; }\n .bp-viewer__bar { display: none; }\n .bp-viewer__scroll { overflow: visible; padding: 0; }\n .bp-viewer__stage { transform: none !important; width: auto !important; }\n .bp-viewer .bp-preview__page { box-shadow: none; margin: 0 !important; break-after: page; page-break-after: always; }\n .bp-viewer .bp-preview__page:last-child { break-after: auto; page-break-after: auto; }\n}\n\n@media (prefers-color-scheme: dark) {\n .bp-viewer { background: #22252a; color: #e8eaed; }\n .bp-viewer__bar { background: #2b2f36; border-bottom-color: #3a3f47; }\n .bp-viewer__btn, .bp-viewer__field { background: #2b2f36; border-color: #3a3f47; color: inherit; }\n .bp-viewer__btn:hover:not(:disabled) { background: #343941; }\n}\n`;\n\n/** Clamps to a range, tolerating a NaN low bound of an empty document. */\nconst clamp = (n: number, lo: number, hi: number) => (n < lo ? lo : n > hi ? hi : n);\n\nfunction button(doc: Document, label: string, title: string): HTMLButtonElement {\n const b = doc.createElement(\"button\");\n b.type = \"button\";\n b.className = \"bp-viewer__btn\";\n b.textContent = label;\n b.title = title;\n b.setAttribute(\"aria-label\", title);\n return b;\n}\n\n/**\n * Mounts a read-only, page-shaped view of a template into an element.\n *\n * The container is expected to have a height — the viewer fills it and scrolls\n * inside itself, so that the toolbar stays put and `fit: \"width\"` has something\n * to measure against. A container with no height collapses to the toolbar.\n */\nexport function createReportViewer(container: HTMLElement, options: ReportViewerOptions): ReportViewer {\n const doc = container.ownerDocument;\n const win = doc.defaultView;\n ensureStyle(doc, \"bp-viewer-css\", VIEWER_CSS);\n\n let opts: ReportViewerOptions = { ...options };\n let destroyed = false;\n let page = 1;\n let zoom = 1;\n let stopInteractions: (() => void) | null = null;\n\n const root = doc.createElement(\"div\");\n root.className = `bp-viewer ${opts.className ?? \"\"}`.trim();\n\n const bar = doc.createElement(\"div\");\n bar.className = \"bp-viewer__bar\";\n bar.setAttribute(\"role\", \"toolbar\");\n bar.setAttribute(\"aria-label\", \"Report controls\");\n\n const prev = button(doc, \"‹\", \"Previous page\");\n const next = button(doc, \"›\", \"Next page\");\n const pageField = doc.createElement(\"input\");\n pageField.className = \"bp-viewer__field\";\n pageField.type = \"text\";\n pageField.inputMode = \"numeric\";\n pageField.setAttribute(\"aria-label\", \"Page number\");\n const count = doc.createElement(\"span\");\n count.className = \"bp-viewer__count\";\n const zoomOut = button(doc, \"−\", \"Zoom out\");\n const zoomIn = button(doc, \"+\", \"Zoom in\");\n const zoomLabel = doc.createElement(\"button\");\n zoomLabel.type = \"button\";\n zoomLabel.className = \"bp-viewer__btn bp-viewer__count\";\n zoomLabel.title = \"Fit to width\";\n const printBtn = button(doc, \"Print\", \"Print or save as PDF\");\n const spacer = doc.createElement(\"span\");\n spacer.className = \"bp-viewer__spacer\";\n\n bar.append(prev, pageField, count, next, spacer, zoomOut, zoomLabel, zoomIn, printBtn);\n\n const scroll = doc.createElement(\"div\");\n scroll.className = \"bp-viewer__scroll\";\n scroll.tabIndex = 0;\n scroll.setAttribute(\"role\", \"region\");\n scroll.setAttribute(\"aria-label\", opts.label ?? \"Report\");\n\n const stage = doc.createElement(\"div\");\n stage.className = \"bp-viewer__stage\";\n scroll.appendChild(stage);\n\n if (opts.toolbar !== false) root.appendChild(bar);\n root.appendChild(scroll);\n container.appendChild(root);\n\n // Read-only throughout: `mode: \"preview\"` is what resolves real data and drops\n // every design-mode affordance. A viewer showing dashed drop targets and\n // \"sample data\" badges would be showing the reader the editor's furniture.\n const preview: ReportPreview = createReportPreview(stage, { ...opts, mode: \"preview\", zoom: 1 });\n\n /** The zoom a fit mode works out to, given the space there actually is. */\n function fitZoom(): number {\n const result = preview.result;\n if (!result) return 1;\n const explicit = opts.zoom;\n if (typeof explicit === \"number\") return explicit;\n const fit = opts.fit ?? \"width\";\n if (fit === \"actual\") return 1;\n // The padding is the scroll box's own, and the page cannot use it.\n const styles = win?.getComputedStyle(scroll);\n const padX = styles ? parseFloat(styles.paddingLeft || \"0\") + parseFloat(styles.paddingRight || \"0\") : 32;\n const padY = styles ? parseFloat(styles.paddingTop || \"0\") + parseFloat(styles.paddingBottom || \"0\") : 48;\n const availableWidth = scroll.clientWidth - padX;\n if (!(availableWidth > 0)) return 1;\n const byWidth = availableWidth / result.page.width;\n if (fit === \"width\") {\n // Never past 100%: the type is already sized for paper, and enlarging it\n // on a wide monitor reads as a rendering fault rather than as a feature.\n return clamp(byWidth, 0.1, 1);\n }\n const availableHeight = scroll.clientHeight - padY;\n if (!(availableHeight > 0)) return clamp(byWidth, 0.1, 1);\n return clamp(Math.min(byWidth, availableHeight / result.page.height), 0.1, 1);\n }\n\n function applyZoom(): void {\n zoom = fitZoom();\n const result = preview.result;\n stage.style.transform = zoom === 1 ? \"\" : `scale(${zoom})`;\n if (result) {\n // A transform is painting, not layout: a scaled element still occupies\n // its unscaled box. Left alone, a page shrunk to fit still reserved its\n // full width and height, so the scroll box kept a page's worth of empty\n // space below the document and scrolled sideways past its right edge.\n //\n // The width is given as the scaled width and the origin is the top left\n // corner, so the box and the paint agree and `margin: 0 auto` still\n // centres what the reader sees. The children stay full width and hang\n // out of that box, which is exactly what the scale then pulls back in.\n const unscaled = stage.scrollHeight;\n stage.style.width = `${Math.round(result.page.width * zoom)}px`;\n stage.style.marginBottom = zoom < 1 ? `${-Math.round(unscaled * (1 - zoom))}px` : \"\";\n }\n zoomLabel.textContent = `${Math.round(zoom * 100)}%`;\n zoomLabel.setAttribute(\"aria-label\", `Zoom, ${Math.round(zoom * 100)} percent. Activate to fit to width.`);\n }\n\n /** The page whose top is nearest the top of the scroll box. */\n function visiblePage(): number {\n const pages = stage.querySelectorAll<HTMLElement>(\"[data-page]\");\n if (!pages.length) return 1;\n const top = scroll.getBoundingClientRect().top;\n let best = 1;\n let bestDistance = Infinity;\n for (const el of pages) {\n const box = el.getBoundingClientRect();\n // Bottom-of-page beats top-of-page: a page still half on screen is the\n // one being read, not the one whose first line has just appeared.\n const distance = box.bottom < top ? Infinity : Math.abs(box.top - top);\n if (distance < bestDistance) {\n bestDistance = distance;\n best = Number(el.getAttribute(\"data-page\")) || 1;\n }\n }\n return best;\n }\n\n function syncBar(): void {\n const total = preview.result?.paged.totalPages ?? 0;\n count.textContent = total ? `/ ${total}` : \"\";\n if (doc.activeElement !== pageField) pageField.value = total ? String(page) : \"\";\n pageField.disabled = total < 2;\n prev.disabled = page <= 1;\n next.disabled = total === 0 || page >= total;\n }\n\n function setPage(next_: number, scrollTo: boolean): void {\n const total = preview.result?.paged.totalPages ?? 0;\n const wanted = total ? clamp(Math.round(next_), 1, total) : 1;\n if (scrollTo) {\n const el = stage.querySelector<HTMLElement>(`[data-page=\"${wanted}\"]`);\n // Measured from rectangles rather than `offsetTop`, for two reasons: the\n // stage is scaled, and a rectangle is already in scaled coordinates while\n // `offsetTop` is not; and `offsetParent` is whatever positioned ancestor\n // the *host* happens to have, which is not ours to assume.\n if (el) scroll.scrollTop += el.getBoundingClientRect().top - scroll.getBoundingClientRect().top;\n }\n if (wanted === page) return;\n page = wanted;\n syncBar();\n opts.onPageChange?.(page);\n }\n\n const onScroll = () => {\n const seen = visiblePage();\n if (seen === page) return;\n page = seen;\n syncBar();\n opts.onPageChange?.(page);\n };\n scroll.addEventListener(\"scroll\", onScroll, { passive: true });\n\n const onKey = (e: KeyboardEvent) => {\n if (e.target === pageField) return;\n if (e.key === \"PageDown\") setPage(page + 1, true);\n else if (e.key === \"PageUp\") setPage(page - 1, true);\n else if (e.key === \"Home\") setPage(1, true);\n else if (e.key === \"End\") setPage(preview.result?.paged.totalPages ?? 1, true);\n else return;\n e.preventDefault();\n };\n scroll.addEventListener(\"keydown\", onKey);\n\n prev.addEventListener(\"click\", () => setPage(page - 1, true));\n next.addEventListener(\"click\", () => setPage(page + 1, true));\n pageField.addEventListener(\"change\", () => setPage(Number(pageField.value), true));\n zoomLabel.addEventListener(\"click\", () => api.setZoom(\"width\"));\n zoomIn.addEventListener(\"click\", () => api.setZoom(clamp(zoom + 0.1, 0.1, 4)));\n zoomOut.addEventListener(\"click\", () => api.setZoom(clamp(zoom - 0.1, 0.1, 4)));\n printBtn.addEventListener(\"click\", () => api.print());\n\n // Fit is against the element, never the window: the viewer is embedded, so a\n // narrow pane in a wide page and a phone are the same problem and the window\n // answers neither. Same reasoning as the designer's own narrow layout.\n const observer = win && \"ResizeObserver\" in win ? new win.ResizeObserver(() => applyZoom()) : null;\n observer?.observe(scroll);\n\n function afterRender(result: LayoutResult): void {\n stopInteractions?.();\n stopInteractions = null;\n if (opts.interactions !== false) {\n stopInteractions = enableChartInteractions(stage, typeof opts.interactions === \"object\" ? opts.interactions : {});\n }\n applyZoom();\n setPage(page, false);\n syncBar();\n opts.onLayout?.(result);\n }\n\n const api: ReportViewer = {\n result: null,\n page: 1,\n pageCount: 0,\n zoom: 1,\n async update(input) {\n opts = { ...opts, ...input };\n const result = await preview.update({ ...input, mode: \"preview\", zoom: 1 });\n if (destroyed) return result;\n afterRender(result);\n return result;\n },\n goToPage(n) {\n setPage(n, true);\n },\n setZoom(value) {\n if (typeof value === \"number\") opts = { ...opts, zoom: value };\n else opts = { ...opts, fit: value, zoom: undefined };\n applyZoom();\n },\n print() {\n const result = preview.result;\n if (!result) return;\n if (opts.onPrint) opts.onPrint(result);\n else win?.print();\n },\n destroy() {\n destroyed = true;\n observer?.disconnect();\n scroll.removeEventListener(\"scroll\", onScroll);\n scroll.removeEventListener(\"keydown\", onKey);\n stopInteractions?.();\n preview.destroy();\n root.remove();\n }\n };\n\n // `result`, `page`, `pageCount` and `zoom` read through to live state rather\n // than being copies a caller could find stale one frame after a scroll.\n Object.defineProperties(api, {\n result: { get: () => preview.result, enumerable: true },\n page: { get: () => page, enumerable: true },\n pageCount: { get: () => preview.result?.paged.totalPages ?? 0, enumerable: true },\n zoom: { get: () => zoom, enumerable: true }\n });\n\n syncBar();\n void api.update({});\n return api;\n}\n"],"mappings":"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","names":["h","renderNode","p","total","h","h","ensureStyle","h"]}
package/package.json CHANGED
@@ -1,8 +1,28 @@
1
1
  {
2
2
  "name": "@broadpaper/renderer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "BroadPaper DOM renderer: measurement, page composition, preview and standalone HTML output.",
5
+ "keywords": [
6
+ "pdf",
7
+ "pdf-generation",
8
+ "report",
9
+ "reporting",
10
+ "report-designer",
11
+ "document-generation",
12
+ "broadpaper",
13
+ "pagination",
14
+ "preview",
15
+ "dom"
16
+ ],
5
17
  "license": "SEE LICENSE IN LICENSE",
18
+ "homepage": "https://www.broadpaper.com",
19
+ "bugs": {
20
+ "email": "support@broadpaper.com"
21
+ },
22
+ "author": {
23
+ "name": "Codingbadger Creative Limited",
24
+ "url": "https://www.broadpaper.com"
25
+ },
6
26
  "type": "module",
7
27
  "main": "./dist/index.js",
8
28
  "module": "./dist/index.js",
@@ -20,9 +40,9 @@
20
40
  ],
21
41
  "sideEffects": false,
22
42
  "dependencies": {
23
- "@broadpaper/license": "0.2.0",
24
- "@broadpaper/core": "0.2.0",
25
- "@broadpaper/blocks": "0.2.0"
43
+ "@broadpaper/blocks": "0.3.0",
44
+ "@broadpaper/core": "0.3.0",
45
+ "@broadpaper/license": "0.3.0"
26
46
  },
27
47
  "scripts": {
28
48
  "build": "tsup src/index.ts --format esm --dts --sourcemap --clean --external @broadpaper/core --external @broadpaper/blocks --external @broadpaper/license",