/** * Autype Document Schema — Zod Definitions * Use these schemas to validate document JSON before sending to the Autype API. * Generated: 2026-03-05T22:34:44.993Z * Source: https://autype.com/llm-resources/document-schema.zod.ts * Install: npm install zod */ import { z } from 'zod'; // Text style schema export const textStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right', 'justify']).optional(), // Page break before this element (useful for h1/h2 to start new pages) pageBreakBefore: z.boolean().optional(), }).strict(); // Math style schema (extends text style with renderAsImage) export const mathStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), renderAsImage: z.boolean().optional(), }).strict(); // Code style schema export const codeStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), renderAsImage: z.boolean().optional(), }).strict(); // Figure caption style schema (for image/chart captions) // Default: centered, smaller font, italic export const figureCaptionStyleSchema = z.object({ // Disable figure captions globally (default: false = captions enabled) disable: z.boolean().optional(), fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), // Default: italic color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Default: center // Prefix format for auto-numbering (e.g., "Figure", "Abb.", "Fig.") prefix: z.string().max(50).optional(), // Default: "Figure" // Spacing between image/chart and caption spacingBefore: z.number().min(0).max(50).optional(), // Default: 4pt }).strict(); // Table caption style schema (for table captions) // Default: centered, smaller font, italic export const tableCaptionStyleSchema = z.object({ // Disable table captions globally (default: false = captions enabled) disable: z.boolean().optional(), fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), // Default: italic color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Default: center // Prefix format for auto-numbering (e.g., "Table", "Tabelle", "Tab.") prefix: z.string().max(50).optional(), // Default: "Table" // Spacing after caption (before table) spacingAfter: z.number().min(0).max(50).optional(), // Default: 4pt }).strict(); // Code caption style schema (for code block captions) // Default: centered, smaller font, italic export const codeCaptionStyleSchema = z.object({ // Disable code captions globally (default: false = captions enabled) disable: z.boolean().optional(), fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), // Default: italic color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Default: center // Prefix format for auto-numbering (e.g., "Listing", "Code", "Quellcode") prefix: z.string().max(50).optional(), // Default: "Listing" // Spacing between code block and caption spacingBefore: z.number().min(0).max(50).optional(), // Default: 4pt }).strict(); // Internal reference link style schema // Default: blue, underlined export const refLinkStyleSchema = z.object({ bold: z.boolean().optional(), italic: z.boolean().optional(), underline: z.boolean().optional(), // Default: true color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), // Default: #0000FF (blue) }).strict(); // List of Abbreviations style schema // Controls how the abbreviation list is rendered export const listOfAbbreviationsStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), // Sort order: 'alphabetical' (by abbreviation) or 'appearance' (order of first use in document) sortOrder: z.enum(['alphabetical', 'appearance']).optional(), // Default: 'alphabetical' // Separator between abbreviation and full text (e.g., " - ", ": ", " = ") separator: z.string().max(10).optional(), // Default: " - " }).strict(); // Block quote style schema (for defaults.styles.blockquote) export const blockquoteStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right', 'justify']).optional(), backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), borderWidth: z.number().min(0).max(20).optional(), borderColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), borderTop: z.boolean().optional(), borderBottom: z.boolean().optional(), borderLeft: z.boolean().optional(), borderRight: z.boolean().optional(), indentLeft: z.number().min(0).max(100).optional(), indentRight: z.number().min(0).max(100).optional(), }).strict(); // Bibliography style schema (for defaults.styles.bibliography) export const bibliographyStyleSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), // Hanging indent for bibliography entries (in cm) hangingIndent: z.number().min(0).max(5).optional(), // Line spacing within entries lineSpacing: z.number().min(1).max(3).optional(), // Space between entries (in pt) entrySpacing: z.number().min(0).max(50).optional(), }).strict(); // Citation style - simple string enum for preset styles // Available: 'apa7', 'harvard', 'ieee', 'chicago', 'mla', 'vancouver' export const citationStyleSchema = z.enum([ 'apa7', // APA 7th Edition: (Smith, 2023) 'harvard', // Harvard: (Smith 2023) 'ieee', // IEEE: [1] 'chicago', // Chicago: (Smith 2023, 42) 'mla', // MLA: (Smith 42) 'vancouver', // Vancouver: (1) ]); // Spacing override schema export const spacingOverrideSchema = z.object({ before: z.number().min(0).max(100).optional(), after: z.number().min(0).max(100).optional(), }).strict(); // Nested list schema (for mixed ordered/unordered support) export const nestedListSchema: z.ZodType<{ ordered: boolean; items: unknown[] }> = z.object({ ordered: z.boolean(), items: z.array(z.lazy(() => listItemSchema)).max(20), }).strict(); // List item schema (recursive) - supports both legacy 'items' and new 'children' format export const listItemSchema: z.ZodType = z.union([ z.string().min(1).max(1000), z.object({ text: z.string().min(1).max(1000), // Legacy format: simple array of nested items (inherits parent's ordered type) items: z.array(z.lazy(() => listItemSchema)).max(20).optional(), // New format: nested list with its own ordered property (supports mixed ordered/unordered) children: z.lazy(() => nestedListSchema).optional(), }).strict(), ]); // Header/footer content schema export const headerFooterContentSchema = z.union([ z.string(), z.object({ type: z.literal('text'), content: z.array(z.object({ text: z.string().min(1).max(500), fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right']).optional(), }).strict()).min(1).max(10), }).strict(), z.object({ type: z.literal('image'), src: z.string().min(1).max(500), caption: z.string().max(200).optional(), width: z.number().min(10).max(500).optional(), height: z.number().min(10).max(200).optional(), align: z.enum(['left', 'center', 'right']).optional(), }).strict(), ]); // Table style schema export const tableStyleSchema = z.object({ borders: z.object({ outer: z.object({ width: z.number().min(0).max(10).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), style: z.enum(['solid', 'dashed', 'dotted']).optional(), }).strict().optional(), inner: z.object({ width: z.number().min(0).max(10).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), style: z.enum(['solid', 'dashed', 'dotted']).optional(), }).strict().optional(), }).strict().optional(), header: z.object({ backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), align: z.enum(['left', 'center', 'right']).optional(), }).strict().optional(), rows: z.object({ backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), align: z.enum(['left', 'center', 'right']).optional(), alternateBackgroundColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), }).strict().optional(), cellPadding: z.object({ top: z.number().min(0).max(50).optional(), right: z.number().min(0).max(50).optional(), bottom: z.number().min(0).max(50).optional(), left: z.number().min(0).max(50).optional(), }).strict().optional(), }).strict(); // Chart dataset schema export const chartDatasetSchema = z.object({ label: z.string().optional(), data: z.array(z.union([ z.number(), z.string(), z.object({ x: z.union([z.number(), z.string()]), y: z.union([z.number(), z.string()]), r: z.number().optional(), }).strict(), ])), }).passthrough(); // Allow additional Chart.js properties // Chart config schema export const chartConfigSchema = z.object({ type: z.enum(['line', 'bar', 'pie', 'doughnut', 'radar', 'polarArea', 'scatter', 'bubble']), width: z.number().min(50).max(4000).optional(), height: z.number().min(50).max(4000).optional(), backgroundColor: z.string().optional(), data: z.object({ labels: z.array(z.union([z.string(), z.number()])).optional(), datasets: z.array(chartDatasetSchema).min(1), }).strict(), options: z.record(z.any()).optional(), }).strict(); // Base element schema with optional id const elementIdSchema = z.string().max(100).optional(); // Element schemas export const textElementSchema = z.object({ id: elementIdSchema, type: z.enum(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'text', 'text2']), text: z.string().min(1).max(5000), fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), align: z.enum(['left', 'center', 'right', 'justify']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), // Anchor ID for internal references (only for headings h1-h6) anchor: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/).max(100).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); export const mathElementSchema = z.object({ id: elementIdSchema, type: z.literal('math'), latex: z.string().min(1).max(5000), align: z.enum(['left', 'center', 'right']).optional(), renderAsImage: z.boolean().optional(), spacing: spacingOverrideSchema.optional(), }).strict(); export const imageElementSchema = z.object({ id: elementIdSchema, type: z.literal('image'), src: z.string().min(1).max(500), width: z.number().min(10).max(2000).optional(), height: z.number().min(10).max(2000).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Caption for figure numbering (e.g., "Sales chart 2024") // In markdown, the alt text (![alt text]) is used as caption caption: z.string().max(500).optional(), // Anchor ID for internal references (e.g., "fig-diagram") anchor: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/).max(100).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // Table cell image schema export const tableCellImageSchema = z.object({ src: z.string().min(1).max(2000), caption: z.string().max(200).optional(), width: z.number().min(10).max(1000).optional(), height: z.number().min(10).max(1000).optional(), }).strict(); // Table cell schema - can be string or object with text/image export const tableCellSchema = z.union([ z.string().min(0).max(1000), z.object({ text: z.string().min(0).max(1000).optional(), image: tableCellImageSchema.optional(), align: z.enum(['left', 'center', 'right']).optional(), }).strict(), ]); export const tableElementSchema = z.object({ id: elementIdSchema, type: z.literal('table'), headers: z.array(tableCellSchema).max(20).optional(), rows: z.array(z.array(tableCellSchema).max(20)).max(100).optional(), // Variable data source support dataSource: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_]*$/).optional(), mapping: z.array(z.string().max(200)).max(20).optional(), caption: z.string().max(500).optional(), // Anchor ID for internal references (e.g., "tab-fees") anchor: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/).max(100).optional(), /** Hide all borders and backgrounds (invisible/borderless table) */ invisible: z.boolean().optional(), /** Hide header row */ hideHeaders: z.boolean().optional(), style: tableStyleSchema.optional(), spacing: spacingOverrideSchema.optional(), }).strict(); export const listElementSchema = z.object({ id: elementIdSchema, type: z.literal('list'), ordered: z.boolean().optional(), items: z.array(listItemSchema).min(1).max(50), spacing: spacingOverrideSchema.optional(), }).strict(); // QR code data schemas for different types export const qrcodeUrlDataSchema = z.object({ url: z.string().min(1).max(2000), }).strict(); export const qrcodeWifiDataSchema = z.object({ ssid: z.string().min(1).max(32), password: z.string().max(63).optional(), encryption: z.enum(['WPA', 'WEP', 'nopass']).optional(), hidden: z.boolean().optional(), }).strict(); export const qrcodeVcardDataSchema = z.object({ firstName: z.string().max(100).optional(), lastName: z.string().max(100).optional(), organization: z.string().max(100).optional(), phone: z.string().max(50).optional(), email: z.string().max(100).optional(), url: z.string().max(200).optional(), address: z.string().max(200).optional(), note: z.string().max(500).optional(), }).strict(); // QR code element with discriminated union based on qrType const qrcodeBaseSchema = { id: elementIdSchema, type: z.literal('qrcode'), size: z.number().min(50).max(1000).optional(), errorCorrection: z.enum(['L', 'M', 'Q', 'H']).optional(), align: z.enum(['left', 'center', 'right']).optional(), spacing: spacingOverrideSchema.optional(), }; export const qrcodeUrlElementSchema = z.object({ ...qrcodeBaseSchema, qrType: z.literal('url'), data: qrcodeUrlDataSchema, }).strict(); export const qrcodeWifiElementSchema = z.object({ ...qrcodeBaseSchema, qrType: z.literal('wifi'), data: qrcodeWifiDataSchema, }).strict(); export const qrcodeVcardElementSchema = z.object({ ...qrcodeBaseSchema, qrType: z.literal('vcard'), data: qrcodeVcardDataSchema, }).strict(); export const qrcodeElementSchema = z.discriminatedUnion('qrType', [ qrcodeUrlElementSchema, qrcodeWifiElementSchema, qrcodeVcardElementSchema, ]); export const chartElementSchema = z.object({ id: elementIdSchema, type: z.literal('chart'), config: chartConfigSchema, width: z.number().min(10).max(2000).optional(), height: z.number().min(10).max(2000).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Caption for figure numbering (e.g., "Revenue breakdown by quarter") caption: z.string().max(500).optional(), // Anchor ID for internal references (e.g., "fig-chart") anchor: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/).max(100).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); export const codeElementSchema = z.object({ id: elementIdSchema, type: z.literal('code'), code: z.string().min(1).max(20000), language: z.string().max(50).optional(), renderAsImage: z.boolean().optional(), backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), width: z.number().min(10).max(2000).optional(), align: z.enum(['left', 'center', 'right']).optional(), // Caption for code listing numbering (only rendered if present) caption: z.string().max(500).optional(), // Anchor ID for internal references (e.g., {#code-example}) anchor: z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/).max(100).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); export const pageBreakElementSchema = z.object({ id: elementIdSchema, type: z.literal('pageBreak'), orientation: z.enum(['portrait', 'landscape']).optional(), }).strict(); // Spacer element - adds vertical spacing // height can be a number (interpreted as lines) or a string with 'px' suffix export const spacerElementSchema = z.object({ id: elementIdSchema, type: z.literal('spacer'), height: z.union([ z.number().min(0.5).max(50), // Lines (e.g., 1, 2, 0.5) z.string().regex(/^\d+(\.\d+)?px$/) // Pixels (e.g., "20px", "50.5px") ]).default(1), }).strict(); // Variable reference element - references a variable by name // The type of rendering is determined by the variable's type in the variables block export const variableRefElementSchema = z.object({ id: elementIdSchema, type: z.literal('variableRef'), variable: z.string().min(1).max(50).regex(/^[a-zA-Z][a-zA-Z0-9_]*$/), spacing: spacingOverrideSchema.optional(), }).strict(); // Table of Contents element - generates automatic TOC from headings export const tocElementSchema = z.object({ id: elementIdSchema, type: z.literal('toc'), title: z.string().max(200).optional(), // e.g. "Table of Contents" maxLevel: z.number().min(1).max(6).optional(), // Default: 3 (h1-h3) hyperlink: z.boolean().optional(), // Default: true - make entries clickable // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // List of Figures element - generates automatic list of figures from images/charts with captions export const listOfFiguresElementSchema = z.object({ id: elementIdSchema, type: z.literal('listOfFigures'), title: z.string().max(200).optional(), // e.g. "List of Figures" // Tab leader style between caption and page number tabStyle: z.enum(['dot', 'hyphen', 'underscore', 'none']).optional(), // Default: 'dot' // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // List of Tables element - generates automatic list of tables with captions export const listOfTablesElementSchema = z.object({ id: elementIdSchema, type: z.literal('listOfTables'), title: z.string().max(200).optional(), // e.g. "List of Tables" // Tab leader style between caption and page number tabStyle: z.enum(['dot', 'hyphen', 'underscore', 'none']).optional(), // Default: 'dot' // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // List of Code Listings element - generates automatic list of code blocks with captions export const listOfCodeListingsElementSchema = z.object({ id: elementIdSchema, type: z.literal('listOfCodeListings'), title: z.string().max(200).optional(), // e.g. "List of Code Listings" // Tab leader style between caption and page number tabStyle: z.enum(['dot', 'hyphen', 'underscore', 'none']).optional(), // Default: 'dot' // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // List of Abbreviations element - generates automatic list of abbreviations used in the document export const listOfAbbreviationsElementSchema = z.object({ id: elementIdSchema, type: z.literal('listOfAbbreviations'), title: z.string().max(200).optional(), // e.g. "List of Abbreviations" // Sort order: alphabetical (default) or document (order of first appearance) sortOrder: z.enum(['alphabetical', 'document']).optional(), // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // Bibliography element - generates automatic list of cited references export const bibliographyElementSchema = z.object({ id: elementIdSchema, type: z.literal('bibliography'), title: z.string().max(200).optional(), // e.g. "References", "Bibliography", "Quellenverzeichnis" // Title styling (uses defaults if not specified) fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // Block quote element - styled block quote container export const blockquoteElementSchema = z.object({ id: elementIdSchema, type: z.literal('blockquote'), content: z.array(z.lazy(() => elementSchema)).min(1).max(200), // Font properties fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), fontWeight: z.enum(['normal', 'bold']).optional(), fontStyle: z.enum(['normal', 'italic']).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), align: z.enum(['left', 'center', 'right', 'justify']).optional(), // Background backgroundColor: z.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), // Border borderWidth: z.number().min(0).max(20).optional(), borderColor: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), borderTop: z.boolean().optional(), borderBottom: z.boolean().optional(), borderLeft: z.boolean().optional(), borderRight: z.boolean().optional(), // Indent indentLeft: z.number().min(0).max(100).optional(), indentRight: z.number().min(0).max(100).optional(), spacing: spacingOverrideSchema.optional(), }).strict(); // Comment anchor element - invisible marker linking to a DocumentComment // Format in markdown: export const commentAnchorElementSchema = z.object({ id: elementIdSchema, type: z.literal('commentAnchor'), commentId: z.string().uuid(), }).strict(); // Union of all elements export const elementSchema: z.ZodType = z.union([ textElementSchema, mathElementSchema, imageElementSchema, tableElementSchema, listElementSchema, qrcodeElementSchema, chartElementSchema, codeElementSchema, variableRefElementSchema, pageBreakElementSchema, spacerElementSchema, tocElementSchema, listOfFiguresElementSchema, listOfTablesElementSchema, listOfCodeListingsElementSchema, listOfAbbreviationsElementSchema, bibliographyElementSchema, blockquoteElementSchema, commentAnchorElementSchema, ]); // Section schemas export const pageSectionSchema = z.object({ id: elementIdSchema, type: z.literal('page'), align: z.enum(['top', 'center', 'bottom']).optional(), startY: z.number().min(0).max(100).optional(), content: z.array(elementSchema).min(0).max(200), }).strict(); // Column layout schema for multi-column sections (e.g., two-column paper layout) export const columnsSchema = z.object({ /** Number of columns (1-4, default: 1) */ count: z.number().min(1).max(4), /** Space between columns in cm (default: 1.27 cm / 0.5 inch) */ space: z.number().min(0).max(5).optional(), /** Show separator line between columns */ separate: z.boolean().optional(), }).strict(); export const flowSectionSchema = z.object({ id: elementIdSchema, type: z.literal('flow'), newPage: z.boolean().optional(), /** Multi-column layout for this section */ columns: columnsSchema.optional(), // limit of elements in flow section content: z.array(elementSchema).min(0).max(5000), }).strict(); export const sectionSchema = z.union([pageSectionSchema, flowSectionSchema]); // Spacing defaults schema export const spacingDefaultsSchema = z.object({ before: z.object({ h1: z.number().min(0).max(100).optional(), h2: z.number().min(0).max(100).optional(), h3: z.number().min(0).max(100).optional(), h4: z.number().min(0).max(100).optional(), h5: z.number().min(0).max(100).optional(), h6: z.number().min(0).max(100).optional(), text: z.number().min(0).max(100).optional(), text2: z.number().min(0).max(100).optional(), table: z.number().min(0).max(100).optional(), list: z.number().min(0).max(100).optional(), image: z.number().min(0).max(100).optional(), qrcode: z.number().min(0).max(100).optional(), chart: z.number().min(0).max(100).optional(), code: z.number().min(0).max(100).optional(), math: z.number().min(0).max(100).optional(), blockquote: z.number().min(0).max(100).optional(), }).strict().optional(), after: z.object({ h1: z.number().min(0).max(100).optional(), h2: z.number().min(0).max(100).optional(), h3: z.number().min(0).max(100).optional(), h4: z.number().min(0).max(100).optional(), h5: z.number().min(0).max(100).optional(), h6: z.number().min(0).max(100).optional(), text: z.number().min(0).max(100).optional(), text2: z.number().min(0).max(100).optional(), table: z.number().min(0).max(100).optional(), list: z.number().min(0).max(100).optional(), image: z.number().min(0).max(100).optional(), qrcode: z.number().min(0).max(100).optional(), chart: z.number().min(0).max(100).optional(), code: z.number().min(0).max(100).optional(), math: z.number().min(0).max(100).optional(), blockquote: z.number().min(0).max(100).optional(), }).strict().optional(), }).strict(); // Header/footer section schema export const headerFooterSectionSchema = z.object({ left: headerFooterContentSchema.optional(), center: headerFooterContentSchema.optional(), right: headerFooterContentSchema.optional(), // Global alignment for all columns, or 'distributed' to align each column to its position // 'distributed': left column → left, center column → center, right column → right align: z.enum(['left', 'center', 'right', 'distributed']).optional(), excludeFirstPage: z.boolean().optional(), }).strict(); // Defaults schema export const defaultsSchema = z.object({ fontFamily: z.string().min(1).max(100).optional(), fontSize: z.number().min(6).max(72).optional(), color: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/).optional(), lineHeight: z.number().min(0.5).max(3).optional(), spacing: spacingDefaultsSchema.optional(), // Heading numbering format string: each character defines the numbering style per level // "1" = numeric (1, 2, 3...), "a" = lowercase alpha (a, b, c...), "A" = uppercase alpha // "i" = lowercase roman (i, ii, iii...), "I" = uppercase roman, "α" = greek lowercase // Example: "1.1.a" = h1 numeric, h2 numeric, h3 lowercase alpha // Empty string or undefined = no numbering headingNumbering: z.string().max(20).optional(), chart: z.object({ colors: z.array(z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)).optional(), borderColors: z.array(z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)).optional(), }).strict().optional(), styles: z.object({ h1: textStyleSchema.optional(), h2: textStyleSchema.optional(), h3: textStyleSchema.optional(), h4: textStyleSchema.optional(), h5: textStyleSchema.optional(), h6: textStyleSchema.optional(), text: textStyleSchema.optional(), text2: textStyleSchema.optional(), table: tableStyleSchema.optional(), math: mathStyleSchema.optional(), code: codeStyleSchema.optional(), // Figure caption style (for images and charts with captions) figureCaption: figureCaptionStyleSchema.optional(), // Table caption style (for tables with captions) tableCaption: tableCaptionStyleSchema.optional(), // Code caption style (for code blocks with captions) codeCaption: codeCaptionStyleSchema.optional(), // Internal reference link style refLink: refLinkStyleSchema.optional(), // List of Abbreviations style listOfAbbreviations: listOfAbbreviationsStyleSchema.optional(), // Bibliography style bibliography: bibliographyStyleSchema.optional(), // Block quote style blockquote: blockquoteStyleSchema.optional(), }).strict().optional(), header: headerFooterSectionSchema.optional(), footer: headerFooterSectionSchema.optional(), // Citation style configuration citationStyle: citationStyleSchema.optional(), }).strict(); // Document settings schema export const documentSettingsSchema = z.object({ type: z.enum(['pdf', 'docx', 'odt']), filename: z.string().min(1).max(100).regex(/^[a-zA-Z0-9_-]+$/).optional(), title: z.string().max(200).optional(), author: z.string().max(100).optional(), subject: z.string().max(200).optional(), keywords: z.array(z.string().min(1).max(50)).max(10).optional(), size: z.enum(['A4', 'A3', 'A5', 'Letter', 'Legal']).optional(), orientation: z.enum(['portrait', 'landscape']).optional(), marginTop: z.number().min(0).max(10).optional(), marginRight: z.number().min(0).max(10).optional(), marginBottom: z.number().min(0).max(10).optional(), marginLeft: z.number().min(0).max(10).optional(), }).strict(); // ============================================================================ // Variable Types Schema // ============================================================================ // Text variable - simple string value (empty strings allowed) export const textVariableSchema = z.string().max(1000); // Image variable - reference to image with optional dimensions and alignment export const imageVariableSchema = z.object({ type: z.literal('image'), src: z.string().min(1).max(500), width: z.number().min(10).max(2000).optional(), height: z.number().min(10).max(2000).optional(), align: z.enum(['left', 'center', 'right']).optional(), caption: z.string().max(200).optional(), }).strict(); // List variable - array of string items (empty lists allowed) export const listVariableSchema = z.object({ type: z.literal('list'), items: z.array(z.string().max(1000)).max(100), ordered: z.boolean().optional(), }).strict(); // Table variable - 2D array with optional column keys for mapping (empty tables allowed) export const tableVariableSchema = z.object({ type: z.literal('table'), columns: z.array(z.string().min(1).max(50)).optional(), // Column keys for named access data: z.array(z.array(z.string().max(1000)).max(20)).max(100), // 2D array of values }).strict(); // Number variable - numeric value for use in charts, calculations, text export const numberVariableSchema = z.object({ type: z.literal('number'), value: z.number(), }).strict(); // Union of all variable value types export const variableValueSchema = z.union([ textVariableSchema, imageVariableSchema, listVariableSchema, tableVariableSchema, numberVariableSchema, ]); // Variable name pattern const variableNamePattern = /^[a-zA-Z][a-zA-Z0-9_]*$/; // Variables schema - map of variable names to values export const variablesSchema = z.record( z.string().min(1).max(50).regex(variableNamePattern), variableValueSchema ); // ============================================================================ // Abbreviations Schema // ============================================================================ // Abbreviation key pattern (the short form, e.g., "API", "HTML", "R&D", "ÄöÜ") // Allows alphanumeric (including umlauts), spaces, and common special characters safe for JSON const abbreviationKeyPattern = /^[A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF0-9][A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF0-9 &_\-.,()/'"]*$/; // Abbreviations schema - map of abbreviation keys to full text // Key: short form (e.g., "API"), Value: full text (e.g., "Application Programming Interface") export const abbreviationsSchema = z.record( z.string().min(1).max(20).regex(abbreviationKeyPattern), z.string().min(1).max(200) ); // ============================================================================ // Citations Schema (CSL-JSON) // ============================================================================ // CSL-JSON name schema (for authors, editors, etc.) export const cslNameSchema = z.object({ family: z.string().max(200).optional(), given: z.string().max(200).optional(), 'dropping-particle': z.string().max(50).optional(), 'non-dropping-particle': z.string().max(50).optional(), suffix: z.string().max(50).optional(), literal: z.string().max(500).optional(), // For institutional authors }).passthrough(); // CSL-JSON date schema export const cslDateSchema = z.object({ 'date-parts': z.array(z.array(z.union([z.number(), z.string()]))).optional(), season: z.union([z.number(), z.string()]).optional(), circa: z.union([z.boolean(), z.number(), z.string()]).optional(), literal: z.string().max(200).optional(), raw: z.string().max(200).optional(), }).passthrough(); // CSL-JSON item types export const cslItemTypeSchema = z.enum([ 'article', 'article-journal', 'article-magazine', 'article-newspaper', 'bill', 'book', 'broadcast', 'chapter', 'classic', 'collection', 'dataset', 'document', 'entry', 'entry-dictionary', 'entry-encyclopedia', 'event', 'figure', 'graphic', 'hearing', 'interview', 'legal_case', 'legislation', 'manuscript', 'map', 'motion_picture', 'musical_score', 'pamphlet', 'paper-conference', 'patent', 'performance', 'periodical', 'personal_communication', 'post', 'post-weblog', 'regulation', 'report', 'review', 'review-book', 'software', 'song', 'speech', 'standard', 'thesis', 'treaty', 'webpage' ]); // Full CSL-JSON item schema (supports all standard CSL-JSON fields) export const cslItemSchema = z.object({ // Required: unique identifier id: z.string().min(1).max(100), // Required: item type type: cslItemTypeSchema, // Standard CSL-JSON fields (all optional) // Names author: z.array(cslNameSchema).optional(), editor: z.array(cslNameSchema).optional(), translator: z.array(cslNameSchema).optional(), 'collection-editor': z.array(cslNameSchema).optional(), composer: z.array(cslNameSchema).optional(), 'container-author': z.array(cslNameSchema).optional(), director: z.array(cslNameSchema).optional(), 'editorial-director': z.array(cslNameSchema).optional(), illustrator: z.array(cslNameSchema).optional(), interviewer: z.array(cslNameSchema).optional(), 'original-author': z.array(cslNameSchema).optional(), recipient: z.array(cslNameSchema).optional(), 'reviewed-author': z.array(cslNameSchema).optional(), // Dates issued: cslDateSchema.optional(), accessed: cslDateSchema.optional(), 'event-date': cslDateSchema.optional(), 'original-date': cslDateSchema.optional(), submitted: cslDateSchema.optional(), // Titles title: z.string().max(1000).optional(), 'title-short': z.string().max(500).optional(), 'container-title': z.string().max(500).optional(), 'container-title-short': z.string().max(200).optional(), 'collection-title': z.string().max(500).optional(), 'original-title': z.string().max(500).optional(), 'reviewed-title': z.string().max(500).optional(), // Numbers volume: z.union([z.string(), z.number()]).optional(), issue: z.union([z.string(), z.number()]).optional(), page: z.string().max(100).optional(), 'page-first': z.union([z.string(), z.number()]).optional(), 'number-of-pages': z.union([z.string(), z.number()]).optional(), 'number-of-volumes': z.union([z.string(), z.number()]).optional(), edition: z.union([z.string(), z.number()]).optional(), version: z.union([z.string(), z.number()]).optional(), 'chapter-number': z.union([z.string(), z.number()]).optional(), 'collection-number': z.union([z.string(), z.number()]).optional(), number: z.union([z.string(), z.number()]).optional(), // Identifiers DOI: z.string().max(200).optional(), ISBN: z.string().max(50).optional(), ISSN: z.string().max(50).optional(), PMID: z.string().max(50).optional(), PMCID: z.string().max(50).optional(), URL: z.string().max(2000).optional(), // Publisher info publisher: z.string().max(500).optional(), 'publisher-place': z.string().max(200).optional(), // Other abstract: z.string().max(5000).optional(), annote: z.string().max(2000).optional(), archive: z.string().max(200).optional(), 'archive-place': z.string().max(200).optional(), 'archive_location': z.string().max(200).optional(), authority: z.string().max(200).optional(), 'call-number': z.string().max(100).optional(), 'citation-key': z.string().max(100).optional(), 'citation-label': z.string().max(100).optional(), dimensions: z.string().max(100).optional(), event: z.string().max(500).optional(), 'event-place': z.string().max(200).optional(), genre: z.string().max(200).optional(), jurisdiction: z.string().max(200).optional(), keyword: z.string().max(500).optional(), language: z.string().max(50).optional(), license: z.string().max(200).optional(), medium: z.string().max(200).optional(), note: z.string().max(2000).optional(), 'original-publisher': z.string().max(500).optional(), 'original-publisher-place': z.string().max(200).optional(), references: z.string().max(2000).optional(), scale: z.string().max(100).optional(), section: z.string().max(200).optional(), source: z.string().max(500).optional(), status: z.string().max(100).optional(), 'year-suffix': z.string().max(10).optional(), }).passthrough(); // Allow additional custom fields // Citations array schema export const citationsSchema = z.array(cslItemSchema).max(1000); // Main document schema export const documentSchema = z.object({ document: documentSettingsSchema, variables: variablesSchema.optional(), abbreviations: abbreviationsSchema.optional(), citations: citationsSchema.optional(), defaults: defaultsSchema.optional(), sections: z.array(sectionSchema).min(0).max(500), }).strict(); // Style definition schema (defaults + optional document settings) // Used for saving style presets that include both styling and document settings export const styleDefinitionSchema = defaultsSchema.extend({ document: documentSettingsSchema.optional(), }); // ============================================================================= // Type Exports with JSDoc Documentation // ============================================================================= /** * Complete document configuration for PDF/DOCX generation. * * A document consists of: * - `document`: Page settings (size, margins, orientation, metadata) * - `defaults`: Global styling defaults (fonts, colors, spacing) * - `variables`: Template variables for text replacement. Use {{varName}} in text. Built-in: {{pageNumber}}, {{totalPages}} * - `sections`: Content sections (page or flow type) */ export type Document = z.infer; /** * Document settings for page configuration and metadata. * * @property type - Output document format: 'pdf', 'docx', or 'odt' * @property filename - Output filename without extension (alphanumeric, underscore, hyphen only) * @property title - Document title for metadata * @property author - Document author for metadata * @property subject - Document subject for metadata * @property keywords - Document keywords for metadata (max 10) * @property size - Page size: 'A4', 'A3', 'A5', 'Letter', 'Legal' * @property orientation - Page orientation: 'portrait' or 'landscape' * @property marginTop - Top margin in cm (0-10) * @property marginRight - Right margin in cm (0-10) * @property marginBottom - Bottom margin in cm (0-10) * @property marginLeft - Left margin in cm (0-10) */ export type DocumentSettings = z.infer; /** * Document section - either a page section or flow section. * * - **Page section**: Styled single page with custom vertical positioning (align, startY) * - **Flow section**: Flowing content that spans multiple pages (newPage option) */ export type Section = z.infer; /** * Page section - styled single page with custom positioning. * * @property type - Must be 'page' * @property id - Optional unique identifier for the section * @property align - Vertical alignment of content: 'top', 'center', 'bottom' * @property startY - Y position in cm (overrides align) * @property content - Array of content elements */ export type PageSection = z.infer; /** * Flow section - flowing content across multiple pages. * * @property type - Must be 'flow' * @property id - Optional unique identifier for the section * @property newPage - Start this section on a new page (default: true) * @property content - Array of content elements */ export type FlowSection = z.infer; /** * Content element - union of all element types. * * Element types: * - **Text**: h1-h6, text, text2 - Text with markdown support (**bold**, *italic*, [link](url)) * - **Math**: LaTeX formula (block-level), e.g. "E = mc^2" * - **Image**: Image with src (storage URL /image/... or public HTTP/HTTPS URL), dimensions, alt text * - **Table**: Data table with headers, rows, styling, optional dataSource for variable binding * - **List**: Ordered (1,2,3) or unordered (bullets) list with nested items * - **QR Code**: URL, WiFi credentials, or vCard QR code * - **Chart**: Chart.js chart (line, bar, pie, doughnut, radar, polarArea, scatter, bubble) * - **Code**: Code block with syntax highlighting (highlight.js language id) * - **Variable Ref**: Reference to a variable by name * - **Page Break**: Force page break with optional orientation change */ export type Element = z.infer; /** * Global default styles and formatting options. * * @property fontFamily - Default font family name * @property fontSize - Default font size in pt (6-72) * @property color - Default text color in hex format (#RGB or #RRGGBB) * @property lineHeight - Default line height multiplier (0.5-3) * @property spacing - Global spacing defaults in pt for element types (before/after for h1-h6, text, text2, table, list, image, qrcode, chart, code, math) * @property chart - Default chart colors for datasets (colors, borderColors as hex arrays) * @property styles - Default styles for specific element types (h1-h6, text2, table, math, code) * @property header - Global page header configuration (left/center/right content, excludeFirstPage) * @property footer - Global page footer configuration (left/center/right content, excludeFirstPage) */ export type Defaults = z.infer; /** * Template variables for text replacement. * * Variables can be: * - **Text**: Simple string value for {{varName}} replacement * - **Image**: Object with type:'image', src, width, height, alt * - **List**: Object with type:'list', items array, ordered boolean * - **Table**: Object with type:'table', columns array, data 2D array * * Built-in variables: {{pageNumber}}, {{totalPages}} */ export type Variables = z.infer; /** * Abbreviations - map of abbreviation keys to full text. * Key: short form (e.g., "API"), Value: full text (e.g., "Application Programming Interface") */ export type Abbreviations = z.infer; /** * CSL-JSON name schema for authors, editors, etc. */ export type CSLName = z.infer; /** * CSL-JSON date schema. */ export type CSLDate = z.infer; /** * CSL-JSON item type enum. */ export type CSLItemType = z.infer; /** * CSL-JSON item - a single citation/reference entry. * Supports all standard CSL-JSON fields. * * @property id - Unique identifier for the citation (e.g., "smith2023") * @property type - Item type (e.g., "book", "article-journal", "webpage") * @property author - Array of author names * @property title - Title of the work * @property issued - Publication date * @property publisher - Publisher name * @property DOI - Digital Object Identifier * @property URL - Web URL * ... and many more CSL-JSON fields */ export type CSLItem = z.infer; /** * Citations array - list of CSL-JSON citation items. */ export type Citations = z.infer; /** * Citation style - simple string enum. * Available styles: 'apa7', 'harvard', 'ieee', 'chicago', 'mla', 'vancouver' */ export type CitationStyle = z.infer; /** * Bibliography style for defaults.styles.bibliography. * * @property fontFamily - Font family for bibliography entries * @property fontSize - Font size in pt * @property fontWeight - Font weight: 'normal' or 'bold' * @property color - Text color in hex format * @property hangingIndent - Hanging indent in cm * @property lineSpacing - Line spacing within entries * @property entrySpacing - Space between entries in pt */ export type BibliographyStyle = z.infer; /** * Bibliography element - generates automatic list of cited references. * * @property id - Optional unique identifier * @property type - Must be 'bibliography' * @property title - Optional title (e.g., "References", "Bibliography") * @property fontFamily - Title font family * @property fontSize - Title font size * @property spacing - Spacing override */ export type BibliographyElement = z.infer; /** Variable value - can be text (string), number, image, list, or table object. */ export type VariableValue = z.infer; /** Text variable - simple string value (max 1000 chars) for {{varName}} replacement. */ export type TextVariable = z.infer; /** * Image variable - reference to image with optional dimensions and alignment. * * @property type - Must be 'image' * @property src - Image file path (storage URL /image/... or public HTTP/HTTPS URL) * @property width - Image width in pixels (10-2000) * @property height - Image height in pixels (10-2000) * @property align - Image alignment: 'left', 'center', 'right' * @property caption - Caption text for figure numbering */ export type ImageVariable = z.infer; /** * List variable - array of string items. * * @property type - Must be 'list' * @property items - Array of list item strings (max 100 items, max 1000 chars each) * @property ordered - true = numbered list (1,2,3), false = bullet list */ export type ListVariable = z.infer; /** * Table variable - 2D array with optional column keys for mapping. * * @property type - Must be 'table' * @property columns - Column keys for named access (optional, max 20) * @property data - 2D array of cell values (max 100 rows, 20 columns, 1000 chars per cell) */ export type TableVariable = z.infer; /** * Number variable - numeric value for charts, calculations, and text substitution. * * @property type - Must be 'number' * @property value - Numeric value */ export type NumberVariable = z.infer; /** * Variable reference element - references a variable by name. * The rendering type is determined by the variable's type in the variables block. * * @property id - Optional unique identifier * @property type - Must be 'variableRef' * @property variable - Variable name (must match a key in variables block, alphanumeric + underscore) * @property spacing - Spacing override (before/after in pt, 0-100) */ export type VariableRefElement = z.infer; /** * Style definition for style presets. * Includes all Defaults properties plus optional document settings. * Used for saving and applying style presets to documents. */ export type StyleDefinition = z.infer; // ============================================================================= // Additional Element Type Exports // ============================================================================= /** * Text element - headings (h1-h6) and paragraphs (text, text2). * * @property id - Optional unique identifier * @property type - Element type: 'h1'-'h6', 'text', 'text2' * @property text - Text content with markdown support (**bold**, *italic*, [link](url)) * @property fontFamily - Font family for this element * @property fontSize - Font size in points (6-72) * @property fontWeight - Font weight: 'normal' or 'bold' * @property align - Text alignment: 'left', 'center', 'right', 'justify' * @property color - Text color in hex format (#RGB or #RRGGBB) * @property spacing - Spacing override (before/after in pt) */ export type TextElement = z.infer; /** * Math element - block-level LaTeX formula. * * @property id - Optional unique identifier * @property type - Must be 'math' * @property latex - LaTeX formula string (e.g. "E = mc^2") * @property align - Horizontal alignment: 'left', 'center', 'right' * @property renderAsImage - If true, render as image for better compatibility * @property spacing - Spacing override (before/after in pt) */ export type MathElement = z.infer; /** * Image element - embedded image. * * @property id - Optional unique identifier * @property type - Must be 'image' * @property src - Image file path (storage URL /image/... or public HTTP/HTTPS URL) * @property alt - Alternative text for accessibility * @property width - Image width in pixels (10-2000) * @property height - Image height in pixels (10-2000) * @property align - Image alignment: 'left', 'center', 'right' * @property spacing - Spacing override (before/after in pt) */ export type ImageElement = z.infer; /** * Table cell image schema type - image embedded in a table cell. * * @property src - Image source URL * @property alt - Alternative text for accessibility * @property width - Image width in pixels (10-1000) * @property height - Image height in pixels (10-1000) */ export type TableCellImageSchema = z.infer; /** * Table cell schema type - can be a string or an object with text/image. * * String cells contain plain text. * Object cells can contain: * - text: Text content * - image: Embedded image (src, alt, width, height) * - align: Cell-specific alignment override */ export type TableCellSchema = z.infer; /** * Table element - data table with headers and rows. * * @property id - Optional unique identifier * @property type - Must be 'table' * @property headers - Table header cells (string or object with text/image, max 20 columns) * @property rows - Table data rows (max 100 rows, 20 columns each, cells can be string or object) * @property dataSource - Variable name for dynamic data (alternative to rows) * @property mapping - Column mapping for dataSource variables * @property caption - Table caption text * @property style - Table styling (borders, header, rows, cellPadding) * @property spacing - Spacing override (before/after in pt) */ export type TableElement = z.infer; /** * List element - ordered or unordered list. * * @property id - Optional unique identifier * @property type - Must be 'list' * @property ordered - true = numbered list (1,2,3), false = bullet list (default) * @property items - List items (string or object with text and nested items/children) * @property spacing - Spacing override (before/after in pt) */ export type ListElement = z.infer; /** * QR code element - URL, WiFi, or vCard QR code. * * @property id - Optional unique identifier * @property type - Must be 'qrcode' * @property qrType - QR code type: 'url', 'wifi', 'vcard' * @property data - QR code data (structure depends on qrType) * @property size - QR code size in pixels (50-1000, always 1:1 ratio) * @property errorCorrection - Error correction level: 'L' (~7%), 'M' (~15%), 'Q' (~25%), 'H' (~30%) * @property align - QR code alignment: 'left', 'center', 'right' * @property spacing - Spacing override (before/after in pt) */ export type QRCodeElement = z.infer; /** * Chart element - Chart.js chart rendered as image. * * @property id - Optional unique identifier * @property type - Must be 'chart' * @property config - Chart.js configuration (type, data, options, backgroundColor) * @property width - Rendered chart image width in pixels (10-2000) * @property height - Rendered chart image height in pixels (10-2000) * @property align - Chart alignment: 'left', 'center', 'right' * @property spacing - Spacing override (before/after in pt) */ export type ChartElement = z.infer; /** * Code element - code block with syntax highlighting. * * @property id - Optional unique identifier * @property type - Must be 'code' * @property code - Code content to render or display * @property language - Programming language key for syntax highlighting (highlight.js language id) * @property renderAsImage - If true, render as image via internal render API * @property backgroundColor - Background color for rendered code image in hex format * @property width - Rendered code image width in pixels (10-2000) * @property align - Code block alignment: 'left', 'center', 'right' * @property spacing - Spacing override (before/after in pt) */ export type CodeElement = z.infer; /** * Page break element - forces a page break. * * @property id - Optional unique identifier * @property type - Must be 'pageBreak' * @property orientation - Page orientation after break: 'portrait' or 'landscape' */ export type PageBreakElement = z.infer; /** * Table of Contents element - generates automatic TOC from document headings. * * @property id - Optional unique identifier * @property type - Must be 'toc' * @property title - Optional title displayed above TOC (e.g. "Table of Contents") * @property maxLevel - Maximum heading level to include (1-6, default: 3) * @property hyperlink - Make TOC entries clickable hyperlinks (default: true) * @property spacing - Spacing override (before/after in pt) */ export type TocElement = z.infer; /** * Chart.js configuration for chart elements. * * @property type - Chart.js chart type: 'line', 'bar', 'pie', 'doughnut', 'radar', 'polarArea', 'scatter', 'bubble' * @property width - Chart width in pixels (50-4000) * @property height - Chart height in pixels (50-4000) * @property backgroundColor - Canvas background color (CSS color string) * @property data - Chart data with labels and datasets * @property options - Chart.js options object */ export type ChartConfig = z.infer; /** * Table styling configuration. * * @property borders - Border configuration (outer and inner with width, color, style) * @property header - Header row styling (backgroundColor, color, fontSize, fontWeight, fontStyle, align) * @property rows - Data row styling (backgroundColor, alternateBackgroundColor, color, fontSize, fontWeight, fontStyle, align) * @property cellPadding - Cell padding in pt (top, right, bottom, left) */ export type TableStyle = z.infer; /** * Header/footer section configuration. * * @property left - Left column content (string, text object with content array, or image object) * @property center - Center column content * @property right - Right column content * @property align - Override alignment for all content: 'left', 'center', 'right' * @property excludeFirstPage - Exclude header/footer from first page */ export type HeaderFooterSection = z.infer; /** * Global spacing defaults for element types. * * @property before - Spacing before elements by type in pt (h1-h6, text, text2, table, list, image, qrcode, chart, code, math) * @property after - Spacing after elements by type in pt */ export type SpacingDefaults = z.infer; /** * Spacing override for individual elements. * * @property before - Spacing before element in pt (0-100) * @property after - Spacing after element in pt (0-100) */ export type SpacingOverride = z.infer; // Type guards for variables // Validation helper // Partial document schema for updates (sections optional) export const partialDocumentSchema = z.object({ document: documentSettingsSchema, variables: variablesSchema.optional(), defaults: defaultsSchema.optional(), sections: z.array(sectionSchema).min(0).max(500).optional(), }).strict(); /** * Partial document schema for updates (sections optional). * Used when updating document settings without replacing all sections. */ export type PartialDocument = z.infer;