pdfnative-react
Advanced tools
+991
-4
| 'use strict'; | ||
| // src/index.ts | ||
| var version = "0.1.0"; | ||
| var ready = false; | ||
| var react = require('react'); | ||
| var pdfnative = require('pdfnative'); | ||
| var ReactReconciler = require('react-reconciler'); | ||
| var constants_js = require('react-reconciler/constants.js'); | ||
| exports.ready = ready; | ||
| function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } | ||
| var ReactReconciler__default = /*#__PURE__*/_interopDefault(ReactReconciler); | ||
| // src/components.tsx | ||
| var h = react.createElement; | ||
| function Document(props) { | ||
| const { children, ...rest } = props; | ||
| return h("document", rest, children); | ||
| } | ||
| function Page(props) { | ||
| return h("page", null, props.children); | ||
| } | ||
| function Heading(props) { | ||
| const { children, ...rest } = props; | ||
| return h("heading", rest, children); | ||
| } | ||
| function Paragraph(props) { | ||
| const { children, ...rest } = props; | ||
| return h("paragraph", rest, children); | ||
| } | ||
| var Text = Paragraph; | ||
| function List(props) { | ||
| const { children, ...rest } = props; | ||
| return h("list", rest, children); | ||
| } | ||
| function Item(props) { | ||
| return h("item", null, props.children); | ||
| } | ||
| function Table(props) { | ||
| const { children, ...rest } = props; | ||
| return h("table", rest, children); | ||
| } | ||
| function Row(props) { | ||
| const { children, ...rest } = props; | ||
| return h("row", rest, children); | ||
| } | ||
| function Cell(props) { | ||
| return h("cell", null, props.children); | ||
| } | ||
| function Image(props) { | ||
| return h("image", { ...props }); | ||
| } | ||
| function Link(props) { | ||
| const { children, ...rest } = props; | ||
| return h("link", rest, children); | ||
| } | ||
| function Spacer(props = {}) { | ||
| return h("spacer", { ...props }); | ||
| } | ||
| function PageBreak() { | ||
| return h("pageBreak", null); | ||
| } | ||
| function TableOfContents(props = {}) { | ||
| return h("toc", { ...props }); | ||
| } | ||
| var Toc = TableOfContents; | ||
| function Barcode(props) { | ||
| return h("barcode", { ...props }); | ||
| } | ||
| function Svg(props) { | ||
| return h("svg", { ...props }); | ||
| } | ||
| function FormField(props) { | ||
| return h("formField", { ...props }); | ||
| } | ||
| // src/reconciler/nodes.ts | ||
| function createElementNode(tag, props) { | ||
| return { kind: "element", tag, props, children: [] }; | ||
| } | ||
| function createTextNode(text) { | ||
| return { kind: "text", text }; | ||
| } | ||
| function isElementNode(node) { | ||
| return node.kind === "element"; | ||
| } | ||
| // src/reconciler/host-config.ts | ||
| var HOST_CONTEXT = Object.freeze({}); | ||
| function appendChild(parent, child) { | ||
| parent.children.push(child); | ||
| } | ||
| function removeChild(parent, child) { | ||
| const index = parent.children.indexOf(child); | ||
| if (index !== -1) parent.children.splice(index, 1); | ||
| } | ||
| function insertBefore(parent, child, before) { | ||
| const index = parent.children.indexOf(before); | ||
| parent.children.splice(index === -1 ? parent.children.length : index, 0, child); | ||
| } | ||
| var currentUpdatePriority = constants_js.DefaultEventPriority; | ||
| var hostConfig = { | ||
| supportsMutation: true, | ||
| supportsPersistence: false, | ||
| supportsHydration: false, | ||
| isPrimaryRenderer: false, | ||
| noTimeout: -1, | ||
| createInstance(type, props) { | ||
| return createElementNode(type, props); | ||
| }, | ||
| createTextInstance(text) { | ||
| return createTextNode(text); | ||
| }, | ||
| appendInitialChild(parent, child) { | ||
| appendChild(parent, child); | ||
| }, | ||
| appendChild, | ||
| appendChildToContainer(container, child) { | ||
| appendChild(container, child); | ||
| }, | ||
| insertBefore, | ||
| insertInContainerBefore(container, child, before) { | ||
| insertBefore(container, child, before); | ||
| }, | ||
| removeChild, | ||
| removeChildFromContainer(container, child) { | ||
| removeChild(container, child); | ||
| }, | ||
| finalizeInitialChildren() { | ||
| return false; | ||
| }, | ||
| commitUpdate(instance, _type, _prevProps, nextProps) { | ||
| instance.props = nextProps; | ||
| }, | ||
| commitTextUpdate(textInstance, _oldText, newText) { | ||
| textInstance.text = newText; | ||
| }, | ||
| shouldSetTextContent() { | ||
| return false; | ||
| }, | ||
| clearContainer(container) { | ||
| container.children.length = 0; | ||
| }, | ||
| getRootHostContext() { | ||
| return HOST_CONTEXT; | ||
| }, | ||
| getChildHostContext() { | ||
| return HOST_CONTEXT; | ||
| }, | ||
| getPublicInstance(instance) { | ||
| return instance; | ||
| }, | ||
| prepareForCommit() { | ||
| return null; | ||
| }, | ||
| resetAfterCommit() { | ||
| }, | ||
| preparePortalMount() { | ||
| }, | ||
| scheduleTimeout: setTimeout, | ||
| cancelTimeout: clearTimeout, | ||
| supportsMicrotasks: true, | ||
| scheduleMicrotask: typeof queueMicrotask === "function" ? queueMicrotask : (cb) => void Promise.resolve().then(cb), | ||
| getInstanceFromNode() { | ||
| return null; | ||
| }, | ||
| beforeActiveInstanceBlur() { | ||
| }, | ||
| afterActiveInstanceBlur() { | ||
| }, | ||
| prepareScopeUpdate() { | ||
| }, | ||
| getInstanceFromScope() { | ||
| return null; | ||
| }, | ||
| detachDeletedInstance() { | ||
| }, | ||
| // ── Transition / priority surface (react-reconciler 0.31+) ────────────── | ||
| NotPendingTransition: null, | ||
| HostTransitionContext: react.createContext( | ||
| null | ||
| ), | ||
| setCurrentUpdatePriority(newPriority) { | ||
| currentUpdatePriority = newPriority; | ||
| }, | ||
| getCurrentUpdatePriority() { | ||
| return currentUpdatePriority; | ||
| }, | ||
| resolveUpdatePriority() { | ||
| return currentUpdatePriority || constants_js.DefaultEventPriority; | ||
| }, | ||
| resetFormInstance() { | ||
| }, | ||
| requestPostPaintCallback() { | ||
| }, | ||
| shouldAttemptEagerTransition() { | ||
| return false; | ||
| }, | ||
| trackSchedulerEvent() { | ||
| }, | ||
| resolveEventType() { | ||
| return null; | ||
| }, | ||
| resolveEventTimeStamp() { | ||
| return -1.1; | ||
| }, | ||
| // ── Suspense-on-commit surface (unused: we never suspend) ─────────────── | ||
| maySuspendCommit() { | ||
| return false; | ||
| }, | ||
| preloadInstance() { | ||
| return true; | ||
| }, | ||
| startSuspendingCommit() { | ||
| }, | ||
| suspendInstance() { | ||
| }, | ||
| waitForCommitToBeReady() { | ||
| return null; | ||
| } | ||
| }; | ||
| var reconciler = ReactReconciler__default.default(hostConfig); | ||
| // src/reconciler/serialize.ts | ||
| var PdfStructureError = class extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "PdfStructureError"; | ||
| } | ||
| }; | ||
| function collectText(node) { | ||
| if (!isElementNode(node)) return node.text; | ||
| let out = ""; | ||
| for (const child of node.children) out += collectText(child); | ||
| return out; | ||
| } | ||
| function elementText(node) { | ||
| if (typeof node.props.text === "string") return node.props.text; | ||
| return node.children.map(collectText).join(""); | ||
| } | ||
| function elementChildren(node) { | ||
| return node.children.filter(isElementNode); | ||
| } | ||
| function compact(obj) { | ||
| const out = {}; | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| if (value !== void 0) out[key] = value; | ||
| } | ||
| return out; | ||
| } | ||
| function toBlock(node) { | ||
| const p = node.props; | ||
| switch (node.tag) { | ||
| case "heading": | ||
| return compact({ | ||
| type: "heading", | ||
| text: elementText(node), | ||
| level: p.level ?? 1, | ||
| color: p.color | ||
| }); | ||
| case "paragraph": | ||
| return compact({ | ||
| type: "paragraph", | ||
| text: elementText(node), | ||
| fontSize: p.fontSize, | ||
| lineHeight: p.lineHeight, | ||
| align: p.align, | ||
| indent: p.indent, | ||
| color: p.color | ||
| }); | ||
| case "list": | ||
| return compact({ | ||
| type: "list", | ||
| items: p.items ?? elementChildren(node).filter((c) => c.tag === "item").map(elementText), | ||
| style: p.ordered ? "numbered" : p.style ?? "bullet", | ||
| fontSize: p.fontSize | ||
| }); | ||
| case "table": | ||
| return toTableBlock(node); | ||
| case "image": | ||
| return compact({ | ||
| type: "image", | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| alt: p.alt | ||
| }); | ||
| case "link": | ||
| return compact({ | ||
| type: "link", | ||
| text: elementText(node), | ||
| url: p.url ?? p.href, | ||
| fontSize: p.fontSize, | ||
| color: p.color | ||
| }); | ||
| case "spacer": | ||
| return { type: "spacer", height: p.height ?? 12 }; | ||
| case "pageBreak": | ||
| return { type: "pageBreak" }; | ||
| case "toc": | ||
| return compact({ | ||
| type: "toc", | ||
| title: p.title, | ||
| maxLevel: p.maxLevel, | ||
| fontSize: p.fontSize, | ||
| indent: p.indent | ||
| }); | ||
| case "barcode": | ||
| return compact({ | ||
| type: "barcode", | ||
| format: p.format, | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| ecLevel: p.ecLevel, | ||
| pdf417ECLevel: p.pdf417ECLevel | ||
| }); | ||
| case "svg": | ||
| return compact({ | ||
| type: "svg", | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| viewBox: p.viewBox, | ||
| fill: p.fill, | ||
| stroke: p.stroke, | ||
| strokeWidth: p.strokeWidth, | ||
| alt: p.alt | ||
| }); | ||
| case "formField": | ||
| return compact({ | ||
| type: "formField", | ||
| fieldType: p.fieldType ?? p.type, | ||
| name: p.name, | ||
| label: p.label, | ||
| value: p.value, | ||
| placeholder: p.placeholder, | ||
| width: p.width, | ||
| height: p.height, | ||
| fontSize: p.fontSize, | ||
| options: p.options, | ||
| readOnly: p.readOnly, | ||
| required: p.required, | ||
| maxLength: p.maxLength, | ||
| checked: p.checked | ||
| }); | ||
| case "page": | ||
| return blocksFrom(node.children); | ||
| default: | ||
| throw new PdfStructureError( | ||
| `<${node.tag}> is not valid here. Expected a block-level component inside <Document> or <Page>.` | ||
| ); | ||
| } | ||
| } | ||
| function toTableBlock(node) { | ||
| const p = node.props; | ||
| const rowNodes = elementChildren(node).filter((c) => c.tag === "row"); | ||
| let headers = p.headers; | ||
| let bodyRows = rowNodes; | ||
| if (!headers && rowNodes.length > 0) { | ||
| const headerRow = rowNodes.find((r) => r.props.header === true) ?? rowNodes[0]; | ||
| headers = elementChildren(headerRow).filter((c) => c.tag === "cell").map(elementText); | ||
| bodyRows = rowNodes.filter((r) => r !== headerRow); | ||
| } | ||
| const rows = p.rows ?? bodyRows.map((r) => ({ | ||
| cells: elementChildren(r).filter((c) => c.tag === "cell").map(elementText), | ||
| type: r.props.variant ?? "default", | ||
| pointed: r.props.pointed ?? false | ||
| })); | ||
| return compact({ | ||
| type: "table", | ||
| headers: headers ?? [], | ||
| rows, | ||
| columns: p.columns, | ||
| clipCells: p.clipCells, | ||
| autoFitColumns: p.autoFitColumns, | ||
| wrap: p.wrap, | ||
| repeatHeader: p.repeatHeader, | ||
| zebra: p.zebra, | ||
| caption: p.caption, | ||
| minRowHeight: p.minRowHeight, | ||
| cellPadding: p.cellPadding | ||
| }); | ||
| } | ||
| function blocksFrom(children) { | ||
| const blocks = []; | ||
| let pageIndex = 0; | ||
| for (const child of children) { | ||
| if (!isElementNode(child)) continue; | ||
| if (child.tag === "page") { | ||
| if (pageIndex > 0) blocks.push({ type: "pageBreak" }); | ||
| pageIndex += 1; | ||
| } | ||
| const result = toBlock(child); | ||
| if (Array.isArray(result)) blocks.push(...result); | ||
| else blocks.push(result); | ||
| } | ||
| return blocks; | ||
| } | ||
| function findDocument(container) { | ||
| const elements = container.children.filter(isElementNode); | ||
| const doc = elements.find((c) => c.tag === "document"); | ||
| if (doc) return doc; | ||
| if (elements.length === 1 && elements[0].tag !== "document") { | ||
| throw new PdfStructureError( | ||
| "The root component must be <Document>. Wrap your content in <Document>\u2026</Document>." | ||
| ); | ||
| } | ||
| throw new PdfStructureError("No <Document> found at the root of the tree."); | ||
| } | ||
| function serialize(container) { | ||
| const doc = findDocument(container); | ||
| const p = doc.props; | ||
| const params = compact({ | ||
| title: p.title, | ||
| blocks: blocksFrom(doc.children), | ||
| footerText: p.footerText, | ||
| fontEntries: p.fontEntries, | ||
| metadata: p.metadata, | ||
| layout: p.layout | ||
| }); | ||
| return params; | ||
| } | ||
| // src/reconciler/render.ts | ||
| var LegacyRoot = 0; | ||
| function noop() { | ||
| } | ||
| function onError(error) { | ||
| throw error instanceof Error ? error : new Error(String(error)); | ||
| } | ||
| function compile(element) { | ||
| const container = { children: [] }; | ||
| const root = reconciler.createContainer( | ||
| container, | ||
| LegacyRoot, | ||
| null, | ||
| false, | ||
| null, | ||
| "pdfnative", | ||
| onError, | ||
| onError, | ||
| onError, | ||
| noop, | ||
| null | ||
| ); | ||
| const r = reconciler; | ||
| const commit = (next) => { | ||
| if (typeof r.updateContainerSync === "function") { | ||
| r.updateContainerSync(next, root, null, noop); | ||
| r.flushSyncWork?.(); | ||
| } else { | ||
| reconciler.flushSync(() => { | ||
| reconciler.updateContainer(next, root, null, noop); | ||
| }); | ||
| } | ||
| }; | ||
| commit(element); | ||
| const params = serialize(container); | ||
| commit(null); | ||
| return params; | ||
| } | ||
| // src/render.ts | ||
| function compileDocument(node) { | ||
| return compile(node); | ||
| } | ||
| function prepare(node, options) { | ||
| const compiled = compile(node); | ||
| const params = options?.fontEntries && options.fontEntries.length > 0 ? { | ||
| ...compiled, | ||
| fontEntries: [...compiled.fontEntries ?? [], ...options.fontEntries] | ||
| } : compiled; | ||
| const layout = options?.layout || params.layout ? { ...params.layout, ...options?.layout } : void 0; | ||
| return { params, layout }; | ||
| } | ||
| function renderToBytes(node, options) { | ||
| const { params, layout } = prepare(node, options); | ||
| return pdfnative.buildDocumentPDFBytes(params, layout); | ||
| } | ||
| function renderToBlob(node, options) { | ||
| const bytes = renderToBytes(node, options); | ||
| return new Blob([bytes], { type: "application/pdf" }); | ||
| } | ||
| function renderToStream(node, options) { | ||
| const { params, layout } = prepare(node, options); | ||
| return pdfnative.buildDocumentPDFStreamTrue(params, layout); | ||
| } | ||
| async function renderToFile(node, path, options) { | ||
| const bytes = renderToBytes(node, options); | ||
| const { writeFile } = await import('fs/promises'); | ||
| await writeFile(path, bytes); | ||
| } | ||
| var INITIAL = { | ||
| url: null, | ||
| blob: null, | ||
| bytes: null, | ||
| loading: true, | ||
| error: null | ||
| }; | ||
| function usePdf(element, options) { | ||
| const [state, setState] = react.useState(INITIAL); | ||
| const [nonce, setNonce] = react.useState(0); | ||
| const optionsRef = react.useRef(options); | ||
| optionsRef.current = options; | ||
| const update = react.useCallback(() => { | ||
| setNonce((n) => n + 1); | ||
| }, []); | ||
| react.useEffect(() => { | ||
| let cancelled = false; | ||
| let url = null; | ||
| setState((prev) => prev.loading ? prev : { ...prev, loading: true }); | ||
| queueMicrotask(() => { | ||
| if (cancelled) return; | ||
| try { | ||
| const bytes = renderToBytes(element, optionsRef.current); | ||
| const blob = new Blob([bytes], { type: "application/pdf" }); | ||
| url = URL.createObjectURL(blob); | ||
| setState({ url, blob, bytes, loading: false, error: null }); | ||
| } catch (err) { | ||
| setState({ | ||
| url: null, | ||
| blob: null, | ||
| bytes: null, | ||
| loading: false, | ||
| error: err instanceof Error ? err : new Error(String(err)) | ||
| }); | ||
| } | ||
| }); | ||
| return () => { | ||
| cancelled = true; | ||
| if (url) URL.revokeObjectURL(url); | ||
| }; | ||
| }, [element, nonce]); | ||
| return { ...state, update }; | ||
| } | ||
| function usePdfStream(element, options) { | ||
| const elementRef = react.useRef(element); | ||
| elementRef.current = element; | ||
| const optionsRef = react.useRef(options); | ||
| optionsRef.current = options; | ||
| const getStream = react.useCallback( | ||
| () => renderToStream(elementRef.current, optionsRef.current), | ||
| [] | ||
| ); | ||
| return { getStream }; | ||
| } | ||
| function toState(result) { | ||
| return { | ||
| url: result.url, | ||
| blob: result.blob, | ||
| bytes: result.bytes, | ||
| loading: result.loading, | ||
| error: result.error | ||
| }; | ||
| } | ||
| function PDFViewer(props) { | ||
| const { document, options, title = "PDF preview", ...rest } = props; | ||
| const { url } = usePdf(document, options); | ||
| return react.createElement("iframe", { | ||
| ...rest, | ||
| title, | ||
| src: url ?? void 0 | ||
| }); | ||
| } | ||
| function BlobProvider(props) { | ||
| const result = usePdf(props.document, props.options); | ||
| return react.createElement( | ||
| "div", | ||
| { style: { display: "contents" } }, | ||
| props.children(toState(result)) | ||
| ); | ||
| } | ||
| function PDFDownloadLink(props) { | ||
| const { document, fileName = "document.pdf", options, children, ...rest } = props; | ||
| const result = usePdf(document, options); | ||
| const content = typeof children === "function" ? children(toState(result)) : children; | ||
| return react.createElement( | ||
| "a", | ||
| { | ||
| ...rest, | ||
| href: result.url ?? void 0, | ||
| download: fileName | ||
| }, | ||
| content | ||
| ); | ||
| } | ||
| function toRows(rows) { | ||
| return rows.map( | ||
| (row) => Array.isArray(row) ? { cells: row, type: "default", pointed: false } : row | ||
| ); | ||
| } | ||
| function blockToElement(block, key) { | ||
| switch (block[0]) { | ||
| case "h1": | ||
| return react.createElement(Heading, { key, level: 1, ...block[2] }, block[1]); | ||
| case "h2": | ||
| return react.createElement(Heading, { key, level: 2, ...block[2] }, block[1]); | ||
| case "h3": | ||
| return react.createElement(Heading, { key, level: 3, ...block[2] }, block[1]); | ||
| case "p": | ||
| return react.createElement(Paragraph, { key, ...block[2] }, block[1]); | ||
| case "ul": | ||
| return react.createElement(List, { key, items: block[1], ...block[2] }); | ||
| case "ol": | ||
| return react.createElement(List, { key, ordered: true, items: block[1], ...block[2] }); | ||
| case "table": | ||
| return react.createElement(Table, { | ||
| key, | ||
| headers: block[1].h, | ||
| rows: toRows(block[1].r), | ||
| columns: block[1].columns, | ||
| zebra: block[1].zebra, | ||
| caption: block[1].caption, | ||
| clipCells: block[1].clipCells, | ||
| autoFitColumns: block[1].autoFitColumns, | ||
| wrap: block[1].wrap, | ||
| repeatHeader: block[1].repeatHeader, | ||
| minRowHeight: block[1].minRowHeight, | ||
| cellPadding: block[1].cellPadding | ||
| }); | ||
| case "img": | ||
| return react.createElement(Image, { key, ...block[1] }); | ||
| case "link": | ||
| return react.createElement(Link, { key, ...block[2] }, block[1]); | ||
| case "sp": | ||
| return react.createElement(Spacer, { key, height: block[1] }); | ||
| case "br": | ||
| return react.createElement(PageBreak, { key }); | ||
| case "page": | ||
| return react.createElement(Page, { key }, block[1].map(blockToElement)); | ||
| case "toc": | ||
| return react.createElement(TableOfContents, { key, ...block[1] }); | ||
| case "qr": | ||
| case "code128": | ||
| case "ean13": | ||
| case "pdf417": | ||
| case "datamatrix": | ||
| return react.createElement(Barcode, { | ||
| key, | ||
| format: block[0], | ||
| data: block[1], | ||
| ...block[2] | ||
| }); | ||
| case "svg": | ||
| return react.createElement(Svg, { key, data: block[1], ...block[2] }); | ||
| case "field": | ||
| return react.createElement(FormField, { key, ...block[1] }); | ||
| default: { | ||
| const kind = block[0]; | ||
| throw new PdfStructureError(`Unknown DocSpec block kind: ${String(kind)}`); | ||
| } | ||
| } | ||
| } | ||
| function specToElement(spec) { | ||
| const children = spec.blocks.map(blockToElement); | ||
| return react.createElement( | ||
| Document, | ||
| { | ||
| title: spec.title, | ||
| footerText: spec.footerText, | ||
| metadata: spec.metadata, | ||
| fontEntries: spec.fontEntries, | ||
| layout: spec.layout | ||
| }, | ||
| children | ||
| ); | ||
| } | ||
| function compileSpec(spec) { | ||
| return compileDocument(specToElement(spec)); | ||
| } | ||
| function renderSpecToBytes(spec, options) { | ||
| return renderToBytes(specToElement(spec), options); | ||
| } | ||
| function renderSpecToBlob(spec, options) { | ||
| return renderToBlob(specToElement(spec), options); | ||
| } | ||
| function renderSpecToStream(spec, options) { | ||
| return renderToStream(specToElement(spec), options); | ||
| } | ||
| function renderSpecToFile(spec, path, options) { | ||
| return renderToFile(specToElement(spec), path, options); | ||
| } | ||
| // src/version.ts | ||
| var version = "0.2.0"; | ||
| // src/spec/schema.ts | ||
| var DRAFT = "https://json-schema.org/draft/2020-12/schema"; | ||
| var ID_BASE = "https://pdfnative.dev/schema/react"; | ||
| function schemaId() { | ||
| return `${ID_BASE}/${version}/doc-spec.schema.json`; | ||
| } | ||
| function headingBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "HeadingSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["h1", "h2", "h3"] }, | ||
| { type: "string", description: "Heading text." }, | ||
| { type: "object", description: "Optional { color }." } | ||
| ] | ||
| }; | ||
| } | ||
| function paragraphBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ParagraphSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "p" }, | ||
| { type: "string", description: "Paragraph text." }, | ||
| { type: "object", description: "Optional { fontSize, lineHeight, align, indent, color }." } | ||
| ] | ||
| }; | ||
| } | ||
| function listBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ListSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["ul", "ol"] }, | ||
| { type: "array", items: { type: "string" }, description: "List items." }, | ||
| { type: "object", description: "Optional { fontSize }." } | ||
| ] | ||
| }; | ||
| } | ||
| function tableBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "TableSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "table" }, | ||
| { | ||
| type: "object", | ||
| required: ["r"], | ||
| properties: { | ||
| h: { type: "array", items: { type: "string" }, description: "Column headers." }, | ||
| r: { | ||
| type: "array", | ||
| description: "Rows: arrays of cell strings, or full PdfRow objects.", | ||
| items: { | ||
| oneOf: [ | ||
| { type: "array", items: { type: "string" } }, | ||
| { | ||
| type: "object", | ||
| required: ["cells", "type", "pointed"], | ||
| properties: { | ||
| cells: { type: "array", items: { type: "string" } }, | ||
| type: { type: "string" }, | ||
| pointed: { type: "boolean" } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| }, | ||
| zebra: { type: ["boolean", "string", "array"] }, | ||
| caption: { type: "string" } | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function imageBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ImageSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "img" }, | ||
| { | ||
| type: "object", | ||
| required: ["data"], | ||
| description: "Image body: { data: Uint8Array, width?, height?, align?, alt? }." | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function linkBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "LinkSpec", | ||
| minItems: 3, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "link" }, | ||
| { type: "string", description: "Link text." }, | ||
| { | ||
| type: "object", | ||
| description: "Options; one of url/href is required.", | ||
| properties: { | ||
| url: { type: "string" }, | ||
| href: { type: "string" } | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function spacerBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "SpacerSpec", | ||
| minItems: 1, | ||
| maxItems: 2, | ||
| prefixItems: [{ const: "sp" }, { type: "number", description: "Height in points." }] | ||
| }; | ||
| } | ||
| function pageBreakBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "PageBreakSpec", | ||
| minItems: 1, | ||
| maxItems: 1, | ||
| prefixItems: [{ const: "br" }] | ||
| }; | ||
| } | ||
| function pageBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "PageSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "page" }, | ||
| { type: "array", description: "Nested blocks for this page.", items: { $ref: "#/$defs/block" } } | ||
| ] | ||
| }; | ||
| } | ||
| function tocBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "TocSpec", | ||
| minItems: 1, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "toc" }, | ||
| { type: "object", description: "Optional { title, maxLevel, fontSize, indent }." } | ||
| ] | ||
| }; | ||
| } | ||
| function barcodeBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "BarcodeSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["qr", "code128", "ean13", "pdf417", "datamatrix"] }, | ||
| { type: "string", description: "Data to encode." }, | ||
| { type: "object", description: "Optional { width, height, align, ecLevel, pdf417ECLevel }." } | ||
| ] | ||
| }; | ||
| } | ||
| function svgBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "SvgSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "svg" }, | ||
| { type: "string", description: "SVG path data or inline markup." }, | ||
| { type: "object", description: "Optional { width, height, align, viewBox, fill, stroke, strokeWidth }." } | ||
| ] | ||
| }; | ||
| } | ||
| function fieldBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "FormFieldSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "field" }, | ||
| { | ||
| type: "object", | ||
| required: ["fieldType", "name"], | ||
| description: "Form-field body: { fieldType, name, label?, value?, options?, \u2026 }." | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function docSpecSchema() { | ||
| return { | ||
| $schema: DRAFT, | ||
| $id: schemaId(), | ||
| title: "pdfnative-react DocSpec", | ||
| description: "Compact, token-frugal document specification. Compiles to the same pdfnative model as the JSX components.", | ||
| type: "object", | ||
| required: ["blocks"], | ||
| properties: { | ||
| title: { type: "string" }, | ||
| footerText: { type: "string" }, | ||
| metadata: { type: "object", description: "DocumentMetadata." }, | ||
| fontEntries: { type: "array", items: { type: "object" } }, | ||
| layout: { type: "object", description: "PdfLayoutOptions overrides." }, | ||
| blocks: { | ||
| type: "array", | ||
| description: "Ordered document blocks (positional tuples).", | ||
| items: { $ref: "#/$defs/block" } | ||
| } | ||
| }, | ||
| $defs: { | ||
| block: { | ||
| oneOf: [ | ||
| headingBlock(), | ||
| paragraphBlock(), | ||
| listBlock(), | ||
| tableBlock(), | ||
| imageBlock(), | ||
| linkBlock(), | ||
| spacerBlock(), | ||
| pageBreakBlock(), | ||
| pageBlock(), | ||
| tocBlock(), | ||
| barcodeBlock(), | ||
| svgBlock(), | ||
| fieldBlock() | ||
| ] | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| function docSpecSchemaId() { | ||
| return schemaId(); | ||
| } | ||
| Object.defineProperty(exports, "downloadBlob", { | ||
| enumerable: true, | ||
| get: function () { return pdfnative.downloadBlob; } | ||
| }); | ||
| Object.defineProperty(exports, "initNodeCompression", { | ||
| enumerable: true, | ||
| get: function () { return pdfnative.initNodeCompression; } | ||
| }); | ||
| Object.defineProperty(exports, "loadFontData", { | ||
| enumerable: true, | ||
| get: function () { return pdfnative.loadFontData; } | ||
| }); | ||
| Object.defineProperty(exports, "registerFont", { | ||
| enumerable: true, | ||
| get: function () { return pdfnative.registerFont; } | ||
| }); | ||
| Object.defineProperty(exports, "registerFonts", { | ||
| enumerable: true, | ||
| get: function () { return pdfnative.registerFonts; } | ||
| }); | ||
| exports.Barcode = Barcode; | ||
| exports.BlobProvider = BlobProvider; | ||
| exports.Cell = Cell; | ||
| exports.Document = Document; | ||
| exports.FormField = FormField; | ||
| exports.Heading = Heading; | ||
| exports.Image = Image; | ||
| exports.Item = Item; | ||
| exports.Link = Link; | ||
| exports.List = List; | ||
| exports.PDFDownloadLink = PDFDownloadLink; | ||
| exports.PDFViewer = PDFViewer; | ||
| exports.Page = Page; | ||
| exports.PageBreak = PageBreak; | ||
| exports.Paragraph = Paragraph; | ||
| exports.PdfStructureError = PdfStructureError; | ||
| exports.Row = Row; | ||
| exports.Spacer = Spacer; | ||
| exports.Svg = Svg; | ||
| exports.Table = Table; | ||
| exports.TableOfContents = TableOfContents; | ||
| exports.Text = Text; | ||
| exports.Toc = Toc; | ||
| exports.compileDocument = compileDocument; | ||
| exports.compileSpec = compileSpec; | ||
| exports.docSpecSchema = docSpecSchema; | ||
| exports.docSpecSchemaId = docSpecSchemaId; | ||
| exports.renderSpecToBlob = renderSpecToBlob; | ||
| exports.renderSpecToBytes = renderSpecToBytes; | ||
| exports.renderSpecToFile = renderSpecToFile; | ||
| exports.renderSpecToStream = renderSpecToStream; | ||
| exports.renderToBlob = renderToBlob; | ||
| exports.renderToBytes = renderToBytes; | ||
| exports.renderToFile = renderToFile; | ||
| exports.renderToStream = renderToStream; | ||
| exports.specToElement = specToElement; | ||
| exports.usePdf = usePdf; | ||
| exports.usePdfStream = usePdfStream; | ||
| exports.version = version; | ||
| //# sourceMappingURL=index.cjs.map | ||
| //# sourceMappingURL=index.cjs.map |
+633
-11
@@ -0,18 +1,640 @@ | ||
| import { ReactElement, ReactNode, CSSProperties } from 'react'; | ||
| import { PdfLayoutOptions, FontEntry, DocumentParams, BarcodeFormat, QRErrorLevel, PdfColor, SvgRenderOptions, FormFieldType, DocumentMetadata, PdfRow, ColumnDef } from 'pdfnative'; | ||
| export { BarcodeFormat, ColumnDef, DocumentBlock, DocumentMetadata, DocumentParams, FontEntry, FormFieldType, PdfColor, PdfLayoutOptions, PdfRow, QRErrorLevel, SvgRenderOptions, downloadBlob, initNodeCompression, loadFontData, registerFont, registerFonts } from 'pdfnative'; | ||
| /** | ||
| * pdfnative-react — React renderer for the pdfnative PDF engine. | ||
| * Public type definitions for pdfnative-react. | ||
| * | ||
| * 0.1.0 is a placeholder release that reserves the package name while the | ||
| * first implemented version (0.2.0) is prepared. The public API below is | ||
| * intentionally minimal; do not depend on it yet. | ||
| * These types mirror the `pdfnative` document model (a declarative *block flow*, | ||
| * not an HTML/CSS box model) while presenting an ergonomic, JSX-friendly surface. | ||
| * | ||
| * Track progress: https://github.com/Nizoka/pdfnative-react | ||
| * @packageDocumentation | ||
| */ | ||
| /** Current package version. */ | ||
| declare const version = "0.1.0"; | ||
| /** Horizontal alignment shared by several blocks. */ | ||
| type Align = 'left' | 'center' | 'right'; | ||
| /** A color accepted by the pdfnative engine: hex, RGB tuple, or PDF operator string. */ | ||
| type Color = string | readonly [number, number, number]; | ||
| /** | ||
| * Whether the declarative React renderer is available in this build. | ||
| * `false` in 0.1.0 (name-reservation placeholder); `true` from 0.2.0 onward. | ||
| * Options accepted by every render entry point. Forwarded to the underlying | ||
| * `pdfnative` builder. | ||
| */ | ||
| declare const ready = false; | ||
| interface RenderOptions { | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode, encryption…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| /** Pre-loaded font entries for non-Latin scripts (see `pdfnative` `loadFontData`). */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| } | ||
| /** | ||
| * The intermediate representation produced by reconciling a React tree. | ||
| * This is exactly the object passed to `pdfnative`'s `buildDocumentPDFBytes`. | ||
| */ | ||
| type CompiledDocument = DocumentParams; | ||
| export { ready, version }; | ||
| /** | ||
| * Public component layer for pdfnative-react. | ||
| * | ||
| * Every component is a thin, side-effect-free factory that emits a *host element* | ||
| * (a lowercase tag understood by the reconciler). Components carry no rendering | ||
| * logic themselves: the tree they form is reconciled and then serialized into a | ||
| * `pdfnative` `DocumentParams` object. | ||
| * | ||
| * The API intentionally mirrors familiar React-PDF ergonomics (`<Document>`, | ||
| * `<Page>`, `<Text>`, `<Image>`, `<Link>`) while mapping onto pdfnative's | ||
| * declarative *block flow* model. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Props for {@link Document}, the required root of every tree. */ | ||
| interface DocumentProps { | ||
| /** Document title (PDF metadata + optional cover title). */ | ||
| readonly title?: string; | ||
| /** Footer text repeated on every page. */ | ||
| readonly footerText?: string; | ||
| /** PDF metadata (author, subject, keywords). */ | ||
| readonly metadata?: DocumentMetadata; | ||
| /** Pre-loaded font entries for non-Latin scripts. */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** The root component. Exactly one `<Document>` must wrap the whole tree. */ | ||
| declare function Document(props: DocumentProps): ReactElement; | ||
| /** Props for {@link Page}. */ | ||
| interface PageProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** | ||
| * An explicit page boundary. Pages are optional: content flows and paginates | ||
| * automatically. Use `<Page>` to force grouped content onto a fresh page. | ||
| */ | ||
| declare function Page(props: PageProps): ReactElement; | ||
| /** Props for {@link Heading}. */ | ||
| interface HeadingProps { | ||
| /** Heading level (1–3). Default: `1`. */ | ||
| readonly level?: 1 | 2 | 3; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Heading text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A section heading. Headings feed the auto-generated `<TableOfContents>`. */ | ||
| declare function Heading(props: HeadingProps): ReactElement; | ||
| /** Props for {@link Paragraph} / {@link Text}. */ | ||
| interface ParagraphProps { | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Line height multiplier. */ | ||
| readonly lineHeight?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** First-line indent in points. */ | ||
| readonly indent?: number; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Paragraph text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A wrapping paragraph of body text. */ | ||
| declare function Paragraph(props: ParagraphProps): ReactElement; | ||
| /** Alias of {@link Paragraph} for React-PDF familiarity. */ | ||
| declare const Text: typeof Paragraph; | ||
| /** Props for {@link List}. */ | ||
| interface ListProps { | ||
| /** Render as a numbered list instead of bullets. */ | ||
| readonly ordered?: boolean; | ||
| /** Explicit list style. Overrides `ordered` when set. */ | ||
| readonly style?: 'bullet' | 'numbered'; | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Items as plain strings (alternatively provide `<Item>` children). */ | ||
| readonly items?: readonly string[]; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A bullet or numbered list. */ | ||
| declare function List(props: ListProps): ReactElement; | ||
| /** Props for {@link Item}. */ | ||
| interface ItemProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A single list item. Use inside `<List>`. */ | ||
| declare function Item(props: ItemProps): ReactElement; | ||
| /** Props for {@link Table}. */ | ||
| interface TableProps { | ||
| /** Column headers. Omit to derive from the first `<Row header>`. */ | ||
| readonly headers?: readonly string[]; | ||
| /** Rows as data (alternatively provide `<Row>`/`<Cell>` children). */ | ||
| readonly rows?: readonly PdfRow[]; | ||
| /** Explicit column definitions (widths, alignment). */ | ||
| readonly columns?: readonly ColumnDef[]; | ||
| /** Clip cell contents to column bounds. Default: `true`. */ | ||
| readonly clipCells?: boolean; | ||
| /** Auto-fit column widths to content. Default: `false`. */ | ||
| readonly autoFitColumns?: boolean; | ||
| /** Cell wrapping policy. Default: `'auto'`. */ | ||
| readonly wrap?: 'auto' | 'always' | 'never'; | ||
| /** Repeat the header row across page breaks. Default: `true`. */ | ||
| readonly repeatHeader?: boolean; | ||
| /** Alternate-row background (zebra striping). */ | ||
| readonly zebra?: boolean | PdfColor; | ||
| /** Caption rendered above the table. */ | ||
| readonly caption?: string; | ||
| /** Minimum row height in points. */ | ||
| readonly minRowHeight?: number; | ||
| /** Horizontal cell padding in points. */ | ||
| readonly cellPadding?: number; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A data table. */ | ||
| declare function Table(props: TableProps): ReactElement; | ||
| /** Props for {@link Row}. */ | ||
| interface RowProps { | ||
| /** Mark this row as the header row. */ | ||
| readonly header?: boolean; | ||
| /** Visual variant forwarded to pdfnative (`PdfRow.type`). */ | ||
| readonly variant?: string; | ||
| /** Emphasize ("pointed") row styling. */ | ||
| readonly pointed?: boolean; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A table row. Use inside `<Table>`. */ | ||
| declare function Row(props: RowProps): ReactElement; | ||
| /** Props for {@link Cell}. */ | ||
| interface CellProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A table cell. Use inside `<Row>`. */ | ||
| declare function Cell(props: CellProps): ReactElement; | ||
| /** Props for {@link Image}. */ | ||
| interface ImageProps { | ||
| /** Raw JPEG or PNG bytes. */ | ||
| readonly data: Uint8Array; | ||
| /** Display width in points. */ | ||
| readonly width?: number; | ||
| /** Display height in points. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** Alt text for tagged-PDF accessibility. */ | ||
| readonly alt?: string; | ||
| } | ||
| /** An embedded raster image (JPEG/PNG). */ | ||
| declare function Image(props: ImageProps): ReactElement; | ||
| /** Props for {@link Link}. */ | ||
| interface LinkProps { | ||
| /** Destination URL. */ | ||
| readonly url?: string; | ||
| /** Alias of `url`. */ | ||
| readonly href?: string; | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Link text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A clickable hyperlink. */ | ||
| declare function Link(props: LinkProps): ReactElement; | ||
| /** Props for {@link Spacer}. */ | ||
| interface SpacerProps { | ||
| /** Vertical whitespace in points. Default: `12`. */ | ||
| readonly height?: number; | ||
| } | ||
| /** Vertical whitespace. */ | ||
| declare function Spacer(props?: SpacerProps): ReactElement; | ||
| /** A hard page break. */ | ||
| declare function PageBreak(): ReactElement; | ||
| /** Props for {@link TableOfContents}. */ | ||
| interface TableOfContentsProps { | ||
| /** Heading shown above the entries. Default: `'Table of Contents'`. */ | ||
| readonly title?: string; | ||
| /** Deepest heading level to include (1–3). Default: `3`. */ | ||
| readonly maxLevel?: 1 | 2 | 3; | ||
| /** Font size for entries. Default: `10`. */ | ||
| readonly fontSize?: number; | ||
| /** Indent per heading level in points. Default: `15`. */ | ||
| readonly indent?: number; | ||
| } | ||
| /** An auto-generated table of contents, built from `<Heading>` blocks. */ | ||
| declare function TableOfContents(props?: TableOfContentsProps): ReactElement; | ||
| /** Alias of {@link TableOfContents}. */ | ||
| declare const Toc: typeof TableOfContents; | ||
| /** Props for {@link Barcode}. */ | ||
| interface BarcodeProps { | ||
| /** Barcode format (`'qr'`, `'code128'`, `'ean13'`, `'pdf417'`, `'datamatrix'`…). */ | ||
| readonly format: BarcodeFormat; | ||
| /** Data to encode. */ | ||
| readonly data: string; | ||
| /** Width in points. */ | ||
| readonly width?: number; | ||
| /** Height in points. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** QR error-correction level. Default: `'M'`. */ | ||
| readonly ecLevel?: QRErrorLevel; | ||
| /** PDF417 error-correction level (0–8). Default: `2`. */ | ||
| readonly pdf417ECLevel?: number; | ||
| } | ||
| /** A 1D or 2D barcode rendered with vector operators. */ | ||
| declare function Barcode(props: BarcodeProps): ReactElement; | ||
| /** Props for {@link Svg}. */ | ||
| interface SvgProps { | ||
| /** SVG path data or inline SVG markup. */ | ||
| readonly data: string; | ||
| /** Display width in points. Default: `200`. */ | ||
| readonly width?: number; | ||
| /** Display height in points. Default: `200`. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** SVG viewBox `[minX, minY, width, height]`. */ | ||
| readonly viewBox?: readonly [number, number, number, number]; | ||
| /** Fill color. `'none'` disables fill. */ | ||
| readonly fill?: SvgRenderOptions['fill']; | ||
| /** Stroke color. */ | ||
| readonly stroke?: SvgRenderOptions['stroke']; | ||
| /** Stroke width in SVG user units. Default: `1`. */ | ||
| readonly strokeWidth?: number; | ||
| /** Alt text for tagged-PDF accessibility. */ | ||
| readonly alt?: string; | ||
| } | ||
| /** Inline vector graphics rendered with PDF path operators. */ | ||
| declare function Svg(props: SvgProps): ReactElement; | ||
| /** Props for {@link FormField}. */ | ||
| interface FormFieldProps { | ||
| /** Field type (`'text'`, `'checkbox'`, `'dropdown'`…). */ | ||
| readonly fieldType: FormFieldType; | ||
| /** Unique field name. */ | ||
| readonly name: string; | ||
| /** Display label rendered before the widget. */ | ||
| readonly label?: string; | ||
| /** Default value. */ | ||
| readonly value?: string; | ||
| /** Placeholder hint. */ | ||
| readonly placeholder?: string; | ||
| /** Widget width in points. */ | ||
| readonly width?: number; | ||
| /** Widget height in points. */ | ||
| readonly height?: number; | ||
| /** Font size for text fields/dropdowns. Default: `10`. */ | ||
| readonly fontSize?: number; | ||
| /** Options for dropdown/listbox fields. */ | ||
| readonly options?: readonly string[]; | ||
| /** Read-only field. Default: `false`. */ | ||
| readonly readOnly?: boolean; | ||
| /** Required field. Default: `false`. */ | ||
| readonly required?: boolean; | ||
| /** Maximum character count. */ | ||
| readonly maxLength?: number; | ||
| /** Initial checked state for checkbox/radio. Default: `false`. */ | ||
| readonly checked?: boolean; | ||
| } | ||
| /** An interactive AcroForm widget. */ | ||
| declare function FormField(props: FormFieldProps): ReactElement; | ||
| /** | ||
| * Render entry points: turn a React element into PDF output. | ||
| * | ||
| * Every function reconciles the tree once (via {@link compile}) and then hands | ||
| * the resulting `DocumentParams` to the `pdfnative` engine. Layout and font | ||
| * options provided here are merged on top of anything set on `<Document>`. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Reconcile a React tree into the `pdfnative` document model (no rendering). */ | ||
| declare function compileDocument(node: ReactNode): DocumentParams; | ||
| /** Render to raw PDF bytes (`Uint8Array`). Works in Node and the browser. */ | ||
| declare function renderToBytes(node: ReactNode, options?: RenderOptions): Uint8Array; | ||
| /** Render to a `Blob` (`application/pdf`) — ideal for browser download/preview. */ | ||
| declare function renderToBlob(node: ReactNode, options?: RenderOptions): Blob; | ||
| /** | ||
| * Render to a true, page-by-page async byte stream — constant-memory output for | ||
| * very large documents. | ||
| */ | ||
| declare function renderToStream(node: ReactNode, options?: RenderOptions): AsyncGenerator<Uint8Array>; | ||
| /** | ||
| * Render and write the PDF to a file. Node.js only. | ||
| * | ||
| * @param node - A React element whose root is `<Document>`. | ||
| * @param path - Destination file path. | ||
| * @param options - Optional layout/font overrides. | ||
| */ | ||
| declare function renderToFile(node: ReactNode, path: string, options?: RenderOptions): Promise<void>; | ||
| /** | ||
| * React hooks for rendering PDFs on the client. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** State returned by {@link usePdf}. */ | ||
| interface UsePdfResult { | ||
| /** Object URL pointing at the rendered PDF blob (revoked automatically). */ | ||
| readonly url: string | null; | ||
| /** The rendered PDF as a `Blob` (`application/pdf`). */ | ||
| readonly blob: Blob | null; | ||
| /** The rendered PDF bytes. */ | ||
| readonly bytes: Uint8Array | null; | ||
| /** `true` while the first render is in flight. */ | ||
| readonly loading: boolean; | ||
| /** Any error thrown during reconciliation or rendering. */ | ||
| readonly error: Error | null; | ||
| /** Force a re-render (e.g. after changing render options). */ | ||
| readonly update: () => void; | ||
| } | ||
| /** | ||
| * Render a React element to a PDF and expose it as bytes, a `Blob`, and an | ||
| * object URL. Re-renders whenever `element` changes or {@link UsePdfResult.update} | ||
| * is called. | ||
| * | ||
| * @param element - A React element whose root is `<Document>`. | ||
| * @param options - Optional layout/font overrides (read on each render). | ||
| */ | ||
| declare function usePdf(element: ReactNode, options?: RenderOptions): UsePdfResult; | ||
| /** State returned by {@link usePdfStream}. */ | ||
| interface UsePdfStreamResult { | ||
| /** | ||
| * Create a fresh true-streaming async generator of PDF byte chunks. | ||
| * Each call starts a new render, so iterate the returned generator once. | ||
| */ | ||
| readonly getStream: () => AsyncGenerator<Uint8Array>; | ||
| } | ||
| /** | ||
| * Expose a stable factory that produces a constant-memory PDF byte stream for | ||
| * the given element. Useful for piping very large documents to the network. | ||
| */ | ||
| declare function usePdfStream(element: ReactNode, options?: RenderOptions): UsePdfStreamResult; | ||
| /** | ||
| * Client components for previewing and downloading rendered PDFs. | ||
| * | ||
| * These mirror the ergonomics of `@react-pdf/renderer` (`<PDFViewer>`, | ||
| * `<PDFDownloadLink>`, `<BlobProvider>`) to ease migration. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** The render-state handed to function-as-children consumers. */ | ||
| interface PdfRenderState { | ||
| readonly url: string | null; | ||
| readonly blob: Blob | null; | ||
| readonly bytes: Uint8Array | null; | ||
| readonly loading: boolean; | ||
| readonly error: Error | null; | ||
| } | ||
| /** Props for {@link PDFViewer}. */ | ||
| interface PDFViewerProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| readonly className?: string; | ||
| readonly style?: CSSProperties; | ||
| readonly width?: string | number; | ||
| readonly height?: string | number; | ||
| readonly title?: string; | ||
| } | ||
| /** Live in-browser PDF preview rendered into an `<iframe>`. */ | ||
| declare function PDFViewer(props: PDFViewerProps): ReactElement; | ||
| /** Props for {@link BlobProvider}. */ | ||
| interface BlobProviderProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| /** Function-as-children receiving the current render state. */ | ||
| readonly children: (state: PdfRenderState) => ReactNode; | ||
| } | ||
| /** Render-prop component exposing the rendered `Blob`/URL and status. */ | ||
| declare function BlobProvider(props: BlobProviderProps): ReactElement; | ||
| /** Props for {@link PDFDownloadLink}. */ | ||
| interface PDFDownloadLinkProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Suggested download file name. Default: `'document.pdf'`. */ | ||
| readonly fileName?: string; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| readonly className?: string; | ||
| readonly style?: CSSProperties; | ||
| /** Link content, or a function receiving the current render state. */ | ||
| readonly children: ReactNode | ((state: PdfRenderState) => ReactNode); | ||
| } | ||
| /** An anchor that downloads the rendered PDF. */ | ||
| declare function PDFDownloadLink(props: PDFDownloadLinkProps): ReactElement; | ||
| /** | ||
| * Compact document specification — the *token-frugal* authoring surface. | ||
| * | ||
| * `DocSpec` is a terse, JSON-serializable description of a document built from | ||
| * short positional tuples (e.g. `['h1', 'Invoice']`, `['p', 'Thank you.']`). | ||
| * It compiles to **exactly the same** `pdfnative` model as the JSX components — | ||
| * it is a thin projection over them — but costs a fraction of the tokens, which | ||
| * makes it ideal for LLM agents and config-driven generation. | ||
| * | ||
| * There is intentionally no CSS/flexbox here either: a `DocSpec` is a flat (or | ||
| * page-grouped) list of blocks, mirroring pdfnative's declarative block flow. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { renderSpecToBytes, type DocSpec } from 'pdfnative-react'; | ||
| * | ||
| * const spec: DocSpec = { | ||
| * title: 'Invoice #1024', | ||
| * footerText: 'Acme Inc', | ||
| * blocks: [ | ||
| * ['h1', 'Invoice #1024'], | ||
| * ['p', 'Thank you for your business.', { align: 'right' }], | ||
| * ['table', { h: ['Item', 'Total'], r: [['Pro plan', '$490.00']] }], | ||
| * ], | ||
| * }; | ||
| * | ||
| * const bytes = renderSpecToBytes(spec); | ||
| * ``` | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Options for a heading block (`['h1' | 'h2' | 'h3', text, opts?]`). */ | ||
| type HeadingSpecOpts = Pick<HeadingProps, 'color'>; | ||
| /** Options for a paragraph block (`['p', text, opts?]`). */ | ||
| type ParagraphSpecOpts = Omit<ParagraphProps, 'text' | 'children'>; | ||
| /** Options for a list block (`['ul' | 'ol', items, opts?]`). */ | ||
| type ListSpecOpts = Pick<ListProps, 'fontSize'>; | ||
| /** Options for a link block (`['link', text, opts]`); `url` (or `href`) is required. */ | ||
| type LinkSpecOpts = Omit<LinkProps, 'text' | 'children'>; | ||
| /** Options for a table-of-contents block (`['toc', opts?]`). */ | ||
| type TocSpecOpts = TableOfContentsProps; | ||
| /** Options for a barcode block (`['qr' | 'code128' | …, data, opts?]`). */ | ||
| type BarcodeSpecOpts = Omit<BarcodeProps, 'format' | 'data'>; | ||
| /** Options for an SVG block (`['svg', data, opts?]`). */ | ||
| type SvgSpecOpts = Omit<SvgProps, 'data'>; | ||
| /** Body of an image block (`['img', body]`). */ | ||
| type ImageSpecBody = ImageProps; | ||
| /** Body of a form-field block (`['field', body]`). */ | ||
| type FormFieldSpecBody = FormFieldProps; | ||
| /** | ||
| * A table row in a {@link TableSpecBody}: either a plain array of cell strings | ||
| * (the common case) or a full {@link PdfRow} when you need row typing/emphasis. | ||
| */ | ||
| type TableRowSpec = readonly string[] | PdfRow; | ||
| /** Body of a table block (`['table', body]`). */ | ||
| interface TableSpecBody { | ||
| /** Column headers. */ | ||
| readonly h?: readonly string[]; | ||
| /** Data rows — arrays of cell strings, or full `PdfRow`s. */ | ||
| readonly r: readonly TableRowSpec[]; | ||
| /** Explicit column definitions (widths, alignment). */ | ||
| readonly columns?: readonly ColumnDef[]; | ||
| /** Alternate-row background (zebra striping). */ | ||
| readonly zebra?: boolean | PdfColor; | ||
| /** Caption rendered above the table. */ | ||
| readonly caption?: string; | ||
| /** Clip cell contents to column bounds. */ | ||
| readonly clipCells?: boolean; | ||
| /** Auto-fit column widths to content. */ | ||
| readonly autoFitColumns?: boolean; | ||
| /** Cell wrapping policy. */ | ||
| readonly wrap?: 'auto' | 'always' | 'never'; | ||
| /** Repeat the header row across page breaks. */ | ||
| readonly repeatHeader?: boolean; | ||
| /** Minimum row height in points. */ | ||
| readonly minRowHeight?: number; | ||
| /** Horizontal cell padding in points. */ | ||
| readonly cellPadding?: number; | ||
| } | ||
| /** Heading: `['h1' | 'h2' | 'h3', text, opts?]`. */ | ||
| type HeadingSpec = readonly ['h1' | 'h2' | 'h3', string, HeadingSpecOpts?]; | ||
| /** Paragraph: `['p', text, opts?]`. */ | ||
| type ParagraphSpec = readonly ['p', string, ParagraphSpecOpts?]; | ||
| /** List: `['ul', items]` (bullet) or `['ol', items]` (numbered). */ | ||
| type ListSpec = readonly ['ul' | 'ol', readonly string[], ListSpecOpts?]; | ||
| /** Table: `['table', body]`. */ | ||
| type TableSpec = readonly ['table', TableSpecBody]; | ||
| /** Image: `['img', body]`. */ | ||
| type ImageSpec = readonly ['img', ImageSpecBody]; | ||
| /** Link: `['link', text, opts]` — `opts.url` (or `opts.href`) is required. */ | ||
| type LinkSpec = readonly ['link', string, LinkSpecOpts]; | ||
| /** Spacer: `['sp']` (default height) or `['sp', height]`. */ | ||
| type SpacerSpec = readonly ['sp', number?]; | ||
| /** Hard page break: `['br']`. */ | ||
| type PageBreakSpec = readonly ['br']; | ||
| /** Page group: `['page', blocks]`. */ | ||
| type PageSpec = readonly ['page', readonly BlockSpec[]]; | ||
| /** Table of contents: `['toc', opts?]`. */ | ||
| type TocSpec = readonly ['toc', TocSpecOpts?]; | ||
| /** Barcode: `['qr' | 'code128' | 'ean13' | 'pdf417' | 'datamatrix', data, opts?]`. */ | ||
| type BarcodeSpec = readonly [ | ||
| 'qr' | 'code128' | 'ean13' | 'pdf417' | 'datamatrix', | ||
| string, | ||
| BarcodeSpecOpts? | ||
| ]; | ||
| /** SVG: `['svg', data, opts?]`. */ | ||
| type SvgSpec = readonly ['svg', string, SvgSpecOpts?]; | ||
| /** Form field: `['field', body]`. */ | ||
| type FormFieldSpec = readonly ['field', FormFieldSpecBody]; | ||
| /** Any single block in a {@link DocSpec}. */ | ||
| type BlockSpec = HeadingSpec | ParagraphSpec | ListSpec | TableSpec | ImageSpec | LinkSpec | SpacerSpec | PageBreakSpec | PageSpec | TocSpec | BarcodeSpec | SvgSpec | FormFieldSpec; | ||
| /** The kind discriminator (first tuple element) of any {@link BlockSpec}. */ | ||
| type BlockSpecKind = BlockSpec[0]; | ||
| /** | ||
| * A compact, JSON-serializable document specification — the token-frugal | ||
| * equivalent of a `<Document>` tree. Compiles to the same `pdfnative` model as | ||
| * the JSX components via {@link specToElement}. | ||
| */ | ||
| interface DocSpec { | ||
| /** Document title (PDF metadata + optional cover title). */ | ||
| readonly title?: string; | ||
| /** Footer text repeated on every page. */ | ||
| readonly footerText?: string; | ||
| /** PDF metadata (author, subject, keywords). */ | ||
| readonly metadata?: DocumentMetadata; | ||
| /** Pre-loaded font entries for non-Latin scripts. */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| /** Ordered document blocks. */ | ||
| readonly blocks: readonly BlockSpec[]; | ||
| } | ||
| /** | ||
| * Compile a compact {@link DocSpec} into PDF output. | ||
| * | ||
| * The spec is projected onto the **existing** JSX components (so the spec and | ||
| * component surfaces can never drift), then handed to the normal render | ||
| * pipeline. Everything here is pure and isomorphic — the same code path as | ||
| * authoring with `<Document>` by hand, just far cheaper to express. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** | ||
| * Turn a compact {@link DocSpec} into a `<Document>` React element. Useful for | ||
| * embedding a spec-authored document inside a larger JSX tree, or for passing to | ||
| * the hooks/viewer components. | ||
| */ | ||
| declare function specToElement(spec: DocSpec): ReactElement; | ||
| /** Compile a {@link DocSpec} into the `pdfnative` document model (no rendering). */ | ||
| declare function compileSpec(spec: DocSpec): DocumentParams; | ||
| /** Render a {@link DocSpec} to raw PDF bytes (`Uint8Array`). */ | ||
| declare function renderSpecToBytes(spec: DocSpec, options?: RenderOptions): Uint8Array; | ||
| /** Render a {@link DocSpec} to a `Blob` (`application/pdf`). */ | ||
| declare function renderSpecToBlob(spec: DocSpec, options?: RenderOptions): Blob; | ||
| /** Render a {@link DocSpec} to a constant-memory async byte stream. */ | ||
| declare function renderSpecToStream(spec: DocSpec, options?: RenderOptions): AsyncGenerator<Uint8Array>; | ||
| /** Render a {@link DocSpec} and write the PDF to a file. Node.js only. */ | ||
| declare function renderSpecToFile(spec: DocSpec, path: string, options?: RenderOptions): Promise<void>; | ||
| /** | ||
| * Versioned JSON Schema (Draft 2020-12) for the compact {@link DocSpec} authoring | ||
| * format, so agents and tooling can self-validate a spec before rendering. | ||
| * | ||
| * The schema is hand-authored, pure data (zero runtime deps — no validator is | ||
| * bundled), and versioned via a `$id` that embeds the package version, so any | ||
| * drift is detectable and pinned by a test. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** A read-only JSON Schema fragment. */ | ||
| type JsonSchema = Readonly<Record<string, unknown>>; | ||
| /** | ||
| * Return the JSON Schema (Draft 2020-12) describing the {@link DocSpec} authoring | ||
| * format. The `$id` embeds the current package version. | ||
| */ | ||
| declare function docSpecSchema(): JsonSchema; | ||
| /** The `$id` (versioned URL) of the current {@link docSpecSchema}. */ | ||
| declare function docSpecSchemaId(): string; | ||
| /** | ||
| * Serialize a reconciled host tree into a `pdfnative` `DocumentParams` object. | ||
| * | ||
| * This is the heart of the "JSX compiles to JSON" promise: a pure, synchronous | ||
| * transform with no side effects, so it is trivially testable in isolation. | ||
| */ | ||
| /** Thrown when a component tree cannot be mapped onto the pdfnative model. */ | ||
| declare class PdfStructureError extends Error { | ||
| constructor(message: string); | ||
| } | ||
| /** | ||
| * Single source of truth for the package version. | ||
| * | ||
| * Kept in its own module so non-barrel files (e.g. the spec schema) can read the | ||
| * version without importing the public barrel — which would create an import | ||
| * cycle. A test pins this constant to `package.json`. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Current package version (kept in sync with `package.json`). */ | ||
| declare const version = "0.2.0"; | ||
| export { type Align, Barcode, type BarcodeProps, type BarcodeSpec, type BarcodeSpecOpts, BlobProvider, type BlobProviderProps, type BlockSpec, type BlockSpecKind, Cell, type CellProps, type Color, type CompiledDocument, type DocSpec, Document, type DocumentProps, FormField, type FormFieldProps, type FormFieldSpec, type FormFieldSpecBody, Heading, type HeadingProps, type HeadingSpec, type HeadingSpecOpts, Image, type ImageProps, type ImageSpec, type ImageSpecBody, Item, type ItemProps, type JsonSchema, Link, type LinkProps, type LinkSpec, type LinkSpecOpts, List, type ListProps, type ListSpec, type ListSpecOpts, PDFDownloadLink, type PDFDownloadLinkProps, PDFViewer, type PDFViewerProps, Page, PageBreak, type PageBreakSpec, type PageProps, type PageSpec, Paragraph, type ParagraphProps, type ParagraphSpec, type ParagraphSpecOpts, type PdfRenderState, PdfStructureError, type RenderOptions, Row, type RowProps, Spacer, type SpacerProps, type SpacerSpec, Svg, type SvgProps, type SvgSpec, type SvgSpecOpts, Table, TableOfContents, type TableOfContentsProps, type TableProps, type TableRowSpec, type TableSpec, type TableSpecBody, Text, Toc, type TocSpec, type TocSpecOpts, type UsePdfResult, type UsePdfStreamResult, compileDocument, compileSpec, docSpecSchema, docSpecSchemaId, renderSpecToBlob, renderSpecToBytes, renderSpecToFile, renderSpecToStream, renderToBlob, renderToBytes, renderToFile, renderToStream, specToElement, usePdf, usePdfStream, version }; |
+633
-11
@@ -0,18 +1,640 @@ | ||
| import { ReactElement, ReactNode, CSSProperties } from 'react'; | ||
| import { PdfLayoutOptions, FontEntry, DocumentParams, BarcodeFormat, QRErrorLevel, PdfColor, SvgRenderOptions, FormFieldType, DocumentMetadata, PdfRow, ColumnDef } from 'pdfnative'; | ||
| export { BarcodeFormat, ColumnDef, DocumentBlock, DocumentMetadata, DocumentParams, FontEntry, FormFieldType, PdfColor, PdfLayoutOptions, PdfRow, QRErrorLevel, SvgRenderOptions, downloadBlob, initNodeCompression, loadFontData, registerFont, registerFonts } from 'pdfnative'; | ||
| /** | ||
| * pdfnative-react — React renderer for the pdfnative PDF engine. | ||
| * Public type definitions for pdfnative-react. | ||
| * | ||
| * 0.1.0 is a placeholder release that reserves the package name while the | ||
| * first implemented version (0.2.0) is prepared. The public API below is | ||
| * intentionally minimal; do not depend on it yet. | ||
| * These types mirror the `pdfnative` document model (a declarative *block flow*, | ||
| * not an HTML/CSS box model) while presenting an ergonomic, JSX-friendly surface. | ||
| * | ||
| * Track progress: https://github.com/Nizoka/pdfnative-react | ||
| * @packageDocumentation | ||
| */ | ||
| /** Current package version. */ | ||
| declare const version = "0.1.0"; | ||
| /** Horizontal alignment shared by several blocks. */ | ||
| type Align = 'left' | 'center' | 'right'; | ||
| /** A color accepted by the pdfnative engine: hex, RGB tuple, or PDF operator string. */ | ||
| type Color = string | readonly [number, number, number]; | ||
| /** | ||
| * Whether the declarative React renderer is available in this build. | ||
| * `false` in 0.1.0 (name-reservation placeholder); `true` from 0.2.0 onward. | ||
| * Options accepted by every render entry point. Forwarded to the underlying | ||
| * `pdfnative` builder. | ||
| */ | ||
| declare const ready = false; | ||
| interface RenderOptions { | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode, encryption…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| /** Pre-loaded font entries for non-Latin scripts (see `pdfnative` `loadFontData`). */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| } | ||
| /** | ||
| * The intermediate representation produced by reconciling a React tree. | ||
| * This is exactly the object passed to `pdfnative`'s `buildDocumentPDFBytes`. | ||
| */ | ||
| type CompiledDocument = DocumentParams; | ||
| export { ready, version }; | ||
| /** | ||
| * Public component layer for pdfnative-react. | ||
| * | ||
| * Every component is a thin, side-effect-free factory that emits a *host element* | ||
| * (a lowercase tag understood by the reconciler). Components carry no rendering | ||
| * logic themselves: the tree they form is reconciled and then serialized into a | ||
| * `pdfnative` `DocumentParams` object. | ||
| * | ||
| * The API intentionally mirrors familiar React-PDF ergonomics (`<Document>`, | ||
| * `<Page>`, `<Text>`, `<Image>`, `<Link>`) while mapping onto pdfnative's | ||
| * declarative *block flow* model. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Props for {@link Document}, the required root of every tree. */ | ||
| interface DocumentProps { | ||
| /** Document title (PDF metadata + optional cover title). */ | ||
| readonly title?: string; | ||
| /** Footer text repeated on every page. */ | ||
| readonly footerText?: string; | ||
| /** PDF metadata (author, subject, keywords). */ | ||
| readonly metadata?: DocumentMetadata; | ||
| /** Pre-loaded font entries for non-Latin scripts. */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** The root component. Exactly one `<Document>` must wrap the whole tree. */ | ||
| declare function Document(props: DocumentProps): ReactElement; | ||
| /** Props for {@link Page}. */ | ||
| interface PageProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** | ||
| * An explicit page boundary. Pages are optional: content flows and paginates | ||
| * automatically. Use `<Page>` to force grouped content onto a fresh page. | ||
| */ | ||
| declare function Page(props: PageProps): ReactElement; | ||
| /** Props for {@link Heading}. */ | ||
| interface HeadingProps { | ||
| /** Heading level (1–3). Default: `1`. */ | ||
| readonly level?: 1 | 2 | 3; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Heading text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A section heading. Headings feed the auto-generated `<TableOfContents>`. */ | ||
| declare function Heading(props: HeadingProps): ReactElement; | ||
| /** Props for {@link Paragraph} / {@link Text}. */ | ||
| interface ParagraphProps { | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Line height multiplier. */ | ||
| readonly lineHeight?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** First-line indent in points. */ | ||
| readonly indent?: number; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Paragraph text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A wrapping paragraph of body text. */ | ||
| declare function Paragraph(props: ParagraphProps): ReactElement; | ||
| /** Alias of {@link Paragraph} for React-PDF familiarity. */ | ||
| declare const Text: typeof Paragraph; | ||
| /** Props for {@link List}. */ | ||
| interface ListProps { | ||
| /** Render as a numbered list instead of bullets. */ | ||
| readonly ordered?: boolean; | ||
| /** Explicit list style. Overrides `ordered` when set. */ | ||
| readonly style?: 'bullet' | 'numbered'; | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Items as plain strings (alternatively provide `<Item>` children). */ | ||
| readonly items?: readonly string[]; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A bullet or numbered list. */ | ||
| declare function List(props: ListProps): ReactElement; | ||
| /** Props for {@link Item}. */ | ||
| interface ItemProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A single list item. Use inside `<List>`. */ | ||
| declare function Item(props: ItemProps): ReactElement; | ||
| /** Props for {@link Table}. */ | ||
| interface TableProps { | ||
| /** Column headers. Omit to derive from the first `<Row header>`. */ | ||
| readonly headers?: readonly string[]; | ||
| /** Rows as data (alternatively provide `<Row>`/`<Cell>` children). */ | ||
| readonly rows?: readonly PdfRow[]; | ||
| /** Explicit column definitions (widths, alignment). */ | ||
| readonly columns?: readonly ColumnDef[]; | ||
| /** Clip cell contents to column bounds. Default: `true`. */ | ||
| readonly clipCells?: boolean; | ||
| /** Auto-fit column widths to content. Default: `false`. */ | ||
| readonly autoFitColumns?: boolean; | ||
| /** Cell wrapping policy. Default: `'auto'`. */ | ||
| readonly wrap?: 'auto' | 'always' | 'never'; | ||
| /** Repeat the header row across page breaks. Default: `true`. */ | ||
| readonly repeatHeader?: boolean; | ||
| /** Alternate-row background (zebra striping). */ | ||
| readonly zebra?: boolean | PdfColor; | ||
| /** Caption rendered above the table. */ | ||
| readonly caption?: string; | ||
| /** Minimum row height in points. */ | ||
| readonly minRowHeight?: number; | ||
| /** Horizontal cell padding in points. */ | ||
| readonly cellPadding?: number; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A data table. */ | ||
| declare function Table(props: TableProps): ReactElement; | ||
| /** Props for {@link Row}. */ | ||
| interface RowProps { | ||
| /** Mark this row as the header row. */ | ||
| readonly header?: boolean; | ||
| /** Visual variant forwarded to pdfnative (`PdfRow.type`). */ | ||
| readonly variant?: string; | ||
| /** Emphasize ("pointed") row styling. */ | ||
| readonly pointed?: boolean; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A table row. Use inside `<Table>`. */ | ||
| declare function Row(props: RowProps): ReactElement; | ||
| /** Props for {@link Cell}. */ | ||
| interface CellProps { | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A table cell. Use inside `<Row>`. */ | ||
| declare function Cell(props: CellProps): ReactElement; | ||
| /** Props for {@link Image}. */ | ||
| interface ImageProps { | ||
| /** Raw JPEG or PNG bytes. */ | ||
| readonly data: Uint8Array; | ||
| /** Display width in points. */ | ||
| readonly width?: number; | ||
| /** Display height in points. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** Alt text for tagged-PDF accessibility. */ | ||
| readonly alt?: string; | ||
| } | ||
| /** An embedded raster image (JPEG/PNG). */ | ||
| declare function Image(props: ImageProps): ReactElement; | ||
| /** Props for {@link Link}. */ | ||
| interface LinkProps { | ||
| /** Destination URL. */ | ||
| readonly url?: string; | ||
| /** Alias of `url`. */ | ||
| readonly href?: string; | ||
| /** Font size in points. */ | ||
| readonly fontSize?: number; | ||
| /** Text color. */ | ||
| readonly color?: PdfColor; | ||
| /** Link text (alternatively provide it as children). */ | ||
| readonly text?: string; | ||
| readonly children?: ReactNode; | ||
| } | ||
| /** A clickable hyperlink. */ | ||
| declare function Link(props: LinkProps): ReactElement; | ||
| /** Props for {@link Spacer}. */ | ||
| interface SpacerProps { | ||
| /** Vertical whitespace in points. Default: `12`. */ | ||
| readonly height?: number; | ||
| } | ||
| /** Vertical whitespace. */ | ||
| declare function Spacer(props?: SpacerProps): ReactElement; | ||
| /** A hard page break. */ | ||
| declare function PageBreak(): ReactElement; | ||
| /** Props for {@link TableOfContents}. */ | ||
| interface TableOfContentsProps { | ||
| /** Heading shown above the entries. Default: `'Table of Contents'`. */ | ||
| readonly title?: string; | ||
| /** Deepest heading level to include (1–3). Default: `3`. */ | ||
| readonly maxLevel?: 1 | 2 | 3; | ||
| /** Font size for entries. Default: `10`. */ | ||
| readonly fontSize?: number; | ||
| /** Indent per heading level in points. Default: `15`. */ | ||
| readonly indent?: number; | ||
| } | ||
| /** An auto-generated table of contents, built from `<Heading>` blocks. */ | ||
| declare function TableOfContents(props?: TableOfContentsProps): ReactElement; | ||
| /** Alias of {@link TableOfContents}. */ | ||
| declare const Toc: typeof TableOfContents; | ||
| /** Props for {@link Barcode}. */ | ||
| interface BarcodeProps { | ||
| /** Barcode format (`'qr'`, `'code128'`, `'ean13'`, `'pdf417'`, `'datamatrix'`…). */ | ||
| readonly format: BarcodeFormat; | ||
| /** Data to encode. */ | ||
| readonly data: string; | ||
| /** Width in points. */ | ||
| readonly width?: number; | ||
| /** Height in points. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** QR error-correction level. Default: `'M'`. */ | ||
| readonly ecLevel?: QRErrorLevel; | ||
| /** PDF417 error-correction level (0–8). Default: `2`. */ | ||
| readonly pdf417ECLevel?: number; | ||
| } | ||
| /** A 1D or 2D barcode rendered with vector operators. */ | ||
| declare function Barcode(props: BarcodeProps): ReactElement; | ||
| /** Props for {@link Svg}. */ | ||
| interface SvgProps { | ||
| /** SVG path data or inline SVG markup. */ | ||
| readonly data: string; | ||
| /** Display width in points. Default: `200`. */ | ||
| readonly width?: number; | ||
| /** Display height in points. Default: `200`. */ | ||
| readonly height?: number; | ||
| /** Horizontal alignment. Default: `'left'`. */ | ||
| readonly align?: Align; | ||
| /** SVG viewBox `[minX, minY, width, height]`. */ | ||
| readonly viewBox?: readonly [number, number, number, number]; | ||
| /** Fill color. `'none'` disables fill. */ | ||
| readonly fill?: SvgRenderOptions['fill']; | ||
| /** Stroke color. */ | ||
| readonly stroke?: SvgRenderOptions['stroke']; | ||
| /** Stroke width in SVG user units. Default: `1`. */ | ||
| readonly strokeWidth?: number; | ||
| /** Alt text for tagged-PDF accessibility. */ | ||
| readonly alt?: string; | ||
| } | ||
| /** Inline vector graphics rendered with PDF path operators. */ | ||
| declare function Svg(props: SvgProps): ReactElement; | ||
| /** Props for {@link FormField}. */ | ||
| interface FormFieldProps { | ||
| /** Field type (`'text'`, `'checkbox'`, `'dropdown'`…). */ | ||
| readonly fieldType: FormFieldType; | ||
| /** Unique field name. */ | ||
| readonly name: string; | ||
| /** Display label rendered before the widget. */ | ||
| readonly label?: string; | ||
| /** Default value. */ | ||
| readonly value?: string; | ||
| /** Placeholder hint. */ | ||
| readonly placeholder?: string; | ||
| /** Widget width in points. */ | ||
| readonly width?: number; | ||
| /** Widget height in points. */ | ||
| readonly height?: number; | ||
| /** Font size for text fields/dropdowns. Default: `10`. */ | ||
| readonly fontSize?: number; | ||
| /** Options for dropdown/listbox fields. */ | ||
| readonly options?: readonly string[]; | ||
| /** Read-only field. Default: `false`. */ | ||
| readonly readOnly?: boolean; | ||
| /** Required field. Default: `false`. */ | ||
| readonly required?: boolean; | ||
| /** Maximum character count. */ | ||
| readonly maxLength?: number; | ||
| /** Initial checked state for checkbox/radio. Default: `false`. */ | ||
| readonly checked?: boolean; | ||
| } | ||
| /** An interactive AcroForm widget. */ | ||
| declare function FormField(props: FormFieldProps): ReactElement; | ||
| /** | ||
| * Render entry points: turn a React element into PDF output. | ||
| * | ||
| * Every function reconciles the tree once (via {@link compile}) and then hands | ||
| * the resulting `DocumentParams` to the `pdfnative` engine. Layout and font | ||
| * options provided here are merged on top of anything set on `<Document>`. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Reconcile a React tree into the `pdfnative` document model (no rendering). */ | ||
| declare function compileDocument(node: ReactNode): DocumentParams; | ||
| /** Render to raw PDF bytes (`Uint8Array`). Works in Node and the browser. */ | ||
| declare function renderToBytes(node: ReactNode, options?: RenderOptions): Uint8Array; | ||
| /** Render to a `Blob` (`application/pdf`) — ideal for browser download/preview. */ | ||
| declare function renderToBlob(node: ReactNode, options?: RenderOptions): Blob; | ||
| /** | ||
| * Render to a true, page-by-page async byte stream — constant-memory output for | ||
| * very large documents. | ||
| */ | ||
| declare function renderToStream(node: ReactNode, options?: RenderOptions): AsyncGenerator<Uint8Array>; | ||
| /** | ||
| * Render and write the PDF to a file. Node.js only. | ||
| * | ||
| * @param node - A React element whose root is `<Document>`. | ||
| * @param path - Destination file path. | ||
| * @param options - Optional layout/font overrides. | ||
| */ | ||
| declare function renderToFile(node: ReactNode, path: string, options?: RenderOptions): Promise<void>; | ||
| /** | ||
| * React hooks for rendering PDFs on the client. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** State returned by {@link usePdf}. */ | ||
| interface UsePdfResult { | ||
| /** Object URL pointing at the rendered PDF blob (revoked automatically). */ | ||
| readonly url: string | null; | ||
| /** The rendered PDF as a `Blob` (`application/pdf`). */ | ||
| readonly blob: Blob | null; | ||
| /** The rendered PDF bytes. */ | ||
| readonly bytes: Uint8Array | null; | ||
| /** `true` while the first render is in flight. */ | ||
| readonly loading: boolean; | ||
| /** Any error thrown during reconciliation or rendering. */ | ||
| readonly error: Error | null; | ||
| /** Force a re-render (e.g. after changing render options). */ | ||
| readonly update: () => void; | ||
| } | ||
| /** | ||
| * Render a React element to a PDF and expose it as bytes, a `Blob`, and an | ||
| * object URL. Re-renders whenever `element` changes or {@link UsePdfResult.update} | ||
| * is called. | ||
| * | ||
| * @param element - A React element whose root is `<Document>`. | ||
| * @param options - Optional layout/font overrides (read on each render). | ||
| */ | ||
| declare function usePdf(element: ReactNode, options?: RenderOptions): UsePdfResult; | ||
| /** State returned by {@link usePdfStream}. */ | ||
| interface UsePdfStreamResult { | ||
| /** | ||
| * Create a fresh true-streaming async generator of PDF byte chunks. | ||
| * Each call starts a new render, so iterate the returned generator once. | ||
| */ | ||
| readonly getStream: () => AsyncGenerator<Uint8Array>; | ||
| } | ||
| /** | ||
| * Expose a stable factory that produces a constant-memory PDF byte stream for | ||
| * the given element. Useful for piping very large documents to the network. | ||
| */ | ||
| declare function usePdfStream(element: ReactNode, options?: RenderOptions): UsePdfStreamResult; | ||
| /** | ||
| * Client components for previewing and downloading rendered PDFs. | ||
| * | ||
| * These mirror the ergonomics of `@react-pdf/renderer` (`<PDFViewer>`, | ||
| * `<PDFDownloadLink>`, `<BlobProvider>`) to ease migration. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** The render-state handed to function-as-children consumers. */ | ||
| interface PdfRenderState { | ||
| readonly url: string | null; | ||
| readonly blob: Blob | null; | ||
| readonly bytes: Uint8Array | null; | ||
| readonly loading: boolean; | ||
| readonly error: Error | null; | ||
| } | ||
| /** Props for {@link PDFViewer}. */ | ||
| interface PDFViewerProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| readonly className?: string; | ||
| readonly style?: CSSProperties; | ||
| readonly width?: string | number; | ||
| readonly height?: string | number; | ||
| readonly title?: string; | ||
| } | ||
| /** Live in-browser PDF preview rendered into an `<iframe>`. */ | ||
| declare function PDFViewer(props: PDFViewerProps): ReactElement; | ||
| /** Props for {@link BlobProvider}. */ | ||
| interface BlobProviderProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| /** Function-as-children receiving the current render state. */ | ||
| readonly children: (state: PdfRenderState) => ReactNode; | ||
| } | ||
| /** Render-prop component exposing the rendered `Blob`/URL and status. */ | ||
| declare function BlobProvider(props: BlobProviderProps): ReactElement; | ||
| /** Props for {@link PDFDownloadLink}. */ | ||
| interface PDFDownloadLinkProps { | ||
| /** The document tree to render (root `<Document>`). */ | ||
| readonly document: ReactNode; | ||
| /** Suggested download file name. Default: `'document.pdf'`. */ | ||
| readonly fileName?: string; | ||
| /** Layout/font overrides. */ | ||
| readonly options?: RenderOptions; | ||
| readonly className?: string; | ||
| readonly style?: CSSProperties; | ||
| /** Link content, or a function receiving the current render state. */ | ||
| readonly children: ReactNode | ((state: PdfRenderState) => ReactNode); | ||
| } | ||
| /** An anchor that downloads the rendered PDF. */ | ||
| declare function PDFDownloadLink(props: PDFDownloadLinkProps): ReactElement; | ||
| /** | ||
| * Compact document specification — the *token-frugal* authoring surface. | ||
| * | ||
| * `DocSpec` is a terse, JSON-serializable description of a document built from | ||
| * short positional tuples (e.g. `['h1', 'Invoice']`, `['p', 'Thank you.']`). | ||
| * It compiles to **exactly the same** `pdfnative` model as the JSX components — | ||
| * it is a thin projection over them — but costs a fraction of the tokens, which | ||
| * makes it ideal for LLM agents and config-driven generation. | ||
| * | ||
| * There is intentionally no CSS/flexbox here either: a `DocSpec` is a flat (or | ||
| * page-grouped) list of blocks, mirroring pdfnative's declarative block flow. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { renderSpecToBytes, type DocSpec } from 'pdfnative-react'; | ||
| * | ||
| * const spec: DocSpec = { | ||
| * title: 'Invoice #1024', | ||
| * footerText: 'Acme Inc', | ||
| * blocks: [ | ||
| * ['h1', 'Invoice #1024'], | ||
| * ['p', 'Thank you for your business.', { align: 'right' }], | ||
| * ['table', { h: ['Item', 'Total'], r: [['Pro plan', '$490.00']] }], | ||
| * ], | ||
| * }; | ||
| * | ||
| * const bytes = renderSpecToBytes(spec); | ||
| * ``` | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Options for a heading block (`['h1' | 'h2' | 'h3', text, opts?]`). */ | ||
| type HeadingSpecOpts = Pick<HeadingProps, 'color'>; | ||
| /** Options for a paragraph block (`['p', text, opts?]`). */ | ||
| type ParagraphSpecOpts = Omit<ParagraphProps, 'text' | 'children'>; | ||
| /** Options for a list block (`['ul' | 'ol', items, opts?]`). */ | ||
| type ListSpecOpts = Pick<ListProps, 'fontSize'>; | ||
| /** Options for a link block (`['link', text, opts]`); `url` (or `href`) is required. */ | ||
| type LinkSpecOpts = Omit<LinkProps, 'text' | 'children'>; | ||
| /** Options for a table-of-contents block (`['toc', opts?]`). */ | ||
| type TocSpecOpts = TableOfContentsProps; | ||
| /** Options for a barcode block (`['qr' | 'code128' | …, data, opts?]`). */ | ||
| type BarcodeSpecOpts = Omit<BarcodeProps, 'format' | 'data'>; | ||
| /** Options for an SVG block (`['svg', data, opts?]`). */ | ||
| type SvgSpecOpts = Omit<SvgProps, 'data'>; | ||
| /** Body of an image block (`['img', body]`). */ | ||
| type ImageSpecBody = ImageProps; | ||
| /** Body of a form-field block (`['field', body]`). */ | ||
| type FormFieldSpecBody = FormFieldProps; | ||
| /** | ||
| * A table row in a {@link TableSpecBody}: either a plain array of cell strings | ||
| * (the common case) or a full {@link PdfRow} when you need row typing/emphasis. | ||
| */ | ||
| type TableRowSpec = readonly string[] | PdfRow; | ||
| /** Body of a table block (`['table', body]`). */ | ||
| interface TableSpecBody { | ||
| /** Column headers. */ | ||
| readonly h?: readonly string[]; | ||
| /** Data rows — arrays of cell strings, or full `PdfRow`s. */ | ||
| readonly r: readonly TableRowSpec[]; | ||
| /** Explicit column definitions (widths, alignment). */ | ||
| readonly columns?: readonly ColumnDef[]; | ||
| /** Alternate-row background (zebra striping). */ | ||
| readonly zebra?: boolean | PdfColor; | ||
| /** Caption rendered above the table. */ | ||
| readonly caption?: string; | ||
| /** Clip cell contents to column bounds. */ | ||
| readonly clipCells?: boolean; | ||
| /** Auto-fit column widths to content. */ | ||
| readonly autoFitColumns?: boolean; | ||
| /** Cell wrapping policy. */ | ||
| readonly wrap?: 'auto' | 'always' | 'never'; | ||
| /** Repeat the header row across page breaks. */ | ||
| readonly repeatHeader?: boolean; | ||
| /** Minimum row height in points. */ | ||
| readonly minRowHeight?: number; | ||
| /** Horizontal cell padding in points. */ | ||
| readonly cellPadding?: number; | ||
| } | ||
| /** Heading: `['h1' | 'h2' | 'h3', text, opts?]`. */ | ||
| type HeadingSpec = readonly ['h1' | 'h2' | 'h3', string, HeadingSpecOpts?]; | ||
| /** Paragraph: `['p', text, opts?]`. */ | ||
| type ParagraphSpec = readonly ['p', string, ParagraphSpecOpts?]; | ||
| /** List: `['ul', items]` (bullet) or `['ol', items]` (numbered). */ | ||
| type ListSpec = readonly ['ul' | 'ol', readonly string[], ListSpecOpts?]; | ||
| /** Table: `['table', body]`. */ | ||
| type TableSpec = readonly ['table', TableSpecBody]; | ||
| /** Image: `['img', body]`. */ | ||
| type ImageSpec = readonly ['img', ImageSpecBody]; | ||
| /** Link: `['link', text, opts]` — `opts.url` (or `opts.href`) is required. */ | ||
| type LinkSpec = readonly ['link', string, LinkSpecOpts]; | ||
| /** Spacer: `['sp']` (default height) or `['sp', height]`. */ | ||
| type SpacerSpec = readonly ['sp', number?]; | ||
| /** Hard page break: `['br']`. */ | ||
| type PageBreakSpec = readonly ['br']; | ||
| /** Page group: `['page', blocks]`. */ | ||
| type PageSpec = readonly ['page', readonly BlockSpec[]]; | ||
| /** Table of contents: `['toc', opts?]`. */ | ||
| type TocSpec = readonly ['toc', TocSpecOpts?]; | ||
| /** Barcode: `['qr' | 'code128' | 'ean13' | 'pdf417' | 'datamatrix', data, opts?]`. */ | ||
| type BarcodeSpec = readonly [ | ||
| 'qr' | 'code128' | 'ean13' | 'pdf417' | 'datamatrix', | ||
| string, | ||
| BarcodeSpecOpts? | ||
| ]; | ||
| /** SVG: `['svg', data, opts?]`. */ | ||
| type SvgSpec = readonly ['svg', string, SvgSpecOpts?]; | ||
| /** Form field: `['field', body]`. */ | ||
| type FormFieldSpec = readonly ['field', FormFieldSpecBody]; | ||
| /** Any single block in a {@link DocSpec}. */ | ||
| type BlockSpec = HeadingSpec | ParagraphSpec | ListSpec | TableSpec | ImageSpec | LinkSpec | SpacerSpec | PageBreakSpec | PageSpec | TocSpec | BarcodeSpec | SvgSpec | FormFieldSpec; | ||
| /** The kind discriminator (first tuple element) of any {@link BlockSpec}. */ | ||
| type BlockSpecKind = BlockSpec[0]; | ||
| /** | ||
| * A compact, JSON-serializable document specification — the token-frugal | ||
| * equivalent of a `<Document>` tree. Compiles to the same `pdfnative` model as | ||
| * the JSX components via {@link specToElement}. | ||
| */ | ||
| interface DocSpec { | ||
| /** Document title (PDF metadata + optional cover title). */ | ||
| readonly title?: string; | ||
| /** Footer text repeated on every page. */ | ||
| readonly footerText?: string; | ||
| /** PDF metadata (author, subject, keywords). */ | ||
| readonly metadata?: DocumentMetadata; | ||
| /** Pre-loaded font entries for non-Latin scripts. */ | ||
| readonly fontEntries?: readonly FontEntry[]; | ||
| /** Layout overrides (page size, margins, colors, PDF/A mode…). */ | ||
| readonly layout?: Partial<PdfLayoutOptions>; | ||
| /** Ordered document blocks. */ | ||
| readonly blocks: readonly BlockSpec[]; | ||
| } | ||
| /** | ||
| * Compile a compact {@link DocSpec} into PDF output. | ||
| * | ||
| * The spec is projected onto the **existing** JSX components (so the spec and | ||
| * component surfaces can never drift), then handed to the normal render | ||
| * pipeline. Everything here is pure and isomorphic — the same code path as | ||
| * authoring with `<Document>` by hand, just far cheaper to express. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** | ||
| * Turn a compact {@link DocSpec} into a `<Document>` React element. Useful for | ||
| * embedding a spec-authored document inside a larger JSX tree, or for passing to | ||
| * the hooks/viewer components. | ||
| */ | ||
| declare function specToElement(spec: DocSpec): ReactElement; | ||
| /** Compile a {@link DocSpec} into the `pdfnative` document model (no rendering). */ | ||
| declare function compileSpec(spec: DocSpec): DocumentParams; | ||
| /** Render a {@link DocSpec} to raw PDF bytes (`Uint8Array`). */ | ||
| declare function renderSpecToBytes(spec: DocSpec, options?: RenderOptions): Uint8Array; | ||
| /** Render a {@link DocSpec} to a `Blob` (`application/pdf`). */ | ||
| declare function renderSpecToBlob(spec: DocSpec, options?: RenderOptions): Blob; | ||
| /** Render a {@link DocSpec} to a constant-memory async byte stream. */ | ||
| declare function renderSpecToStream(spec: DocSpec, options?: RenderOptions): AsyncGenerator<Uint8Array>; | ||
| /** Render a {@link DocSpec} and write the PDF to a file. Node.js only. */ | ||
| declare function renderSpecToFile(spec: DocSpec, path: string, options?: RenderOptions): Promise<void>; | ||
| /** | ||
| * Versioned JSON Schema (Draft 2020-12) for the compact {@link DocSpec} authoring | ||
| * format, so agents and tooling can self-validate a spec before rendering. | ||
| * | ||
| * The schema is hand-authored, pure data (zero runtime deps — no validator is | ||
| * bundled), and versioned via a `$id` that embeds the package version, so any | ||
| * drift is detectable and pinned by a test. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** A read-only JSON Schema fragment. */ | ||
| type JsonSchema = Readonly<Record<string, unknown>>; | ||
| /** | ||
| * Return the JSON Schema (Draft 2020-12) describing the {@link DocSpec} authoring | ||
| * format. The `$id` embeds the current package version. | ||
| */ | ||
| declare function docSpecSchema(): JsonSchema; | ||
| /** The `$id` (versioned URL) of the current {@link docSpecSchema}. */ | ||
| declare function docSpecSchemaId(): string; | ||
| /** | ||
| * Serialize a reconciled host tree into a `pdfnative` `DocumentParams` object. | ||
| * | ||
| * This is the heart of the "JSX compiles to JSON" promise: a pure, synchronous | ||
| * transform with no side effects, so it is trivially testable in isolation. | ||
| */ | ||
| /** Thrown when a component tree cannot be mapped onto the pdfnative model. */ | ||
| declare class PdfStructureError extends Error { | ||
| constructor(message: string); | ||
| } | ||
| /** | ||
| * Single source of truth for the package version. | ||
| * | ||
| * Kept in its own module so non-barrel files (e.g. the spec schema) can read the | ||
| * version without importing the public barrel — which would create an import | ||
| * cycle. A test pins this constant to `package.json`. | ||
| * | ||
| * @packageDocumentation | ||
| */ | ||
| /** Current package version (kept in sync with `package.json`). */ | ||
| declare const version = "0.2.0"; | ||
| export { type Align, Barcode, type BarcodeProps, type BarcodeSpec, type BarcodeSpecOpts, BlobProvider, type BlobProviderProps, type BlockSpec, type BlockSpecKind, Cell, type CellProps, type Color, type CompiledDocument, type DocSpec, Document, type DocumentProps, FormField, type FormFieldProps, type FormFieldSpec, type FormFieldSpecBody, Heading, type HeadingProps, type HeadingSpec, type HeadingSpecOpts, Image, type ImageProps, type ImageSpec, type ImageSpecBody, Item, type ItemProps, type JsonSchema, Link, type LinkProps, type LinkSpec, type LinkSpecOpts, List, type ListProps, type ListSpec, type ListSpecOpts, PDFDownloadLink, type PDFDownloadLinkProps, PDFViewer, type PDFViewerProps, Page, PageBreak, type PageBreakSpec, type PageProps, type PageSpec, Paragraph, type ParagraphProps, type ParagraphSpec, type ParagraphSpecOpts, type PdfRenderState, PdfStructureError, type RenderOptions, Row, type RowProps, Spacer, type SpacerProps, type SpacerSpec, Svg, type SvgProps, type SvgSpec, type SvgSpecOpts, Table, TableOfContents, type TableOfContentsProps, type TableProps, type TableRowSpec, type TableSpec, type TableSpecBody, Text, Toc, type TocSpec, type TocSpecOpts, type UsePdfResult, type UsePdfStreamResult, compileDocument, compileSpec, docSpecSchema, docSpecSchemaId, renderSpecToBlob, renderSpecToBytes, renderSpecToFile, renderSpecToStream, renderToBlob, renderToBytes, renderToFile, renderToStream, specToElement, usePdf, usePdfStream, version }; |
+931
-4
@@ -1,7 +0,934 @@ | ||
| // src/index.ts | ||
| var version = "0.1.0"; | ||
| var ready = false; | ||
| import { createContext, useState, useRef, useCallback, useEffect, createElement } from 'react'; | ||
| import { buildDocumentPDFBytes, buildDocumentPDFStreamTrue } from 'pdfnative'; | ||
| export { downloadBlob, initNodeCompression, loadFontData, registerFont, registerFonts } from 'pdfnative'; | ||
| import ReactReconciler from 'react-reconciler'; | ||
| import { DefaultEventPriority } from 'react-reconciler/constants.js'; | ||
| export { ready, version }; | ||
| // src/components.tsx | ||
| var h = createElement; | ||
| function Document(props) { | ||
| const { children, ...rest } = props; | ||
| return h("document", rest, children); | ||
| } | ||
| function Page(props) { | ||
| return h("page", null, props.children); | ||
| } | ||
| function Heading(props) { | ||
| const { children, ...rest } = props; | ||
| return h("heading", rest, children); | ||
| } | ||
| function Paragraph(props) { | ||
| const { children, ...rest } = props; | ||
| return h("paragraph", rest, children); | ||
| } | ||
| var Text = Paragraph; | ||
| function List(props) { | ||
| const { children, ...rest } = props; | ||
| return h("list", rest, children); | ||
| } | ||
| function Item(props) { | ||
| return h("item", null, props.children); | ||
| } | ||
| function Table(props) { | ||
| const { children, ...rest } = props; | ||
| return h("table", rest, children); | ||
| } | ||
| function Row(props) { | ||
| const { children, ...rest } = props; | ||
| return h("row", rest, children); | ||
| } | ||
| function Cell(props) { | ||
| return h("cell", null, props.children); | ||
| } | ||
| function Image(props) { | ||
| return h("image", { ...props }); | ||
| } | ||
| function Link(props) { | ||
| const { children, ...rest } = props; | ||
| return h("link", rest, children); | ||
| } | ||
| function Spacer(props = {}) { | ||
| return h("spacer", { ...props }); | ||
| } | ||
| function PageBreak() { | ||
| return h("pageBreak", null); | ||
| } | ||
| function TableOfContents(props = {}) { | ||
| return h("toc", { ...props }); | ||
| } | ||
| var Toc = TableOfContents; | ||
| function Barcode(props) { | ||
| return h("barcode", { ...props }); | ||
| } | ||
| function Svg(props) { | ||
| return h("svg", { ...props }); | ||
| } | ||
| function FormField(props) { | ||
| return h("formField", { ...props }); | ||
| } | ||
| // src/reconciler/nodes.ts | ||
| function createElementNode(tag, props) { | ||
| return { kind: "element", tag, props, children: [] }; | ||
| } | ||
| function createTextNode(text) { | ||
| return { kind: "text", text }; | ||
| } | ||
| function isElementNode(node) { | ||
| return node.kind === "element"; | ||
| } | ||
| // src/reconciler/host-config.ts | ||
| var HOST_CONTEXT = Object.freeze({}); | ||
| function appendChild(parent, child) { | ||
| parent.children.push(child); | ||
| } | ||
| function removeChild(parent, child) { | ||
| const index = parent.children.indexOf(child); | ||
| if (index !== -1) parent.children.splice(index, 1); | ||
| } | ||
| function insertBefore(parent, child, before) { | ||
| const index = parent.children.indexOf(before); | ||
| parent.children.splice(index === -1 ? parent.children.length : index, 0, child); | ||
| } | ||
| var currentUpdatePriority = DefaultEventPriority; | ||
| var hostConfig = { | ||
| supportsMutation: true, | ||
| supportsPersistence: false, | ||
| supportsHydration: false, | ||
| isPrimaryRenderer: false, | ||
| noTimeout: -1, | ||
| createInstance(type, props) { | ||
| return createElementNode(type, props); | ||
| }, | ||
| createTextInstance(text) { | ||
| return createTextNode(text); | ||
| }, | ||
| appendInitialChild(parent, child) { | ||
| appendChild(parent, child); | ||
| }, | ||
| appendChild, | ||
| appendChildToContainer(container, child) { | ||
| appendChild(container, child); | ||
| }, | ||
| insertBefore, | ||
| insertInContainerBefore(container, child, before) { | ||
| insertBefore(container, child, before); | ||
| }, | ||
| removeChild, | ||
| removeChildFromContainer(container, child) { | ||
| removeChild(container, child); | ||
| }, | ||
| finalizeInitialChildren() { | ||
| return false; | ||
| }, | ||
| commitUpdate(instance, _type, _prevProps, nextProps) { | ||
| instance.props = nextProps; | ||
| }, | ||
| commitTextUpdate(textInstance, _oldText, newText) { | ||
| textInstance.text = newText; | ||
| }, | ||
| shouldSetTextContent() { | ||
| return false; | ||
| }, | ||
| clearContainer(container) { | ||
| container.children.length = 0; | ||
| }, | ||
| getRootHostContext() { | ||
| return HOST_CONTEXT; | ||
| }, | ||
| getChildHostContext() { | ||
| return HOST_CONTEXT; | ||
| }, | ||
| getPublicInstance(instance) { | ||
| return instance; | ||
| }, | ||
| prepareForCommit() { | ||
| return null; | ||
| }, | ||
| resetAfterCommit() { | ||
| }, | ||
| preparePortalMount() { | ||
| }, | ||
| scheduleTimeout: setTimeout, | ||
| cancelTimeout: clearTimeout, | ||
| supportsMicrotasks: true, | ||
| scheduleMicrotask: typeof queueMicrotask === "function" ? queueMicrotask : (cb) => void Promise.resolve().then(cb), | ||
| getInstanceFromNode() { | ||
| return null; | ||
| }, | ||
| beforeActiveInstanceBlur() { | ||
| }, | ||
| afterActiveInstanceBlur() { | ||
| }, | ||
| prepareScopeUpdate() { | ||
| }, | ||
| getInstanceFromScope() { | ||
| return null; | ||
| }, | ||
| detachDeletedInstance() { | ||
| }, | ||
| // ── Transition / priority surface (react-reconciler 0.31+) ────────────── | ||
| NotPendingTransition: null, | ||
| HostTransitionContext: createContext( | ||
| null | ||
| ), | ||
| setCurrentUpdatePriority(newPriority) { | ||
| currentUpdatePriority = newPriority; | ||
| }, | ||
| getCurrentUpdatePriority() { | ||
| return currentUpdatePriority; | ||
| }, | ||
| resolveUpdatePriority() { | ||
| return currentUpdatePriority || DefaultEventPriority; | ||
| }, | ||
| resetFormInstance() { | ||
| }, | ||
| requestPostPaintCallback() { | ||
| }, | ||
| shouldAttemptEagerTransition() { | ||
| return false; | ||
| }, | ||
| trackSchedulerEvent() { | ||
| }, | ||
| resolveEventType() { | ||
| return null; | ||
| }, | ||
| resolveEventTimeStamp() { | ||
| return -1.1; | ||
| }, | ||
| // ── Suspense-on-commit surface (unused: we never suspend) ─────────────── | ||
| maySuspendCommit() { | ||
| return false; | ||
| }, | ||
| preloadInstance() { | ||
| return true; | ||
| }, | ||
| startSuspendingCommit() { | ||
| }, | ||
| suspendInstance() { | ||
| }, | ||
| waitForCommitToBeReady() { | ||
| return null; | ||
| } | ||
| }; | ||
| var reconciler = ReactReconciler(hostConfig); | ||
| // src/reconciler/serialize.ts | ||
| var PdfStructureError = class extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "PdfStructureError"; | ||
| } | ||
| }; | ||
| function collectText(node) { | ||
| if (!isElementNode(node)) return node.text; | ||
| let out = ""; | ||
| for (const child of node.children) out += collectText(child); | ||
| return out; | ||
| } | ||
| function elementText(node) { | ||
| if (typeof node.props.text === "string") return node.props.text; | ||
| return node.children.map(collectText).join(""); | ||
| } | ||
| function elementChildren(node) { | ||
| return node.children.filter(isElementNode); | ||
| } | ||
| function compact(obj) { | ||
| const out = {}; | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| if (value !== void 0) out[key] = value; | ||
| } | ||
| return out; | ||
| } | ||
| function toBlock(node) { | ||
| const p = node.props; | ||
| switch (node.tag) { | ||
| case "heading": | ||
| return compact({ | ||
| type: "heading", | ||
| text: elementText(node), | ||
| level: p.level ?? 1, | ||
| color: p.color | ||
| }); | ||
| case "paragraph": | ||
| return compact({ | ||
| type: "paragraph", | ||
| text: elementText(node), | ||
| fontSize: p.fontSize, | ||
| lineHeight: p.lineHeight, | ||
| align: p.align, | ||
| indent: p.indent, | ||
| color: p.color | ||
| }); | ||
| case "list": | ||
| return compact({ | ||
| type: "list", | ||
| items: p.items ?? elementChildren(node).filter((c) => c.tag === "item").map(elementText), | ||
| style: p.ordered ? "numbered" : p.style ?? "bullet", | ||
| fontSize: p.fontSize | ||
| }); | ||
| case "table": | ||
| return toTableBlock(node); | ||
| case "image": | ||
| return compact({ | ||
| type: "image", | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| alt: p.alt | ||
| }); | ||
| case "link": | ||
| return compact({ | ||
| type: "link", | ||
| text: elementText(node), | ||
| url: p.url ?? p.href, | ||
| fontSize: p.fontSize, | ||
| color: p.color | ||
| }); | ||
| case "spacer": | ||
| return { type: "spacer", height: p.height ?? 12 }; | ||
| case "pageBreak": | ||
| return { type: "pageBreak" }; | ||
| case "toc": | ||
| return compact({ | ||
| type: "toc", | ||
| title: p.title, | ||
| maxLevel: p.maxLevel, | ||
| fontSize: p.fontSize, | ||
| indent: p.indent | ||
| }); | ||
| case "barcode": | ||
| return compact({ | ||
| type: "barcode", | ||
| format: p.format, | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| ecLevel: p.ecLevel, | ||
| pdf417ECLevel: p.pdf417ECLevel | ||
| }); | ||
| case "svg": | ||
| return compact({ | ||
| type: "svg", | ||
| data: p.data, | ||
| width: p.width, | ||
| height: p.height, | ||
| align: p.align, | ||
| viewBox: p.viewBox, | ||
| fill: p.fill, | ||
| stroke: p.stroke, | ||
| strokeWidth: p.strokeWidth, | ||
| alt: p.alt | ||
| }); | ||
| case "formField": | ||
| return compact({ | ||
| type: "formField", | ||
| fieldType: p.fieldType ?? p.type, | ||
| name: p.name, | ||
| label: p.label, | ||
| value: p.value, | ||
| placeholder: p.placeholder, | ||
| width: p.width, | ||
| height: p.height, | ||
| fontSize: p.fontSize, | ||
| options: p.options, | ||
| readOnly: p.readOnly, | ||
| required: p.required, | ||
| maxLength: p.maxLength, | ||
| checked: p.checked | ||
| }); | ||
| case "page": | ||
| return blocksFrom(node.children); | ||
| default: | ||
| throw new PdfStructureError( | ||
| `<${node.tag}> is not valid here. Expected a block-level component inside <Document> or <Page>.` | ||
| ); | ||
| } | ||
| } | ||
| function toTableBlock(node) { | ||
| const p = node.props; | ||
| const rowNodes = elementChildren(node).filter((c) => c.tag === "row"); | ||
| let headers = p.headers; | ||
| let bodyRows = rowNodes; | ||
| if (!headers && rowNodes.length > 0) { | ||
| const headerRow = rowNodes.find((r) => r.props.header === true) ?? rowNodes[0]; | ||
| headers = elementChildren(headerRow).filter((c) => c.tag === "cell").map(elementText); | ||
| bodyRows = rowNodes.filter((r) => r !== headerRow); | ||
| } | ||
| const rows = p.rows ?? bodyRows.map((r) => ({ | ||
| cells: elementChildren(r).filter((c) => c.tag === "cell").map(elementText), | ||
| type: r.props.variant ?? "default", | ||
| pointed: r.props.pointed ?? false | ||
| })); | ||
| return compact({ | ||
| type: "table", | ||
| headers: headers ?? [], | ||
| rows, | ||
| columns: p.columns, | ||
| clipCells: p.clipCells, | ||
| autoFitColumns: p.autoFitColumns, | ||
| wrap: p.wrap, | ||
| repeatHeader: p.repeatHeader, | ||
| zebra: p.zebra, | ||
| caption: p.caption, | ||
| minRowHeight: p.minRowHeight, | ||
| cellPadding: p.cellPadding | ||
| }); | ||
| } | ||
| function blocksFrom(children) { | ||
| const blocks = []; | ||
| let pageIndex = 0; | ||
| for (const child of children) { | ||
| if (!isElementNode(child)) continue; | ||
| if (child.tag === "page") { | ||
| if (pageIndex > 0) blocks.push({ type: "pageBreak" }); | ||
| pageIndex += 1; | ||
| } | ||
| const result = toBlock(child); | ||
| if (Array.isArray(result)) blocks.push(...result); | ||
| else blocks.push(result); | ||
| } | ||
| return blocks; | ||
| } | ||
| function findDocument(container) { | ||
| const elements = container.children.filter(isElementNode); | ||
| const doc = elements.find((c) => c.tag === "document"); | ||
| if (doc) return doc; | ||
| if (elements.length === 1 && elements[0].tag !== "document") { | ||
| throw new PdfStructureError( | ||
| "The root component must be <Document>. Wrap your content in <Document>\u2026</Document>." | ||
| ); | ||
| } | ||
| throw new PdfStructureError("No <Document> found at the root of the tree."); | ||
| } | ||
| function serialize(container) { | ||
| const doc = findDocument(container); | ||
| const p = doc.props; | ||
| const params = compact({ | ||
| title: p.title, | ||
| blocks: blocksFrom(doc.children), | ||
| footerText: p.footerText, | ||
| fontEntries: p.fontEntries, | ||
| metadata: p.metadata, | ||
| layout: p.layout | ||
| }); | ||
| return params; | ||
| } | ||
| // src/reconciler/render.ts | ||
| var LegacyRoot = 0; | ||
| function noop() { | ||
| } | ||
| function onError(error) { | ||
| throw error instanceof Error ? error : new Error(String(error)); | ||
| } | ||
| function compile(element) { | ||
| const container = { children: [] }; | ||
| const root = reconciler.createContainer( | ||
| container, | ||
| LegacyRoot, | ||
| null, | ||
| false, | ||
| null, | ||
| "pdfnative", | ||
| onError, | ||
| onError, | ||
| onError, | ||
| noop, | ||
| null | ||
| ); | ||
| const r = reconciler; | ||
| const commit = (next) => { | ||
| if (typeof r.updateContainerSync === "function") { | ||
| r.updateContainerSync(next, root, null, noop); | ||
| r.flushSyncWork?.(); | ||
| } else { | ||
| reconciler.flushSync(() => { | ||
| reconciler.updateContainer(next, root, null, noop); | ||
| }); | ||
| } | ||
| }; | ||
| commit(element); | ||
| const params = serialize(container); | ||
| commit(null); | ||
| return params; | ||
| } | ||
| // src/render.ts | ||
| function compileDocument(node) { | ||
| return compile(node); | ||
| } | ||
| function prepare(node, options) { | ||
| const compiled = compile(node); | ||
| const params = options?.fontEntries && options.fontEntries.length > 0 ? { | ||
| ...compiled, | ||
| fontEntries: [...compiled.fontEntries ?? [], ...options.fontEntries] | ||
| } : compiled; | ||
| const layout = options?.layout || params.layout ? { ...params.layout, ...options?.layout } : void 0; | ||
| return { params, layout }; | ||
| } | ||
| function renderToBytes(node, options) { | ||
| const { params, layout } = prepare(node, options); | ||
| return buildDocumentPDFBytes(params, layout); | ||
| } | ||
| function renderToBlob(node, options) { | ||
| const bytes = renderToBytes(node, options); | ||
| return new Blob([bytes], { type: "application/pdf" }); | ||
| } | ||
| function renderToStream(node, options) { | ||
| const { params, layout } = prepare(node, options); | ||
| return buildDocumentPDFStreamTrue(params, layout); | ||
| } | ||
| async function renderToFile(node, path, options) { | ||
| const bytes = renderToBytes(node, options); | ||
| const { writeFile } = await import('fs/promises'); | ||
| await writeFile(path, bytes); | ||
| } | ||
| var INITIAL = { | ||
| url: null, | ||
| blob: null, | ||
| bytes: null, | ||
| loading: true, | ||
| error: null | ||
| }; | ||
| function usePdf(element, options) { | ||
| const [state, setState] = useState(INITIAL); | ||
| const [nonce, setNonce] = useState(0); | ||
| const optionsRef = useRef(options); | ||
| optionsRef.current = options; | ||
| const update = useCallback(() => { | ||
| setNonce((n) => n + 1); | ||
| }, []); | ||
| useEffect(() => { | ||
| let cancelled = false; | ||
| let url = null; | ||
| setState((prev) => prev.loading ? prev : { ...prev, loading: true }); | ||
| queueMicrotask(() => { | ||
| if (cancelled) return; | ||
| try { | ||
| const bytes = renderToBytes(element, optionsRef.current); | ||
| const blob = new Blob([bytes], { type: "application/pdf" }); | ||
| url = URL.createObjectURL(blob); | ||
| setState({ url, blob, bytes, loading: false, error: null }); | ||
| } catch (err) { | ||
| setState({ | ||
| url: null, | ||
| blob: null, | ||
| bytes: null, | ||
| loading: false, | ||
| error: err instanceof Error ? err : new Error(String(err)) | ||
| }); | ||
| } | ||
| }); | ||
| return () => { | ||
| cancelled = true; | ||
| if (url) URL.revokeObjectURL(url); | ||
| }; | ||
| }, [element, nonce]); | ||
| return { ...state, update }; | ||
| } | ||
| function usePdfStream(element, options) { | ||
| const elementRef = useRef(element); | ||
| elementRef.current = element; | ||
| const optionsRef = useRef(options); | ||
| optionsRef.current = options; | ||
| const getStream = useCallback( | ||
| () => renderToStream(elementRef.current, optionsRef.current), | ||
| [] | ||
| ); | ||
| return { getStream }; | ||
| } | ||
| function toState(result) { | ||
| return { | ||
| url: result.url, | ||
| blob: result.blob, | ||
| bytes: result.bytes, | ||
| loading: result.loading, | ||
| error: result.error | ||
| }; | ||
| } | ||
| function PDFViewer(props) { | ||
| const { document, options, title = "PDF preview", ...rest } = props; | ||
| const { url } = usePdf(document, options); | ||
| return createElement("iframe", { | ||
| ...rest, | ||
| title, | ||
| src: url ?? void 0 | ||
| }); | ||
| } | ||
| function BlobProvider(props) { | ||
| const result = usePdf(props.document, props.options); | ||
| return createElement( | ||
| "div", | ||
| { style: { display: "contents" } }, | ||
| props.children(toState(result)) | ||
| ); | ||
| } | ||
| function PDFDownloadLink(props) { | ||
| const { document, fileName = "document.pdf", options, children, ...rest } = props; | ||
| const result = usePdf(document, options); | ||
| const content = typeof children === "function" ? children(toState(result)) : children; | ||
| return createElement( | ||
| "a", | ||
| { | ||
| ...rest, | ||
| href: result.url ?? void 0, | ||
| download: fileName | ||
| }, | ||
| content | ||
| ); | ||
| } | ||
| function toRows(rows) { | ||
| return rows.map( | ||
| (row) => Array.isArray(row) ? { cells: row, type: "default", pointed: false } : row | ||
| ); | ||
| } | ||
| function blockToElement(block, key) { | ||
| switch (block[0]) { | ||
| case "h1": | ||
| return createElement(Heading, { key, level: 1, ...block[2] }, block[1]); | ||
| case "h2": | ||
| return createElement(Heading, { key, level: 2, ...block[2] }, block[1]); | ||
| case "h3": | ||
| return createElement(Heading, { key, level: 3, ...block[2] }, block[1]); | ||
| case "p": | ||
| return createElement(Paragraph, { key, ...block[2] }, block[1]); | ||
| case "ul": | ||
| return createElement(List, { key, items: block[1], ...block[2] }); | ||
| case "ol": | ||
| return createElement(List, { key, ordered: true, items: block[1], ...block[2] }); | ||
| case "table": | ||
| return createElement(Table, { | ||
| key, | ||
| headers: block[1].h, | ||
| rows: toRows(block[1].r), | ||
| columns: block[1].columns, | ||
| zebra: block[1].zebra, | ||
| caption: block[1].caption, | ||
| clipCells: block[1].clipCells, | ||
| autoFitColumns: block[1].autoFitColumns, | ||
| wrap: block[1].wrap, | ||
| repeatHeader: block[1].repeatHeader, | ||
| minRowHeight: block[1].minRowHeight, | ||
| cellPadding: block[1].cellPadding | ||
| }); | ||
| case "img": | ||
| return createElement(Image, { key, ...block[1] }); | ||
| case "link": | ||
| return createElement(Link, { key, ...block[2] }, block[1]); | ||
| case "sp": | ||
| return createElement(Spacer, { key, height: block[1] }); | ||
| case "br": | ||
| return createElement(PageBreak, { key }); | ||
| case "page": | ||
| return createElement(Page, { key }, block[1].map(blockToElement)); | ||
| case "toc": | ||
| return createElement(TableOfContents, { key, ...block[1] }); | ||
| case "qr": | ||
| case "code128": | ||
| case "ean13": | ||
| case "pdf417": | ||
| case "datamatrix": | ||
| return createElement(Barcode, { | ||
| key, | ||
| format: block[0], | ||
| data: block[1], | ||
| ...block[2] | ||
| }); | ||
| case "svg": | ||
| return createElement(Svg, { key, data: block[1], ...block[2] }); | ||
| case "field": | ||
| return createElement(FormField, { key, ...block[1] }); | ||
| default: { | ||
| const kind = block[0]; | ||
| throw new PdfStructureError(`Unknown DocSpec block kind: ${String(kind)}`); | ||
| } | ||
| } | ||
| } | ||
| function specToElement(spec) { | ||
| const children = spec.blocks.map(blockToElement); | ||
| return createElement( | ||
| Document, | ||
| { | ||
| title: spec.title, | ||
| footerText: spec.footerText, | ||
| metadata: spec.metadata, | ||
| fontEntries: spec.fontEntries, | ||
| layout: spec.layout | ||
| }, | ||
| children | ||
| ); | ||
| } | ||
| function compileSpec(spec) { | ||
| return compileDocument(specToElement(spec)); | ||
| } | ||
| function renderSpecToBytes(spec, options) { | ||
| return renderToBytes(specToElement(spec), options); | ||
| } | ||
| function renderSpecToBlob(spec, options) { | ||
| return renderToBlob(specToElement(spec), options); | ||
| } | ||
| function renderSpecToStream(spec, options) { | ||
| return renderToStream(specToElement(spec), options); | ||
| } | ||
| function renderSpecToFile(spec, path, options) { | ||
| return renderToFile(specToElement(spec), path, options); | ||
| } | ||
| // src/version.ts | ||
| var version = "0.2.0"; | ||
| // src/spec/schema.ts | ||
| var DRAFT = "https://json-schema.org/draft/2020-12/schema"; | ||
| var ID_BASE = "https://pdfnative.dev/schema/react"; | ||
| function schemaId() { | ||
| return `${ID_BASE}/${version}/doc-spec.schema.json`; | ||
| } | ||
| function headingBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "HeadingSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["h1", "h2", "h3"] }, | ||
| { type: "string", description: "Heading text." }, | ||
| { type: "object", description: "Optional { color }." } | ||
| ] | ||
| }; | ||
| } | ||
| function paragraphBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ParagraphSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "p" }, | ||
| { type: "string", description: "Paragraph text." }, | ||
| { type: "object", description: "Optional { fontSize, lineHeight, align, indent, color }." } | ||
| ] | ||
| }; | ||
| } | ||
| function listBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ListSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["ul", "ol"] }, | ||
| { type: "array", items: { type: "string" }, description: "List items." }, | ||
| { type: "object", description: "Optional { fontSize }." } | ||
| ] | ||
| }; | ||
| } | ||
| function tableBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "TableSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "table" }, | ||
| { | ||
| type: "object", | ||
| required: ["r"], | ||
| properties: { | ||
| h: { type: "array", items: { type: "string" }, description: "Column headers." }, | ||
| r: { | ||
| type: "array", | ||
| description: "Rows: arrays of cell strings, or full PdfRow objects.", | ||
| items: { | ||
| oneOf: [ | ||
| { type: "array", items: { type: "string" } }, | ||
| { | ||
| type: "object", | ||
| required: ["cells", "type", "pointed"], | ||
| properties: { | ||
| cells: { type: "array", items: { type: "string" } }, | ||
| type: { type: "string" }, | ||
| pointed: { type: "boolean" } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| }, | ||
| zebra: { type: ["boolean", "string", "array"] }, | ||
| caption: { type: "string" } | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function imageBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "ImageSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "img" }, | ||
| { | ||
| type: "object", | ||
| required: ["data"], | ||
| description: "Image body: { data: Uint8Array, width?, height?, align?, alt? }." | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function linkBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "LinkSpec", | ||
| minItems: 3, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "link" }, | ||
| { type: "string", description: "Link text." }, | ||
| { | ||
| type: "object", | ||
| description: "Options; one of url/href is required.", | ||
| properties: { | ||
| url: { type: "string" }, | ||
| href: { type: "string" } | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function spacerBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "SpacerSpec", | ||
| minItems: 1, | ||
| maxItems: 2, | ||
| prefixItems: [{ const: "sp" }, { type: "number", description: "Height in points." }] | ||
| }; | ||
| } | ||
| function pageBreakBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "PageBreakSpec", | ||
| minItems: 1, | ||
| maxItems: 1, | ||
| prefixItems: [{ const: "br" }] | ||
| }; | ||
| } | ||
| function pageBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "PageSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "page" }, | ||
| { type: "array", description: "Nested blocks for this page.", items: { $ref: "#/$defs/block" } } | ||
| ] | ||
| }; | ||
| } | ||
| function tocBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "TocSpec", | ||
| minItems: 1, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "toc" }, | ||
| { type: "object", description: "Optional { title, maxLevel, fontSize, indent }." } | ||
| ] | ||
| }; | ||
| } | ||
| function barcodeBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "BarcodeSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { enum: ["qr", "code128", "ean13", "pdf417", "datamatrix"] }, | ||
| { type: "string", description: "Data to encode." }, | ||
| { type: "object", description: "Optional { width, height, align, ecLevel, pdf417ECLevel }." } | ||
| ] | ||
| }; | ||
| } | ||
| function svgBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "SvgSpec", | ||
| minItems: 2, | ||
| maxItems: 3, | ||
| prefixItems: [ | ||
| { const: "svg" }, | ||
| { type: "string", description: "SVG path data or inline markup." }, | ||
| { type: "object", description: "Optional { width, height, align, viewBox, fill, stroke, strokeWidth }." } | ||
| ] | ||
| }; | ||
| } | ||
| function fieldBlock() { | ||
| return { | ||
| type: "array", | ||
| title: "FormFieldSpec", | ||
| minItems: 2, | ||
| maxItems: 2, | ||
| prefixItems: [ | ||
| { const: "field" }, | ||
| { | ||
| type: "object", | ||
| required: ["fieldType", "name"], | ||
| description: "Form-field body: { fieldType, name, label?, value?, options?, \u2026 }." | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| function docSpecSchema() { | ||
| return { | ||
| $schema: DRAFT, | ||
| $id: schemaId(), | ||
| title: "pdfnative-react DocSpec", | ||
| description: "Compact, token-frugal document specification. Compiles to the same pdfnative model as the JSX components.", | ||
| type: "object", | ||
| required: ["blocks"], | ||
| properties: { | ||
| title: { type: "string" }, | ||
| footerText: { type: "string" }, | ||
| metadata: { type: "object", description: "DocumentMetadata." }, | ||
| fontEntries: { type: "array", items: { type: "object" } }, | ||
| layout: { type: "object", description: "PdfLayoutOptions overrides." }, | ||
| blocks: { | ||
| type: "array", | ||
| description: "Ordered document blocks (positional tuples).", | ||
| items: { $ref: "#/$defs/block" } | ||
| } | ||
| }, | ||
| $defs: { | ||
| block: { | ||
| oneOf: [ | ||
| headingBlock(), | ||
| paragraphBlock(), | ||
| listBlock(), | ||
| tableBlock(), | ||
| imageBlock(), | ||
| linkBlock(), | ||
| spacerBlock(), | ||
| pageBreakBlock(), | ||
| pageBlock(), | ||
| tocBlock(), | ||
| barcodeBlock(), | ||
| svgBlock(), | ||
| fieldBlock() | ||
| ] | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| function docSpecSchemaId() { | ||
| return schemaId(); | ||
| } | ||
| export { Barcode, BlobProvider, Cell, Document, FormField, Heading, Image, Item, Link, List, PDFDownloadLink, PDFViewer, Page, PageBreak, Paragraph, PdfStructureError, Row, Spacer, Svg, Table, TableOfContents, Text, Toc, compileDocument, compileSpec, docSpecSchema, docSpecSchemaId, renderSpecToBlob, renderSpecToBytes, renderSpecToFile, renderSpecToStream, renderToBlob, renderToBytes, renderToFile, renderToStream, specToElement, usePdf, usePdfStream, version }; | ||
| //# sourceMappingURL=index.js.map | ||
| //# sourceMappingURL=index.js.map |
+19
-7
| { | ||
| "name": "pdfnative-react", | ||
| "version": "0.1.0", | ||
| "version": "0.2.0", | ||
| "description": "React renderer for pdfnative — declarative JSX components (<Document>, <Page>, <Table>, <Barcode>…) that compile to PDF on-device with zero SaaS round-trips. Live preview, streaming, 22 Unicode scripts. The frontend gateway to the pdfnative ecosystem.", | ||
@@ -35,5 +35,7 @@ "type": "module", | ||
| "lint": "eslint src/", | ||
| "lint:fix": "eslint src/ --fix", | ||
| "typecheck": "tsc --noEmit", | ||
| "typecheck:tests": "tsc --project tsconfig.test.json --noEmit", | ||
| "typecheck:all": "npm run typecheck && npm run typecheck:tests", | ||
| "typecheck:samples": "tsc --project samples/tsconfig.json --noEmit", | ||
| "typecheck:all": "npm run typecheck && npm run typecheck:tests && npm run typecheck:samples", | ||
| "prepublishOnly": "npm run build" | ||
@@ -60,3 +62,8 @@ }, | ||
| "ssr", | ||
| "frontend" | ||
| "frontend", | ||
| "ai-agent", | ||
| "agentic", | ||
| "json-schema", | ||
| "sbom", | ||
| "supply-chain" | ||
| ], | ||
@@ -67,3 +74,3 @@ "author": "Nizoka <hello@pdfnative.dev> (https://pdfnative.dev)", | ||
| "type": "git", | ||
| "url": "https://github.com/Nizoka/pdfnative-react.git" | ||
| "url": "git+https://github.com/Nizoka/pdfnative-react.git" | ||
| }, | ||
@@ -86,14 +93,19 @@ "homepage": "https://github.com/Nizoka/pdfnative-react#readme", | ||
| "peerDependencies": { | ||
| "react": ">=18.0.0" | ||
| "react": "^19.0.0" | ||
| }, | ||
| "dependencies": { | ||
| "pdfnative": "^1.3.0", | ||
| "react-reconciler": "^0.29.0" | ||
| "react-reconciler": "^0.31.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.0.0", | ||
| "@testing-library/react": "^16.1.0", | ||
| "@types/node": "^22.0.0", | ||
| "@types/react": "^19.0.0", | ||
| "@types/react-reconciler": "^0.28.0", | ||
| "@types/react-reconciler": "^0.32.0", | ||
| "@vitest/coverage-v8": "^4.1.7", | ||
| "eslint": "^9.0.0", | ||
| "jsdom": "^25.0.0", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0", | ||
| "tsup": "^8.0.0", | ||
@@ -100,0 +112,0 @@ "typescript": "^5.4.0", |
+163
-18
@@ -7,32 +7,170 @@ # pdfnative-react | ||
| [](https://www.npmjs.com/package/pdfnative) | ||
| [](https://scorecard.dev/viewer/?uri=github.com/Nizoka/pdfnative-react) | ||
| > **Preview release.** `0.1.0` reserves the package name. The declarative React | ||
| > renderer lands in `0.2.0` — follow the [repository](https://github.com/Nizoka/pdfnative-react) | ||
| > for progress. | ||
| Write PDFs the way you write UIs. `pdfnative-react` turns declarative JSX into | ||
| Write PDFs the way you write UIs. **pdfnative-react** turns declarative JSX into | ||
| real, on-device PDF documents powered by the zero-dependency | ||
| [`pdfnative`](https://www.npmjs.com/package/pdfnative) engine — no SaaS | ||
| round-trips, your documents never leave the process. | ||
| [`pdfnative`](https://www.npmjs.com/package/pdfnative) engine — no DOM, no | ||
| headless browser, no SaaS round-trips. Your documents never leave the process. | ||
| ```tsx | ||
| // Coming in 0.2.0 | ||
| import { Document, Page, Heading, Text, Table, usePdf } from 'pdfnative-react'; | ||
| import { Document, Heading, Text, Table, renderToBytes } from 'pdfnative-react'; | ||
| function Invoice() { | ||
| return ( | ||
| <Document title="Invoice #1024"> | ||
| <Page> | ||
| const bytes = renderToBytes( | ||
| <Document title="Invoice #1024" footerText="Acme Inc"> | ||
| <Heading level={1}>Invoice #1024</Heading> | ||
| <Text>Thank you for your business.</Text> | ||
| <Table | ||
| headers={['Item', 'Qty', 'Total']} | ||
| rows={[['Pro plan', '1', '$49.00']]} | ||
| headers={['Item', 'Qty', 'Total']} | ||
| rows={[ | ||
| { cells: ['Pro plan', '1', '$49.00'], type: 'default', pointed: false }, | ||
| ]} | ||
| zebra | ||
| /> | ||
| </Page> | ||
| </Document> | ||
| ); | ||
| </Document>, | ||
| ); | ||
| // → Uint8Array, a valid PDF (%PDF-… …%%EOF) | ||
| ``` | ||
| ## Why pdfnative-react | ||
| - **Declarative & familiar.** Components mirror `@react-pdf/renderer` | ||
| ergonomics (`Document`, `Page`, `Text`, `Image`, `Link`, `usePdf`, | ||
| `PDFViewer`, `PDFDownloadLink`, `BlobProvider`). | ||
| - **On-device.** A custom React reconciler compiles your tree — synchronously, | ||
| with no DOM — to the `pdfnative` model and renders the bytes locally. | ||
| - **Honest model.** Components map 1:1 onto pdfnative blocks. There is no | ||
| CSS/flexbox engine and no `<View>` — it is a declarative *block flow*. | ||
| - **Token-frugal AI authoring.** A compact `DocSpec` lets LLM agents emit | ||
| documents with a fraction of the tokens of JSX, validated by a versioned JSON | ||
| Schema — see [Agent authoring](#agent-authoring-token-frugal). | ||
| - **Typed, tested, tree-shakeable.** Strict TypeScript, dual ESM + CJS, source | ||
| maps, provenance-signed publishes. | ||
| ## Install | ||
| ```bash | ||
| npm install pdfnative-react pdfnative react | ||
| ``` | ||
| Requires **React 19** and **Node.js ≥ 20**. | ||
| ## Components | ||
| Every component maps 1:1 onto a pdfnative block. | ||
| | Component | Renders | | ||
| |---|---| | ||
| | `Document` | The required root (`title`, `footerText`, `metadata`, `fontEntries`, `layout`). | | ||
| | `Page` | An explicit page boundary (content auto-paginates otherwise). | | ||
| | `Heading` | A section heading (`level` 1–3); feeds the auto `TableOfContents`. | | ||
| | `Paragraph` / `Text` | A wrapping paragraph (`fontSize`, `lineHeight`, `align`, `indent`, `color`). | | ||
| | `List` / `Item` | A bullet or numbered (`ordered`) list. | | ||
| | `Table` / `Row` / `Cell` | A data table (data-driven `headers`/`rows`, or JSX `<Row>`/`<Cell>`). | | ||
| | `Image` | An embedded JPEG/PNG (`data: Uint8Array`). | | ||
| | `Link` | A clickable hyperlink (`url`/`href`). | | ||
| | `Spacer` | Vertical whitespace (`height`). | | ||
| | `PageBreak` | A hard page break. | | ||
| | `TableOfContents` / `Toc` | An auto-generated TOC built from headings. | | ||
| | `Barcode` | QR, Code 128, EAN-13, PDF417, Data Matrix (`format`, `data`). | | ||
| | `Svg` | Inline vector graphics (path data or markup). | | ||
| | `FormField` | Interactive AcroForm widgets (`fieldType`, `name`). | | ||
| ## Rendering | ||
| ```ts | ||
| import { | ||
| renderToBytes, // (node, options?) => Uint8Array | ||
| renderToBlob, // (node, options?) => Blob (application/pdf) | ||
| renderToStream, // (node, options?) => AsyncGenerator<Uint8Array> (constant memory) | ||
| renderToFile, // (node, path, options?) => Promise<void> (Node only) | ||
| compileDocument, // (node) => DocumentParams (inspect the model, no render) | ||
| } from 'pdfnative-react'; | ||
| ``` | ||
| `options` is `{ layout?: Partial<PdfLayoutOptions>; fontEntries?: FontEntry[] }` | ||
| and merges on top of anything set on `<Document>` — page size, margins, colors, | ||
| PDF/A mode, encryption, and non-Latin fonts. | ||
| ## Hooks & client components | ||
| Client modules carry `'use client'`. | ||
| ```tsx | ||
| 'use client'; | ||
| import { usePdf } from 'pdfnative-react'; | ||
| function Preview({ doc }: { doc: React.ReactElement }) { | ||
| const { url, loading } = usePdf(doc); | ||
| return loading ? <p>Rendering…</p> : <iframe title="preview" src={url} />; | ||
| } | ||
| ``` | ||
| - `usePdf(element, options?)` → `{ url, blob, bytes, loading, error, update }` | ||
| - `usePdfStream(element, options?)` → `{ getStream() }` | ||
| - `PDFViewer` — live `<iframe>` preview. | ||
| - `PDFDownloadLink` — one-click download (supports a render-prop child). | ||
| - `BlobProvider` — render-prop access to the raw `Blob`. | ||
| ## Agent authoring (token-frugal) | ||
| pdfnative-react is a *library*, so the place LLM agents spend tokens is | ||
| **authoring** documents. The compact `DocSpec` expresses the same document as | ||
| terse, JSON-serializable tuples — and compiles to the **exact same** PDF as the | ||
| JSX, because it is built on the very same components. | ||
| ```ts | ||
| import { renderSpecToBytes, type DocSpec } from 'pdfnative-react'; | ||
| const spec: DocSpec = { | ||
| title: 'Invoice #1024', | ||
| footerText: 'Acme Inc', | ||
| blocks: [ | ||
| ['h1', 'Invoice #1024'], | ||
| ['p', 'Thank you for your business.', { align: 'right' }], | ||
| ['table', { h: ['Item', 'Total'], r: [['Pro plan', '$49.00']], zebra: true }], | ||
| ['qr', 'https://acme.example/pay/1024', { align: 'right' }], | ||
| ], | ||
| }; | ||
| const bytes = renderSpecToBytes(spec); | ||
| ``` | ||
| The equivalent JSX is several times more tokens for a typical document (the gap | ||
| widens on larger ones), because every block carries opening/closing tags and | ||
| prop names. Same bytes out, far fewer tokens in. | ||
| - `compileSpec(spec)` → `DocumentParams` · `specToElement(spec)` → `<Document>` element | ||
| - `renderSpecToBytes` / `renderSpecToBlob` / `renderSpecToStream` / `renderSpecToFile` | ||
| - `docSpecSchema()` → a Draft 2020-12 JSON Schema whose `$id` embeds the package | ||
| version, so agents can self-validate a spec before rendering. | ||
| Block tuples: `['h1'|'h2'|'h3', text, opts?]`, `['p', text, opts?]`, | ||
| `['ul'|'ol', items, opts?]`, `['table', { h?, r }]`, `['img', { data }]`, | ||
| `['link', text, { url }]`, `['sp', height?]`, `['br']`, `['page', blocks]`, | ||
| `['toc', opts?]`, `['qr'|'code128'|'ean13'|'pdf417'|'datamatrix', data, opts?]`, | ||
| `['svg', data, opts?]`, `['field', { fieldType, name, … }]`. | ||
| ## Fonts & environment | ||
| Re-exported from the engine: `registerFonts`, `registerFont`, `loadFontData` | ||
| (Node), `downloadBlob` (browser), `initNodeCompression` (Node). Pass non-Latin | ||
| fonts via the `fontEntries` render option. | ||
| ## Migrating from `@react-pdf/renderer` | ||
| | `@react-pdf/renderer` | pdfnative-react | | ||
| |---|---| | ||
| | `<Document>` / `<Page>` | `<Document>` / `<Page>` | | ||
| | `<Text>` | `<Text>` (alias of `<Paragraph>`) | | ||
| | `<View>` + flexbox styles | *(none — declarative block flow; use blocks + `<Spacer>`)* | | ||
| | `StyleSheet` | per-component props (`align`, `color`, `fontSize`, …) | | ||
| | `<PDFViewer>` / `<PDFDownloadLink>` / `<BlobProvider>` | same names, same shape | | ||
| | `usePDF()` | `usePdf()` | | ||
| ## Examples | ||
| Runnable, type-checked examples live in [samples/](samples/README.md): typography, | ||
| tables, images, links, barcodes, SVG, form fields, multi-page structure, custom | ||
| fonts, layout/PDF-A, the client hooks/components, and the compact agent spec. | ||
| ## The pdfnative ecosystem | ||
@@ -47,4 +185,11 @@ | ||
| ## Documentation | ||
| - [Knowledge Base](docs/KNOWLEDGE_BASE.md) — architecture, the compile pipeline, | ||
| the react-reconciler version contract, and the agent authoring contract. | ||
| - [AGENTS.md](AGENTS.md) — guidance for AI agents working in this repo. | ||
| - [CHANGELOG.md](CHANGELOG.md) · [ROADMAP.md](ROADMAP.md) · [CONTRIBUTING.md](CONTRIBUTING.md) | ||
| ## License | ||
| [MIT](LICENSE) © 2026 Nizoka — [Plika](https://plika.app) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
301869
3267.57%2534
8637.93%194
295.92%14
55.56%1
Infinity%1
Infinity%+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated