@maily-to/shared 0.0.2 → 2.0.0-beta.1

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,"file":"index.cjs","names":[],"sources":["../src/alignment.ts","../src/field-mode.ts","../src/border.ts","../src/button-kind.ts","../src/constants.ts","../src/font.ts","../src/css-variables.ts","../src/html-code-block.ts","../src/math.ts","../src/image-width.ts","../src/mark.ts","../src/node.ts","../src/is.ts","../src/font-style.ts","../src/font-weight.ts","../src/function.ts","../src/invariant.ts","../src/margin.ts","../src/object.ts","../src/padding.ts","../src/promise.ts","../src/text-direction.ts","../src/theme.ts","../src/variable.ts","../src/visibility.ts"],"sourcesContent":["export const TEXT_ALIGNMENTS = {\n LEFT: 'left',\n CENTER: 'center',\n RIGHT: 'right',\n} as const;\n\nexport type AllowedTextAlignment =\n (typeof TEXT_ALIGNMENTS)[keyof typeof TEXT_ALIGNMENTS];\n\nexport const allowedTextAligns: readonly AllowedTextAlignment[] = Object.freeze(\n Object.values(TEXT_ALIGNMENTS)\n);\n\nexport const DEFAULT_TEXT_ALIGN: AllowedTextAlignment = TEXT_ALIGNMENTS.LEFT;\n\nexport function isAllowedTextAlignment(\n value: unknown\n): value is AllowedTextAlignment {\n return (\n typeof value === 'string' &&\n allowedTextAligns.includes(value as AllowedTextAlignment)\n );\n}\n","/**\n * Controls how multi-value fields (padding, margin, border) are edited.\n * \"none\" disables the field, \"uniform\" links all sides to one value,\n * \"mixed\" allows independent per-side values, \"preset\" uses predefined\n * size options (e.g. small/medium/large).\n */\nexport const FIELD_MODE = {\n NONE: 'none',\n MIXED: 'mixed',\n UNIFORM: 'uniform',\n PRESET: 'preset',\n} as const;\n\nexport type AllowedFieldMode = (typeof FIELD_MODE)[keyof typeof FIELD_MODE];\n\nexport const allowedFieldModes = Object.values(\n FIELD_MODE\n) as readonly AllowedFieldMode[];\n\nexport function isAllowedFieldMode(mode: unknown): mode is AllowedFieldMode {\n return (\n typeof mode === 'string' &&\n allowedFieldModes.includes(mode as AllowedFieldMode)\n );\n}\n","import type { Properties } from 'csstype';\n\nimport type { AllowedFieldMode } from './field-mode';\nimport { FIELD_MODE } from './field-mode';\n\nexport const BORDER_STYLES = {\n SOLID: 'solid',\n DASHED: 'dashed',\n DOTTED: 'dotted',\n} as const;\n\nexport type AllowedBorderStyle =\n (typeof BORDER_STYLES)[keyof typeof BORDER_STYLES];\n\nexport const DEFAULT_BORDER_STYLE: AllowedBorderStyle = BORDER_STYLES.SOLID;\n\nexport const DEFAULT_BORDER_COLOR = '#000000';\n\n/**\n * Full border configuration for a node. Includes border style, color,\n * per-side widths (top/right/bottom/left), per-corner radii, and\n * field modes that control whether each group is edited uniformly\n * or independently.\n */\nexport type BorderStyleConfig = {\n borderStyle: AllowedBorderStyle;\n\n borderColor: string;\n\n borderWidthMode: AllowedFieldMode;\n borderTopWidth: number;\n borderRightWidth: number;\n borderBottomWidth: number;\n borderLeftWidth: number;\n\n borderRadiusMode: AllowedFieldMode;\n borderTopLeftRadius: number;\n borderTopRightRadius: number;\n borderBottomRightRadius: number;\n borderBottomLeftRadius: number;\n};\n\n/**\n * Converts a BorderStyleConfig into CSS properties for inline styles.\n * When all widths or radii are equal (or the mode is \"uniform\"), emits\n * shorthand CSS; otherwise emits per-side longhand properties.\n */\nexport function getBorderStyle(config: BorderStyleConfig): Properties {\n const {\n borderStyle = DEFAULT_BORDER_STYLE,\n borderColor = DEFAULT_BORDER_COLOR,\n borderWidthMode,\n borderTopWidth,\n borderRightWidth,\n borderBottomWidth,\n borderLeftWidth,\n borderRadiusMode,\n borderTopLeftRadius,\n borderTopRightRadius,\n borderBottomRightRadius,\n borderBottomLeftRadius,\n } = config;\n\n const isBorderWidthUniform =\n borderWidthMode === FIELD_MODE.UNIFORM ||\n (borderLeftWidth === borderRightWidth &&\n borderRightWidth === borderBottomWidth &&\n borderBottomWidth === borderTopWidth);\n const isBorderRadiusUniform =\n borderRadiusMode === FIELD_MODE.UNIFORM ||\n (borderTopLeftRadius === borderTopRightRadius &&\n borderTopRightRadius === borderBottomRightRadius &&\n borderBottomRightRadius === borderBottomLeftRadius);\n\n return {\n ...(isBorderWidthUniform\n ? { border: `${borderTopWidth}px ${borderStyle} ${borderColor}` }\n : {\n borderTopWidth: `${borderTopWidth}px`,\n borderRightWidth: `${borderRightWidth}px`,\n borderBottomWidth: `${borderBottomWidth}px`,\n borderLeftWidth: `${borderLeftWidth}px`,\n borderStyle,\n borderColor,\n }),\n ...(isBorderRadiusUniform\n ? { borderRadius: `${borderTopLeftRadius}px` }\n : {\n borderRadius: `${borderTopLeftRadius}px ${borderTopRightRadius}px ${borderBottomRightRadius}px ${borderBottomLeftRadius}px`,\n }),\n };\n}\n","/**\n * Button layout kinds.\n * \"tight\" renders the button at its content width,\n * \"full-width\" stretches it to fill the container.\n */\nexport const BUTTON_KINDS = {\n TIGHT: 'tight',\n FULL_WIDTH: 'full-width',\n} as const;\n\nexport type AllowedButtonKind =\n (typeof BUTTON_KINDS)[keyof typeof BUTTON_KINDS];\n\nexport const allowedButtonKinds: readonly AllowedButtonKind[] = Object.freeze(\n Object.values(BUTTON_KINDS)\n);\n\nexport const DEFAULT_BUTTON_KIND: AllowedButtonKind = BUTTON_KINDS.TIGHT;\n\nexport function isAllowedButtonKind(\n value: unknown\n): value is AllowedButtonKind {\n return (\n typeof value === 'string' &&\n allowedButtonKinds.includes(value as AllowedButtonKind)\n );\n}\n","/** HTML data attribute key used to identify node types in rendered output. */\nexport const DATA_NODE_TYPE_KEY = 'data-node-type';\n\n/** HTML data attribute key used to store serialized visibility rules. */\nexport const DATA_VISIBILITY_RULE_KEY = 'data-visibility-rule';\n\n/** Prefix prepended to all Maily error messages for easy identification. */\nexport const MAILY_ERROR_PREFIX = '[Maily Error]';\n\n/** URL for filing bug reports against the Maily repository. */\nexport const MAILY_BUG_REPORT_URL =\n 'https://github.com/arikchakma/maily.to/issues/new';\n","import type { Properties } from 'csstype';\n\n/**\n * List of allowed fallback font families. These are system fonts or\n * generic font families that serve as fallbacks when a web font\n * fails to load.\n */\nexport const allowedFallbackFonts = [\n 'Arial',\n 'Helvetica',\n 'Verdana',\n 'Georgia',\n 'Times New Roman',\n 'serif',\n 'sans-serif',\n 'monospace',\n 'cursive',\n 'fantasy',\n] as const;\n\nexport type FallbackFont = (typeof allowedFallbackFonts)[number];\n\nexport const allowedFontFormats = [\n 'woff',\n 'woff2',\n 'truetype',\n 'opentype',\n 'embedded-opentype',\n 'svg',\n] as const;\n\nexport type FontFormat = (typeof allowedFontFormats)[number];\n\ntype FontStyle = Properties['fontStyle'];\n\n/**\n * Configuration for a font used in the editor or renderer.\n * Specifies the primary font family, a fallback, and an optional\n * web font URL with its format for @font-face loading.\n */\nexport interface FontProps {\n fontFamily: string;\n fallbackFontFamily: FallbackFont;\n webFont?: {\n url: string;\n format: FontFormat;\n };\n fontStyle?: FontStyle;\n fontWeight?: number;\n}\n\n/**\n * Default font configuration: Inter with sans-serif fallback,\n * loaded from the Maily CDN as woff2.\n */\nexport const DEFAULT_FONT: FontProps = {\n fallbackFontFamily: 'sans-serif',\n fontFamily: 'Inter',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/inter.woff2',\n format: 'woff2',\n },\n};\n\n/**\n * Injects a @font-face style element into the document head to load\n * the given font. Only works in browser environments.\n */\nexport function loadFont(font: FontProps): void {\n const style = getFontFaceStyle(font);\n\n const styleElement = document.createElement('style');\n styleElement.textContent = style;\n document.head.appendChild(styleElement);\n}\n\n/**\n * Generates a @font-face CSS rule string for the given font configuration.\n * Includes the font family, style, weight, MSO fallback, and web font\n * source URL when available.\n */\nexport function getFontFaceStyle(font: FontProps): string {\n const {\n fontFamily,\n fallbackFontFamily,\n webFont,\n fontWeight = 400,\n fontStyle = 'normal',\n } = font;\n\n const src = webFont\n ? `src: url(${webFont.url}) format('${webFont.format}');`\n : '';\n\n const style = /* css */ `@font-face {font-family: '${fontFamily}';font-style: ${fontStyle};font-weight: ${fontWeight};mso-font-alt: '${fallbackFontFamily}';${src}}`;\n\n return style;\n}\n","import type { Properties } from 'csstype';\n\nimport { DEFAULT_FONT } from './font';\nimport type { EditorThemeOptions } from './theme';\n\ndeclare module 'csstype' {\n interface Properties {\n [index: `--mly-${string}`]: any;\n }\n}\n\n/**\n * Returns a CSS variable object for the given name and value.\n * If the value is nullish or empty, returns an empty object so that\n * the default CSS variable value is not overridden. Numbers are\n * automatically suffixed with \"px\".\n */\nexport function getVariableValue(\n name: `--mly-${string}`,\n value: unknown\n): Properties {\n if (value === undefined || value === null || value === '') {\n return {};\n }\n\n return {\n [name]: typeof value === 'number' ? `${value}px` : value,\n };\n}\n\n/**\n * Converts an EditorThemeOptions object into a flat map of Maily CSS\n * custom properties (--mly-*). Used to apply theme overrides as inline\n * styles on the editor or renderer root element. Covers body background,\n * container dimensions and borders, button colors, link color, and\n * font family settings.\n */\nexport function getMailyCssVariables(theme: EditorThemeOptions): Properties {\n const font = theme.font || DEFAULT_FONT;\n\n return {\n ...getVariableValue(\n '--mly-body-background-color',\n theme.body?.backgroundColor\n ),\n ...getVariableValue('--mly-body-padding-top', theme.body?.paddingTop),\n ...getVariableValue('--mly-body-padding-right', theme.body?.paddingRight),\n ...getVariableValue('--mly-body-padding-bottom', theme.body?.paddingBottom),\n ...getVariableValue('--mly-body-padding-left', theme.body?.paddingLeft),\n\n ...getVariableValue(\n '--mly-container-background-color',\n theme.container?.backgroundColor\n ),\n ...getVariableValue('--mly-container-max-width', theme.container?.maxWidth),\n ...getVariableValue('--mly-container-min-width', theme.container?.minWidth),\n\n ...getVariableValue(\n '--mly-container-padding-top',\n theme.container?.paddingTop\n ),\n ...getVariableValue(\n '--mly-container-padding-right',\n theme.container?.paddingRight\n ),\n ...getVariableValue(\n '--mly-container-padding-bottom',\n theme.container?.paddingBottom\n ),\n ...getVariableValue(\n '--mly-container-padding-left',\n theme.container?.paddingLeft\n ),\n\n ...getVariableValue(\n '--mly-container-border-radius',\n theme.container?.borderRadius\n ),\n ...getVariableValue(\n '--mly-container-border-width',\n theme.container?.borderWidth\n ),\n ...getVariableValue(\n '--mly-container-border-color',\n theme.container?.borderColor\n ),\n\n ...getVariableValue(\n '--mly-button-background-color',\n theme.button?.backgroundColor\n ),\n ...getVariableValue('--mly-button-text-color', theme.button?.color),\n ...getVariableValue('--mly-button-padding-top', theme.button?.paddingTop),\n ...getVariableValue(\n '--mly-button-padding-right',\n theme.button?.paddingRight\n ),\n ...getVariableValue(\n '--mly-button-padding-bottom',\n theme.button?.paddingBottom\n ),\n ...getVariableValue('--mly-button-padding-left', theme.button?.paddingLeft),\n\n ...getVariableValue('--mly-link-color', theme.link?.color),\n\n ...getVariableValue('--mly-font-family', font.fontFamily),\n ...getVariableValue('--mly-font-fallback-family', font.fallbackFontFamily),\n '--mly-font': `var(--mly-font-family), var(--mly-font-fallback-family)`,\n };\n}\n","/**\n * Tab options for the HTML code block node.\n * \"code\" shows the raw HTML source, \"preview\" renders it visually.\n */\nexport const HTML_CODE_BLOCK_TABS = {\n CODE: 'code',\n PREVIEW: 'preview',\n} as const;\n\nexport type AllowedHtmlCodeBlockTab =\n (typeof HTML_CODE_BLOCK_TABS)[keyof typeof HTML_CODE_BLOCK_TABS];\n\nexport const DEFAULT_HTML_CODE_BLOCK_TAB: AllowedHtmlCodeBlockTab =\n HTML_CODE_BLOCK_TABS.CODE;\n","export function clamp(value: number, [min, max]: [number, number]): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function percentage(portion: number, total: number): number {\n return clamp((portion / total) * 100, [0, 100]);\n}\n\nexport function absoluteFromPercentage(\n percentage: number,\n total: number\n): number {\n return Math.round((total * percentage) / 100);\n}\n\nexport function roundTo(value: number, decimals: number): number {\n const factor = Math.pow(10, decimals);\n return Math.round(value * factor) / factor;\n}\n","import { clamp } from './math';\n\n/**\n * Represents an image width value — can be a pixel number, a percentage\n * string (e.g. \"50%\"), or the literal \"auto\" for full-width.\n */\nexport type ImageWidth = number | string;\n\nexport const MIN_IMAGE_WIDTH_PERCENTAGE = 5;\nexport const MAX_IMAGE_WIDTH_PERCENTAGE = 100;\n\nconst MAX_IMAGE_WIDTH_PIXELS = 600;\n\n/**\n * Converts any ImageWidth value to a clamped percentage number.\n * Handles \"auto\" (→ 100), percentage strings (e.g. \"50%\" → 50),\n * and legacy pixel numbers (converted relative to maxWidthPixels).\n * Returns 100 for null or unrecognized values.\n */\nexport function parseWidthToPercentage(\n width: ImageWidth | null,\n maxWidthPixels: number = MAX_IMAGE_WIDTH_PIXELS\n): number {\n if (typeof width === 'string') {\n if (width === 'auto') {\n return 100;\n }\n\n return width.endsWith('%')\n ? clampImageWidthPercentage(parseInt(width))\n : 100;\n }\n\n // it's here for backwards compatibility\n // use string instead of fixed number\n if (typeof width === 'number') {\n const percentage = Math.round((100 * width) / maxWidthPixels);\n return clampImageWidthPercentage(percentage);\n }\n\n return 100;\n}\n\nexport function clampImageWidthPercentage(width: number) {\n return clamp(width, [MIN_IMAGE_WIDTH_PERCENTAGE, MAX_IMAGE_WIDTH_PERCENTAGE]);\n}\n","/** Registry of all inline mark type strings used in the document JSON. */\nexport const MAILY_MARK_TYPES = {\n BOLD: 'bold',\n ITALIC: 'italic',\n STRIKE: 'strike',\n UNDERLINE: 'underline',\n LINK: 'link',\n CODE: 'code',\n TEXT_STYLE: 'textStyle',\n HIGHLIGHT: 'highlight',\n} as const;\n\nexport type MailyMarkType =\n (typeof MAILY_MARK_TYPES)[keyof typeof MAILY_MARK_TYPES];\n\nexport const allowedMarkTypes = Object.values(MAILY_MARK_TYPES);\n\n/**\n * Base shape shared by all inline marks. Individual mark types\n * narrow the type field and optionally add typed attrs.\n */\nexport type BaseMailyMark = {\n type: MailyMarkType;\n attrs?: Record<string, any>;\n};\n\n/** Bold */\nexport type BoldMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.BOLD;\n};\n\n/** Italic */\nexport type ItalicMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.ITALIC;\n};\n\n/** Strike */\nexport type StrikeMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.STRIKE;\n};\n\n/** Underline */\nexport type UnderlineMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.UNDERLINE;\n};\n\n/** Link */\nexport type LinkAttributes = {\n href: string;\n target?: string;\n};\n\nexport type LinkMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.LINK;\n attrs: LinkAttributes;\n};\n\n/** Code */\nexport type CodeAttributes = {};\n\nexport type CodeMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.CODE;\n attrs: CodeAttributes;\n};\n\n/** Text Style */\nexport type TextStyleAttributes = {\n color: string;\n};\n\nexport type TextStyleMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.TEXT_STYLE;\n attrs: TextStyleAttributes;\n};\n\n/** Highlight */\nexport type HighlightAttributes = {\n color: string;\n};\n\nexport type HighlightMark = BaseMailyMark & {\n type: typeof MAILY_MARK_TYPES.HIGHLIGHT;\n attrs: HighlightAttributes;\n};\n\nexport type AnyMailyMark =\n | BoldMark\n | ItalicMark\n | StrikeMark\n | UnderlineMark\n | LinkMark\n | CodeMark\n | TextStyleMark\n | HighlightMark;\n","import type { AllowedTextAlignment } from './alignment';\nimport type { BorderStyleConfig } from './border';\nimport type { AllowedButtonKind } from './button-kind';\nimport type { AllowedFieldMode } from './field-mode';\nimport type { FontStyleAttributes } from './font-style';\nimport type { AllowedHtmlCodeBlockTab } from './html-code-block';\nimport type { MarginStyleConfig } from './margin';\nimport type { AnyMailyMark } from './mark';\nimport type { PaddingStyleConfig } from './padding';\nimport type { TextDirection } from './text-direction';\nimport type { VisibilityRule } from './visibility';\n\n/**\n * Registry of all document node type strings. These map 1:1 to\n * Tiptap extension names and are used as discriminators in the\n * document JSON.\n */\nexport const MAILY_NODE_TYPES = {\n DOCUMENT: 'doc',\n PARAGRAPH: 'paragraph',\n TEXT: 'text',\n HEADING: 'heading',\n VARIABLE: 'variable',\n IMAGE: 'image',\n SPACER: 'spacer',\n SECTION: 'section',\n COLUMNS: 'columns',\n COLUMN: 'column',\n BULLET_LIST: 'bulletList',\n ORDERED_LIST: 'orderedList',\n LIST_ITEM: 'listItem',\n HORIZONTAL_RULE: 'horizontalRule',\n BUTTON: 'button',\n REPEAT: 'repeat',\n HTML_CODE_BLOCK: 'htmlCodeBlock',\n BLOCKQUOTE: 'blockquote',\n HARD_BREAK: 'hardBreak',\n FOOTER: 'footer',\n INLINE_IMAGE: 'inlineImage',\n LINK_CARD: 'linkCard',\n} as const;\n\n/**\n * Tiptap extension types that attach attributes to nodes but are\n * not document nodes themselves (e.g. fontStyle, visibility).\n */\nexport const MAILY_EXTENSION_TYPES = {\n FONT_STYLE: 'fontStyle',\n VISIBILITY: 'visibility',\n} as const;\n\nexport const allowedNodeTypes = Object.values(MAILY_NODE_TYPES);\n\nexport type MailyNodeType =\n (typeof MAILY_NODE_TYPES)[keyof typeof MAILY_NODE_TYPES];\n\n/**\n * Global attributes mixed into every node by Tiptap extensions:\n * text direction, unique id, and conditional visibility.\n */\nexport type WithExtraAttrs = {\n dir?: TextDirection | null;\n id?: string | null;\n visibilityRule?: VisibilityRule | null;\n};\n\n/**\n * Base shape shared by all document nodes. Individual node types\n * narrow the type field, add typed attrs, and constrain content.\n */\nexport type BaseMailyNode = {\n type: MailyNodeType;\n attrs?: Record<string, any> & WithExtraAttrs;\n content?: AnyMailyNode[];\n marks?: AnyMailyMark[];\n text?: string;\n};\n\n/** Document */\nexport type DocumentAttributes = {\n version: number;\n};\n\nexport type DocumentNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.DOCUMENT;\n attrs: DocumentAttributes;\n content: BaseMailyNode[];\n};\n\n/** Paragraph */\nexport type ParagraphAttributes = {\n id: string;\n textAlign: AllowedTextAlignment;\n} & FontStyleAttributes;\n\nexport type ParagraphNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.PARAGRAPH;\n attrs: ParagraphAttributes;\n};\n\n/** Text */\nexport type TextNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.TEXT;\n text: string;\n};\n\n/** Heading */\nexport type HeadingLevel = 1 | 2 | 3;\nexport type HeadingAttributes = {\n id: string;\n level: HeadingLevel;\n} & FontStyleAttributes;\n\nexport type HeadingNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.HEADING;\n attrs: HeadingAttributes;\n};\n\n/**\n * Variable\n * Attributes for a template variable node. The id and label must not\n * contain `|` characters — that delimiter is used when serializing\n * variable data to plain text (see serializeVariableToText).\n */\nexport type VariableAttributes = {\n /** Stored as `data-id`. Identifies which variable this node represents. */\n id: string | null;\n /** Stored as `data-label`. Display text shown in the editor instead of the id. */\n label?: string | null;\n /** Stored as `data-fallback`. Used when the variable has no value at render time. */\n fallback?: string | null;\n required?: boolean;\n hideDefaultValue?: boolean;\n /** The character that opens the variable suggestion popup (e.g. `@`). */\n variableSuggestionChar?: string;\n};\n\nexport type VariableNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.VARIABLE;\n attrs: VariableAttributes;\n content: never;\n};\n\n/** Image */\nexport type ImageAttributes = {\n src: string;\n alt?: string;\n title?: string;\n width: string;\n\n align: AllowedTextAlignment;\n externalLink: string | null;\n} & BorderStyleConfig;\n\nexport type ImageNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.IMAGE;\n attrs: ImageAttributes;\n content: never;\n};\n\n/** Spacer */\nexport type SpacerAttributes = {\n height: number;\n heightMode: AllowedFieldMode;\n};\n\nexport type SpacerNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.SPACER;\n attrs: SpacerAttributes;\n content: never;\n};\n\n/** Section */\nexport type SectionAttributes = {\n backgroundColor: string;\n align: AllowedTextAlignment;\n} & BorderStyleConfig &\n PaddingStyleConfig &\n MarginStyleConfig;\n\nexport type SectionNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.SECTION;\n attrs: SectionAttributes;\n content: AnyMailyNode[];\n};\n\n/** Bullet List */\nexport type BulletListAttributes = {\n id: string;\n};\n\nexport type BulletListNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.BULLET_LIST;\n attrs: BulletListAttributes;\n content: AnyMailyNode[];\n};\n\n/** Ordered List */\nexport type OrderedListAttributes = {\n id: string;\n};\n\nexport type OrderedListNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.ORDERED_LIST;\n attrs: OrderedListAttributes;\n content: AnyMailyNode[];\n};\n\n/** List Item */\nexport type ListItemAttributes = {\n id: string;\n};\n\nexport type ListItemNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.LIST_ITEM;\n attrs: ListItemAttributes;\n content: AnyMailyNode[];\n};\n\n/** Blockquote */\nexport type BlockquoteAttributes = {\n id: string;\n} & FontStyleAttributes;\n\nexport type BlockquoteNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.BLOCKQUOTE;\n attrs: BlockquoteAttributes;\n content: AnyMailyNode[];\n};\n\n/** Horizontal Rule */\nexport type HorizontalRuleAttributes = {\n id: string;\n};\n\nexport type HorizontalRuleNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.HORIZONTAL_RULE;\n attrs: HorizontalRuleAttributes;\n content: never;\n};\n\n/** Button */\nexport type ButtonAttributes = {\n url: string;\n kind: AllowedButtonKind;\n alignment: AllowedTextAlignment | null;\n backgroundColor: string | null;\n color: string | null;\n paddingMode: AllowedFieldMode;\n paddingTop: number;\n paddingRight: number;\n paddingBottom: number;\n paddingLeft: number;\n} & BorderStyleConfig &\n FontStyleAttributes;\n\nexport type ButtonNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.BUTTON;\n attrs: ButtonAttributes;\n content: AnyMailyNode[];\n};\n\n/** HTML Code Block */\nexport type HtmlCodeBlockAttributes = {\n activeTab: AllowedHtmlCodeBlockTab;\n language: string;\n};\n\nexport type HtmlCodeBlockNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.HTML_CODE_BLOCK;\n attrs: HtmlCodeBlockAttributes;\n content: AnyMailyNode[];\n};\n\n/** Repeat */\nexport type RepeatAttributes = {\n each: string;\n};\n\nexport type RepeatNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.REPEAT;\n attrs: RepeatAttributes;\n content: AnyMailyNode[];\n};\n\n/** Columns (container for multiple columns) */\nexport type ColumnsAttributes = {\n columnCount: number;\n gap: number;\n};\n\nexport type ColumnsNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.COLUMNS;\n attrs: ColumnsAttributes;\n content: ColumnNode[];\n};\n\n/** Column (individual column within columns container) */\nexport type ColumnAttributes = {\n width: number | null; // null = auto (equal space), number = percentage\n verticalAlign: 'top' | 'middle' | 'bottom';\n};\n\nexport type ColumnNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.COLUMN;\n attrs: ColumnAttributes;\n content: AnyMailyNode[];\n};\n\n/** Hard Break */\nexport type HardBreakNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.HARD_BREAK;\n content: never;\n};\n\n/** Footer */\nexport type FooterAttributes = {\n id: string;\n textAlign: AllowedTextAlignment;\n} & FontStyleAttributes;\n\nexport type FooterNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.FOOTER;\n attrs: FooterAttributes;\n};\n\n/** Inline Image */\nexport type InlineImageAttributes = {\n src: string;\n alt?: string;\n title?: string;\n width: number;\n height: number;\n externalLink: string | null;\n};\n\nexport type InlineImageNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.INLINE_IMAGE;\n attrs: InlineImageAttributes;\n content: never;\n};\n\n/** Link Card */\nexport type LinkCardAttributes = {\n title: string;\n description: string;\n link: string;\n linkTitle: string;\n image: string;\n subTitle: string;\n badgeText: string;\n};\n\nexport type LinkCardNode = BaseMailyNode & {\n type: typeof MAILY_NODE_TYPES.LINK_CARD;\n attrs: LinkCardAttributes;\n content: never;\n};\n\nexport type AnyMailyNode =\n | DocumentNode\n | ParagraphNode\n | TextNode\n | HeadingNode\n | VariableNode\n | ImageNode\n | SpacerNode\n | SectionNode\n | RepeatNode\n | ColumnsNode\n | ColumnNode\n | BulletListNode\n | OrderedListNode\n | ListItemNode\n | HorizontalRuleNode\n | ButtonNode\n | HtmlCodeBlockNode\n | BlockquoteNode\n | HardBreakNode\n | FooterNode\n | InlineImageNode\n | LinkCardNode;\n","import type {\n AnyMailyMark,\n BoldMark,\n CodeMark,\n HighlightMark,\n ItalicMark,\n LinkMark,\n MailyMarkType,\n StrikeMark,\n TextStyleMark,\n UnderlineMark,\n} from './mark';\nimport { allowedMarkTypes, MAILY_MARK_TYPES } from './mark';\nimport type {\n AnyMailyNode,\n BlockquoteNode,\n BulletListNode,\n ButtonNode,\n ColumnNode,\n ColumnsNode,\n DocumentNode,\n FooterNode,\n HardBreakNode,\n HeadingNode,\n HorizontalRuleNode,\n HtmlCodeBlockNode,\n ImageNode,\n InlineImageNode,\n LinkCardNode,\n ListItemNode,\n MailyNodeType,\n OrderedListNode,\n ParagraphNode,\n RepeatNode,\n SectionNode,\n SpacerNode,\n TextNode,\n VariableNode,\n} from './node';\nimport { allowedNodeTypes, MAILY_NODE_TYPES } from './node';\n\nfunction guard<T extends AnyMailyNode | AnyMailyMark>(type: string) {\n return (node: AnyMailyNode | AnyMailyMark): node is T => node.type === type;\n}\n\n/**\n * Type guards for Maily nodes and marks. Provides narrowing functions\n * for every node type (is.paragraph, is.button, etc.) and mark type\n * (is.bold, is.link, etc.), plus structural checks like is.parent\n * and is.marked. Each guard narrows the input to its specific type.\n *\n * Example:\n * if (is.paragraph(node)) {\n * node.attrs.textAlign // narrowed to ParagraphNode\n * }\n */\nexport const is = {\n any(value: unknown): value is AnyMailyNode | AnyMailyMark {\n return this.node(value) || this.mark(value);\n },\n parent(\n node: AnyMailyNode\n ): node is AnyMailyNode & { content: AnyMailyNode[] } {\n return Array.isArray(node?.content);\n },\n marked(node: AnyMailyNode): node is AnyMailyNode & { marks: AnyMailyMark[] } {\n return Array.isArray(node?.marks);\n },\n // Node\n node(node: unknown): node is AnyMailyNode {\n return (\n typeof node === 'object' &&\n node !== null &&\n 'type' in node &&\n allowedNodeTypes.includes(node.type as MailyNodeType)\n );\n },\n doc: guard<DocumentNode>(MAILY_NODE_TYPES.DOCUMENT),\n paragraph: guard<ParagraphNode>(MAILY_NODE_TYPES.PARAGRAPH),\n text: guard<TextNode>(MAILY_NODE_TYPES.TEXT),\n heading: guard<HeadingNode>(MAILY_NODE_TYPES.HEADING),\n variable: guard<VariableNode>(MAILY_NODE_TYPES.VARIABLE),\n image: guard<ImageNode>(MAILY_NODE_TYPES.IMAGE),\n inlineImage: guard<InlineImageNode>(MAILY_NODE_TYPES.INLINE_IMAGE),\n spacer: guard<SpacerNode>(MAILY_NODE_TYPES.SPACER),\n section: guard<SectionNode>(MAILY_NODE_TYPES.SECTION),\n button: guard<ButtonNode>(MAILY_NODE_TYPES.BUTTON),\n columns: guard<ColumnsNode>(MAILY_NODE_TYPES.COLUMNS),\n column: guard<ColumnNode>(MAILY_NODE_TYPES.COLUMN),\n repeat: guard<RepeatNode>(MAILY_NODE_TYPES.REPEAT),\n bulletList: guard<BulletListNode>(MAILY_NODE_TYPES.BULLET_LIST),\n orderedList: guard<OrderedListNode>(MAILY_NODE_TYPES.ORDERED_LIST),\n listItem: guard<ListItemNode>(MAILY_NODE_TYPES.LIST_ITEM),\n horizontalRule: guard<HorizontalRuleNode>(MAILY_NODE_TYPES.HORIZONTAL_RULE),\n htmlCodeBlock: guard<HtmlCodeBlockNode>(MAILY_NODE_TYPES.HTML_CODE_BLOCK),\n blockquote: guard<BlockquoteNode>(MAILY_NODE_TYPES.BLOCKQUOTE),\n hardBreak: guard<HardBreakNode>(MAILY_NODE_TYPES.HARD_BREAK),\n footer: guard<FooterNode>(MAILY_NODE_TYPES.FOOTER),\n linkCard: guard<LinkCardNode>(MAILY_NODE_TYPES.LINK_CARD),\n\n // Mark\n mark(mark: unknown): mark is AnyMailyMark {\n return (\n typeof mark === 'object' &&\n mark !== null &&\n 'type' in mark &&\n allowedMarkTypes.includes(mark.type as MailyMarkType)\n );\n },\n bold: guard<BoldMark>(MAILY_MARK_TYPES.BOLD),\n italic: guard<ItalicMark>(MAILY_MARK_TYPES.ITALIC),\n strike: guard<StrikeMark>(MAILY_MARK_TYPES.STRIKE),\n underline: guard<UnderlineMark>(MAILY_MARK_TYPES.UNDERLINE),\n link: guard<LinkMark>(MAILY_MARK_TYPES.LINK),\n code: guard<CodeMark>(MAILY_MARK_TYPES.CODE),\n textStyle: guard<TextStyleMark>(MAILY_MARK_TYPES.TEXT_STYLE),\n highlight: guard<HighlightMark>(MAILY_MARK_TYPES.HIGHLIGHT),\n};\n\n/** Type guard that checks if a value is defined (not null or undefined). */\nexport function isDef<T = any>(val?: T | null): val is T {\n return val !== undefined && val !== null;\n}\n\n/** Type guard that checks if a value is a boolean. */\nexport function isBoolean(val: any): val is boolean {\n return typeof val === 'boolean';\n}\n\n/** Type guard that checks if a value is a number. */\nexport function isNumber(val: any): val is number {\n return typeof val === 'number';\n}\n\n/** Type guard that checks if a value is a string. */\nexport function isString(val: unknown): val is string {\n return typeof val === 'string';\n}\n\n/** Type guard that checks if a value is a non-null object. */\nexport function isObject(val: unknown): val is object {\n return typeof val === 'object' && val !== null;\n}\n","import type { Properties } from 'csstype';\n\nimport type { FallbackFont, FontFormat } from './font';\nimport type { FontWeight } from './font-weight';\nimport { isDef } from './is';\n\n/**\n * Entry for the font picker dropdown. Web fonts include a CDN URL\n * and format for @font-face loading; system fonts omit the webFont field.\n */\nexport type FontFamilyItem = {\n fontFamily: string;\n label?: string;\n webFont?: { url: string; format: FontFormat };\n};\n\n/**\n * Built-in font options served from the Maily CDN. System fonts\n * (Arial, Helvetica, etc.) are included without a webFont field.\n */\nexport const DEFAULT_FONT_FAMILIES: FontFamilyItem[] = [\n {\n fontFamily: 'Inter',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/inter.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Roboto',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/roboto.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Open Sans',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/open-sans.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Lato',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/lato.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Montserrat',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/montserrat.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Poppins',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/poppins.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Raleway',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/raleway.woff2',\n format: 'woff2',\n },\n },\n {\n fontFamily: 'Ubuntu',\n webFont: {\n url: 'https://cdn.usemaily.com/fonts/v0/ubuntu.woff2',\n format: 'woff2',\n },\n },\n { fontFamily: 'Arial' },\n { fontFamily: 'Helvetica' },\n { fontFamily: 'Georgia' },\n { fontFamily: 'Times New Roman' },\n { fontFamily: 'Verdana' },\n { fontFamily: 'Courier New' },\n { fontFamily: 'Trebuchet MS' },\n];\n\nexport type FontFamily = string;\n\nexport const FONT_STYLES = {\n NORMAL: 'normal',\n ITALIC: 'italic',\n} as const;\n\nexport type FontStyleValue = (typeof FONT_STYLES)[keyof typeof FONT_STYLES];\n\n/** Preset options for the font size dropdown in the editor. */\nexport const FONT_SIZE_PRESETS = [\n { value: 12, label: 'Small' },\n { value: 14, label: 'Normal' },\n { value: 16, label: 'Medium' },\n { value: 18, label: 'Large' },\n { value: 24, label: 'XL' },\n { value: 32, label: '2XL' },\n] as const;\n\n/** Preset options for the line height dropdown in the editor. */\nexport const LINE_HEIGHT_PRESETS = [\n { value: 1, label: 'Tight' },\n { value: 1.25, label: 'Snug' },\n { value: 1.5, label: 'Normal' },\n { value: 1.75, label: 'Relaxed' },\n { value: 2, label: 'Loose' },\n] as const;\n\nexport const DEFAULT_FONT_FAMILY: FontFamily = 'Inter';\nexport const DEFAULT_FONT_SIZE = 15;\nexport const DEFAULT_FONT_WEIGHT: FontWeight = 400;\nexport const DEFAULT_LINE_HEIGHT = 1.75;\nexport const DEFAULT_FONT_FALLBACK: FallbackFont = 'sans-serif';\n\n/**\n * Per-node font style overrides. Null values mean \"inherit from\n * the theme defaults\" — only non-null values produce inline CSS.\n */\nexport type FontStyleAttributes = {\n fontFamily: FontFamily | null;\n fontFallback: FallbackFont | null;\n fontSize: number | null;\n fontWeight: FontWeight | null;\n lineHeight: number | null;\n fontStyle: FontStyleValue | null;\n};\n\n/**\n * Baseline font values used when a node's FontStyleAttributes are null.\n * Each node type (paragraph, heading, button, etc.) has its own defaults\n * defined in the renderer theme.\n */\nexport type NodeFontStyleDefaults = {\n fontSize: number;\n lineHeight: number;\n fontWeight: number;\n fontStyle?: FontStyleValue;\n};\n\n/**\n * Builds a CSS properties object from a node's font attributes,\n * falling back to the provided defaults for any null values.\n * Only includes properties that have a resolved value.\n */\nexport function getFontStyle(\n attrs: FontStyleAttributes,\n defaults?: Partial<NodeFontStyleDefaults>\n): Properties {\n const {\n fontFamily,\n fontFallback,\n fontSize,\n fontWeight,\n lineHeight,\n fontStyle,\n } = attrs;\n\n const size = fontSize ?? defaults?.fontSize;\n const weight = fontWeight ?? defaults?.fontWeight;\n const line = lineHeight ?? defaults?.lineHeight;\n const style = fontStyle ?? defaults?.fontStyle;\n\n const fontFamilyParts = [fontFamily, fontFallback].filter(Boolean);\n\n return {\n ...(fontFamilyParts.length > 0 && {\n fontFamily: fontFamilyParts.join(', '),\n }),\n ...(isDef(size) ? { fontSize: `${size}px` } : {}),\n ...(isDef(weight) ? { fontWeight: weight } : {}),\n ...(isDef(line) ? { lineHeight: line } : {}),\n ...(isDef(style) ? { fontStyle: style } : {}),\n };\n}\n","/**\n * Numeric font weight values mapped to semantic names.\n * Normal (400), Medium (500), Semibold (600), Bold (700), Extra Bold (800).\n */\nexport const FONT_WEIGHTS = {\n NORMAL: 400,\n MEDIUM: 500,\n SEMIBOLD: 600,\n BOLD: 700,\n EXTRA_BOLD: 800,\n} as const;\n\nexport type FontWeight = (typeof FONT_WEIGHTS)[keyof typeof FONT_WEIGHTS];\n","export type Noop = () => void;\n\n/** A no-operation function. Useful as a default callback or placeholder. */\nexport function noop(): void {}\n","import { MAILY_BUG_REPORT_URL, MAILY_ERROR_PREFIX } from './constants';\n\n/**\n * Assertion function that throws if the value is false, null, or undefined.\n * Used as a development-time guard to catch impossible states early.\n * Logs a bug report URL before throwing to help with issue filing.\n */\nexport function invariant(value: boolean, message?: string): asserts value;\n\nexport function invariant<T>(\n value: T | null | undefined,\n message?: string\n): asserts value is T;\n\nexport function invariant(value: any, message?: string) {\n if (value === false || value === null || typeof value === 'undefined') {\n console.error(\n `${MAILY_ERROR_PREFIX}: The following error is a bug in Maily;\nPlease open an issue! ${MAILY_BUG_REPORT_URL}`\n );\n throw new Error(message);\n }\n}\n","import type { Properties } from 'csstype';\n\nimport type { AllowedFieldMode } from './field-mode';\nimport { FIELD_MODE } from './field-mode';\n\n/**\n * Per-side margin configuration for a node. Includes a field mode\n * that controls whether margin is edited uniformly or per-side,\n * and individual values for each side in pixels.\n */\nexport type MarginStyleConfig = {\n marginMode: AllowedFieldMode;\n marginTop: number;\n marginRight: number;\n marginBottom: number;\n marginLeft: number;\n};\n\n/**\n * Converts a MarginStyleConfig into CSS properties for inline styles.\n * When all sides are equal (or mode is \"uniform\"), emits shorthand CSS;\n * otherwise emits per-side longhand properties.\n */\nexport function getMarginStyle(config: MarginStyleConfig): Properties {\n const { marginMode, marginTop, marginRight, marginBottom, marginLeft } =\n config;\n\n const isMarginUniform =\n marginMode === FIELD_MODE.UNIFORM ||\n (marginLeft === marginRight &&\n marginRight === marginBottom &&\n marginBottom === marginTop);\n\n if (isMarginUniform) {\n return { margin: `${marginTop}px` };\n }\n\n return {\n marginTop: `${marginTop}px`,\n marginRight: `${marginRight}px`,\n marginBottom: `${marginBottom}px`,\n marginLeft: `${marginLeft}px`,\n };\n}\n","function isMergableObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * One-level deep merge of plain objects. Nested plain objects are\n * shallow-merged; primitives, arrays, and null are replaced outright.\n * Does not mutate the target — returns a new object.\n *\n * Example:\n * deepMerge({ a: 1, nested: { x: 1, y: 2 } }, { nested: { y: 3 } })\n * // => { a: 1, nested: { x: 1, y: 3 } }\n */\nexport function deepMerge<T extends object>(\n target: T,\n ...sources: Partial<T>[]\n): T {\n const result: Record<string, unknown> = { ...target } as Record<\n string,\n unknown\n >;\n\n for (const source of sources) {\n for (const [key, value] of Object.entries(source as object)) {\n result[key] =\n isMergableObject(value) && isMergableObject(result[key])\n ? { ...result[key], ...value }\n : value;\n }\n }\n\n return result as T;\n}\n\nexport function isObjectDeepEqual(first: object, second: object): boolean {\n if (first === second) {\n return true;\n }\n\n for (const key of Object.keys(first) as Array<keyof typeof first>) {\n if (first[key] !== (second as typeof first)[key]) {\n return false;\n }\n }\n\n return true;\n}\n","import type { Properties } from 'csstype';\n\nimport type { AllowedFieldMode } from './field-mode';\nimport { FIELD_MODE } from './field-mode';\n\n/**\n * Per-side padding configuration for a node. Includes a field mode\n * that controls whether padding is edited uniformly or per-side,\n * and individual values for each side in pixels.\n */\nexport type PaddingStyleConfig = {\n paddingMode: AllowedFieldMode;\n paddingTop: number;\n paddingRight: number;\n paddingBottom: number;\n paddingLeft: number;\n};\n\n/**\n * Converts a PaddingStyleConfig into CSS properties for inline styles.\n * When all sides are equal (or mode is \"uniform\"), emits shorthand CSS;\n * otherwise emits per-side longhand properties.\n */\nexport function getPaddingStyle(config: PaddingStyleConfig): Properties {\n const { paddingMode, paddingTop, paddingRight, paddingBottom, paddingLeft } =\n config;\n\n const isPaddingUniform =\n paddingMode === FIELD_MODE.UNIFORM ||\n (paddingLeft === paddingRight &&\n paddingRight === paddingBottom &&\n paddingBottom === paddingTop);\n\n if (isPaddingUniform) {\n return { padding: `${paddingTop}px` };\n }\n\n return {\n paddingTop: `${paddingTop}px`,\n paddingRight: `${paddingRight}px`,\n paddingBottom: `${paddingBottom}px`,\n paddingLeft: `${paddingLeft}px`,\n };\n}\n","/** Returns a promise that resolves after the given number of milliseconds. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/** Text direction values matching the HTML `dir` attribute: ltr, rtl, auto. */\nexport const TEXT_DIRECTIONS = {\n LTR: 'ltr',\n RTL: 'rtl',\n AUTO: 'auto',\n} as const;\n\nexport type TextDirection =\n (typeof TEXT_DIRECTIONS)[keyof typeof TEXT_DIRECTIONS];\n\nexport const allowedTextDirections: readonly TextDirection[] = Object.freeze(\n Object.values(TEXT_DIRECTIONS)\n);\n\nexport const DEFAULT_TEXT_DIRECTION: TextDirection = TEXT_DIRECTIONS.AUTO;\n\nexport function isAllowedTextDirection(value: unknown): value is TextDirection {\n return (\n typeof value === 'string' &&\n allowedTextDirections.includes(value as TextDirection)\n );\n}\n\n/**\n * Returns a CSS `direction` property object for the given dir value.\n * Returns an empty object for \"auto\" or nullish values, since the\n * browser default is sufficient in those cases.\n */\nexport function getCssDirection(\n dir: TextDirection | null | undefined\n): Record<string, Omit<TextDirection, typeof TEXT_DIRECTIONS.AUTO>> {\n if (dir && dir !== TEXT_DIRECTIONS.AUTO) {\n return { direction: dir };\n }\n\n return {};\n}\n","import type { FontProps } from './font';\nimport { DEFAULT_FONT } from './font';\nimport { FONT_STYLES } from './font-style';\nimport type { FontStyleValue, NodeFontStyleDefaults } from './font-style';\nimport { FONT_WEIGHTS } from './font-weight';\nimport type { HeadingLevel } from './node';\nimport { MAILY_NODE_TYPES } from './node';\n\n/**\n * Shared theme properties used by both the editor and renderer.\n * Controls global styling for the body, container, buttons, links,\n * and font. Extended by EditorThemeOptions and RendererThemeOptions.\n */\nexport interface BaseThemeOptions {\n container?: Partial<{\n backgroundColor: string;\n maxWidth: number;\n minWidth: number;\n borderRadius: number;\n borderWidth: number;\n borderColor: string;\n\n paddingTop: number;\n paddingRight: number;\n paddingBottom: number;\n paddingLeft: number;\n }>;\n body?: Partial<{\n backgroundColor: string;\n paddingTop: number;\n paddingRight: number;\n paddingBottom: number;\n paddingLeft: number;\n }>;\n button?: Partial<{\n paddingTop: number;\n paddingRight: number;\n paddingBottom: number;\n paddingLeft: number;\n backgroundColor: string;\n color: string;\n }>;\n link?: Partial<{\n color: string;\n }>;\n font?: Pick<\n FontProps,\n 'fontFamily' | 'fallbackFontFamily' | 'webFont'\n > | null;\n listMarker?: Partial<{\n color: string;\n }>;\n}\n\n/**\n * Editor-specific theme. Currently identical to the base — the editor\n * does not yet support per-node color customization, which is why\n * it's kept separate from the renderer theme.\n */\nexport interface EditorThemeOptions extends BaseThemeOptions {}\n\n/** Per-node styling overrides in the renderer theme. */\nexport type NodeThemeEntry = Partial<{\n color: string;\n fontSize: number;\n lineHeight: number;\n fontWeight: number;\n}>;\n\nexport type HeadingLevelTheme = Partial<{\n fontSize: number;\n lineHeight: number;\n fontWeight: number;\n}>;\n\n/**\n * Renderer-specific theme. Extends the base with per-node styling\n * grouped by node type — each key corresponds to a node and contains\n * all relevant style properties for that node.\n */\nexport interface RendererThemeOptions extends BaseThemeOptions {\n paragraph?: NodeThemeEntry;\n heading?: Partial<{\n defaults: Partial<{ color: string }>;\n h1: HeadingLevelTheme;\n h2: HeadingLevelTheme;\n h3: HeadingLevelTheme;\n }>;\n footer?: NodeThemeEntry;\n blockquote?: NodeThemeEntry &\n Partial<{\n borderColor: string;\n fontStyle: FontStyleValue;\n }>;\n horizontalRule?: Partial<{ color: string }>;\n code?: Partial<{ color: string; backgroundColor: string }>;\n\n button?: BaseThemeOptions['button'] &\n Partial<{\n fontSize: number;\n lineHeight: number;\n fontWeight: number;\n }>;\n linkCard?: Partial<{\n titleColor: string;\n descriptionColor: string;\n badgeTextColor: string;\n badgeBackgroundColor: string;\n subTitleColor: string;\n borderColor: string;\n }>;\n}\n\nexport const DEFAULT_LINK_TEXT_COLOR = '#111827';\n\nexport const DEFAULT_RENDERER_THEME: RendererThemeOptions = {\n paragraph: {\n color: '#374151',\n fontSize: 15,\n lineHeight: 1.75,\n fontWeight: FONT_WEIGHTS.NORMAL,\n },\n heading: {\n defaults: { color: '#111827' },\n h1: {\n fontSize: 36,\n lineHeight: 1.1111111,\n fontWeight: FONT_WEIGHTS.SEMIBOLD,\n },\n h2: {\n fontSize: 30,\n lineHeight: 1.3333333,\n fontWeight: FONT_WEIGHTS.SEMIBOLD,\n },\n h3: {\n fontSize: 24,\n lineHeight: 1.6,\n fontWeight: FONT_WEIGHTS.SEMIBOLD,\n },\n },\n footer: {\n color: '#64748B',\n fontSize: 14,\n lineHeight: 1.7142857,\n fontWeight: FONT_WEIGHTS.NORMAL,\n },\n blockquote: {\n color: '#374151',\n borderColor: '#D1D5DB',\n fontSize: 15,\n lineHeight: 1.75,\n fontWeight: FONT_WEIGHTS.MEDIUM,\n fontStyle: FONT_STYLES.ITALIC,\n },\n horizontalRule: { color: '#EAEAEA' },\n code: { color: '#111827', backgroundColor: '#EFEFEF' },\n linkCard: {\n titleColor: '#111827',\n descriptionColor: '#374151',\n badgeTextColor: '#374151',\n badgeBackgroundColor: '#fff085',\n subTitleColor: '#6B7280',\n borderColor: '#E5E7EB',\n },\n\n container: {\n backgroundColor: '#ffffff',\n maxWidth: 600,\n minWidth: 300,\n paddingTop: 8,\n paddingRight: 8,\n paddingBottom: 8,\n paddingLeft: 8,\n\n borderRadius: 0,\n borderWidth: 0,\n borderColor: 'transparent',\n },\n body: {\n backgroundColor: '#ffffff',\n\n paddingTop: 0,\n paddingRight: 0,\n paddingBottom: 0,\n paddingLeft: 0,\n },\n button: {\n backgroundColor: '#000000',\n color: '#ffffff',\n paddingTop: 10,\n paddingRight: 32,\n paddingBottom: 10,\n paddingLeft: 32,\n fontSize: 14,\n lineHeight: 1.4285714,\n fontWeight: FONT_WEIGHTS.SEMIBOLD,\n },\n link: {\n color: DEFAULT_LINK_TEXT_COLOR,\n },\n listMarker: {\n color: '#d1d5dc',\n },\n font: DEFAULT_FONT,\n};\n\nexport const DEFAULT_EDITOR_THEME: EditorThemeOptions = {\n container: {\n backgroundColor: '#ffffff',\n maxWidth: 600,\n minWidth: 300,\n paddingTop: 8,\n paddingRight: 8,\n paddingBottom: 8,\n paddingLeft: 8,\n\n borderRadius: 0,\n borderWidth: 0,\n borderColor: 'transparent',\n },\n body: {\n backgroundColor: '#ffffff',\n\n paddingTop: 0,\n paddingRight: 0,\n paddingBottom: 0,\n paddingLeft: 0,\n },\n button: {\n backgroundColor: '#000000',\n color: '#ffffff',\n paddingTop: 10,\n paddingRight: 32,\n paddingBottom: 10,\n paddingLeft: 32,\n },\n link: {\n color: DEFAULT_LINK_TEXT_COLOR,\n },\n font: DEFAULT_FONT,\n};\n\n/**\n * Resolves the baseline font defaults for a node type from the\n * renderer theme. Falls back to paragraph defaults for unknown types.\n */\nexport function getNodeFontStyleDefaults(\n nodeType: string,\n level?: HeadingLevel\n): NodeFontStyleDefaults {\n const paragraph = DEFAULT_RENDERER_THEME.paragraph!;\n const fallback: NodeFontStyleDefaults = {\n fontSize: paragraph.fontSize!,\n lineHeight: paragraph.lineHeight!,\n fontWeight: paragraph.fontWeight!,\n };\n\n if (nodeType === MAILY_NODE_TYPES.HEADING) {\n const heading = DEFAULT_RENDERER_THEME.heading;\n const key = `h${level ?? 1}` as 'h1' | 'h2' | 'h3';\n const entry = heading?.[key];\n return {\n fontSize: entry?.fontSize ?? fallback.fontSize,\n lineHeight: entry?.lineHeight ?? fallback.lineHeight,\n fontWeight: entry?.fontWeight ?? fallback.fontWeight,\n };\n }\n\n if (nodeType === MAILY_NODE_TYPES.BUTTON) {\n const btn = DEFAULT_RENDERER_THEME.button;\n return {\n fontSize: btn?.fontSize ?? fallback.fontSize,\n lineHeight: btn?.lineHeight ?? fallback.lineHeight,\n fontWeight: btn?.fontWeight ?? fallback.fontWeight,\n };\n }\n\n if (nodeType === MAILY_NODE_TYPES.BLOCKQUOTE) {\n const bq = DEFAULT_RENDERER_THEME.blockquote;\n return {\n fontSize: bq?.fontSize ?? fallback.fontSize,\n lineHeight: bq?.lineHeight ?? fallback.lineHeight,\n fontWeight: bq?.fontWeight ?? fallback.fontWeight,\n fontStyle: bq?.fontStyle ?? undefined,\n };\n }\n\n if (nodeType === MAILY_NODE_TYPES.FOOTER) {\n const ft = DEFAULT_RENDERER_THEME.footer;\n return {\n fontSize: ft?.fontSize ?? fallback.fontSize,\n lineHeight: ft?.lineHeight ?? fallback.lineHeight,\n fontWeight: ft?.fontWeight ?? fallback.fontWeight,\n };\n }\n\n return fallback;\n}\n","import { MAILY_NODE_TYPES } from './node';\nimport type { VariableAttributes } from './node';\n\n/** Default opening delimiter for variable placeholders in text. */\nexport const DEFAULT_VARIABLE_START_TRIGGER = '{{';\n\n/** Default closing delimiter for variable placeholders in text. */\nexport const DEFAULT_VARIABLE_END_TRIGGER = '}}';\n\n/**\n * Regex that matches variable placeholders like {{name}} in a string.\n * Uses a non-greedy match to handle multiple variables in one string.\n */\nexport const VARIABLE_PLACEHOLDER_REGEX = /({{.*?}})/g;\n\n/**\n * Delimiter used between fields inside a serialized variable string.\n * Format: {{id|label|required|hideDefaultValue}}.\n */\nexport const VARIABLE_TEXT_DELIMITER = '|';\n\n/**\n * Serializes a variable node's attributes into a delimited string.\n * Output format: `{{id|label|required|hideDefaultValue}}`.\n * Used to represent structured variable data as a template string\n * in plain text contexts.\n */\nexport function serializeVariableToText(variable: VariableAttributes): string {\n const { id, label, required = true, hideDefaultValue = false } = variable;\n\n return [\n DEFAULT_VARIABLE_START_TRIGGER,\n defaultStringifyValue(id),\n VARIABLE_TEXT_DELIMITER,\n defaultStringifyValue(label),\n VARIABLE_TEXT_DELIMITER,\n required,\n VARIABLE_TEXT_DELIMITER,\n hideDefaultValue,\n DEFAULT_VARIABLE_END_TRIGGER,\n ].join('');\n}\n\nfunction defaultStringifyValue(value: string | null | undefined) {\n return value ?? '';\n}\n\n/**\n * Deserializes a variable string back into VariableAttributes.\n * Expected input format: `{{id|label|required|hideDefaultValue}}`.\n * Missing fields default to undefined; boolean fields are parsed\n * from \"true\"/\"false\" strings.\n */\nexport function deserializeVariableFromText(text: string): VariableAttributes {\n const inner = text\n .slice(\n DEFAULT_VARIABLE_START_TRIGGER.length,\n text.length - DEFAULT_VARIABLE_END_TRIGGER.length\n )\n .trim();\n\n const [id, label, required, hideDefaultValue] = inner.split(\n VARIABLE_TEXT_DELIMITER\n );\n\n return {\n id,\n label: label || null,\n required: parseBoolean(required, true),\n hideDefaultValue: parseBoolean(hideDefaultValue, false),\n };\n}\n\n/**\n * Parses a string containing variable placeholders (e.g. \"Hello {{name}}\")\n * into a Tiptap-compatible document JSON with text and variable nodes.\n * Splits on {{...}} boundaries and creates the appropriate node type\n * for each segment.\n */\nexport function buildDocFromVariableText(content: string) {\n const parts = content.split(VARIABLE_PLACEHOLDER_REGEX);\n\n return {\n type: MAILY_NODE_TYPES.DOCUMENT,\n content: [\n {\n type: MAILY_NODE_TYPES.PARAGRAPH,\n content: parts\n .flatMap((part) => {\n if (\n part.startsWith(DEFAULT_VARIABLE_START_TRIGGER) &&\n part.endsWith(DEFAULT_VARIABLE_END_TRIGGER)\n ) {\n return {\n type: MAILY_NODE_TYPES.VARIABLE,\n attrs: deserializeVariableFromText(part),\n };\n }\n\n if (part) {\n return { type: MAILY_NODE_TYPES.TEXT, text: part };\n }\n\n return [];\n })\n .filter(Boolean),\n },\n ],\n };\n}\n\n/**\n * Checks if a string contains at least one variable placeholder.\n * Returns true for strings like \"Hello {{name}}\", false for plain text.\n * Uses index-based scanning instead of regex for performance.\n */\nexport function hasVariableInText(text: string): boolean {\n const start = text.indexOf(DEFAULT_VARIABLE_START_TRIGGER);\n if (start === -1) {\n return false;\n }\n\n return (\n text.indexOf(\n DEFAULT_VARIABLE_END_TRIGGER,\n start + DEFAULT_VARIABLE_START_TRIGGER.length\n ) !== -1\n );\n}\n\nfunction parseBoolean(\n value?: string,\n defaultValue?: boolean\n): boolean | undefined {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n\n return defaultValue;\n}\n","/**\n * Comparison operators for conditional visibility rules.\n * Used to evaluate a template variable against a value\n * to decide whether a node should be shown or hidden.\n */\nexport const VISIBILITY_OPERATORS = {\n IS_TRUE: 'is_true',\n IS_FALSE: 'is_false',\n EQUALS: 'equals',\n NOT_EQUALS: 'not_equals',\n CONTAINS: 'contains',\n NOT_CONTAINS: 'not_contains',\n STARTS_WITH: 'starts_with',\n ENDS_WITH: 'ends_with',\n GREATER_THAN: 'greater_than',\n LESS_THAN: 'less_than',\n} as const;\n\nexport type AllowedVisibilityOperator =\n (typeof VISIBILITY_OPERATORS)[keyof typeof VISIBILITY_OPERATORS];\n\nexport const VISIBILITY_OPERATOR_OPTIONS: {\n value: AllowedVisibilityOperator;\n label: string;\n}[] = [\n { value: VISIBILITY_OPERATORS.IS_TRUE, label: 'Is true' },\n { value: VISIBILITY_OPERATORS.IS_FALSE, label: 'Is false' },\n { value: VISIBILITY_OPERATORS.EQUALS, label: 'Equals' },\n { value: VISIBILITY_OPERATORS.NOT_EQUALS, label: 'Not equals' },\n { value: VISIBILITY_OPERATORS.CONTAINS, label: 'Contains' },\n { value: VISIBILITY_OPERATORS.NOT_CONTAINS, label: 'Not contains' },\n { value: VISIBILITY_OPERATORS.STARTS_WITH, label: 'Starts with' },\n { value: VISIBILITY_OPERATORS.ENDS_WITH, label: 'Ends with' },\n { value: VISIBILITY_OPERATORS.GREATER_THAN, label: 'Greater than' },\n { value: VISIBILITY_OPERATORS.LESS_THAN, label: 'Less than' },\n];\n\n/**\n * Unary operators that don't require a comparison value.\n * The UI hides the value input field when one of these is selected.\n */\nexport const OPERATORS_WITHOUT_VALUE: AllowedVisibilityOperator[] = [\n VISIBILITY_OPERATORS.IS_TRUE,\n VISIBILITY_OPERATORS.IS_FALSE,\n];\n\nexport const VISIBILITY_ACTIONS = {\n NONE: 'none',\n SHOW: 'show',\n HIDE: 'hide',\n} as const;\n\nexport type AllowedVisibilityAction =\n (typeof VISIBILITY_ACTIONS)[keyof typeof VISIBILITY_ACTIONS];\n\nexport const VISIBILITY_ACTION_OPTIONS: {\n value: AllowedVisibilityAction;\n label: string;\n}[] = [\n { value: VISIBILITY_ACTIONS.NONE, label: 'None' },\n { value: VISIBILITY_ACTIONS.SHOW, label: 'Show if' },\n { value: VISIBILITY_ACTIONS.HIDE, label: 'Hide if' },\n];\n\n/**\n * Defines when a node is conditionally shown or hidden based on\n * a template variable. Evaluated at render time by the renderer.\n */\nexport type VisibilityRule = {\n action: AllowedVisibilityAction;\n variable: string;\n operator: AllowedVisibilityOperator;\n value: string;\n};\n\n/** Mixin for nodes that support conditional visibility. */\nexport type WithVisibilityAttrs = {\n visibilityRule?: VisibilityRule | null;\n};\n"],"mappings":"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"}