@hyper-elements/react 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/kinds.ts","../../core/src/resolve.ts","../../core/src/markup.ts","../../core/src/image/satori.ts","../../core/src/image/write.ts","../src/image/index.ts"],"names":["weight"],"mappings":"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","file":"image.js","sourcesContent":["/**\n * Value kinds: what a prop or style property accepts, what it's normalized to,\n * the editor that edits it, and a JSON Schema of its values (for property\n * discovery). Component definitions are made of these, so types, validation\n * and editors come from one place.\n */\nimport type { Json } from \"./model\";\n\nexport type JsonSchema = { [key: string]: unknown };\n\n/** Conceptual editors, mapped by each editor (the builder, others) to its own widgets. */\nexport type EditorKind =\n | \"color\"\n | \"length\"\n | \"insets\"\n | \"border\"\n | \"radius\"\n | \"choice\"\n | \"fontFamily\"\n | \"fontFaces\"\n | \"items\"\n | \"fontWeight\"\n | \"lineHeight\"\n | \"image\"\n | \"link\"\n | \"text\"\n | \"number\"\n | \"toggle\";\n\nexport type Parsed<T> = { ok: true; value: T } | { ok: false; message: string };\n\nexport interface Kind<In = unknown, Out extends Json = Json> {\n readonly name: string;\n readonly editor: EditorKind;\n /** What it accepts, in words (for messages and descriptions). */\n readonly accepts: string;\n parse(input: unknown): Parsed<Out>;\n jsonSchema(): JsonSchema;\n /** Type only: what it accepts as input. */\n readonly __in?: In;\n}\n\nexport type InputOf<K> = K extends Kind<infer I, Json> ? I : never;\nexport type OutputOf<K> = K extends Kind<unknown, infer O> ? O : never;\n\nconst ok = <T>(value: T): Parsed<T> => ({ ok: true, value });\nconst fail = (message: string): Parsed<never> => ({ ok: false, message });\nconst show = (input: unknown): string => {\n try {\n return typeof input === \"string\" ? JSON.stringify(input) : typeof input === \"function\" ? \"a function\" : JSON.stringify(input) ?? String(input);\n } catch {\n return String(input);\n }\n};\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nfunction kind<In, Out extends Json>(spec: Omit<Kind<In, Out>, \"__in\">): Kind<In, Out> {\n return spec;\n}\n\n// ─── Numbers and lengths ────────────────────────────────────────────────────\n\nconst NUMBER = \"-?(?:\\\\d+(?:\\\\.\\\\d*)?|\\\\.\\\\d+)\";\n\nexport type LengthUnit = \"px\" | \"%\" | \"pt\" | \"mm\" | \"in\";\n/** A length in px: a number, or a string with px. */\nexport type Px = number | `${number}px`;\nexport type LengthInput<U extends LengthUnit> = number | `${number}${U}`;\n/** px as a number, any other unit as a string (\"50%\", \"12pt\"). */\nexport type Length = number | string;\n\nconst LENGTH = new RegExp(`^(${NUMBER})(px|%|pt|mm|in)?$`);\n\n/** A length's value and unit, from a number (px) or a string (\"12px\", \"50%\", \"12\" as px). */\nexport function readLength(input: unknown): { value: number; unit: LengthUnit } | undefined {\n if (typeof input === \"number\") return Number.isFinite(input) ? { value: input, unit: \"px\" } : undefined;\n if (typeof input !== \"string\") return undefined;\n const match = LENGTH.exec(input.trim());\n return match ? { value: Number(match[1]), unit: (match[2] as LengthUnit | undefined) ?? \"px\" } : undefined;\n}\n\nfunction px(input: unknown, min: number, what: string): Parsed<number> {\n const length = readLength(input);\n if (!length || length.unit !== \"px\") return fail(`${what} must be a number of px or a px string (\"8px\"), not ${show(input)}`);\n if (length.value < min) return fail(`${what} can't be less than ${min}, not ${show(input)}`);\n return ok(length.value);\n}\n\n/** A length as a document holds it: px as a number, other units as a string. */\nexport type LengthOf<U extends LengthUnit> = number | `${number}${Exclude<U, \"px\">}`;\n\nexport function length<const U extends LengthUnit = \"px\">(\n units: readonly U[] = [\"px\"] as unknown as readonly U[],\n options: { min?: number } = {}\n): Kind<LengthInput<U>, LengthOf<U>> {\n const min = options.min ?? 0;\n const accepts = `a length (${units.map((u) => (u === \"px\" ? \"a number of px\" : u)).join(\", \")})`;\n return kind<LengthInput<U>, LengthOf<U>>({\n name: \"length\",\n editor: \"length\",\n accepts,\n parse(input) {\n const parsed = readLength(input);\n if (!parsed) return fail(`expected ${accepts}, not ${show(input)}`);\n if (!units.includes(parsed.unit as U)) return fail(`${parsed.unit} isn't accepted here; expected ${accepts}`);\n if (parsed.value < min) return fail(`can't be less than ${min}, not ${show(input)}`);\n return ok((parsed.unit === \"px\" ? parsed.value : `${parsed.value}${parsed.unit}`) as LengthOf<U>);\n },\n jsonSchema: () => ({\n anyOf: [\n ...(units.includes(\"px\" as U) ? [{ type: \"number\", minimum: min }] : []),\n { type: \"string\", pattern: `^${NUMBER}(${units.map((u) => (u === \"%\" ? \"%\" : u)).join(\"|\")})${units.includes(\"px\" as U) ? \"?\" : \"\"}$` },\n ],\n }),\n });\n}\n\nexport function number(options: { min?: number; max?: number; integer?: boolean } = {}): Kind<number, number> {\n const { min = -Infinity, max = Infinity, integer = false } = options;\n const accepts = `${integer ? \"an integer\" : \"a number\"}${Number.isFinite(min) ? ` from ${min}` : \"\"}${Number.isFinite(max) ? ` to ${max}` : \"\"}`;\n return kind({\n name: \"number\",\n editor: \"number\",\n accepts,\n parse(input) {\n if (typeof input !== \"number\" || !Number.isFinite(input) || (integer && !Number.isInteger(input)) || input < min || input > max) {\n return fail(`expected ${accepts}, not ${show(input)}`);\n }\n return ok(input);\n },\n jsonSchema: () => ({\n type: integer ? \"integer\" : \"number\",\n ...(Number.isFinite(min) ? { minimum: min } : {}),\n ...(Number.isFinite(max) ? { maximum: max } : {}),\n }),\n });\n}\n\n/** A share of a Row's free width (a grow factor), or \"none\" to take its own size. */\nexport function flex(): Kind<number | \"none\", number | \"none\"> {\n return kind({\n name: \"flex\",\n editor: \"number\",\n accepts: 'a share of the free width (0 or more), or \"none\"',\n parse: (input) =>\n input === \"none\" || (typeof input === \"number\" && Number.isFinite(input) && input >= 0)\n ? ok(input)\n : fail(`expected a share of the free width (0 or more) or \"none\", not ${show(input)}`),\n jsonSchema: () => ({ anyOf: [{ type: \"number\", minimum: 0 }, { const: \"none\" }] }),\n });\n}\n\n// ─── Insets, borders, corners ───────────────────────────────────────────────\n\nexport type InsetsInput =\n | Px\n | string\n | [Px, Px]\n | [Px, Px, Px]\n | [Px, Px, Px, Px]\n | { top?: Px; right?: Px; bottom?: Px; left?: Px };\nexport type Insets = { top: number; right: number; bottom: number; left: number };\n\nconst SIDES = [\"top\", \"right\", \"bottom\", \"left\"] as const;\n\n/** CSS's 1-to-4 value order: top, right, bottom, left. */\nfunction fourSides<T>(values: T[]): [T, T, T, T] {\n const [a, b = a, c = a, d = b] = values;\n return [a, b, c, d];\n}\n\nexport function insets(): Kind<InsetsInput, Insets> {\n const accepts = 'px for all sides, \"8px 16px\" (1 to 4 values), [v, h] or [t, r, b, l], or { top, right, bottom, left }';\n const read = (values: unknown[]): Parsed<Insets> => {\n if (values.length < 1 || values.length > 4) return fail(`expected ${accepts}`);\n const out: number[] = [];\n for (const value of values) {\n const side = px(value, 0, \"each side\");\n if (!side.ok) return side;\n out.push(side.value);\n }\n const [top, right, bottom, left] = fourSides(out);\n return ok({ top, right, bottom, left });\n };\n return kind({\n name: \"insets\",\n editor: \"insets\",\n accepts,\n parse(input) {\n if (typeof input === \"number\") return read([input]);\n if (typeof input === \"string\") return read(input.trim().split(/\\s+/));\n if (Array.isArray(input)) return read(input);\n if (isObject(input)) {\n const extra = Object.keys(input).find((key) => !(SIDES as readonly string[]).includes(key));\n if (extra) return fail(`\"${extra}\" isn't a side; expected top, right, bottom or left`);\n const out = { top: 0, right: 0, bottom: 0, left: 0 };\n for (const side of SIDES) {\n if (input[side] === undefined) continue;\n const value = px(input[side], 0, side);\n if (!value.ok) return value;\n out[side] = value.value;\n }\n return ok(out);\n }\n return fail(`expected ${accepts}, not ${show(input)}`);\n },\n jsonSchema: () => {\n const side = { anyOf: [{ type: \"number\", minimum: 0 }, { type: \"string\", pattern: `^${NUMBER}(px)?$` }] };\n return {\n anyOf: [\n side.anyOf[0],\n { type: \"string\" },\n { type: \"array\", items: side, minItems: 1, maxItems: 4 },\n { type: \"object\", properties: Object.fromEntries(SIDES.map((s) => [s, side])), additionalProperties: false },\n ],\n };\n },\n });\n}\n\nexport type BorderStyle = \"solid\" | \"dashed\" | \"dotted\" | \"none\";\nexport type BorderSideInput = { width?: Px; style?: BorderStyle; color?: string };\nexport type BorderInput =\n | string\n | BorderSideInput\n | { top?: BorderSideInput | string; right?: BorderSideInput | string; bottom?: BorderSideInput | string; left?: BorderSideInput | string };\nexport type BorderSide = { width: number; style: BorderStyle; color: string };\nexport type Border = { top?: BorderSide; right?: BorderSide; bottom?: BorderSide; left?: BorderSide };\n\nconst BORDER_STYLES: readonly BorderStyle[] = [\"solid\", \"dashed\", \"dotted\", \"none\"];\n\nfunction borderSide(input: unknown): Parsed<BorderSide> {\n const out: BorderSide = { width: 1, style: \"solid\", color: \"#000000\" };\n if (typeof input === \"string\") {\n for (const token of input.match(/rgba?\\([^)]*\\)|\\S+/g) ?? []) {\n if (readLength(token)) {\n const width = px(token, 0, \"a border's width\");\n if (!width.ok) return width;\n out.width = width.value;\n } else if ((BORDER_STYLES as readonly string[]).includes(token)) {\n out.style = token as BorderStyle;\n } else {\n const c = parseColor(token);\n if (!c.ok) return fail(`\"${token}\" in ${show(input)} isn't a width, a style (${BORDER_STYLES.join(\", \")}) or a color`);\n out.color = c.value;\n }\n }\n return ok(out);\n }\n if (!isObject(input)) return fail(`expected \"1px solid #ccc\" or { width, style, color }, not ${show(input)}`);\n const extra = Object.keys(input).find((key) => ![\"width\", \"style\", \"color\"].includes(key));\n if (extra) return fail(`\"${extra}\" isn't part of a border side; expected width, style or color`);\n if (input.width !== undefined) {\n const width = px(input.width, 0, \"a border's width\");\n if (!width.ok) return width;\n out.width = width.value;\n }\n if (input.style !== undefined) {\n if (!(BORDER_STYLES as readonly unknown[]).includes(input.style)) return fail(`a border's style is one of ${BORDER_STYLES.join(\", \")}, not ${show(input.style)}`);\n out.style = input.style as BorderStyle;\n }\n if (input.color !== undefined) {\n const c = parseColor(input.color);\n if (!c.ok) return c;\n out.color = c.value;\n }\n return ok(out);\n}\n\n/** One line, as a border side: `\"1px solid #eee\"` or `{ width, style, color }` (a table's line between rows). */\nexport function line(): Kind<string | BorderSideInput, BorderSide> {\n return kind<string | BorderSideInput, BorderSide>({\n name: \"line\",\n editor: \"border\",\n accepts: '\"1px solid #ccc\" or { width, style, color }',\n parse: (input) => borderSide(input),\n jsonSchema: () => ({\n anyOf: [\n { type: \"string\" },\n {\n type: \"object\",\n properties: { width: { anyOf: [{ type: \"number\", minimum: 0 }, { type: \"string\" }] }, style: { enum: [...BORDER_STYLES] }, color: { type: \"string\" } },\n additionalProperties: false,\n },\n ],\n }),\n });\n}\n\nexport function border(): Kind<BorderInput, Border> {\n return kind({\n name: \"border\",\n editor: \"border\",\n accepts: '\"1px solid #ccc\", { width, style, color } for all sides, or { top, right, bottom, left } with one of those each',\n parse(input) {\n const perSide = isObject(input) && Object.keys(input).some((key) => (SIDES as readonly string[]).includes(key));\n if (!perSide) {\n const side = borderSide(input);\n return side.ok ? ok({ top: side.value, right: side.value, bottom: side.value, left: side.value }) : side;\n }\n const out: Border = {};\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (!(SIDES as readonly string[]).includes(key)) return fail(`\"${key}\" isn't a side; expected top, right, bottom or left`);\n const side = borderSide(value);\n if (!side.ok) return fail(`${key}: ${side.message}`);\n out[key as (typeof SIDES)[number]] = side.value;\n }\n return ok(out);\n },\n jsonSchema: () => {\n const side = {\n anyOf: [\n { type: \"string\" },\n {\n type: \"object\",\n properties: { width: { anyOf: [{ type: \"number\", minimum: 0 }, { type: \"string\" }] }, style: { enum: [...BORDER_STYLES] }, color: { type: \"string\" } },\n additionalProperties: false,\n },\n ],\n };\n return { anyOf: [...side.anyOf, { type: \"object\", properties: Object.fromEntries(SIDES.map((s) => [s, side])), additionalProperties: false }] };\n },\n });\n}\n\nexport type RadiusInput = Px | [Px, Px, Px, Px] | { topLeft?: Px; topRight?: Px; bottomRight?: Px; bottomLeft?: Px };\nexport type Corners = { topLeft: number; topRight: number; bottomRight: number; bottomLeft: number };\nexport type Radius = number | Corners;\n\nconst CORNERS = [\"topLeft\", \"topRight\", \"bottomRight\", \"bottomLeft\"] as const;\n\nexport function radius(): Kind<RadiusInput, Radius> {\n const accepts = \"px for every corner, [topLeft, topRight, bottomRight, bottomLeft], or { topLeft, topRight, bottomRight, bottomLeft }\";\n return kind<RadiusInput, Radius>({\n name: \"radius\",\n editor: \"radius\",\n accepts,\n parse(input) {\n let values: unknown[];\n if (Array.isArray(input)) {\n if (input.length !== 4) return fail(`expected ${accepts}`);\n values = input;\n } else if (isObject(input)) {\n const extra = Object.keys(input).find((key) => !(CORNERS as readonly string[]).includes(key));\n if (extra) return fail(`\"${extra}\" isn't a corner; expected ${CORNERS.join(\", \")}`);\n values = CORNERS.map((corner) => input[corner] ?? 0);\n } else {\n return px(input, 0, \"a radius\");\n }\n const out: number[] = [];\n for (const value of values) {\n const corner = px(value, 0, \"a corner\");\n if (!corner.ok) return corner;\n out.push(corner.value);\n }\n if (out.every((value) => value === out[0])) return ok(out[0]);\n const [topLeft, topRight, bottomRight, bottomLeft] = out;\n return ok({ topLeft, topRight, bottomRight, bottomLeft });\n },\n jsonSchema: () => {\n const corner = { anyOf: [{ type: \"number\", minimum: 0 }, { type: \"string\", pattern: `^${NUMBER}(px)?$` }] };\n return {\n anyOf: [\n ...corner.anyOf,\n { type: \"array\", items: corner, minItems: 4, maxItems: 4 },\n { type: \"object\", properties: Object.fromEntries(CORNERS.map((c) => [c, corner])), additionalProperties: false },\n ],\n };\n },\n });\n}\n\n// ─── Choices, text, flags ───────────────────────────────────────────────────\n\nexport function enumOf<const V extends readonly (string | number)[]>(values: V): Kind<V[number], V[number]> {\n const accepts = `one of ${values.map((v) => JSON.stringify(v)).join(\", \")}`;\n return kind({\n name: \"enum\",\n editor: \"choice\",\n accepts,\n parse: (input) => ((values as readonly unknown[]).includes(input) ? ok(input as V[number]) : fail(`expected ${accepts}, not ${show(input)}`)),\n jsonSchema: () => ({ enum: [...values] }),\n });\n}\n\n/** Text, at most `maxLength` long; with a `pattern`, text it finds something in (`mismatch` says what's missing). */\nexport function text(options: { maxLength?: number; pattern?: RegExp; mismatch?: string } = {}): Kind<string, string> {\n return kind({\n name: \"text\",\n editor: \"text\",\n accepts: \"text\",\n parse(input) {\n if (typeof input !== \"string\") return fail(`expected text, not ${show(input)}`);\n if (options.maxLength !== undefined && input.length > options.maxLength) return fail(`is longer than ${options.maxLength} characters`);\n if (options.pattern && !options.pattern.test(input)) return fail(options.mismatch ?? `doesn't match ${options.pattern.source}`);\n return ok(input);\n },\n jsonSchema: () => ({ type: \"string\", ...(options.maxLength !== undefined ? { maxLength: options.maxLength } : {}), ...(options.pattern ? { pattern: options.pattern.source } : {}) }),\n });\n}\n\nexport function boolean(): Kind<boolean, boolean> {\n return kind({\n name: \"boolean\",\n editor: \"toggle\",\n accepts: \"true or false\",\n parse: (input) => (typeof input === \"boolean\" ? ok(input) : fail(`expected true or false, not ${show(input)}`)),\n jsonSchema: () => ({ type: \"boolean\" }),\n });\n}\n\n// ─── Colors ─────────────────────────────────────────────────────────────────\n\nconst NAMED_COLORS = new Set(\n (\n \"aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood \" +\n \"cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray \" +\n \"darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen \" +\n \"darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue \" +\n \"firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew \" +\n \"hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan \" +\n \"lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray \" +\n \"lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue \" +\n \"mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred \" +\n \"midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid \" +\n \"palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple \" +\n \"rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue \" +\n \"slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white \" +\n \"whitesmoke yellow yellowgreen transparent\"\n ).split(\" \")\n);\n\nconst HEX = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;\nconst RGB = /^rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*(?:,\\s*(\\d*\\.?\\d+%?)\\s*)?\\)$/i;\n\nconst hex2 = (n: number) => n.toString(16).padStart(2, \"0\");\n\n/** A color as \"#rrggbb\", \"rgba(r, g, b, a)\" when it's see-through, or a lowercase CSS name. */\nexport function parseColor(input: unknown): Parsed<string> {\n if (typeof input !== \"string\") return fail(`expected a color (\"#0879a1\", \"rgb(…)\", \"rgba(…)\" or a CSS name), not ${show(input)}`);\n const value = input.trim().toLowerCase();\n const hex = HEX.exec(value);\n if (hex) {\n const digits = hex[1].length <= 4 ? [...hex[1]].map((d) => d + d).join(\"\") : hex[1];\n if (digits.length === 6 || digits.slice(6) === \"ff\") return ok(`#${digits.slice(0, 6)}`);\n const alpha = Math.round((parseInt(digits.slice(6), 16) / 255) * 1000) / 1000;\n return ok(`rgba(${parseInt(digits.slice(0, 2), 16)}, ${parseInt(digits.slice(2, 4), 16)}, ${parseInt(digits.slice(4, 6), 16)}, ${alpha})`);\n }\n const rgb = RGB.exec(value);\n if (rgb) {\n const channels = rgb.slice(1, 4).map(Number);\n if (channels.some((c) => c > 255)) return fail(`a color channel is 0 to 255 in ${show(input)}`);\n let alpha = 1;\n if (rgb[4] !== undefined) {\n alpha = rgb[4].endsWith(\"%\") ? Number(rgb[4].slice(0, -1)) / 100 : Number(rgb[4]);\n if (!(alpha >= 0 && alpha <= 1)) return fail(`a color's alpha is 0 to 1 in ${show(input)}`);\n }\n return ok(alpha === 1 ? `#${channels.map(hex2).join(\"\")}` : `rgba(${channels.join(\", \")}, ${alpha})`);\n }\n if (NAMED_COLORS.has(value)) return ok(value);\n return fail(`expected a color (\"#0879a1\", \"rgb(…)\", \"rgba(…)\" or a CSS name), not ${show(input)}`);\n}\n\nexport function color(): Kind<string, string> {\n return kind({\n name: \"color\",\n editor: \"color\",\n accepts: 'a color (\"#0879a1\", \"rgb(…)\", \"rgba(…)\" or a CSS name)',\n parse: parseColor,\n jsonSchema: () => ({ type: \"string\" }),\n });\n}\n\n// ─── Fonts ──────────────────────────────────────────────────────────────────\n\n/** A font stack, as CSS writes it: \"Inter, Arial, sans-serif\". Web fonts are declared once, in the root's `fonts`. */\nexport type FontFamilyInput = string;\nexport type FontFamily = string;\n\n/** What can't be in a font name or stack: what would end a declaration or a stylesheet. */\nconst FONT_UNSAFE = /[;{}<>\\\\\\n\\r]/;\n\nexport function fontFamily(): Kind<FontFamilyInput, FontFamily> {\n const accepts = 'a font stack, as CSS writes it (\"Inter, Arial, sans-serif\")';\n return kind<FontFamilyInput, FontFamily>({\n name: \"fontFamily\",\n editor: \"fontFamily\",\n accepts,\n parse(input) {\n if (isObject(input) && \"src\" in input) return fail(\"a web font is declared once, in the root's fonts ({ family, src, weight, style }); fontFamily names it in a stack\");\n if (typeof input !== \"string\" || !input.trim() || FONT_UNSAFE.test(input)) return fail(`expected ${accepts}, not ${show(input)}`);\n return ok(input.trim());\n },\n jsonSchema: () => ({ type: \"string\", minLength: 1 }),\n });\n}\n\n/** The families of a font stack, unquoted: \"'Brand Sans', Arial\" is [\"Brand Sans\", \"Arial\"]. */\nexport function fontStack(stack: string): string[] {\n return stack\n .split(\",\")\n .map((family) => family.trim().replace(/^([\"'])(.*)\\1$/, \"$2\"))\n .filter(Boolean);\n}\n\nexport type FontFormat = \"woff2\" | \"woff\" | \"truetype\" | \"opentype\";\nconst FONT_FORMATS: readonly FontFormat[] = [\"woff2\", \"woff\", \"truetype\", \"opentype\"];\n\nexport type FontFaceInput = {\n /** The name text uses for it in a fontFamily stack. */\n family: string;\n /** Its file: an http(s) URL, or a data:font URI. */\n src: string;\n /** The file's format; from its extension where not given. */\n format?: FontFormat;\n /** The weight this file draws (400, \"bold\"), or the range a variable font covers (\"100 900\" or [100, 900]). */\n weight?: FontWeightInput | number | `${number} ${number}` | readonly [number, number];\n style?: \"normal\" | \"italic\";\n};\nexport type FontFace = { family: string; src: string; format?: FontFormat; weight?: number | [number, number]; style?: \"normal\" | \"italic\" };\n\n/** Web fonts: the faces a template loads, each a family's file for a weight and style. */\n/** A list of items, each an object of the given fields, all of them required (a menu's links, a line of icons). */\nexport function items<const F extends Record<string, Kind<any, Json>>>(\n fields: F,\n noun: string\n): Kind<Array<{ [K in keyof F]: InputOf<F[K]> }>, Array<{ [K in keyof F]: OutputOf<F[K]> }>> {\n const names = Object.keys(fields);\n const shape = `{ ${names.join(\", \")} }`;\n const accepts = `a list of ${noun}s, each ${shape}`;\n return kind({\n name: \"items\",\n editor: \"items\",\n accepts,\n parse(input) {\n if (!Array.isArray(input)) return fail(`expected ${accepts}, not ${show(input)}`);\n const out: Array<Record<string, Json>> = [];\n for (const [index, item] of input.entries()) {\n const at = `${noun} ${index + 1}`;\n if (!isObject(item)) return fail(`${at}: expected ${shape}, not ${show(item)}`);\n const extra = Object.keys(item).find((key) => !names.includes(key));\n if (extra) return fail(`${at}: \"${extra}\" isn't one of its fields (${names.join(\", \")})`);\n const value: Record<string, Json> = {};\n for (const name of names) {\n if (item[name] === undefined) return fail(`${at}: ${name} is missing`);\n const parsed = fields[name].parse(item[name]);\n if (!parsed.ok) return fail(`${at}: ${name}: ${parsed.message}`);\n value[name] = parsed.value;\n }\n out.push(value);\n }\n return ok(out as Array<{ [K in keyof F]: OutputOf<F[K]> }>);\n },\n jsonSchema: () => ({\n type: \"array\",\n items: { type: \"object\", required: names, properties: Object.fromEntries(names.map((name) => [name, fields[name].jsonSchema()])), additionalProperties: false },\n }),\n });\n}\n\nexport function fontFaces(): Kind<FontFaceInput[], FontFace[]> {\n const accepts = \"a list of { family, src, format, weight, style }\";\n const face = (input: unknown, index: number): Parsed<FontFace> => {\n const at = `font ${index + 1}`;\n if (!isObject(input)) return fail(`${at}: expected { family, src, format, weight, style }, not ${show(input)}`);\n const extra = Object.keys(input).find((key) => ![\"family\", \"src\", \"format\", \"weight\", \"style\"].includes(key));\n if (extra) return fail(`${at}: \"${extra}\" isn't part of a font; expected family, src, format, weight or style`);\n if (typeof input.family !== \"string\" || !input.family.trim() || FONT_UNSAFE.test(input.family) || input.family.includes(\",\")) {\n return fail(`${at}: family is one font's name (\"Inter\"), not ${show(input.family)}`);\n }\n if (typeof input.src !== \"string\" || !input.src.trim() || unsafeUrl(input.src, /^data:(font|application)\\//)) {\n return fail(`${at}: src is the font file's http(s) URL (or a data:font URI), not ${show(input.src)}`);\n }\n const out: FontFace = { family: input.family.trim(), src: input.src.trim() };\n if (input.format !== undefined) {\n if (!(FONT_FORMATS as readonly unknown[]).includes(input.format)) return fail(`${at}: format is one of ${FONT_FORMATS.join(\", \")}, not ${show(input.format)}`);\n out.format = input.format as FontFormat;\n }\n if (input.weight !== undefined) {\n // A range as written (\"100 900\"), or as parsed ([100, 900]): a font is checked again once it's been read.\n const range = typeof input.weight === \"string\" ? /^\\s*(\\d+)\\s+(\\d+)\\s*$/.exec(input.weight) : Array.isArray(input.weight) && input.weight.length === 2 && input.weight.every(Number.isInteger) ? [null, ...input.weight] : null;\n const weight = input.weight === \"normal\" ? 400 : input.weight === \"bold\" ? 700 : input.weight;\n if (range && Number(range[1]) >= 1 && Number(range[2]) <= 1000 && Number(range[1]) <= Number(range[2])) out.weight = [Number(range[1]), Number(range[2])];\n else if (typeof weight === \"number\" && Number.isInteger(weight) && weight >= 1 && weight <= 1000) out.weight = weight;\n else return fail(`${at}: weight is 1 to 1000, \"normal\", \"bold\", or a range (\"100 900\"), not ${show(input.weight)}`);\n }\n if (input.style !== undefined) {\n if (input.style !== \"normal\" && input.style !== \"italic\") return fail(`${at}: style is \"normal\" or \"italic\", not ${show(input.style)}`);\n out.style = input.style;\n }\n return ok(out);\n };\n return kind<FontFaceInput[], FontFace[]>({\n name: \"fontFaces\",\n editor: \"fontFaces\",\n accepts,\n parse(input) {\n if (!Array.isArray(input)) return fail(`expected ${accepts}, not ${show(input)}`);\n const out: FontFace[] = [];\n for (const [index, item] of input.entries()) {\n const parsed = face(item, index);\n if (!parsed.ok) return parsed;\n out.push(parsed.value);\n }\n return ok(out);\n },\n jsonSchema: () => ({\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"family\", \"src\"],\n properties: {\n family: { type: \"string\", minLength: 1 },\n src: { type: \"string\", minLength: 1 },\n format: { enum: [...FONT_FORMATS] },\n weight: { anyOf: [{ type: \"number\", minimum: 1, maximum: 1000 }, { type: \"string\" }, { type: \"array\", items: { type: \"integer\", minimum: 1, maximum: 1000 }, minItems: 2, maxItems: 2 }] },\n style: { enum: [\"normal\", \"italic\"] },\n },\n additionalProperties: false,\n },\n }),\n });\n}\n\nexport type FontWeightInput = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | \"normal\" | \"bold\";\n\nexport function fontWeight(): Kind<FontWeightInput, number> {\n return kind({\n name: \"fontWeight\",\n editor: \"fontWeight\",\n accepts: '100 to 900 in hundreds, \"normal\" or \"bold\"',\n parse(input) {\n if (input === \"normal\") return ok(400);\n if (input === \"bold\") return ok(700);\n if (typeof input === \"number\" && input >= 100 && input <= 900 && input % 100 === 0) return ok(input);\n return fail(`expected 100 to 900 in hundreds, \"normal\" or \"bold\", not ${show(input)}`);\n },\n jsonSchema: () => ({ anyOf: [{ enum: [100, 200, 300, 400, 500, 600, 700, 800, 900] }, { enum: [\"normal\", \"bold\"] }] }),\n });\n}\n\nexport type LineHeightInput = number | `${number}px` | `${number}%` | \"normal\";\n/** A multiple of the font size (1.5), or a fixed height in px (\"24px\"). */\nexport type LineHeight = number | `${number}px` | \"normal\";\n\nexport function lineHeight(): Kind<LineHeightInput, LineHeight> {\n const accepts = 'a multiple of the font size (1.5), a percentage (\"150%\"), px (\"24px\") or \"normal\" (the font\\'s own)';\n return kind<LineHeightInput, LineHeight>({\n name: \"lineHeight\",\n editor: \"lineHeight\",\n accepts,\n parse(input) {\n if (input === \"normal\") return ok(\"normal\");\n if (typeof input === \"number\") return input > 0 && Number.isFinite(input) ? ok(input) : fail(`expected ${accepts}, not ${show(input)}`);\n const parsed = typeof input === \"string\" && /^\\s*-?[\\d.]+\\s*$/.test(input) ? undefined : readLength(input);\n if (!parsed || parsed.value <= 0 || (parsed.unit !== \"px\" && parsed.unit !== \"%\")) return fail(`expected ${accepts}, not ${show(input)}`);\n return ok(parsed.unit === \"%\" ? Math.round(parsed.value * 1000) / 100000 : (`${parsed.value}px` as const));\n },\n jsonSchema: () => ({ anyOf: [{ type: \"number\", exclusiveMinimum: 0 }, { type: \"string\", pattern: `^${NUMBER}(px|%)$` }, { const: \"normal\" }] }),\n });\n}\n\n// ─── URLs: images and links ─────────────────────────────────────────────────\n\n/**\n * An email service's tag in a URL, as it's written or as Elements 0.2's components kept it from the writers (a mark,\n * `\\uFDD0<n>\\uFDD1`). Its service fills it in later, perhaps with nothing.\n */\nconst SERVICE_TAG = /<%[\\s\\S]*?%>|\\{%[\\s\\S]*?%\\}|\\{\\{[\\s\\S]*?\\}\\}|\\uFDD0\\d+\\uFDD1/g;\n\n/** A URL scheme that runs code or reads local files, after the characters browsers ignore in one, and without what a service fills in. */\nfunction unsafeUrl(url: string, allowedData?: RegExp): boolean {\n // eslint-disable-next-line no-control-regex\n const compact = url.replace(SERVICE_TAG, \"\").replace(/[\\u0000- \\u007f-\\u009f]/g, \"\").toLowerCase();\n if (compact.startsWith(\"data:\")) return !allowedData || !allowedData.test(compact);\n return /^(javascript|vbscript|file):/.test(compact);\n}\n\nexport type AssetInput = string | { src: string; width?: number; height?: number };\n/** A URL, or one with the image's own size in px (used to size it where no width is set). */\nexport type Asset = string | { src: string; width?: number; height?: number };\n\nconst IMAGE_DATA = /^data:image\\/(png|jpeg|jpg|gif|webp|svg\\+xml)[;,]/;\n\nexport function asset(): Kind<AssetInput, Asset> {\n const accepts = \"an image URL (http, https or a data:image URI), or { src, width, height }\";\n return kind<AssetInput, Asset>({\n name: \"asset\",\n editor: \"image\",\n accepts,\n parse(input) {\n const src = typeof input === \"string\" ? input : isObject(input) ? input.src : undefined;\n if (typeof src !== \"string\" || !src.trim()) return fail(`expected ${accepts}, not ${show(input)}`);\n if (unsafeUrl(src, IMAGE_DATA)) return fail(`${show(src)} isn't an image URL Elements will write`);\n if (typeof input === \"string\") return ok(src.trim());\n const extra = Object.keys(input as object).find((key) => ![\"src\", \"width\", \"height\"].includes(key));\n if (extra) return fail(`\"${extra}\" isn't part of an image; expected src, width or height`);\n const out: { src: string; width?: number; height?: number } = { src: src.trim() };\n for (const side of [\"width\", \"height\"] as const) {\n const value = (input as Record<string, unknown>)[side];\n if (value === undefined) continue;\n if (typeof value !== \"number\" || !(value > 0)) return fail(`an image's ${side} is its size in px, not ${show(value)}`);\n out[side] = value;\n }\n return ok(out);\n },\n jsonSchema: () => ({\n anyOf: [\n { type: \"string\", minLength: 1 },\n {\n type: \"object\",\n required: [\"src\"],\n properties: { src: { type: \"string\", minLength: 1 }, width: { type: \"number\", exclusiveMinimum: 0 }, height: { type: \"number\", exclusiveMinimum: 0 } },\n additionalProperties: false,\n },\n ],\n }),\n });\n}\n\nexport type LinkTarget = \"_blank\" | \"_self\";\nexport type LinkInput = string | { href: string; target?: LinkTarget };\nexport type LinkValue = string | { href: string; target?: LinkTarget };\n\n// ─── Videos ─────────────────────────────────────────────────────────────────\n\nexport type VideoProvider = \"youtube\" | \"vimeo\";\nconst YOUTUBE = /^https:\\/\\/(?:www\\.|m\\.)?(?:youtube\\.com\\/(?:watch\\?(?:[^#]*&)?v=|embed\\/|shorts\\/|live\\/)|youtu\\.be\\/)([A-Za-z0-9_-]{6,})/;\nconst VIMEO = /^https:\\/\\/(?:www\\.|player\\.)?vimeo\\.com\\/(?:video\\/)?(\\d+)/;\n\n/**\n * A video's provider and id, and what follows from them without the network: its player's URL, and a YouTube\n * video's thumbnail (a Vimeo video's is only found by asking Vimeo).\n */\nexport function videoParts(url: string): { provider: VideoProvider; id: string; embed: string; thumbnail?: string } | undefined {\n const youtube = YOUTUBE.exec(url);\n if (youtube) return { provider: \"youtube\", id: youtube[1], embed: `https://www.youtube.com/embed/${youtube[1]}`, thumbnail: `https://img.youtube.com/vi/${youtube[1]}/0.jpg` };\n const vimeo = VIMEO.exec(url);\n if (vimeo) return { provider: \"vimeo\", id: vimeo[1], embed: `https://player.vimeo.com/video/${vimeo[1]}` };\n return undefined;\n}\n\nexport function video(): Kind<string, string> {\n const accepts = \"a YouTube or Vimeo video's https link\";\n return kind<string, string>({\n name: \"video\",\n editor: \"link\",\n accepts,\n parse(input) {\n if (typeof input !== \"string\" || !videoParts(input.trim())) return fail(`expected ${accepts}, not ${show(input)}`);\n return ok(input.trim());\n },\n jsonSchema: () => ({ type: \"string\", pattern: \"^https://\" }),\n });\n}\n\nexport function link(): Kind<LinkInput, LinkValue> {\n const accepts = \"a URL (http, https, mailto, tel, a path, or a merge tag) or { href, target }\";\n return kind<LinkInput, LinkValue>({\n name: \"link\",\n editor: \"link\",\n accepts,\n parse(input) {\n const href = typeof input === \"string\" ? input : isObject(input) ? input.href : undefined;\n // A link to nothing is { href: \"\" }, as a template from another format says one has no URL; a string that's empty is a mistake.\n if (typeof href !== \"string\" || (!href.trim() && (typeof input === \"string\" || href !== \"\"))) return fail(`expected ${accepts}, not ${show(input)}`);\n if (unsafeUrl(href)) return fail(`${show(href)} isn't a link Elements will write (scripts, data and local files aren't)`);\n if (typeof input === \"string\") return ok(href.trim());\n const extra = Object.keys(input as object).find((key) => ![\"href\", \"target\"].includes(key));\n if (extra) return fail(`\"${extra}\" isn't part of a link; expected href or target`);\n const target = (input as Record<string, unknown>).target;\n if (target === undefined) return ok(href ? href.trim() : { href });\n if (target !== \"_blank\" && target !== \"_self\") return fail(`a link's target is \"_blank\" or \"_self\", not ${show(target)}`);\n return ok({ href: href.trim(), target });\n },\n jsonSchema: () => ({\n anyOf: [\n { type: \"string\", minLength: 1 },\n { type: \"object\", required: [\"href\"], properties: { href: { type: \"string\" }, target: { enum: [\"_blank\", \"_self\"] } }, additionalProperties: false },\n ],\n }),\n });\n}\n\n/** More attributes for an element, by name (a link's class, a `data-` attribute): not its handlers, which run scripts. */\nexport function attributes(): Kind<Record<string, string>, Record<string, string>> {\n const accepts = \"attributes by name, each a text value (no on… handlers, href or style)\";\n return kind<Record<string, string>, Record<string, string>>({\n name: \"attributes\",\n editor: \"text\",\n accepts,\n parse(input) {\n if (!isObject(input)) return fail(`expected ${accepts}, not ${show(input)}`);\n for (const [name, value] of Object.entries(input)) {\n if (!/^[a-zA-Z_:][\\w:.-]*$/.test(name) || /^(on|href$|style$)/i.test(name) || typeof value !== \"string\") return fail(`${show(name)} isn't an attribute Elements will write, with ${show(value)}`);\n }\n return ok(input as Record<string, string>);\n },\n jsonSchema: () => ({ type: \"object\", additionalProperties: { type: \"string\" } }),\n });\n}\n\n/** A link's href and target, from either form. */\nexport function linkParts(value: LinkValue): { href: string; target?: LinkTarget } {\n return typeof value === \"string\" ? { href: value } : value;\n}\n\n/** An image's URL and own size, from either form. */\nexport function assetParts(value: Asset): { src: string; width?: number; height?: number } {\n return typeof value === \"string\" ? { src: value } : value;\n}\n","/**\n * Resolving a document: what every target needs worked out the same way\n * before it writes anything. Defaults applied, text styles inherited, and\n * every box's width on desktop in px, from the root's width down. Targets\n * only turn this into markup; none of them decides layout on its own.\n */\nimport type { ComponentDefinition, Target } from \"./define\";\nimport { Diagnostics, type Diagnostic } from \"./diagnostics\";\nimport { assetParts, readLength, type Asset, type Border, type BorderSide, type FontFamily, type Insets, type LineHeight } from \"./kinds\";\nimport { isNode, type ElementsDocument, type Json, type Node } from \"./model\";\nimport { pageBox } from \"./document\";\nimport { expandDocument, templatePathsOf, type TemplatePaths } from \"./expand\";\nimport { propsWithDefaults, registryOf, type ComponentsOption, type Registry } from \"./registry\";\n\n/** Text styles as they apply to a node: its own, its component's defaults, else its parent's. */\nexport interface ResolvedText {\n color: string;\n fontFamily: FontFamily;\n fontSize: number;\n fontWeight: number;\n fontStyle: \"normal\" | \"italic\";\n lineHeight: LineHeight;\n /** The line height in px at this node's font size. */\n lineHeightPx: number;\n letterSpacing: number;\n textAlign: \"left\" | \"center\" | \"right\" | \"justify\";\n textTransform: \"none\" | \"uppercase\" | \"lowercase\" | \"capitalize\";\n}\n\nexport interface ResolvedNode {\n /** The author's id, if any. */\n id?: string;\n /**\n * Where it is in the tree as laid out (\"root.children[2]\"): its place in the template, but inside a Grid (laid out\n * as Rows of cells) and inside what a custom component renders. The document's `templatePath` gives the template's.\n */\n path: string;\n type: string;\n definition: ComponentDefinition;\n /** Its props, with the definition's defaults where not given. */\n props: Record<string, Json>;\n /** Its component's default style under its own (inherited text styles are in `text`). */\n style: Record<string, Json>;\n hover?: Record<string, Json>;\n mobile?: Record<string, Json>;\n hidden?: { mobile?: boolean; desktop?: boolean };\n text: ResolvedText;\n /** The width its parent gives it on desktop, in px (null inside text). */\n available: number | null;\n /**\n * Its width on desktop in px, padding and border included; null where it\n * takes its own size and that size isn't known (a Button's text, an Image\n * without its size, text inside text).\n */\n width: number | null;\n /** Its width inside its padding and border, in px, or null. */\n contentWidth: number | null;\n children: Array<ResolvedNode | string>;\n /** A Table's column widths in px, each column's from its cells, the rest shared (null where the table's width isn't known). */\n columns?: number[] | null;\n /** On a Table and its rows, where a column of its own px sits beside one with no width: how each column is written. \"px\" stays that wide in a narrower window, \"share\" stays its share of the table, \"free\" takes what they leave (otherwise, none: every column is a share of the table). */\n columnSizes?: Array<\"px\" | \"share\" | \"free\"> | null;\n}\n\nexport interface ResolvedDocument {\n root: ResolvedNode;\n /** At their places in the template. */\n diagnostics: Diagnostic[];\n /** A path in the tree as laid out (a node's `path`, and what follows it: \".props.src\") as the template's, for messages. */\n templatePath?: (path: string) => string;\n}\n\n/**\n * The laid-out tree's format (ResolvedNode), as prepare() gives it to the packages that write a PDF or an image: each\n * reads the version it's built with and refuses another. Raise it only where a reader of the tree would break (a\n * field renamed, removed or meaning something else), not for one added.\n */\nexport const LAYOUT_VERSION = 1;\n\n/** What a root's text styles start from, under its own defaults. */\nconst INITIAL_TEXT: Omit<ResolvedText, \"lineHeightPx\"> = {\n color: \"#000000\",\n fontFamily: \"Arial, Helvetica, sans-serif\",\n fontSize: 16,\n fontWeight: 400,\n fontStyle: \"normal\",\n lineHeight: 1.5,\n letterSpacing: 0,\n textAlign: \"left\",\n textTransform: \"none\",\n};\n\nconst INHERITED = Object.keys(INITIAL_TEXT) as Array<keyof typeof INITIAL_TEXT>;\n\n/** A length in px, given the width a percentage is of. */\nexport function toPx(value: Json | undefined, of: number | null): number | null {\n const length = readLength(value);\n if (!length) return null;\n switch (length.unit) {\n case \"px\":\n return length.value;\n case \"%\":\n return of === null ? null : (of * length.value) / 100;\n case \"pt\":\n return (length.value * 96) / 72;\n case \"mm\":\n return (length.value * 96) / 25.4;\n case \"in\":\n return length.value * 96;\n }\n}\n\nexport function lineHeightPx(lineHeight: LineHeight, fontSize: number): number {\n // A font's own (\"normal\") is about this: for what can't leave it to the font (a PDF's lines, an estimate of a height).\n if (lineHeight === \"normal\") return Math.round(1.2 * fontSize * 100) / 100;\n return typeof lineHeight === \"number\" ? Math.round(lineHeight * fontSize * 100) / 100 : Number.parseFloat(lineHeight);\n}\n\n/** The horizontal space a box's padding and border take. */\nexport function horizontalFrame(style: Record<string, Json>): number {\n const padding = style.padding as Insets | undefined;\n const border = style.border as Border | undefined;\n return (padding ? padding.left + padding.right : 0) + (border?.left?.width ?? 0) + (border?.right?.width ?? 0);\n}\n\ninterface Context {\n d: Diagnostics;\n registry: Registry;\n /** The root's type: Rows stack on phones by default in a responsive one. */\n responsive: boolean;\n /** The root's type: a Row wraps on a Page, and in an Email where it stays side by side on phones. A PDF and an image draw one line. */\n root: string;\n}\n\n/**\n * Resolves a valid document (see `checkDocument`), its custom components\n * expanded first: what's resolved is primitives only.\n */\nexport function resolveDocument(input: ElementsDocument, options?: ComponentsOption | Registry): ResolvedDocument {\n const registry = registryOf(options);\n const expanded = expandDocument(input, registry);\n const resolved = resolveExpanded(expanded.document, registry, expanded.paths);\n return { ...resolved, diagnostics: [...expanded.diagnostics, ...resolved.diagnostics] };\n}\n\n/**\n * Resolves a document whose custom components are already expanded (`expandDocument`): primitives only. With the\n * expansion's `paths`, its messages (and its `templatePath`) say where things are in the template.\n */\nexport function resolveExpanded(document: ElementsDocument, options?: ComponentsOption | Registry, paths?: TemplatePaths): ResolvedDocument {\n const registry = registryOf(options);\n const d = new Diagnostics();\n const definition = registry.get(document.root.type)!;\n const context: Context = { d, registry, responsive: !!definition.responsive, root: document.root.type };\n const root = resolveNode(document.root, \"root\", { ...INITIAL_TEXT, lineHeightPx: 24 }, null, context);\n // A Document's content is as wide as its page inside the margins; a Canvas's, as it inside its padding.\n const width = root.type === \"Document\" ? pageBox(root.props).contentWidth : toPx(root.props.width, null);\n root.width = width;\n root.contentWidth = width === null ? null : width - horizontalFrame(root.style);\n root.children = resolveChildren(document.root, \"root\", root, root.contentWidth, \"stack\", context);\n const templatePath = paths ? templatePathsOf(document, paths) : (path: string) => path;\n return { root, diagnostics: d.list.map((diagnostic) => ({ ...diagnostic, path: templatePath(diagnostic.path) })), templatePath };\n}\n\nfunction styleOf(node: Node, definition: ComponentDefinition, props: Record<string, Json>, context: Context): Record<string, Json> {\n const style: Record<string, Json> = { ...((definition.defaultStyle ?? {}) as Record<string, Json>) };\n const by = definition.defaultStyleBy;\n if (by) Object.assign(style, by.values[String(props[by.prop])] ?? {});\n return { ...style, ...node.style };\n}\n\nfunction textOf(parent: ResolvedText, style: Record<string, Json>): ResolvedText {\n const text = { ...parent } as Record<string, unknown>;\n for (const key of INHERITED) if (style[key] !== undefined) text[key] = style[key];\n const out = text as unknown as ResolvedText;\n out.lineHeightPx = lineHeightPx(out.lineHeight, out.fontSize);\n return out;\n}\n\n/** A direction of its own (a block's, or the root's) sets where its text starts, unless its author aligned it. */\nfunction directed(text: ResolvedText, dir: Json | undefined, ownAlign: Json | undefined): ResolvedText {\n if (ownAlign !== undefined || (dir !== \"rtl\" && dir !== \"ltr\")) return text;\n return { ...text, textAlign: dir === \"rtl\" ? \"right\" : \"left\" };\n}\n\n/** A node with its own values worked out; its width and children are set by its parent's layout. */\nfunction resolveNode(node: Node, path: string, parentText: ResolvedText, available: number | null, context: Context): ResolvedNode {\n const definition = context.registry.get(node.type)!;\n const props = propsWithDefaults(definition, node.props);\n const style = styleOf(node, definition, props, context);\n return {\n ...(node.id ? { id: node.id } : {}),\n path,\n type: node.type,\n definition,\n props,\n style,\n ...(node.hover ? { hover: node.hover } : {}),\n ...(node.mobile ? { mobile: node.mobile } : {}),\n ...(node.hidden ? { hidden: node.hidden } : {}),\n text: directed(textOf(parentText, style), props.dir, node.style?.textAlign),\n available,\n width: null,\n contentWidth: null,\n children: [],\n };\n}\n\nfunction clamp(width: number, style: Record<string, Json>, of: number | null): number {\n const min = toPx(style.minWidth, of);\n const max = toPx(style.maxWidth, of);\n if (max !== null) width = Math.min(width, max);\n if (min !== null) width = Math.max(width, min);\n return width;\n}\n\n/** The size a node has of its own: an image's, when its document gives it. */\nfunction intrinsicWidth(resolved: ResolvedNode, available: number | null): number | null {\n // An Svg's drawing is as wide as it says, its padding and border around it; it's never narrowed (the Page draws it\n // so): wider than its room, it overflows it.\n if (resolved.type === \"Svg\" && typeof resolved.props.width === \"number\") return resolved.props.width + horizontalFrame(resolved.style);\n if (resolved.type !== \"Image\" || resolved.props.src === undefined) return null;\n const own = assetParts(resolved.props.src as Asset).width ?? null;\n if (own === null) return null;\n return clamp(available === null ? own : Math.min(own, available), resolved.style, available);\n}\n\n/** The width a node takes of `available` where its parent stacks its children. */\nfunction stackedWidth(resolved: ResolvedNode, available: number | null): number | null {\n const set = toPx(resolved.style.width, available);\n if (set !== null) return clamp(set, resolved.style, available);\n if (resolved.definition.sizing === \"stretch\") return available === null ? null : clamp(available, resolved.style, available);\n if (resolved.definition.sizing === \"shrink\") return intrinsicWidth(resolved, available);\n return null;\n}\n\nfunction setWidth(resolved: ResolvedNode, width: number | null): void {\n resolved.width = width === null ? null : Math.round(width);\n resolved.contentWidth = resolved.width === null ? null : Math.max(0, resolved.width - horizontalFrame(resolved.style));\n}\n\ntype Layout = \"stack\" | \"row\" | \"text\";\n\nfunction resolveChildren(node: Node, path: string, parent: ResolvedNode, available: number | null, layout: Layout, context: Context): Array<ResolvedNode | string> {\n const out: Array<ResolvedNode | string> = [];\n const nodes: Array<{ source: Node; resolved: ResolvedNode; at: string }> = [];\n (node.children ?? []).forEach((child, index) => {\n if (!isNode(child)) {\n out.push(child);\n return;\n }\n const at = `${path}.children[${index}]`;\n // Inside text, text takes no width of its own; what isn't text (an Image, a nested List) does.\n const inText = layout === \"text\" && !!context.registry.get(child.type)?.categories.includes(\"inline\");\n const resolved = resolveNode(child, at, parent.text, inText ? null : available, context);\n if (resolved.style.flex !== undefined && layout !== \"row\") {\n context.d.warning(\"style/ignored\", `flex only applies to a child of a Row; this ${child.type} is in a ${parent.type}`, `${at}.style.flex`, child.id);\n }\n nodes.push({ source: child, resolved, at });\n out.push(resolved);\n });\n\n if (layout === \"row\") layOutRow(parent, nodes, available, path, context);\n else {\n for (const { resolved, at, source } of nodes) {\n if (resolved.available === null && layout === \"text\") continue;\n // A stack that stretches its children widens those that take their own size (a Button, an Image), as flexbox does;\n // not an Svg, whose width is its drawing's. Across a Layer is its justifyItems (its alignItems is down it).\n const across = parent.type === \"Layer\" ? parent.style.justifyItems : parent.style.alignItems;\n const stretched = layout === \"stack\" && across === \"stretch\" && resolved.definition.sizing === \"shrink\" && resolved.style.width === undefined && resolved.type !== \"Svg\";\n const width = stretched && resolved.available !== null ? clamp(resolved.available, resolved.style, resolved.available) : stackedWidth(resolved, resolved.available);\n if (width !== null && resolved.available !== null && width > resolved.available + 0.5) {\n context.d.warning(\"layout/overflow\", `This ${source.type} is ${Math.round(width)}px wide, wider than the ${Math.round(resolved.available)}px it's in`, at, source.id);\n }\n setWidth(resolved, width);\n }\n }\n\n for (const { source, resolved, at } of nodes) {\n if (source.type === \"Table\") {\n resolved.children = resolveTable(source, at, resolved, context);\n continue;\n }\n const own: Layout = source.type === \"Row\" ? \"row\" : resolved.definition.children.text ? \"text\" : \"stack\";\n // A box without a known width (a Link around an image) gives its children what it was given; one inside text, what the text was given.\n let inner = resolved.contentWidth ?? resolved.available ?? (layout === \"text\" ? available : null);\n // A List's items are beside its markers' column.\n if (source.type === \"List\" && inner !== null) inner = Math.max(0, inner - (toPx(resolved.style.indent, null) ?? 0));\n resolved.children = resolveChildren(source, at, resolved, inner, own, context);\n // A block Link around an image is as wide as it; inside text, a Link is a run and takes no width.\n if (resolved.width === null && source.type === \"Link\" && layout !== \"text\") {\n const image = resolved.children.find((c): c is ResolvedNode => typeof c !== \"string\" && c.type === \"Image\");\n if (image && image.width !== null) setWidth(resolved, image.width + horizontalFrame(resolved.style));\n }\n }\n return out;\n}\n\n/**\n * A Row, laid out as CSS flexbox lays out the Page's (one line, no\n * wrapping), so email gets the widths a browser gives:\n * - each child starts from its base: its width (px or %), its own size\n * (`flex: \"none\"`: an image's), or, for a child sharing what's left (by its\n * `flex`, 1 by default), its padding and border;\n * - what's left is shared by `flex`; what's missing is taken back from the\n * children with a width, in proportion to their width inside their padding;\n * - a child that reaches its `minWidth` or `maxWidth` (or no room inside its\n * padding) keeps it, and the rest is shared again among the others.\n * Widths are whole px. Children hidden on desktop aren't in it.\n */\nfunction layOutRow(row: ResolvedNode, nodes: Array<{ source: Node; resolved: ResolvedNode; at: string }>, available: number | null, path: string, context: Context): void {\n if (available === null) return;\n // A child shown only on phones takes no room on desktop; on phones, where it shows, the Row's children stack: it's as wide as the Row.\n const desktop = nodes.filter(({ resolved }) => !resolved.hidden?.desktop);\n for (const { resolved } of nodes) if (resolved.hidden?.desktop) setWidth(resolved, available);\n const gap = toPx(row.style.gap, null) ?? 0;\n const items = desktop.map(({ resolved }) => flexItem(resolved, available));\n // A Row that wraps breaks where its children no longer fit, and each line is laid out on its own.\n const wraps = row.style.flexWrap === \"wrap\" && (context.root === \"Page\" || (context.root === \"Email\" && row.props.stackOnMobile === false));\n for (const line of wraps ? wrappedLines(items, gap, available) : [items.map((_, index) => index)]) {\n const room = available - gap * Math.max(0, line.length - 1);\n const ownItems = line.map((index) => items[index]);\n const widths = roundTogether(flexWidths(ownItems, room));\n // A child that takes its own size, and whose size isn't known (a Button without a width), takes no room and stays unknown.\n line.forEach((index, at) => setWidth(desktop[index].resolved, items[index].unknown ? null : widths[at]));\n\n const asked = ownItems.reduce((sum, item) => sum + item.base, 0);\n const used = widths.reduce((sum, width) => sum + width, 0);\n if (used > room + 0.5) {\n context.d.warning(\"layout/overflow\", `The Row's children need ${Math.round(used - room)}px more than the ${Math.round(available)}px it has, at their minimum widths`, path, row.id);\n } else if (asked > room + 0.5 && ownItems.some((item) => item.shrink > 0 && item.inner > 0)) {\n context.d.warning(\"layout/shrunk\", `The Row's children's widths add up to ${Math.round(asked)}px, more than the ${Math.round(room)}px it has: they're narrowed to fit, as a browser does`, path, row.id);\n }\n }\n}\n\n/** A wrapping Row's children by line, as flexbox breaks them: at the child whose size no longer fits beside the others. */\nfunction wrappedLines(items: FlexItem[], gap: number, available: number): number[][] {\n const lines: number[][] = [];\n let used = 0;\n items.forEach((item, index) => {\n const size = Math.max(item.min, Math.min(item.max, item.base));\n const line = lines[lines.length - 1];\n if (line && used + gap + size <= available + 0.5) {\n line.push(index);\n used += gap + size;\n } else {\n lines.push([index]);\n used = size;\n }\n });\n return lines;\n}\n\ninterface FlexItem {\n /** Where it starts from, padding and border included. */\n base: number;\n /** Its base inside its padding and border: what shrinking takes from. */\n inner: number;\n min: number;\n max: number;\n grow: number;\n shrink: number;\n /** It takes its own size, and that size isn't known. */\n unknown: boolean;\n}\n\n/** A Row's child as a flex item: its base and limits, as the Page writes it (`flex: 0 1 <width>`, `0 0 auto`, or `<flex> 1 0%`, with `min-width: 0`). */\nfunction flexItem(resolved: ResolvedNode, available: number): FlexItem {\n const { style } = resolved;\n const frame = horizontalFrame(style);\n const width = toPx(style.width, available);\n // An Svg is the size it says in a Row too (the Page draws it `flex: none`), not a share of what's left.\n const own = style.flex === \"none\" || style.flex === 0 || resolved.type === \"Svg\";\n const intrinsic = own && width === null ? intrinsicWidth(resolved, available) : null;\n const base = width !== null ? width : own ? (intrinsic ?? 0) : frame;\n // Its content can't be narrower than nothing: at least its padding and border.\n const min = Math.max(frame, toPx(style.minWidth, available) ?? 0);\n const max = Math.max(min, toPx(style.maxWidth, available) ?? Number.POSITIVE_INFINITY);\n return {\n base,\n inner: Math.max(0, base - frame),\n min,\n max,\n grow: width !== null || own ? 0 : typeof style.flex === \"number\" ? style.flex : 1,\n shrink: own ? 0 : 1,\n unknown: own && width === null && intrinsic === null,\n };\n}\n\n/** CSS flexbox's widths for one line of items in `room` px (css-flexbox-1, \"Resolving Flexible Lengths\"). */\nfunction flexWidths(items: FlexItem[], room: number): number[] {\n const clamp = (item: FlexItem, width: number) => Math.max(item.min, Math.min(item.max, width));\n const sum = (values: number[]) => values.reduce((total, value) => total + value, 0);\n const hypothetical = items.map((item) => clamp(item, item.base));\n const growing = sum(hypothetical) <= room;\n const factor = (item: FlexItem) => (growing ? item.grow : item.shrink);\n const target = items.map((item) => item.base);\n const frozen = items.map((item, index) => {\n const inflexible = factor(item) === 0 || (growing ? item.base > hypothetical[index] : item.base < hypothetical[index]);\n if (inflexible) target[index] = hypothetical[index];\n return inflexible;\n });\n const initialFree = room - sum(items.map((item, index) => (frozen[index] ? target[index] : item.base)));\n\n for (let round = 0; round <= items.length && frozen.includes(false); round++) {\n const open = items.map((_, index) => index).filter((index) => !frozen[index]);\n let free = room - sum(items.map((item, index) => (frozen[index] ? target[index] : item.base)));\n // Flex factors that add up to less than 1 share only that part of the space.\n const factors = sum(open.map((index) => factor(items[index])));\n if (factors < 1 && Math.abs(initialFree * factors) < Math.abs(free)) free = initialFree * factors;\n const weights = open.map((index) => (growing ? items[index].grow : items[index].shrink * items[index].inner));\n const total = sum(weights);\n open.forEach((index, k) => {\n target[index] = items[index].base + (total > 0 ? (free * weights[k]) / total : 0);\n });\n // Clamp to the limits; freeze those that went past one, the side that went past most.\n const violation = open.map((index) => clamp(items[index], target[index]) - target[index]);\n const net = sum(violation);\n open.forEach((index, k) => {\n const past = Math.abs(net) < 1e-9 || (net > 0 ? violation[k] > 0 : violation[k] < 0);\n target[index] = clamp(items[index], target[index]);\n if (past) frozen[index] = true;\n });\n }\n return target;\n}\n\n/** Widths in whole px that add up to their rounded total (the largest remainders get the leftover px). */\nfunction roundTogether(widths: number[]): number[] {\n const floors = widths.map(Math.floor);\n let left = Math.round(widths.reduce((sum, width) => sum + width, 0)) - floors.reduce((sum, width) => sum + width, 0);\n const order = widths.map((width, index) => ({ index, rest: width - Math.floor(width) })).sort((a, b) => b.rest - a.rest || a.index - b.index);\n for (const { index } of order) {\n if (left <= 0) break;\n floors[index] += 1;\n left -= 1;\n }\n return floors;\n}\n\n/** `free` px shared by `weights`, in whole px that add up to it (the largest remainders get the leftover px). */\nfunction share(free: number, weights: number[]): number[] {\n const total = weights.reduce((sum, w) => sum + w, 0);\n const room = Math.floor(free);\n const exact = weights.map((w) => (total ? (room * w) / total : 0));\n const widths = exact.map(Math.floor);\n let left = room - widths.reduce((a, b) => a + b, 0);\n const order = exact.map((value, index) => ({ index, rest: value - Math.floor(value) })).sort((a, b) => b.rest - a.rest || a.index - b.index);\n for (const { index } of order) {\n if (left <= 0) break;\n widths[index] += 1;\n left -= 1;\n }\n return widths;\n}\n\n/**\n * A Table: its rows as wide as its content, its columns as wide as their\n * cells say (a cell spanning one column, the first that has a width), the\n * rest sharing what's left; each cell as wide as the columns it spans, its\n * padding its own or the table's `cellPadding`, its bottom border its own or\n * the table's `rowBorder` (but in the last row).\n */\nfunction resolveTable(source: Node, path: string, table: ResolvedNode, context: Context): Array<ResolvedNode | string> {\n const available = table.contentWidth;\n const rows = (source.children ?? []).flatMap((child, index) => {\n if (!isNode(child)) return [];\n const at = `${path}.children[${index}]`;\n const row = resolveNode(child, at, table.text, available, context);\n setWidth(row, available);\n const cells = (child.children ?? []).flatMap((cell, cellIndex) => {\n if (!isNode(cell)) return [];\n const resolved = resolveNode(cell, `${at}.children[${cellIndex}]`, row.text, available, context);\n if (resolved.style.padding === undefined && table.style.cellPadding !== undefined) resolved.style.padding = table.style.cellPadding;\n return [{ source: cell, resolved, at: `${at}.children[${cellIndex}]` }];\n });\n return [{ row, cells }];\n });\n\n const span = (cell: ResolvedNode) => Math.max(1, Number(cell.props.colSpan) || 1);\n const count = Math.max(0, ...rows.map(({ cells }) => cells.reduce((sum, { resolved }) => sum + span(resolved), 0)));\n let columns: number[] | null = null;\n if (available !== null && count > 0) {\n const set: Array<number | null> = new Array(count).fill(null);\n const own: boolean[] = new Array(count).fill(false);\n for (const { cells } of rows) {\n let column = 0;\n for (const { resolved } of cells) {\n if (span(resolved) === 1 && set[column] === null) {\n const width = toPx(resolved.style.width, available);\n set[column] = width === null ? null : Math.round(width);\n own[column] = typeof resolved.style.width === \"number\";\n }\n column += span(resolved);\n }\n }\n const fixed = set.reduce<number>((sum, width) => sum + (width ?? 0), 0);\n // (Each column's width is rounded: by half a pixel at most.)\n if (fixed > available + count / 2) {\n context.d.warning(\"layout/overflow\", `The Table's columns are ${Math.round(fixed - available)}px wider than the ${Math.round(available)}px it has`, path, table.id);\n }\n const free = set.map((width, index) => (width === null ? index : -1)).filter((index) => index >= 0);\n const shared = share(Math.max(0, available - fixed), free.map(() => 1));\n columns = set.map((width, index) => (width === null ? shared[free.indexOf(index)] : width));\n // Every column has a width and they don't add up to the table: they grow or shrink in proportion to fill it, as\n // the writers' shares of the table draw them (too wide is still warned about, above).\n if (free.length === 0 && fixed > 0 && Math.abs(fixed - available) > 0.5) columns = share(available, columns);\n // A column of its own px stays that wide in a narrower window, the others taking what's left: as a share of the\n // table it would shrink past its words. With no other to take it, the whole table would run past the window.\n if (free.length > 0 && own.some(Boolean)) {\n const sizes = set.map<\"px\" | \"share\" | \"free\">((width, index) => (own[index] ? \"px\" : width === null ? \"free\" : \"share\"));\n table.columnSizes = sizes;\n for (const { row } of rows) row.columnSizes = sizes;\n }\n }\n table.columns = columns;\n\n // The line between rows is each cell's bottom border, but in the last row and where a cell sets its own.\n const rowBorder = table.style.rowBorder as BorderSide | undefined;\n rows.forEach(({ cells }, index) => {\n if (!rowBorder || index === rows.length - 1) return;\n for (const { resolved } of cells) {\n const own = resolved.style.border as Border | undefined;\n if (!own?.bottom) resolved.style.border = { ...own, bottom: rowBorder };\n }\n });\n\n for (const { row, cells } of rows) {\n let column = 0;\n for (const { source: cell, resolved, at } of cells) {\n const width = columns ? columns.slice(column, column + span(resolved)).reduce((sum, w) => sum + w, 0) : null;\n column += span(resolved);\n resolved.available = width;\n setWidth(resolved, width);\n resolved.children = resolveChildren(cell, at, resolved, resolved.contentWidth, \"text\", context);\n }\n row.children = cells.map(({ resolved }) => resolved);\n }\n return rows.map(({ row }) => row);\n}\n\n/** Errors for each component the target doesn't render (custom components: for what they're made of). */\nexport function checkTarget(input: ElementsDocument, target: Target, options?: ComponentsOption | Registry): Diagnostic[] {\n const registry = registryOf(options);\n const expanded = expandDocument(input, registry);\n return checkTargetExpanded(expanded.document, target, registry, expanded.paths);\n}\n\n/** `checkTarget` for a document whose custom components are already expanded (its `paths`: each node's place in the template). */\nexport function checkTargetExpanded(document: ElementsDocument, target: Target, options?: ComponentsOption | Registry, paths?: TemplatePaths): Diagnostic[] {\n const registry = registryOf(options);\n const d = new Diagnostics();\n const visit = (node: Node, path: string) => {\n const definition = registry.get(node.type);\n if (definition && !definition.targets[target]) {\n const why = definition.unsupported?.[target];\n d.error(\"target/unsupported\", `A ${node.type} can't be rendered to ${target}${why ? `: ${why}` : \"\"}`, path, node.id);\n // What's in it goes with it: one error.\n return;\n }\n node.children?.forEach((child, index) => isNode(child) && visit(child, `${path}.children[${index}]`));\n };\n visit(document.root, \"root\");\n const templatePath = paths ? templatePathsOf(document, paths) : (path: string) => path;\n return d.list.map((diagnostic) => ({ ...diagnostic, path: templatePath(diagnostic.path) }));\n}\n","/**\n * The words of markup an Html block holds, for what can't show markup: the plain-text version, a PDF, an image. Not a\n * browser: what a browser hides (a style sheet, a script, a comment, which holds Outlook's own markup) is left out, a\n * block ends its line, a table's cells are a space apart, and the entities text uses most are read; everything else is\n * its words as written.\n */\n\nconst DROPPED = /<(style|script|head|title|template)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>|<!--[\\s\\S]*?-->/gi;\n/** Tags that end a line (a block, a cell's row, a line break). */\nconst LINE = /<\\s*(br|\\/?(p|div|h[1-6]|li|ul|ol|tr|table|blockquote|section|article|header|footer|pre))\\b[^>]*>/gi;\n/** The end of a table's cell: its words and the next cell's are apart. */\nconst CELL = /<\\s*\\/\\s*(td|th)\\s*>/gi;\nconst TAG = /<[^>]*>/g;\nconst ENTITIES: Record<string, string> = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\", nbsp: \" \", copy: \"©\", reg: \"®\", trade: \"™\", mdash: \"—\", ndash: \"–\", hellip: \"…\", rsquo: \"’\", lsquo: \"‘\", rdquo: \"”\", ldquo: \"“\", bull: \"•\", middot: \"·\", euro: \"€\", pound: \"£\" };\n\nconst decode = (text: string) =>\n text.replace(/&(#x[0-9a-f]+|#\\d+|[a-z]+);/gi, (match, name: string) => {\n if (name[0] !== \"#\") return Object.prototype.hasOwnProperty.call(ENTITIES, name.toLowerCase()) ? ENTITIES[name.toLowerCase()] : match;\n const code = name[1] === \"x\" || name[1] === \"X\" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);\n return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : match;\n });\n\n/** Markup's words, a line for each block, blank lines between none. */\nexport function markupText(html: string): string {\n const text = decode(html.replace(DROPPED, \"\").replace(LINE, \"\\n\").replace(CELL, \" \").replace(TAG, \"\"));\n return text\n .split(\"\\n\")\n .map((line) => line.replace(/[ \\t\\r\\f\\v\\u00a0]+/g, \" \").trim())\n .filter(Boolean)\n .join(\"\\n\");\n}\n\n/** An Html block's plain text: its own, else its markup's words. */\nexport function htmlText(node: { props: Record<string, unknown> }): string {\n return (typeof node.props.text === \"string\" ? node.props.text : markupText(String(node.props.html ?? \"\"))).trim();\n}\n","/**\n * The image model (model.ts) drawn by Satori (the boxes laid out by Yoga, the text drawn as paths) and resvg (that\n * SVG as pixels): the only file that imports an engine. Another engine is another file like this one, drawing the\n * same canvas; the writer doesn't change.\n */\nimport { Resvg } from \"@resvg/resvg-js\";\nimport { createElement, isValidElement, type ReactElement, type ReactNode } from \"react\";\nimport satori from \"satori\";\nimport type { Block, Engine, Picture, Run } from \"./model\";\n\n/** The tree without styles left undefined (Satori reads every key it's given) and empty children. */\nfunction clean(node: ReactNode): ReactNode {\n if (Array.isArray(node)) return node.map(clean);\n if (!isValidElement(node)) return node;\n const { style, children, ...props } = (node as ReactElement<{ style?: Record<string, unknown>; children?: ReactNode }>).props;\n const kept = style && Object.fromEntries(Object.entries(style).filter(([, value]) => value !== undefined && value !== null));\n const list = ([] as ReactNode[]).concat(children ?? []).filter((child) => child !== null && child !== undefined && child !== false).map(clean);\n return createElement(node.type as string, { ...props, key: node.key, ...(kept ? { style: kept } : {}) }, ...list);\n}\n\nconst picture = (item: Picture, key?: number): ReactElement => createElement(\"img\", { key, src: item.src, alt: item.alt, style: item.style });\n\nconst run = (item: Run, key: number): ReactNode => {\n if (typeof item === \"string\") return item;\n if (item.kind === \"span\") return createElement(\"span\", { key, style: item.style }, ...item.runs.map(run));\n return picture(item, key);\n};\n\nconst block = (item: Block, key?: number): ReactElement => {\n switch (item.kind) {\n case \"box\":\n return createElement(\"div\", { key, style: item.style }, ...item.children.map(block));\n case \"text\":\n return createElement(\"div\", { key, style: item.style }, ...item.runs.map(run));\n case \"image\":\n return picture(item, key);\n case \"drawing\":\n return createElement(\n \"svg\",\n { key, width: item.width, height: item.height, viewBox: item.viewBox, xmlns: \"http://www.w3.org/2000/svg\" },\n ...item.shapes.map((shape, i) => createElement(shape.kind, { key: i, ...shape.attributes }))\n );\n }\n};\n\nexport const satoriResvg: Engine = async (canvas, output) => {\n const tree = clean(createElement(\"div\", { style: canvas.style }, ...canvas.children.map(block)));\n const fonts = canvas.fonts.map((font) => ({ name: font.family, data: font.data, weight: font.weight, style: font.style }));\n const svg = await satori(tree as ReactElement, { width: canvas.width, height: canvas.height, fonts });\n if (output.format === \"svg\") return new TextEncoder().encode(svg);\n return new Uint8Array(new Resvg(svg, { fitTo: { mode: \"width\", value: Math.round(canvas.width * output.scale) }, font: { loadSystemFonts: false } }).render().asPng());\n};\n","/**\n * A laid-out Canvas as an image to draw (model.ts): flexbox boxes at the widths the resolver gave them (as every\n * other output), text in the font a stack names that's given (else the first given), images from their bytes, an\n * Svg's shapes as a drawing. No engine is named here: an engine's adapter draws what this writes (satori.ts).\n */\nimport { assetParts, fontStack, htmlText, type Asset, type Border, type BorderSide, type Radius, type ResolvedNode } from \"../index\";\nimport type { Block, DrawnCanvas, Font, Picture, Run, Shape, Style } from \"./model\";\n\nexport interface WriteContext {\n /** The font families given, lowercased, and the first (what a stack without one of them is drawn in). */\n families: ReadonlySet<string>;\n fallback: string;\n /** The files of those families. */\n fonts: Font[];\n /** An image's source as a data URI (nothing is fetched), or undefined where there's none. */\n image(src: string): string | undefined;\n}\n\nconst shown = (node: ResolvedNode) => !node.hidden?.desktop;\nconst nodes = (node: ResolvedNode) => node.children.filter((child): child is ResolvedNode => typeof child !== \"string\" && shown(child));\nconst present = <T>(items: Array<T | undefined>): T[] => items.filter((item): item is T => item !== undefined);\nconst px = (value: number) => `${Math.round(value * 100) / 100}px`;\ntype Sides = { top: number; right: number; bottom: number; left: number };\n\nconst FLEX: Record<string, string> = { start: \"flex-start\", center: \"center\", end: \"flex-end\", stretch: \"stretch\", \"space-between\": \"space-between\" };\nconst flex = (value: unknown) => (typeof value === \"string\" ? FLEX[value] : undefined);\n\n/**\n * A block of text's style, its alignment also as the flex box's: Satori aligns by textAlign only the lines of text\n * that wrap, so a line that doesn't would sit at the start.\n */\nconst aligned = (style: Style): Style => {\n const justify = { center: \"center\", right: \"flex-end\" }[String(style.textAlign)];\n return justify ? { ...style, justifyContent: justify } : style;\n};\n\nexport function writeCanvas(root: ResolvedNode, context: WriteContext): DrawnCanvas {\n const write = new Writer(context);\n return {\n width: Number(root.props.width),\n height: Number(root.props.height),\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n width: px(Number(root.props.width)),\n height: px(Number(root.props.height)),\n overflow: \"hidden\",\n justifyContent: flex(root.style.justifyContent),\n ...write.box(root),\n ...write.text(root),\n ...gap(root),\n },\n children: present(nodes(root).map((child) => write.block(child, root))),\n fonts: context.fonts,\n };\n}\n\nconst gap = (node: ResolvedNode): Style => (typeof node.style.gap === \"number\" ? { gap: px(node.style.gap) } : {});\n\nclass Writer {\n constructor(private readonly context: WriteContext) {}\n\n /** The family a stack is drawn in: the first given in it, else the first given. */\n family(stack: string): string {\n return fontStack(stack).find((family) => this.context.families.has(family.toLowerCase())) ?? this.context.fallback;\n }\n\n text(node: ResolvedNode): Style {\n const text = node.text;\n return {\n color: text.color,\n fontFamily: this.family(String(text.fontFamily)),\n fontSize: px(text.fontSize),\n fontWeight: text.fontWeight as number,\n fontStyle: text.fontStyle,\n lineHeight: px(text.lineHeightPx),\n letterSpacing: text.letterSpacing ? px(text.letterSpacing) : undefined,\n textAlign: text.textAlign,\n textTransform: text.textTransform === \"none\" ? undefined : text.textTransform,\n };\n }\n\n box(node: ResolvedNode): Style {\n const style = node.style;\n const out: Style = {};\n const padding = style.padding as Sides | undefined;\n if (padding) out.padding = `${px(padding.top)} ${px(padding.right)} ${px(padding.bottom)} ${px(padding.left)}`;\n if (typeof style.backgroundColor === \"string\") out.backgroundColor = style.backgroundColor;\n const border = style.border as Border | undefined;\n for (const side of [\"top\", \"right\", \"bottom\", \"left\"] as const) {\n const line = border?.[side];\n if (line) out[`border${side[0].toUpperCase()}${side.slice(1)}`] = line.style === \"none\" ? \"none\" : `${px(line.width)} ${line.style} ${line.color}`;\n }\n const radius = style.borderRadius as Radius | undefined;\n if (typeof radius === \"number\") out.borderRadius = px(radius);\n else if (radius) out.borderRadius = `${px(radius.topLeft)} ${px(radius.topRight)} ${px(radius.bottomRight)} ${px(radius.bottomLeft)}`;\n if (typeof style.height === \"number\") out.height = px(style.height);\n if (style.overflow === \"hidden\") out.overflow = \"hidden\";\n return out;\n }\n\n /** Where a child of its own size sits across a stack, as in the other outputs. */\n alignSelf(child: ResolvedNode, stack: ResolvedNode): string | undefined {\n const sized = child.style.width !== undefined || child.style.maxWidth !== undefined;\n if (child.type === \"Divider\" || (child.definition.sizing !== \"shrink\" && !sized)) return undefined;\n const align = stack.style.alignItems as string | undefined;\n if (align === \"stretch\") return undefined;\n if (align) return flex(align);\n return { left: \"flex-start\", justify: \"flex-start\", center: \"center\", right: \"flex-end\" }[stack.text.textAlign];\n }\n\n block(node: ResolvedNode, parent: ResolvedNode, row = false): Block | undefined {\n const width = node.width !== null ? px(node.width) : undefined;\n const placed: Style = row\n ? { width, flexShrink: 0, flexGrow: 0, alignSelf: node.definition.sizing === \"shrink\" ? flex(parent.style.alignItems ?? \"start\") : undefined }\n : { alignSelf: this.alignSelf(node, parent), width: node.style.width !== undefined || node.style.maxWidth !== undefined || node.definition.sizing === \"shrink\" ? width : undefined };\n switch (node.type) {\n case \"Text\":\n case \"Heading\":\n return this.paragraph(node, { ...placed, ...this.box(node) });\n case \"Button\":\n return { kind: \"box\", style: { display: \"flex\", justifyContent: \"center\", ...placed, ...this.box(node), width }, children: [this.paragraph(node, { textAlign: \"center\" })] };\n case \"Image\":\n return this.image(node, placed);\n case \"Divider\": {\n const line = node.style.borderTop as BorderSide | undefined;\n return {\n kind: \"box\",\n style: { display: \"flex\", ...this.box(node), ...placed, width },\n children: [{ kind: \"box\", style: { display: \"flex\", flexGrow: 1, borderTop: line ? `${px(line.width)} ${line.style} ${line.color}` : undefined }, children: [] }],\n };\n }\n case \"Row\":\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"row\", ...this.box(node), ...placed, ...gap(node), alignItems: flex(node.style.alignItems ?? \"start\"), justifyContent: flex(node.style.justifyContent) },\n children: present(nodes(node).map((child) => this.block(child, node, true))),\n };\n case \"Layer\":\n return this.layer(node, placed);\n case \"Svg\":\n return this.svg(node, placed);\n case \"Link\":\n // A Link of words (a Menu's links too) is a block of text; one holding an Image, that image.\n if (node.children.some((child) => typeof child === \"string\" || child.definition.categories.includes(\"inline\"))) return this.paragraph(node, { ...placed, ...this.box(node) });\n return { kind: \"box\", style: { display: \"flex\", flexDirection: \"column\", ...placed }, children: present(nodes(node).map((child) => this.block(child, node))) };\n case \"List\":\n return this.list(node, placed);\n case \"Table\":\n return this.table(node, placed);\n case \"Html\":\n // An image can't draw markup: its plain text (validate() says so).\n return { kind: \"text\", style: aligned({ display: \"flex\", ...this.text(node), ...placed }), runs: [htmlText(node)] };\n case \"Video\":\n // An image can't play a video: its title, as an email or a PDF without its thumbnail shows it.\n return { kind: \"text\", style: aligned({ display: \"flex\", ...this.text(node), ...this.box(node), ...placed }), runs: [String(node.props.title ?? \"Video\")] };\n case \"Section\":\n case \"Column\":\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"column\", ...this.box(node), ...placed, ...gap(node), justifyContent: flex(node.style.justifyContent) },\n children: present(nodes(node).map((child) => this.block(child, node))),\n };\n default:\n // Every block a Canvas takes is a case above (a test checks): one without a case fails rather than disappears.\n throw new Error(`[Elements] The image writer draws no ${node.type}`);\n }\n }\n\n /** A List: each item a marker (its number, or a bullet) beside its text, a List in it under the text. */\n list(node: ResolvedNode, placed: Style): Block {\n const indent = typeof node.style.indent === \"number\" ? node.style.indent : 24;\n const start = Number(node.props.start ?? 1);\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"column\", ...this.box(node), ...placed, ...gap(node) },\n children: nodes(node).map((item, i): Block => ({\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"row\" },\n children: [\n { kind: \"text\", style: { display: \"flex\", ...this.text(item), width: px(indent), flexShrink: 0, textAlign: \"left\" }, runs: [node.props.type === \"number\" ? `${start + i}.` : \"•\"] },\n { kind: \"box\", style: { display: \"flex\", flexDirection: \"column\", flexGrow: 1, flexShrink: 1 }, children: this.itemParts(item) },\n ],\n })),\n };\n }\n\n /** A list item's text and the Lists in it, in their order: each run of text a paragraph, each List under it. */\n itemParts(item: ResolvedNode): Block[] {\n const parts: Block[] = [];\n let text: ResolvedNode[\"children\"] = [];\n const flush = () => {\n if (text.some((child) => typeof child !== \"string\" || child.trim() !== \"\")) parts.push(this.paragraph({ ...item, children: text }, {}));\n text = [];\n };\n for (const child of item.children) {\n if (typeof child !== \"string\" && child.type === \"List\") {\n flush();\n if (shown(child)) parts.push(this.list(child, {}));\n } else text.push(child);\n }\n flush();\n return parts;\n }\n\n /** A Table: its rows one under another, each cell at its columns' width (a header row's text bold). */\n table(node: ResolvedNode, placed: Style): Block {\n const columns = node.columns ?? null;\n const cellPadding = node.style.cellPadding as Sides | undefined;\n const rowBorder = node.style.rowBorder as BorderSide | undefined;\n const rows = nodes(node);\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"column\", ...this.box(node), ...placed },\n children: rows.map((row, r): Block => {\n let column = 0;\n const line = r < rows.length - 1 && rowBorder ? `${px(rowBorder.width)} ${rowBorder.style} ${rowBorder.color}` : undefined;\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"row\", ...this.box(row), borderBottom: line },\n children: nodes(row).map((cell): Block => {\n const span = Math.max(1, Number(cell.props.colSpan ?? 1));\n const width = columns ? columns.slice(column, column + span).reduce((sum, w) => sum + w, 0) : undefined;\n column += span;\n const padding = cellPadding ? `${px(cellPadding.top)} ${px(cellPadding.right)} ${px(cellPadding.bottom)} ${px(cellPadding.left)}` : undefined;\n return {\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"column\", padding, ...this.box(cell), width: width !== undefined ? px(width) : undefined, flexGrow: width === undefined ? 1 : 0, flexShrink: 0 },\n children: [this.paragraph(cell, row.props.header ? { fontWeight: 700 } : {})],\n };\n }),\n };\n }),\n };\n }\n\n /** A block of text: its runs, a line break starting a new line. */\n paragraph(node: ResolvedNode, style: Style): Block {\n const breaks = node.children.some((child) => typeof child !== \"string\" && child.type === \"LineBreak\");\n return { kind: \"text\", style: aligned({ display: \"flex\", flexWrap: \"wrap\", ...this.text(node), ...style, whiteSpace: breaks ? \"pre-wrap\" : undefined }), runs: this.runs(node) };\n }\n\n runs(node: ResolvedNode): Run[] {\n return present(\n node.children.map((child): Run | undefined => {\n if (typeof child === \"string\") return child;\n if (!shown(child)) return undefined;\n switch (child.type) {\n case \"LineBreak\":\n return \"\\n\";\n case \"MergeTag\":\n return `{{${child.props.name}}}`;\n case \"Image\":\n return this.image(child, {});\n default: {\n const style: Style = { ...this.text(child), textAlign: undefined };\n if (child.style.textDecoration !== undefined) style.textDecoration = String(child.style.textDecoration);\n if (typeof child.style.backgroundColor === \"string\") style.backgroundColor = child.style.backgroundColor;\n return { kind: \"span\", style, runs: this.runs(child) };\n }\n }\n })\n );\n }\n\n image(node: ResolvedNode, placed: Style, cover?: number): Picture | undefined {\n const { src, width, height } = assetParts(node.props.src as Asset);\n const source = this.context.image(src);\n if (!source) return undefined;\n const drawn = node.width ?? width;\n const style: Style = { ...placed, ...this.box(node) };\n if (cover !== undefined) Object.assign(style, { width: \"100%\", height: px(cover), objectFit: \"cover\" });\n else if (drawn && width && height) Object.assign(style, { width: px(drawn), height: typeof node.style.height === \"number\" ? px(node.style.height) : px((drawn * height) / width) });\n return { kind: \"image\", src: source, alt: String(node.props.alt ?? \"\"), style };\n }\n\n /** A Layer: its first child in place, the others over it, each in the Layer's whole box, placed by its alignment. */\n layer(node: ResolvedNode, placed: Style): Block {\n const [base, ...over] = nodes(node);\n const height = typeof node.style.height === \"number\" ? node.style.height : undefined;\n const under = base && (base.type === \"Image\" && height !== undefined ? this.image(base, {}, height) : this.block(base, node));\n return {\n kind: \"box\",\n style: { display: \"flex\", position: \"relative\", ...this.box(node), ...placed, overflow: node.style.borderRadius !== undefined ? \"hidden\" : undefined },\n children: present([\n under,\n ...over.map((child): Block => ({\n kind: \"box\",\n style: { display: \"flex\", flexDirection: \"column\", position: \"absolute\", top: 0, right: 0, bottom: 0, left: 0, justifyContent: flex(node.style.alignItems ?? \"start\"), alignItems: flex(node.style.justifyItems ?? \"stretch\") },\n children: present([this.block(child, node)]),\n })),\n ]),\n };\n }\n\n /** An Svg as a drawing, its shapes in its viewBox's units. */\n svg(node: ResolvedNode, placed: Style): Block {\n const { width, height, viewBox } = node.props as { width: number; height: number; viewBox?: string };\n const paint = (value: unknown) => (value === \"transparent\" ? \"none\" : (value as string | undefined));\n const KINDS: Record<string, Shape[\"kind\"]> = { Rect: \"rect\", Circle: \"circle\", Ellipse: \"ellipse\", Line: \"line\", Polygon: \"polygon\", Path: \"path\" };\n const shapes = nodes(node).map((shape): Shape => ({\n kind: KINDS[shape.type],\n attributes: { ...shape.props, fill: paint(shape.style.fill), stroke: paint(shape.style.stroke), strokeWidth: shape.style.strokeWidth, opacity: shape.style.opacity },\n }));\n return { kind: \"box\", style: { display: \"flex\", ...this.box(node), ...placed, width: undefined }, children: [{ kind: \"drawing\", width, height, viewBox: viewBox ?? `0 0 ${width} ${height}`, shapes }] };\n }\n}\n","/**\n * Image output for Elements: a `<Canvas>` written as PNG or SVG on the server, without a browser or the network. Its\n * content is laid out at the widths every output uses, written as a canvas any engine can draw (the core's\n * src/image/model.ts), and drawn by Satori (layout and text as paths) and resvg (pixels), whose adapter (satori.ts\n * beside it) is the only file naming them.\n *\n * const { image } = await renderImage(<SocialCard />, { fonts: [{ family: \"Inter\", src: interRegular }] });\n *\n * Nothing is fetched: fonts are given (`fonts`, or the Canvas's `fonts` as data: URIs), and so are images (`images`\n * by URL, or data: URIs). An image needs at least one font: its text is drawn with the fonts it's given.\n */\nimport { assetParts, fontStack, LAYOUT_VERSION, satoriResvg, writeCanvas, type Asset, type Font, type FontFace } from \"@unlayer-internal/elements-core/image\";\nimport { ElementsError, prepare, type Diagnostic, type ResolvedNode } from \"@hyper-elements/react\";\nimport type { ReactElement } from \"react\";\n\nexport interface ImageFont {\n /** The family a template names in its fontFamily stacks (\"Inter\"). */\n family: string;\n /** The font file (TTF, OTF or WOFF; not WOFF2). */\n src: Uint8Array | ArrayBuffer;\n weight?: number;\n style?: \"normal\" | \"italic\";\n}\n\nexport interface ImageOptions {\n /** Font files, by the family the template names: an image loads none itself, and needs one. */\n fonts?: ImageFont[];\n /** Image files (PNG, JPEG, GIF, SVG), by the URL the template gives: an image loads none itself. */\n images?: Record<string, Uint8Array | ArrayBuffer>;\n /** PNG (the default) or SVG. */\n format?: \"png\" | \"svg\";\n /** Pixels per px, for a sharper PNG (2 for a phone's screen). */\n scale?: number;\n}\n\nexport interface ImageResult {\n /** The image file. */\n image: Uint8Array;\n /** Its size in pixels. */\n width: number;\n height: number;\n /** Warnings and notes (errors are thrown). */\n diagnostics: Diagnostic[];\n}\n\n/** A Canvas written as an image. Rejects with an `ElementsError` listing the problems if it has errors. */\nexport async function renderImage(element: ReactElement, options: ImageOptions = {}): Promise<ImageResult> {\n const { root, diagnostics, layout } = prepare(element);\n readable(layout, \"image\");\n if (!root) throw new ElementsError(diagnostics);\n if (root.type !== \"Canvas\") {\n throw new ElementsError([...diagnostics, { code: \"image/root\", severity: \"error\", message: `Image output is a Canvas's: this template is a ${root.type}`, path: \"root\" }]);\n }\n const found: Diagnostic[] = [];\n const fonts = fontsOf(root, options.fonts ?? [], found);\n const images = imageSources(root, options.images ?? {}, found);\n if (found.some((d) => d.severity === \"error\")) throw new ElementsError([...diagnostics, ...found]);\n\n found.push(...missingFaces(root, fonts));\n const width = Number(root.props.width);\n const height = Number(root.props.height);\n const families = new Set(fonts.map((font) => font.family.toLowerCase()));\n const canvas = writeCanvas(root, { families, fallback: fonts[0]?.family ?? \"\", fonts, image: (src) => images.get(src) });\n const scale = options.scale ?? 1;\n if (options.format === \"svg\") return { image: await satoriResvg(canvas, { format: \"svg\", scale }), width, height, diagnostics: [...diagnostics, ...found] };\n const image = await satoriResvg(canvas, { format: \"png\", scale });\n return { image, width: Math.round(width * scale), height: Math.round(height * scale), diagnostics: [...diagnostics, ...found] };\n}\n\n/**\n * The tree prepare() gives, read as this entry's copy of the core lays it out: from another copy of Elements that\n * writes another layout version (a second install of the package, or an older one beside it), it's refused rather than\n * drawn wrong.\n */\nfunction readable(layout: unknown, output: \"pdf\" | \"image\"): void {\n if (layout === LAYOUT_VERSION) return;\n throw new ElementsError([\n {\n code: `${output}/version`,\n severity: \"error\",\n message: `@hyper-elements/react/${output} reads layout version ${LAYOUT_VERSION}; the main entry writes ${typeof layout === \"number\" ? layout : \"none\"}: update @hyper-elements/react`,\n path: \"root\",\n },\n ]);\n}\n\n// ─── Fonts ─────────────────────────────────────────────────────────────────\n\nconst buffer = (bytes: Uint8Array | ArrayBuffer): ArrayBuffer => {\n if (bytes instanceof ArrayBuffer) return bytes;\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n};\nconst weight = (value: number | undefined) => (Math.min(900, Math.max(100, Math.round((value ?? 400) / 100) * 100)) as Font[\"weight\"]);\n\n/** The fonts the image is drawn with: those given, and the Canvas's own given as data: URIs. None is an error. */\n/** A WOFF2 file, by its first bytes: an image draws TTF, OTF and WOFF files, not these. */\nconst woff2 = (bytes: ArrayBuffer | Uint8Array) => String.fromCharCode(...new Uint8Array(buffer(bytes)).subarray(0, 4)) === \"wOF2\";\n\nfunction fontsOf(root: ResolvedNode, given: ImageFont[], found: Diagnostic[]): Font[] {\n const drawn = (family: string, bytes: ArrayBuffer | Uint8Array) => {\n if (!woff2(bytes)) return true;\n found.push({ code: \"image/font\", severity: \"warning\", message: `${family}'s file is WOFF2: an image draws TTF, OTF and WOFF files. Its text is drawn in another font meanwhile`, path: \"root.props.fonts\" });\n return false;\n };\n const out: Font[] = given.filter((font) => drawn(font.family, font.src)).map((font) => ({ family: font.family, data: buffer(font.src), weight: weight(font.weight), style: font.style ?? \"normal\" }));\n const named = new Set(out.map((font) => font.family.toLowerCase()));\n for (const face of (root.props.fonts as FontFace[] | undefined) ?? []) {\n if (named.has(face.family.toLowerCase())) continue;\n const data = /^data:[^,]*;base64,(.*)$/.exec(face.src)?.[1];\n if (data && !drawn(face.family, Uint8Array.from(Buffer.from(data, \"base64\")))) continue;\n if (data) {\n out.push({ family: face.family, data: buffer(Uint8Array.from(Buffer.from(data, \"base64\"))), weight: weight(Array.isArray(face.weight) ? face.weight[0] : face.weight), style: face.style ?? \"normal\" });\n continue;\n }\n found.push({ code: \"image/font\", severity: \"warning\", message: `Image output loads no fonts: give ${face.family}'s file in renderImage's fonts option. Its text is drawn in another font meanwhile`, path: \"root.props.fonts\" });\n }\n if (!out.length && hasText(root)) {\n found.push({ code: \"image/font\", severity: \"error\", message: \"An image draws its text with the fonts it's given, and has none: give a font file in renderImage's fonts option\", path: \"root\" });\n }\n return out;\n}\n\n/** Text in a weight or style of a family that isn't given: drawn in the nearest given (a browser would fake it). */\nfunction missingFaces(root: ResolvedNode, fonts: Font[]): Diagnostic[] {\n const out = new Map<string, Diagnostic>();\n const given = new Map<string, Font[]>();\n for (const font of fonts) given.set(font.family.toLowerCase(), [...(given.get(font.family.toLowerCase()) ?? []), font]);\n const visit = (node: ResolvedNode) => {\n if (node.hidden?.desktop) return;\n if (node.children.some((child) => typeof child === \"string\" && child.trim())) {\n const stack = fontStack(String(node.text.fontFamily));\n const family = stack.find((name) => given.has(name.toLowerCase())) ?? fonts[0]?.family;\n const faces = family ? given.get(family.toLowerCase()) ?? [] : [];\n const weight = node.text.fontWeight;\n const style = node.text.fontStyle;\n if (family && !faces.some((face) => face.weight === weight && (face.style ?? \"normal\") === style)) {\n const key = `${family}|${weight}|${style}`;\n if (!out.has(key)) {\n const has = faces.map((face) => `${face.weight}${face.style === \"italic\" ? \" italic\" : \"\"}`).join(\", \");\n out.set(key, {\n code: \"image/font-weight\",\n severity: \"warning\",\n message: `Text in ${family} at ${weight}${style === \"italic\" ? \" italic\" : \"\"}, which is given at ${has} only: it's drawn in the nearest. Give that file in renderImage's fonts option`,\n path: node.path,\n ...(node.id ? { nodeId: node.id } : {}),\n });\n }\n }\n }\n for (const child of node.children) if (typeof child !== \"string\") visit(child);\n };\n visit(root);\n return [...out.values()];\n}\n\n/** Whether it draws any text (shown on desktop). */\nfunction hasText(node: ResolvedNode): boolean {\n if (node.hidden?.desktop) return false;\n return node.children.some((child) => (typeof child === \"string\" ? child.trim() !== \"\" : hasText(child)));\n}\n\n// ─── Images ────────────────────────────────────────────────────────────────\n\nfunction mime(bytes: Uint8Array): string | undefined {\n if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) return \"image/png\";\n if (bytes[0] === 0xff && bytes[1] === 0xd8) return \"image/jpeg\";\n if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46) return \"image/gif\";\n const head = new TextDecoder().decode(bytes.subarray(0, 256)).trimStart();\n if (head.startsWith(\"<svg\") || head.startsWith(\"<?xml\")) return \"image/svg+xml\";\n return undefined;\n}\n\nconst DRAWN = /^data:image\\/(png|jpeg|jpg|gif|svg\\+xml)[;,]/;\n\n/** Each image's source as an engine loads it without the network: a data URI; an image without one, an error. */\nfunction imageSources(root: ResolvedNode, given: Record<string, Uint8Array | ArrayBuffer>, found: Diagnostic[]): Map<string, string> {\n const out = new Map<string, string>();\n const visit = (node: ResolvedNode) => {\n if (node.hidden?.desktop) return;\n if (node.type === \"Image\") {\n const { src } = assetParts(node.props.src as Asset);\n const bytes = given[src] ? new Uint8Array(buffer(given[src])) : undefined;\n const type = bytes && mime(bytes);\n if (DRAWN.test(src)) out.set(src, src);\n else if (bytes && type) out.set(src, `data:${type};base64,${Buffer.from(bytes).toString(\"base64\")}`);\n else {\n found.push({\n code: \"image/image\",\n severity: \"error\",\n message: bytes ? \"This image's file isn't a PNG, JPEG, GIF or SVG\" : \"Image output loads no images: give this one's file in renderImage's images option (by its URL), or its src as a data: URI\",\n path: `${node.path}.props.src`,\n ...(node.id ? { nodeId: node.id } : {}),\n });\n }\n }\n for (const child of node.children) if (typeof child !== \"string\") visit(child);\n };\n visit(root);\n return out;\n}\n"]}