import { Block, BlockType, ColumnLayout, ContentDirection, CustomBlock, CustomFont, SpacingValue, TemplateContent } from "@templatical/types"; //#region src/render-context.d.ts /** * A function type that renders a single block to MJML markup. */ type BlockRenderer = (block: Block, context: RenderContext) => string; /** * Per-block-type renderer overrides, keyed by `block.type`. An entry replaces the * built-in renderer for that type wholesale — including its hidden-on-all- * viewports check, which becomes the override's responsibility. * * Generalises `renderCustomBlock`, which is the same idea for one block type. Its * reason to exist is that Templatical Cloud's render output is a deliberate * *superset*: countdown blocks resolve to a server-generated animated GIF and * video thumbnails get a composited play button, neither of which a browser can * do at render time. With this hook Cloud's renderer is the published one plus * two injected functions, so parity for every other block type holds by * construction instead of by review. */ type BlockRendererMap = Partial>; export declare const DEFAULT_SOCIAL_ICONS_BASE_URL: string; /** * Immutable context passed through the block rendering chain. */ export declare class RenderContext { readonly containerWidth: number; readonly customFonts: CustomFont[]; readonly defaultFallbackFont: string; readonly allowHtmlBlocks: boolean; /** * Map of custom block id → pre-rendered HTML, populated by `renderToMjml` * before the synchronous render pass. Set when the consumer provides a * `renderCustomBlock` option. Empty by default. */ readonly customBlockHtml: ReadonlyMap; /** * Base URL (no trailing slash) for the social icon PNG assets. Resolved to * `${baseUrl}/${style}/${platform}.png`. Outlook desktop has no SVG support * and rejects base64 data URIs in ``, so PNGs must be served over * HTTP. Default points at the version-pinned jsDelivr mirror of this * package; consumers can override to self-host. */ readonly socialIconsBaseUrl: string; /** * Per-block-type renderer overrides from `RenderOptions.blockRenderers`. * Consulted before every built-in renderer — see {@link BlockRendererMap}. */ readonly blockRenderers: BlockRendererMap; /** * Writing direction of the email, resolved once in `renderToMjml`. * Nested columns copy it via `withContainerWidth` — dropping it there * would leave a two-column RTL section rendering LTR inside the columns. */ readonly contentDirection: ContentDirection; constructor(containerWidth: number, customFonts: CustomFont[], defaultFallbackFont: string, allowHtmlBlocks: boolean, /** * Map of custom block id → pre-rendered HTML, populated by `renderToMjml` * before the synchronous render pass. Set when the consumer provides a * `renderCustomBlock` option. Empty by default. */ customBlockHtml?: ReadonlyMap, /** * Base URL (no trailing slash) for the social icon PNG assets. Resolved to * `${baseUrl}/${style}/${platform}.png`. Outlook desktop has no SVG support * and rejects base64 data URIs in ``, so PNGs must be served over * HTTP. Default points at the version-pinned jsDelivr mirror of this * package; consumers can override to self-host. */ socialIconsBaseUrl?: string, /** * Per-block-type renderer overrides from `RenderOptions.blockRenderers`. * Consulted before every built-in renderer — see {@link BlockRendererMap}. */ blockRenderers?: BlockRendererMap, /** * Writing direction of the email, resolved once in `renderToMjml`. * Nested columns copy it via `withContainerWidth` — dropping it there * would leave a two-column RTL section rendering LTR inside the columns. */ contentDirection?: ContentDirection); /** * Create a new context with a different container width. * Used when rendering columns with narrower widths. */ withContainerWidth(width: number): RenderContext; /** * Resolve a font family name to include custom font fallbacks. * If the font matches a custom font, returns `'FontName', fallback`. * Otherwise returns the original font family string. */ resolveFontFamily(fontFamily: string): string; } //#endregion //#region src/escape.d.ts /** * Escape HTML special characters (& < > " '). * Equivalent to PHP htmlspecialchars with ENT_QUOTES | ENT_HTML5. */ export declare function escapeHtml(text: string): string; /** * Escape a string for use in an HTML attribute value. * Same implementation as escapeHtml for consistency with PHP. */ export declare function escapeAttr(text: string): string; /** * Replace merge tag span elements with their data attribute values. * Converts `Label` to `{{name}}`. * Also handles `data-logic-merge-tag` attributes. * * Uses a single-pass linear scan instead of an `[^>]*…[^>]*` regex because * the latter is polynomial-ReDoS over inputs that contain many `` — the engine retries `[^>]*` at every span * position. The scan below resolves each `` open tag with a bounded, * quote-aware `findOpenTagEnd`, keeping the work strictly O(n). */ export declare function convertMergeTagsToValues(html: string): string; //#endregion //#region src/visibility.d.ts /** * Check if a block is hidden on all viewports. */ export declare function isHiddenOnAll(block: Block): boolean; /** * Get the MJML css-class attribute string for a block. * Returns a string like ` css-class="tpl-hide-desktop"` or empty string. * * `extraClasses` are appended to the visibility classes on the *same* * attribute. MJML keeps only one `css-class` per element, so a caller needing * both must merge here rather than emitting a second attribute. */ export declare function getCssClassAttr(block: Block, extraClasses?: string[]): string; /** * Get the CSS classes for visibility hiding. */ export declare function getCssClasses(block: Block): string; //#endregion //#region src/rich-text.d.ts /** * Marks the `mj-text` of a block whose content is editor-authored rich text * (paragraph and title). MJML puts a `css-class` on the wrapping ``, which * is what {@link richTextStylesheet} scopes its selectors to. * * The scope is the point. A bare `p { … }` rule would also reach an `html` * block — the renderer puts consumer markup in `mj-text` too — and the editor * previews those in a sandboxed `srcdoc` iframe on UA defaults. Styling them * would trade this mismatch for another one, and an inlined declaration would * beat the consumer's own stylesheet. */ export declare const RICH_TEXT_CSS_CLASS = "tpl-rich-text"; /** * The class carrying one specific paragraph gap, e.g. `tpl-rich-text-8`. * * Every rich-text block gets one of these *instead of* a shared paragraph rule. * That is a hard constraint, not a preference: `.tpl-rich-text p` and * `.tpl-rich-text-4 p` have identical specificity (one class, one type), so a * surviving base rule would win on source order and flatten every per-block gap * back to the default. */ export declare function richTextGapClass(gap: number): string; /** * The paragraph gap a block asks for, or the built-in default. * * The fallback must produce a *rule* rather than nothing: silence hands the * paragraph back to MJML's `p { margin: 13px 0 }` skeleton default and reopens * the canvas/export mismatch. `0` is a legitimate gap, so this tests for * absence rather than falsiness. * * Mirrored on the canvas side by `getBlockWrapperStyle` in * `@templatical/editor`. */ export declare function resolveParagraphGap(spacing: number | undefined): number; //#endregion //#region src/columns.d.ts /** * Get width percentages for each column in a layout. */ export declare function getWidthPercentages(layout: ColumnLayout): string[]; /** * Get width in pixels for each column in a layout. */ export declare function getWidthPixels(layout: ColumnLayout, containerWidth: number): number[]; //#endregion //#region src/padding.d.ts /** * Convert a SpacingValue to a CSS padding string like "10px 10px 10px 10px". */ export declare function toPaddingString(padding: SpacingValue): string; //#endregion //#region src/renderers/index.d.ts /** * Render a single block to MJML markup. * Dispatches to the appropriate block-type renderer. * * A `blockRenderers` override for the block's type wins over the built-in * renderer — see {@link RenderContext.blockRenderers}. A type with neither gets a * placeholder marker plus a warning rather than disappearing. */ export declare function renderBlock(block: Block, context: RenderContext): string; //#endregion //#region src/unrenderable.d.ts /** * Prefix every unrenderable-block marker carries. Stable and greppable on * purpose: it is what a consumer searches for after noticing a gap in a rendered * email, and what a send pipeline can scan for before shipping. */ export declare const UNRENDERABLE_MARKER_PREFIX = "templatical:unrenderable-block"; /** * Emit the placeholder for a block no renderer can handle — no built-in renderer * and no `blockRenderers` override for its type. * * **A marker, not a throw and not silence.** The renderer runs inside send * pipelines, so killing an entire render over one block is worse than shipping a * marked gap. Silence is worse still: a block that simply vanishes reaches * recipients as a missing section with nothing anywhere explaining why. The rule * is general rather than countdown-specific, so * a block type added to `@templatical/types` before its renderer lands degrades * the same way. * * `mj-raw` is used because it is the one element that passes its content through * verbatim and is valid everywhere a block renders — inside `mj-column`, which is * where every non-section block ends up. */ export declare function renderUnrenderableBlock(block: Block): string; //#endregion //#region src/index.d.ts export interface RenderOptions { customFonts?: CustomFont[]; defaultFallbackFont?: string; allowHtmlBlocks?: boolean; /** * Resolves custom blocks to their HTML representation. Called once per * custom block in the content tree before MJML rendering. The renderer * has no built-in knowledge of how to render custom blocks; consumers * provide this function. * * Editor consumers: pass `editor.renderCustomBlock`. * * Headless consumers (Node.js, server, CLI): provide your own resolver, * typically using the same liquid template + field values pipeline as * the editor uses. If omitted, custom blocks fall back to * `block.renderedHtml` (if present) and otherwise are omitted from the * output. */ renderCustomBlock?: (block: CustomBlock) => Promise; /** * Per-block-type renderer overrides, keyed by `block.type`. An entry replaces * the built-in renderer for that type wholesale — including its * hidden-on-all-viewports check, which becomes the override's responsibility. * * ```ts * renderToMjml(content, { * blockRenderers: { * countdown: (block) => ``, * video: (block, ctx) => renderVideoWithPlayButton(block, ctx), * }, * }); * ``` * * This generalises {@link renderCustomBlock}, which is the same idea for a * single block type. It exists so a backend whose render output is a *superset* * of the browser's — a server-generated countdown GIF, a composited video * thumbnail — can inject exactly that delta instead of forking the renderer: * parity for every other block type then holds by construction. * * A block type with neither a built-in renderer nor an override emits an * `mj-raw` placeholder comment and warns, rather than vanishing from the * output. */ blockRenderers?: BlockRendererMap; /** * Resolves the definition-level CSS for a custom block type. Called once * per unique `customType` present in the content tree (not per instance). * The non-empty results are deduped by content and emitted as additional * `` blocks inside `` alongside the built-in visibility * media queries. * * Editor consumers: pass a function that reads * `blockRegistry.getDefinition(customType)?.stylesheet`. * * Headless consumers: provide your own resolver, typically from the same * definitions map used by `renderCustomBlock`. Return `undefined` or `null` * for definitions without a stylesheet — those are skipped. */ getCustomBlockStylesheet?: (customType: string) => string | undefined | null; /** * Base URL (no trailing slash) for the social icon PNG assets. Resolved to * `${baseUrl}/${style}/${platform}.png` per icon. Defaults to the * version-pinned jsDelivr mirror of this package. Override to self-host * (e.g., behind your own CDN or for air-gapped environments). * * Why PNGs: Outlook desktop (Word rendering engine) does not support SVG * and rejects base64 data URIs in ``, so social icons must be * served as raster images over HTTP for cross-client compatibility. */ socialIconsBaseUrl?: string; /** * Embedder shell applied at render. Cloned and spliced around `content` * via `applyLayout`; `content` is not mutated. Omitted, the document is * rendered as authored and must not contain `slot` or `wrapper`. */ layout?: TemplateContent; } /** * Render template content to an MJML string. * * The function is async because resolving custom blocks may require * asynchronous work (e.g., the editor's liquid renderer dynamically imports * `liquidjs`). When the content has no custom blocks or `renderCustomBlock` * is omitted, no async work is performed but the function still resolves * synchronously — i.e., it always returns a Promise. */ export declare function renderToMjml(content: TemplateContent, options?: RenderOptions): Promise; //#endregion export type { BlockRenderer, BlockRendererMap }; //# sourceMappingURL=index.d.ts.map