dpk-editor 0.1.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":["../src/utils/shellBridge.ts","../src/extensions/PreserveStyles.ts","../src/extensions/editorExtensions.ts","../src/utils/presets.ts","../src/utils/resolveToolbarConfig.ts","../src/utils/escapeHtml.ts","../src/utils/buildButtonHtml.ts","../src/components/EmailButtonDialog.tsx","../src/components/Toolbar.tsx","../src/components/RichTextEditor.tsx","../src/components/EmailEditor.tsx"],"names":["Extension","StarterKit","Image","TextAlign","TextStyle","Color","Highlight","Placeholder","jsxs","jsx","useState","useRef","useEffect","useMemo","Heading2","Heading3","Heading4","Heading5","Heading6","useEditorState","Bold","Italic","Underline","Strikethrough","Code","List","ListOrdered","Quote","AlignLeft","AlignCenter","AlignRight","LinkIcon","ImageIcon","MousePointerClick","Pilcrow","Minus","PanelBottom","FileCode","Fragment","forwardRef","RichTextEditor","useEditor","useImperativeHandle","useCallback","EditorContent"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { EmailHtmlDocument } from \"../types\";\n\n/**\n * Document-shell bridge utilities.\n *\n * A `contentEditable`/ProseMirror surface cannot host `<html>`/`<head>`/\n * `<body>` — it silently strips them and keeps only body-level flow content.\n * So `<EmailEditor>` edits only the **body fragment** and preserves the\n * surrounding document shell verbatim across edits.\n *\n * These are pure string utilities (no DOM required): they operate by locating\n * the `<body ...>` tag with a regex, so they work the same in the browser,\n * during SSR, and in tests.\n */\n\n// Matches the opening <body ...> tag (case-insensitive, any attributes,\n// across newlines). Non-greedy so it stops at the first `>`.\nconst BODY_OPEN_RE = /<body\\b[^>]*>/i;\n// Matches the closing </body> tag.\nconst BODY_CLOSE_RE = /<\\/body\\s*>/i;\n\n/**\n * Split a full email HTML document into `{ prefix, body, suffix }`.\n *\n * - `prefix` = everything up to and including the opening `<body ...>` tag.\n * - `body` = the inner body HTML (the editable fragment).\n * - `suffix` = the closing `</body>` tag plus everything after it.\n *\n * If the input has no `<body>` tag it is treated as a **bare fragment**: the\n * whole input becomes `body`, and `prefix`/`suffix` are empty strings.\n */\nexport function splitEmailHtml(html: string): EmailHtmlDocument {\n const input = html ?? \"\";\n\n const openMatch = input.match(BODY_OPEN_RE);\n if (!openMatch || openMatch.index === undefined) {\n // Bare fragment — no shell to preserve.\n return { prefix: \"\", body: input, suffix: \"\" };\n }\n\n const openStart = openMatch.index;\n const openEnd = openStart + openMatch[0].length;\n const prefix = input.slice(0, openEnd);\n\n const rest = input.slice(openEnd);\n const closeMatch = rest.match(BODY_CLOSE_RE);\n\n if (!closeMatch || closeMatch.index === undefined) {\n // Opening <body> but no closing tag — treat the remainder as body and\n // synthesize an empty suffix so a rejoin still nests inside the shell.\n return { prefix, body: rest, suffix: \"\" };\n }\n\n const body = rest.slice(0, closeMatch.index);\n const suffix = rest.slice(closeMatch.index);\n\n return { prefix, body, suffix };\n}\n\n/**\n * Reassemble a document from a shell and a (possibly edited) body fragment.\n *\n * If the shell has no surrounding markup (a bare fragment was originally\n * supplied) the body is returned as-is, so `onChange` emits in the same shape\n * the consumer provided `value`.\n */\nexport function joinEmailHtml(\n shell: EmailHtmlDocument,\n newBody: string,\n): string {\n const body = newBody ?? \"\";\n if (!shell.prefix && !shell.suffix) {\n return body;\n }\n return `${shell.prefix}${body}${shell.suffix}`;\n}\n\n/**\n * Convenience: extract just the editable body fragment from a full document\n * (or return the input unchanged if it is already a bare fragment).\n */\nexport function extractEmailBody(html: string): string {\n return splitEmailHtml(html).body;\n}\n","import { Extension } from \"@tiptap/react\";\n\n/**\n * A TipTap extension that preserves the inline `style` attribute on every node\n * and mark in the schema.\n *\n * TipTap (ProseMirror) strips any HTML attribute a node/mark does not declare.\n * For article editing that is fine; for **email** HTML it is lossy — buttons\n * lose their padding/background, headings lose their color, and so on. This\n * extension declares a global `style` attribute so arbitrary inline-styled\n * email HTML round-trips.\n *\n * It preserves `style` only on elements that map to a known node or mark\n * (paragraph, heading, list, blockquote, link, image, hr, …). Raw\n * `<table>`/`<td>` layouts are still flattened by ProseMirror's schema — those\n * survive via the document-shell bridge, not here.\n */\nexport const PreserveStyles = Extension.create({\n name: \"preserveStyles\",\n\n addGlobalAttributes() {\n return [\n {\n // ⚠️ MUST be the string shorthand \"*\" (= all nodes and marks).\n // NEVER [\"*\"] — an array means \"a type literally named *\", which\n // matches nothing and silently preserves no styles. That bug passes\n // tsc/eslint/build cleanly, so it is asserted against in the tests.\n types: \"*\",\n attributes: {\n style: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"style\"),\n renderHTML: (attributes) =>\n attributes.style ? { style: attributes.style as string } : {},\n },\n },\n },\n ];\n },\n});\n\nexport default PreserveStyles;\n","import Highlight from \"@tiptap/extension-highlight\";\nimport Image from \"@tiptap/extension-image\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport TextAlign from \"@tiptap/extension-text-align\";\n// @tiptap/extension-text-style v3 re-exports both TextStyle and Color, so we\n// do NOT need @tiptap/extension-color as a separate dependency.\nimport { Color, TextStyle } from \"@tiptap/extension-text-style\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport type { Extensions } from \"@tiptap/react\";\n\nimport { PreserveStyles } from \"./PreserveStyles\";\n\n/**\n * The canonical extension set for the email editor.\n *\n * Notes / watch-outs baked in here:\n * - StarterKit v3 already bundles Link, Underline, lists, blockquote, code and\n * headings — we do NOT add @tiptap/extension-link or -underline separately\n * (that throws duplicate-extension warnings). Link is configured through\n * StarterKit.configure({ link: {...} }).\n * - Image is block-level and base64 is disabled (uploads should resolve to a\n * hosted URL, which is what email clients can render).\n * - PreserveStyles must be present so inline `style` survives the round-trip.\n * - Placeholder is NOT bundled by StarterKit v3, so it is added explicitly to\n * power the empty-state hint (it sets the `is-editor-empty` class and the\n * `data-placeholder` attribute that styles.css renders via `::before`).\n *\n * Exported as a factory so the React component and the headless tests build an\n * identical extension set. Pass the empty-state placeholder text in.\n */\nexport function createEmailExtensions(placeholder?: string): Extensions {\n return [\n StarterKit.configure({\n link: {\n openOnClick: false,\n autolink: true,\n HTMLAttributes: { rel: \"noopener noreferrer\" },\n },\n }),\n Image.configure({ inline: false, allowBase64: false }),\n TextAlign.configure({ types: [\"heading\", \"paragraph\"] }),\n TextStyle,\n Color,\n Highlight.configure({ multicolor: true }),\n Placeholder.configure({\n placeholder: placeholder ?? \"Write your email…\",\n // Emit data-placeholder so styles.css can render it via attr(); also\n // keep the default is-editor-empty class for the :first-child selector.\n showOnlyWhenEditable: true,\n }),\n PreserveStyles,\n ];\n}\n","/**\n * Preset block snippets — small, inline-styled, email-safe HTML fragments\n * inserted at the caret via `editor.chain().focus().insertContent(snippet)`.\n *\n * Every snippet uses inline styles only (no classes) so it survives both the\n * editor schema (via PreserveStyles) and downstream email clients.\n */\nexport const PRESET_PARAGRAPH =\n '<p style=\"margin:0 0 12px;color:#4b5563;line-height:1.6;\">Write your message here.</p>';\n\nexport const PRESET_DIVIDER =\n '<hr style=\"border:none;border-top:1px solid #e5e7eb;margin:20px 0;\" />';\n\nexport const PRESET_FOOTER =\n '<p style=\"margin:20px 0 0;color:#9ca3af;font-size:12px;\">© Your Company</p>';\n\nexport const PRESET_HEADING =\n '<h2 style=\"margin:0 0 12px;color:#111827;font-size:24px;line-height:1.3;\">Heading</h2>';\n\nexport const presets = {\n paragraph: PRESET_PARAGRAPH,\n divider: PRESET_DIVIDER,\n footer: PRESET_FOOTER,\n heading: PRESET_HEADING,\n} as const;\n","import type {\n AlignButtons,\n BlockButtons,\n HeadingButtons,\n InlineButtons,\n ListButtons,\n ResolvedToolbarConfig,\n ToolbarConfig,\n ToolbarGroup,\n} from \"../types\";\n\n/**\n * Resolve a loose `ToolbarConfig` into the flat `ResolvedToolbarConfig` the\n * Toolbar renders from. Rules:\n * - A group key may be a boolean or a per-button object (or omitted).\n * - Omitted / `true` / `{}` → group enabled, every button enabled.\n * - `false` → group disabled (and all its buttons disabled).\n * - An object → group enabled, each listed button overridden; unlisted buttons\n * default to `true`.\n */\nfunction resolveGroup<TButtons extends Record<string, boolean | undefined>>(\n group: ToolbarGroup<TButtons> | undefined,\n defaults: Required<TButtons>,\n): { enabled: boolean; buttons: Required<TButtons> } {\n // Omitted → all on.\n if (group === undefined || group === true) {\n return { enabled: true, buttons: { ...defaults } };\n }\n // Whole group off → disable every button too.\n if (group === false) {\n const off = {} as Required<TButtons>;\n for (const key of Object.keys(defaults) as (keyof TButtons)[]) {\n off[key] = false as Required<TButtons>[keyof TButtons];\n }\n return { enabled: false, buttons: off };\n }\n // Per-button object → group on, merge overrides over the all-on defaults.\n return { enabled: true, buttons: { ...defaults, ...group } };\n}\n\nconst INLINE_DEFAULTS: Required<InlineButtons> = {\n bold: true,\n italic: true,\n underline: true,\n strike: true,\n code: true,\n};\nconst HEADING_DEFAULTS: Required<HeadingButtons> = {\n h2: true,\n h3: true,\n h4: true,\n h5: true,\n h6: true,\n};\nconst LIST_DEFAULTS: Required<ListButtons> = {\n bullet: true,\n ordered: true,\n blockquote: true,\n};\nconst ALIGN_DEFAULTS: Required<AlignButtons> = {\n left: true,\n center: true,\n right: true,\n};\nconst BLOCK_DEFAULTS: Required<BlockButtons> = {\n paragraph: true,\n divider: true,\n footer: true,\n};\n\n/** A single-control group is on unless explicitly set to `false`. */\nfunction resolveToggle(value: boolean | undefined): boolean {\n return value !== false;\n}\n\nexport function resolveToolbarConfig(\n config?: ToolbarConfig,\n): ResolvedToolbarConfig {\n const c = config ?? {};\n return {\n inline: resolveGroup(c.inline, INLINE_DEFAULTS),\n headings: resolveGroup(c.headings, HEADING_DEFAULTS),\n lists: resolveGroup(c.lists, LIST_DEFAULTS),\n align: resolveGroup(c.align, ALIGN_DEFAULTS),\n link: resolveToggle(c.link),\n image: resolveToggle(c.image),\n button: resolveToggle(c.button),\n blocks: resolveGroup(c.blocks, BLOCK_DEFAULTS),\n html: resolveToggle(c.html),\n };\n}\n","/**\n * Escape a string for safe insertion into HTML text or a double-quoted\n * attribute value. Used by `buildButtonHtml` for the button label and href.\n */\nexport function escapeHtml(value: string): string {\n return String(value)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n","import type { EmailButtonConfig } from \"../types\";\nimport { escapeHtml } from \"./escapeHtml\";\n\n/**\n * Build email-safe HTML for a call-to-action button.\n *\n * Two non-obvious decisions, both load-bearing:\n *\n * 1. The anchor is wrapped in a `<p style=\"...;text-align:X\">`, **never** a\n * `<div>`. TipTap has no `div` node and unwraps a `<div>` into a paragraph,\n * losing the `text-align`. A paragraph is a real node whose `text-align`\n * the TextAlign extension preserves — so the alignment round-trips.\n *\n * 2. The button is an `<a style=\"display:inline-block;...\">` (or\n * `display:block` when full-width), not a `<button>`, because email clients\n * need bulletproof, inline-styled markup.\n *\n * Both the label and the href are HTML-escaped.\n */\nexport function buildButtonHtml(config: EmailButtonConfig): string {\n const {\n text,\n href,\n bgColor,\n textColor,\n align,\n radius,\n fullWidth,\n } = config;\n\n const safeText = escapeHtml(text || \"Button\");\n const safeHref = escapeHtml(href || \"#\");\n const safeRadius = Number.isFinite(radius) ? Math.max(0, radius) : 0;\n\n // Full-width buttons span the content area as a block and center their own\n // label; otherwise they hug their content as an inline-block.\n const display = fullWidth ? \"block\" : \"inline-block\";\n const anchorTextAlign = fullWidth ? \"text-align:center;\" : \"\";\n\n const anchorStyle = [\n `display:${display}`,\n \"padding:12px 24px\",\n `background-color:${bgColor}`,\n `color:${textColor}`,\n \"text-decoration:none\",\n \"font-weight:600\",\n \"font-family:Arial,Helvetica,sans-serif\",\n \"font-size:14px\",\n \"line-height:1.4\",\n `border-radius:${safeRadius}px`,\n anchorTextAlign,\n ]\n .filter(Boolean)\n .join(\";\");\n\n const paragraphStyle = `margin:16px 0;text-align:${align}`;\n\n return (\n `<p style=\"${paragraphStyle}\">` +\n `<a href=\"${safeHref}\" target=\"_blank\" rel=\"noopener noreferrer\" ` +\n `style=\"${anchorStyle}\">${safeText}</a>` +\n `</p>`\n );\n}\n","import { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport type { EmailButtonConfig } from \"../types\";\nimport { buildButtonHtml } from \"../utils/buildButtonHtml\";\n\nexport type EmailButtonDialogProps = {\n /** Whether the dialog is open. */\n open: boolean;\n /** Called when the user confirms; receives the email-safe button HTML. */\n onConfirm: (html: string, config: EmailButtonConfig) => void;\n /** Called when the user dismisses the dialog (Esc / click-outside / cancel). */\n onClose: () => void;\n /** Optional initial values for the form. */\n initial?: Partial<EmailButtonConfig>;\n};\n\nconst DEFAULT_CONFIG: EmailButtonConfig = {\n text: \"Click here\",\n href: \"https://\",\n bgColor: \"#2563eb\",\n textColor: \"#ffffff\",\n align: \"left\",\n radius: 6,\n fullWidth: false,\n};\n\nconst BG_SWATCHES = [\n \"#2563eb\",\n \"#16a34a\",\n \"#dc2626\",\n \"#7c3aed\",\n \"#ea580c\",\n \"#0891b2\",\n \"#111827\",\n];\nconst TEXT_SWATCHES = [\"#ffffff\", \"#111827\", \"#f9fafb\", \"#1f2937\"];\n\n// <input type=color> only accepts #rrggbb. Named colors, #rgb shorthand, or\n// anything else must be coerced or it throws/blanks the control.\nconst HEX6_RE = /^#[0-9a-fA-F]{6}$/;\nfunction toColorInputValue(value: string): string {\n return HEX6_RE.test(value) ? value : \"#000000\";\n}\n\ntype ColorFieldProps = {\n label: string;\n value: string;\n swatches: string[];\n onChange: (value: string) => void;\n};\n\nfunction ColorField({ label, value, swatches, onChange }: ColorFieldProps) {\n return (\n <div className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">{label}</span>\n <div className=\"rte-color-row\">\n {swatches.map((swatch) => (\n <button\n key={swatch}\n type=\"button\"\n className={\n \"rte-swatch\" + (value === swatch ? \" rte-swatch--active\" : \"\")\n }\n style={{ backgroundColor: swatch }}\n aria-label={`Use ${swatch}`}\n aria-pressed={value === swatch}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onChange(swatch)}\n />\n ))}\n <input\n type=\"color\"\n className=\"rte-color-input\"\n value={toColorInputValue(value)}\n aria-label={`${label} picker`}\n onChange={(e) => onChange(e.target.value)}\n />\n <input\n type=\"text\"\n className=\"rte-hex-input\"\n value={value}\n aria-label={`${label} hex`}\n spellCheck={false}\n onChange={(e) => onChange(e.target.value)}\n />\n </div>\n </div>\n );\n}\n\n/**\n * Modal dialog for configuring an email CTA button. Supports Esc to close,\n * body-scroll lock while open, click-outside to dismiss, and a live preview of\n * the rendered button.\n */\nexport function EmailButtonDialog({\n open,\n onConfirm,\n onClose,\n initial,\n}: EmailButtonDialogProps) {\n const [config, setConfig] = useState<EmailButtonConfig>({\n ...DEFAULT_CONFIG,\n ...initial,\n });\n const overlayRef = useRef<HTMLDivElement>(null);\n\n // Reset the form to its initial state each time the dialog opens.\n useEffect(() => {\n if (open) {\n setConfig({ ...DEFAULT_CONFIG, ...initial });\n }\n }, [open, initial]);\n\n // Esc to close + body-scroll lock while open.\n useEffect(() => {\n if (!open) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.stopPropagation();\n onClose();\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = previousOverflow;\n };\n }, [open, onClose]);\n\n const previewHtml = useMemo(() => buildButtonHtml(config), [config]);\n\n if (!open) return null;\n\n const update = <K extends keyof EmailButtonConfig>(\n key: K,\n value: EmailButtonConfig[K],\n ) => setConfig((prev) => ({ ...prev, [key]: value }));\n\n const handleSubmit = () => {\n onConfirm(buildButtonHtml(config), config);\n };\n\n return (\n <div\n ref={overlayRef}\n className=\"rte-dialog-overlay\"\n role=\"presentation\"\n onMouseDown={(e) => {\n // Click-outside (on the overlay itself, not the panel) dismisses.\n if (e.target === overlayRef.current) onClose();\n }}\n >\n <div\n className=\"rte-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Insert button\"\n >\n <div className=\"rte-dialog-header\">\n <h2 className=\"rte-dialog-title\">Insert button</h2>\n <button\n type=\"button\"\n className=\"rte-dialog-close\"\n aria-label=\"Close\"\n onClick={onClose}\n >\n ×\n </button>\n </div>\n\n <div className=\"rte-dialog-body\">\n <label className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">Text</span>\n <input\n type=\"text\"\n className=\"rte-text-input\"\n value={config.text}\n onChange={(e) => update(\"text\", e.target.value)}\n />\n </label>\n\n <label className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">Link URL</span>\n <input\n type=\"text\"\n className=\"rte-text-input\"\n value={config.href}\n onChange={(e) => update(\"href\", e.target.value)}\n />\n </label>\n\n <ColorField\n label=\"Background color\"\n value={config.bgColor}\n swatches={BG_SWATCHES}\n onChange={(v) => update(\"bgColor\", v)}\n />\n\n <ColorField\n label=\"Text color\"\n value={config.textColor}\n swatches={TEXT_SWATCHES}\n onChange={(v) => update(\"textColor\", v)}\n />\n\n <div className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">Alignment</span>\n <div className=\"rte-align-row\">\n {([\"left\", \"center\", \"right\"] as const).map((a) => (\n <button\n key={a}\n type=\"button\"\n className={\n \"rte-align-btn\" +\n (config.align === a ? \" rte-align-btn--active\" : \"\")\n }\n aria-pressed={config.align === a}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => update(\"align\", a)}\n >\n {a}\n </button>\n ))}\n </div>\n </div>\n\n <label className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">\n Corner radius: {config.radius}px\n </span>\n <input\n type=\"range\"\n min={0}\n max={32}\n value={config.radius}\n onChange={(e) => update(\"radius\", Number(e.target.value))}\n />\n </label>\n\n <label className=\"rte-checkbox-field\">\n <input\n type=\"checkbox\"\n checked={config.fullWidth}\n onChange={(e) => update(\"fullWidth\", e.target.checked)}\n />\n <span>Full width</span>\n </label>\n\n <div className=\"rte-dialog-field\">\n <span className=\"rte-dialog-label\">Preview</span>\n <div\n className=\"rte-button-preview\"\n // Preview only — content is built from the same escaped builder.\n dangerouslySetInnerHTML={{ __html: previewHtml }}\n />\n </div>\n </div>\n\n <div className=\"rte-dialog-footer\">\n <button\n type=\"button\"\n className=\"rte-btn rte-btn--ghost\"\n onClick={onClose}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"rte-btn rte-btn--primary\"\n onClick={handleSubmit}\n >\n Insert\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nexport default EmailButtonDialog;\n","import type { Editor } from \"@tiptap/react\";\nimport { useEditorState } from \"@tiptap/react\";\nimport {\n AlignCenter,\n AlignLeft,\n AlignRight,\n Bold,\n Code,\n FileCode,\n Heading2,\n Heading3,\n Heading4,\n Heading5,\n Heading6,\n Image as ImageIcon,\n Italic,\n Link as LinkIcon,\n List,\n ListOrdered,\n Minus,\n MousePointerClick,\n PanelBottom,\n Pilcrow,\n Quote,\n Strikethrough,\n Underline,\n} from \"lucide-react\";\nimport { Fragment, type ReactNode } from \"react\";\n\nimport type { ResolvedToolbarConfig } from \"../types\";\n\ntype HeadingLevel = 2 | 3 | 4 | 5 | 6;\n\nexport type ToolbarProps = {\n editor: Editor;\n config: ResolvedToolbarConfig;\n /** Toggle the raw HTML source view. */\n htmlMode: boolean;\n onToggleHtml: () => void;\n /** Caller-provided handlers for the controls that need extra UI. */\n onLink: () => void;\n onImage: () => void;\n onButton: () => void;\n onInsertParagraph: () => void;\n onInsertDivider: () => void;\n onInsertFooter: () => void;\n};\n\ntype TbButtonProps = {\n onClick: () => void;\n active?: boolean;\n label: string;\n disabled?: boolean;\n children: ReactNode;\n};\n\nfunction TbButton({ onClick, active, label, disabled, children }: TbButtonProps) {\n return (\n <button\n type=\"button\"\n className={\"rte-tb-btn\" + (active ? \" rte-tb-btn--active\" : \"\")}\n // Prevent the button from stealing the editor's selection on press.\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n aria-pressed={!!active}\n aria-label={label}\n title={label}\n disabled={disabled}\n >\n {children}\n </button>\n );\n}\n\nfunction Divider() {\n return <span className=\"rte-tb-divider\" aria-hidden=\"true\" />;\n}\n\nconst HEADING_ICONS: Record<HeadingLevel, ReactNode> = {\n 2: <Heading2 size={16} />,\n 3: <Heading3 size={16} />,\n 4: <Heading4 size={16} />,\n 5: <Heading5 size={16} />,\n 6: <Heading6 size={16} />,\n};\n\nexport function Toolbar({\n editor,\n config,\n htmlMode,\n onToggleHtml,\n onLink,\n onImage,\n onButton,\n onInsertParagraph,\n onInsertDivider,\n onInsertFooter,\n}: ToolbarProps) {\n // useEditorState re-renders only when the selected slice changes, keeping\n // active-state computation cheap even on large documents.\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n bold: e.isActive(\"bold\"),\n italic: e.isActive(\"italic\"),\n underline: e.isActive(\"underline\"),\n strike: e.isActive(\"strike\"),\n code: e.isActive(\"code\"),\n h2: e.isActive(\"heading\", { level: 2 }),\n h3: e.isActive(\"heading\", { level: 3 }),\n h4: e.isActive(\"heading\", { level: 4 }),\n h5: e.isActive(\"heading\", { level: 5 }),\n h6: e.isActive(\"heading\", { level: 6 }),\n bulletList: e.isActive(\"bulletList\"),\n orderedList: e.isActive(\"orderedList\"),\n blockquote: e.isActive(\"blockquote\"),\n alignLeft: e.isActive({ textAlign: \"left\" }),\n alignCenter: e.isActive({ textAlign: \"center\" }),\n alignRight: e.isActive({ textAlign: \"right\" }),\n link: e.isActive(\"link\"),\n image: e.isActive(\"image\"),\n }),\n });\n\n const headingActive: Record<HeadingLevel, boolean> = {\n 2: state.h2,\n 3: state.h3,\n 4: state.h4,\n 5: state.h5,\n 6: state.h6,\n };\n\n // In HTML source mode only the source toggle is interactive.\n const disabled = htmlMode;\n\n // Build each group as an array of rendered buttons, gated by both the group\n // `enabled` flag and the per-button flag. A group with zero visible buttons\n // contributes nothing (and gets no divider).\n const inline = config.inline.enabled\n ? [\n config.inline.buttons.bold && (\n <TbButton\n key=\"bold\"\n label=\"Bold\"\n active={state.bold}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} />\n </TbButton>\n ),\n config.inline.buttons.italic && (\n <TbButton\n key=\"italic\"\n label=\"Italic\"\n active={state.italic}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} />\n </TbButton>\n ),\n config.inline.buttons.underline && (\n <TbButton\n key=\"underline\"\n label=\"Underline\"\n active={state.underline}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <Underline size={16} />\n </TbButton>\n ),\n config.inline.buttons.strike && (\n <TbButton\n key=\"strike\"\n label=\"Strikethrough\"\n active={state.strike}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} />\n </TbButton>\n ),\n config.inline.buttons.code && (\n <TbButton\n key=\"code\"\n label=\"Inline code\"\n active={state.code}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} />\n </TbButton>\n ),\n ]\n : [];\n\n const headings = config.headings.enabled\n ? ([2, 3, 4, 5, 6] as const).map((level) =>\n config.headings.buttons[`h${level}` as keyof typeof config.headings.buttons] ? (\n <TbButton\n key={`h${level}`}\n label={`Heading ${level}`}\n active={headingActive[level]}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleHeading({ level }).run()}\n >\n {HEADING_ICONS[level]}\n </TbButton>\n ) : (\n false\n ),\n )\n : [];\n\n const lists = config.lists.enabled\n ? [\n config.lists.buttons.bullet && (\n <TbButton\n key=\"bullet\"\n label=\"Bullet list\"\n active={state.bulletList}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} />\n </TbButton>\n ),\n config.lists.buttons.ordered && (\n <TbButton\n key=\"ordered\"\n label=\"Ordered list\"\n active={state.orderedList}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} />\n </TbButton>\n ),\n config.lists.buttons.blockquote && (\n <TbButton\n key=\"quote\"\n label=\"Quote\"\n active={state.blockquote}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} />\n </TbButton>\n ),\n ]\n : [];\n\n const align = config.align.enabled\n ? [\n config.align.buttons.left && (\n <TbButton\n key=\"left\"\n label=\"Align left\"\n active={state.alignLeft}\n disabled={disabled}\n onClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n >\n <AlignLeft size={16} />\n </TbButton>\n ),\n config.align.buttons.center && (\n <TbButton\n key=\"center\"\n label=\"Align center\"\n active={state.alignCenter}\n disabled={disabled}\n onClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n >\n <AlignCenter size={16} />\n </TbButton>\n ),\n config.align.buttons.right && (\n <TbButton\n key=\"right\"\n label=\"Align right\"\n active={state.alignRight}\n disabled={disabled}\n onClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n >\n <AlignRight size={16} />\n </TbButton>\n ),\n ]\n : [];\n\n const inserts = [\n config.link && (\n <TbButton\n key=\"link\"\n label=\"Link\"\n active={state.link}\n disabled={disabled}\n onClick={onLink}\n >\n <LinkIcon size={16} />\n </TbButton>\n ),\n config.image && (\n <TbButton\n key=\"image\"\n label=\"Image\"\n active={state.image}\n disabled={disabled}\n onClick={onImage}\n >\n <ImageIcon size={16} />\n </TbButton>\n ),\n config.button && (\n <TbButton\n key=\"button\"\n label=\"Button\"\n disabled={disabled}\n onClick={onButton}\n >\n <MousePointerClick size={16} />\n </TbButton>\n ),\n ];\n\n const blocks = config.blocks.enabled\n ? [\n config.blocks.buttons.paragraph && (\n <TbButton\n key=\"paragraph\"\n label=\"Insert paragraph\"\n disabled={disabled}\n onClick={onInsertParagraph}\n >\n <Pilcrow size={16} />\n </TbButton>\n ),\n config.blocks.buttons.divider && (\n <TbButton\n key=\"divider\"\n label=\"Insert divider\"\n disabled={disabled}\n onClick={onInsertDivider}\n >\n <Minus size={16} />\n </TbButton>\n ),\n config.blocks.buttons.footer && (\n <TbButton\n key=\"footer\"\n label=\"Insert footer\"\n disabled={disabled}\n onClick={onInsertFooter}\n >\n <PanelBottom size={16} />\n </TbButton>\n ),\n ]\n : [];\n\n const html = config.html\n ? [\n <TbButton\n key=\"html\"\n label=\"HTML source\"\n active={htmlMode}\n onClick={onToggleHtml}\n >\n <FileCode size={16} />\n </TbButton>,\n ]\n : [];\n\n // Keep only groups that have at least one visible button, then render them\n // with a divider between each — so hidden groups leave no dangling dividers.\n const groups = [inline, headings, lists, align, inserts, blocks, html]\n .map((g) => g.filter(Boolean))\n .filter((g) => g.length > 0);\n\n return (\n <div className=\"rte-toolbar\" role=\"toolbar\" aria-label=\"Formatting\">\n {groups.map((buttons, i) => (\n <Fragment key={i}>\n {i > 0 && <Divider />}\n <div className=\"rte-tb-group\">{buttons}</div>\n </Fragment>\n ))}\n </div>\n );\n}\n\nexport default Toolbar;\n","import { EditorContent, useEditor } from \"@tiptap/react\";\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { createEmailExtensions } from \"../extensions/editorExtensions\";\nimport type { ToolbarConfig } from \"../types\";\nimport { PRESET_DIVIDER, PRESET_FOOTER, PRESET_PARAGRAPH } from \"../utils/presets\";\nimport { resolveToolbarConfig } from \"../utils/resolveToolbarConfig\";\nimport { EmailButtonDialog } from \"./EmailButtonDialog\";\nimport { Toolbar } from \"./Toolbar\";\n\nexport type RichTextEditorHandle = {\n /** Insert raw HTML or plain text at the current caret position. */\n insertAtCaret: (htmlOrText: string) => void;\n};\n\nexport type RichTextEditorProps = {\n /** Body-level HTML fragment (no `<html>`/`<body>`). */\n value: string;\n /** Fires with the updated body-level HTML fragment. */\n onChange: (value: string) => void;\n /** Empty-state placeholder text. */\n placeholder?: string;\n /** Resolve an uploaded file to a hosted image URL. */\n onUploadImage?: (file: File) => Promise<string>;\n /** Extra class on the editor wrapper. */\n className?: string;\n /** Inline style applied to the editable surface (e.g. minHeight/height). */\n editorStyle?: React.CSSProperties;\n /** Which toolbar controls to render (defaults to everything on). */\n toolbar?: ToolbarConfig;\n};\n\n/**\n * The generic body-HTML rich-text editor: a toolbar plus an editable surface\n * operating on a plain HTML fragment. `<EmailEditor>` wraps this to add the\n * document-shell bridge and placeholder chips.\n */\nexport const RichTextEditor = forwardRef<\n RichTextEditorHandle,\n RichTextEditorProps\n>(function RichTextEditor(\n {\n value,\n onChange,\n placeholder = \"Write your email…\",\n onUploadImage,\n className,\n editorStyle,\n toolbar,\n },\n ref,\n) {\n const toolbarConfig = useMemo(\n () => resolveToolbarConfig(toolbar),\n [toolbar],\n );\n\n const extensions = useMemo(\n () => createEmailExtensions(placeholder),\n [placeholder],\n );\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [htmlMode, setHtmlMode] = useState(false);\n const [buttonDialogOpen, setButtonDialogOpen] = useState(false);\n\n const editor = useEditor({\n extensions,\n content: value,\n // REQUIRED for SSR/Next.js — rendering immediately causes a hydration\n // mismatch because the server has no DOM.\n immediatelyRender: false,\n editorProps: {\n attributes: {\n class: \"rte-content\",\n },\n },\n onUpdate: ({ editor: e }) => {\n onChange(e.getHTML());\n },\n });\n\n // Sync external `value` changes into the editor without fighting the user's\n // typing: only when it actually differs from the editor's current HTML, and\n // with emitUpdate:false so it does not re-fire onChange.\n if (editor && !htmlMode && value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n\n useImperativeHandle(\n ref,\n () => ({\n insertAtCaret: (htmlOrText: string) => {\n editor?.chain().focus().insertContent(htmlOrText).run();\n },\n }),\n [editor],\n );\n\n const insertSnippet = useCallback(\n (snippet: string) => {\n editor?.chain().focus().insertContent(snippet).run();\n },\n [editor],\n );\n\n const handleLink = useCallback(() => {\n if (!editor) return;\n const previous = editor.getAttributes(\"link\").href as string | undefined;\n const url = window.prompt(\"Link URL\", previous ?? \"https://\");\n if (url === null) return; // cancelled\n if (url.trim() === \"\") {\n editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n return;\n }\n editor\n .chain()\n .focus()\n .extendMarkRange(\"link\")\n .setLink({ href: url.trim() })\n .run();\n }, [editor]);\n\n const handleImageClick = useCallback(() => {\n if (!editor) return;\n if (onUploadImage) {\n fileInputRef.current?.click();\n } else {\n const url = window.prompt(\"Image URL\", \"https://\");\n if (url && url.trim()) {\n editor.chain().focus().setImage({ src: url.trim() }).run();\n }\n }\n }, [editor, onUploadImage]);\n\n const handleFileSelected = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n // Reset so selecting the same file again re-triggers change.\n e.target.value = \"\";\n if (!file || !onUploadImage || !editor) return;\n try {\n const src = await onUploadImage(file);\n if (src) editor.chain().focus().setImage({ src }).run();\n } catch (err) {\n // Surface upload failures without crashing the editor.\n console.error(\"dpk-editor: image upload failed\", err);\n }\n },\n [editor, onUploadImage],\n );\n\n const handleButtonConfirm = useCallback(\n (html: string) => {\n setButtonDialogOpen(false);\n editor?.chain().focus().insertContent(html).run();\n },\n [editor],\n );\n\n if (!editor) return null;\n\n return (\n <div className={\"rte-root\" + (className ? ` ${className}` : \"\")}>\n <Toolbar\n editor={editor}\n config={toolbarConfig}\n htmlMode={htmlMode}\n onToggleHtml={() => setHtmlMode((m) => !m)}\n onLink={handleLink}\n onImage={handleImageClick}\n onButton={() => setButtonDialogOpen(true)}\n onInsertParagraph={() => insertSnippet(PRESET_PARAGRAPH)}\n onInsertDivider={() => insertSnippet(PRESET_DIVIDER)}\n onInsertFooter={() => insertSnippet(PRESET_FOOTER)}\n />\n\n {htmlMode ? (\n <textarea\n className=\"rte-source\"\n style={editorStyle}\n value={value}\n spellCheck={false}\n onChange={(e) => onChange(e.target.value)}\n aria-label=\"HTML source\"\n />\n ) : (\n <EditorContent\n editor={editor}\n className=\"rte-editor\"\n style={editorStyle}\n />\n )}\n\n {onUploadImage && (\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={handleFileSelected}\n />\n )}\n\n <EmailButtonDialog\n open={buttonDialogOpen}\n onConfirm={handleButtonConfirm}\n onClose={() => setButtonDialogOpen(false)}\n />\n </div>\n );\n});\n\nexport default RichTextEditor;\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nimport type {\n EmailHtmlDocument,\n EmailPlaceholder,\n ToolbarConfig,\n} from \"../types\";\nimport { joinEmailHtml, splitEmailHtml } from \"../utils/shellBridge\";\nimport {\n RichTextEditor,\n type RichTextEditorHandle,\n} from \"./RichTextEditor\";\n\nexport type EmailEditorProps = {\n /** Full HTML document OR a bare body fragment — both are supported. */\n value: string;\n /** Fires with HTML in the same shape `value` was provided (shell re-applied). */\n onChange: (value: string) => void;\n /** Optional merge tokens; render a chip row that inserts at the caret. */\n placeholders?: EmailPlaceholder[];\n /** Resolve an uploaded file to a hosted image URL (else a URL prompt is used). */\n onUploadImage?: (file: File) => Promise<string>;\n /** Minimum height (px) of the editable surface. */\n minHeight?: number;\n /** Empty-state placeholder text for the editable surface. */\n placeholder?: string;\n /** Which toolbar controls to render (defaults to everything on). */\n toolbar?: ToolbarConfig;\n /** Extra class on the editor wrapper. */\n className?: string;\n};\n\n/**\n * The batteries-included email editor. Owns the document-shell bridge so the\n * surrounding `<!doctype>`/`<html>`/`<body style>` (and any `<table>` layout\n * inside the body) is preserved verbatim across edits, while only the body\n * fragment is handed to the underlying `<RichTextEditor>`.\n */\nexport function EmailEditor({\n value,\n onChange,\n placeholders,\n onUploadImage,\n minHeight = 288,\n placeholder,\n toolbar,\n className,\n}: EmailEditorProps) {\n // Recompute the shell from `value` on each render.\n const shell = useMemo<EmailHtmlDocument>(() => splitEmailHtml(value), [value]);\n\n // Keep the latest shell in a ref so the onChange closure always rejoins with\n // the current prefix/suffix. Assign the ref in an EFFECT (never during\n // render) — strict react-hooks lint flags a render-time ref write.\n const shellRef = useRef<EmailHtmlDocument>(shell);\n useEffect(() => {\n shellRef.current = shell;\n }, [shell]);\n\n const editorRef = useRef<RichTextEditorHandle>(null);\n\n const handleBodyChange = useCallback(\n (newBody: string) => {\n onChange(joinEmailHtml(shellRef.current, newBody));\n },\n [onChange],\n );\n\n const editorStyle = useMemo<React.CSSProperties>(\n () => ({ minHeight }),\n [minHeight],\n );\n\n const insertToken = useCallback((token: string) => {\n editorRef.current?.insertAtCaret(token);\n }, []);\n\n return (\n <div className={\"rte-email\" + (className ? ` ${className}` : \"\")}>\n {placeholders && placeholders.length > 0 && (\n <div className=\"rte-chips\" role=\"group\" aria-label=\"Merge tokens\">\n {placeholders.map((p) => (\n <button\n key={p.token}\n type=\"button\"\n className=\"rte-chip\"\n title={`Insert ${p.token}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => insertToken(p.token)}\n >\n {p.label}\n </button>\n ))}\n </div>\n )}\n\n <RichTextEditor\n ref={editorRef}\n value={shell.body}\n onChange={handleBodyChange}\n onUploadImage={onUploadImage}\n placeholder={placeholder}\n editorStyle={editorStyle}\n toolbar={toolbar}\n />\n </div>\n );\n}\n\nexport default EmailEditor;\n"]}
@@ -0,0 +1,334 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { Extension, Extensions } from '@tiptap/react';
4
+
5
+ /**
6
+ * Public types for dpk-editor.
7
+ *
8
+ * Kept in a dedicated module so both the components and the building-block
9
+ * utilities can import them without introducing circular dependencies, and so
10
+ * consumers get a single, stable surface to import from.
11
+ */
12
+ /** Configuration for a single email-safe call-to-action button. */
13
+ type EmailButtonConfig = {
14
+ /** Visible label of the button. */
15
+ text: string;
16
+ /** Destination URL. */
17
+ href: string;
18
+ /** Background color (any CSS color; used verbatim in the inline style). */
19
+ bgColor: string;
20
+ /** Text color (any CSS color; used verbatim in the inline style). */
21
+ textColor: string;
22
+ /** Horizontal alignment of the button within its block. */
23
+ align: "left" | "center" | "right";
24
+ /** Corner radius in pixels (typically 0–32). */
25
+ radius: number;
26
+ /** When true the anchor spans the full content width (block display). */
27
+ fullWidth: boolean;
28
+ };
29
+ /**
30
+ * A merge token (a.k.a. personalization placeholder) such as
31
+ * `{{FirstName}}`. Rendered as a chip in `<EmailEditor>` that inserts the
32
+ * raw token text at the caret when clicked.
33
+ */
34
+ type EmailPlaceholder = {
35
+ /** The literal token inserted into the document, e.g. `{{FirstName}}`. */
36
+ token: string;
37
+ /** Human-readable label shown on the chip, e.g. "First name". */
38
+ label: string;
39
+ };
40
+ /** Per-button visibility within the inline-formatting group. */
41
+ type InlineButtons = {
42
+ bold?: boolean;
43
+ italic?: boolean;
44
+ underline?: boolean;
45
+ strike?: boolean;
46
+ code?: boolean;
47
+ };
48
+ /** Per-button visibility within the headings group (H2–H6). */
49
+ type HeadingButtons = {
50
+ h2?: boolean;
51
+ h3?: boolean;
52
+ h4?: boolean;
53
+ h5?: boolean;
54
+ h6?: boolean;
55
+ };
56
+ /** Per-button visibility within the lists group. */
57
+ type ListButtons = {
58
+ bullet?: boolean;
59
+ ordered?: boolean;
60
+ blockquote?: boolean;
61
+ };
62
+ /** Per-button visibility within the text-align group. */
63
+ type AlignButtons = {
64
+ left?: boolean;
65
+ center?: boolean;
66
+ right?: boolean;
67
+ };
68
+ /** Per-button visibility within the preset-blocks group. */
69
+ type BlockButtons = {
70
+ paragraph?: boolean;
71
+ divider?: boolean;
72
+ footer?: boolean;
73
+ };
74
+ /**
75
+ * A group of buttons can be controlled two ways:
76
+ * - a `boolean` — show (`true`/omitted) or hide (`false`) the whole group;
77
+ * - an object of per-button booleans — show the group but override individual
78
+ * buttons (each defaults to `true` when the group is shown).
79
+ *
80
+ * `true` and `{}` are equivalent (group on, all buttons on). `false` hides the
81
+ * whole group regardless of any per-button values.
82
+ */
83
+ type ToolbarGroup<TButtons> = boolean | TButtons;
84
+ /**
85
+ * Controls which toolbar controls render. Every key defaults to `true` (all
86
+ * controls visible).
87
+ *
88
+ * - Single-control groups (`link`, `image`, `button`, `html`) are plain
89
+ * booleans: set to `false` to hide.
90
+ * - Multi-button groups (`inline`, `headings`, `lists`, `align`, `blocks`)
91
+ * accept either a boolean (whole group) OR an object for per-button control,
92
+ * e.g. `inline: { underline: false }` keeps Bold/Italic/Strike/Code but hides
93
+ * Underline, while `inline: false` hides the entire inline group.
94
+ */
95
+ type ToolbarConfig = {
96
+ /** Bold / Italic / Underline / Strikethrough / Inline code group. */
97
+ inline?: ToolbarGroup<InlineButtons>;
98
+ /** Heading level buttons (H2–H6). */
99
+ headings?: ToolbarGroup<HeadingButtons>;
100
+ /** Bullet list / Ordered list / Blockquote group. */
101
+ lists?: ToolbarGroup<ListButtons>;
102
+ /** Text-align left/center/right group. */
103
+ align?: ToolbarGroup<AlignButtons>;
104
+ /** Link control. */
105
+ link?: boolean;
106
+ /** Image control. */
107
+ image?: boolean;
108
+ /** Email CTA-button control (opens the button dialog). */
109
+ button?: boolean;
110
+ /** Preset block snippets group (Paragraph / Divider / Footer). */
111
+ blocks?: ToolbarGroup<BlockButtons>;
112
+ /** Raw HTML source toggle. */
113
+ html?: boolean;
114
+ };
115
+ /**
116
+ * The fully-resolved toolbar config used internally: every group is expanded to
117
+ * `{ enabled, buttons: {...all booleans} }`, so the Toolbar renders from a flat,
118
+ * non-optional shape. Produced by `resolveToolbarConfig`.
119
+ */
120
+ type ResolvedToolbarConfig = {
121
+ inline: {
122
+ enabled: boolean;
123
+ buttons: Required<InlineButtons>;
124
+ };
125
+ headings: {
126
+ enabled: boolean;
127
+ buttons: Required<HeadingButtons>;
128
+ };
129
+ lists: {
130
+ enabled: boolean;
131
+ buttons: Required<ListButtons>;
132
+ };
133
+ align: {
134
+ enabled: boolean;
135
+ buttons: Required<AlignButtons>;
136
+ };
137
+ link: boolean;
138
+ image: boolean;
139
+ button: boolean;
140
+ blocks: {
141
+ enabled: boolean;
142
+ buttons: Required<BlockButtons>;
143
+ };
144
+ html: boolean;
145
+ };
146
+ /**
147
+ * The split representation of an email document. `prefix` is everything up to
148
+ * and including the opening `<body ...>` tag, `body` is the editable inner
149
+ * HTML, and `suffix` is `</body>` plus anything after it. For a bare fragment
150
+ * (no `<body>`), `prefix` and `suffix` are empty strings.
151
+ */
152
+ type EmailHtmlDocument = {
153
+ prefix: string;
154
+ body: string;
155
+ suffix: string;
156
+ };
157
+
158
+ type EmailEditorProps = {
159
+ /** Full HTML document OR a bare body fragment — both are supported. */
160
+ value: string;
161
+ /** Fires with HTML in the same shape `value` was provided (shell re-applied). */
162
+ onChange: (value: string) => void;
163
+ /** Optional merge tokens; render a chip row that inserts at the caret. */
164
+ placeholders?: EmailPlaceholder[];
165
+ /** Resolve an uploaded file to a hosted image URL (else a URL prompt is used). */
166
+ onUploadImage?: (file: File) => Promise<string>;
167
+ /** Minimum height (px) of the editable surface. */
168
+ minHeight?: number;
169
+ /** Empty-state placeholder text for the editable surface. */
170
+ placeholder?: string;
171
+ /** Which toolbar controls to render (defaults to everything on). */
172
+ toolbar?: ToolbarConfig;
173
+ /** Extra class on the editor wrapper. */
174
+ className?: string;
175
+ };
176
+ /**
177
+ * The batteries-included email editor. Owns the document-shell bridge so the
178
+ * surrounding `<!doctype>`/`<html>`/`<body style>` (and any `<table>` layout
179
+ * inside the body) is preserved verbatim across edits, while only the body
180
+ * fragment is handed to the underlying `<RichTextEditor>`.
181
+ */
182
+ declare function EmailEditor({ value, onChange, placeholders, onUploadImage, minHeight, placeholder, toolbar, className, }: EmailEditorProps): react_jsx_runtime.JSX.Element;
183
+
184
+ type RichTextEditorHandle = {
185
+ /** Insert raw HTML or plain text at the current caret position. */
186
+ insertAtCaret: (htmlOrText: string) => void;
187
+ };
188
+ type RichTextEditorProps = {
189
+ /** Body-level HTML fragment (no `<html>`/`<body>`). */
190
+ value: string;
191
+ /** Fires with the updated body-level HTML fragment. */
192
+ onChange: (value: string) => void;
193
+ /** Empty-state placeholder text. */
194
+ placeholder?: string;
195
+ /** Resolve an uploaded file to a hosted image URL. */
196
+ onUploadImage?: (file: File) => Promise<string>;
197
+ /** Extra class on the editor wrapper. */
198
+ className?: string;
199
+ /** Inline style applied to the editable surface (e.g. minHeight/height). */
200
+ editorStyle?: React.CSSProperties;
201
+ /** Which toolbar controls to render (defaults to everything on). */
202
+ toolbar?: ToolbarConfig;
203
+ };
204
+ /**
205
+ * The generic body-HTML rich-text editor: a toolbar plus an editable surface
206
+ * operating on a plain HTML fragment. `<EmailEditor>` wraps this to add the
207
+ * document-shell bridge and placeholder chips.
208
+ */
209
+ declare const RichTextEditor: react.ForwardRefExoticComponent<RichTextEditorProps & react.RefAttributes<RichTextEditorHandle>>;
210
+
211
+ type EmailButtonDialogProps = {
212
+ /** Whether the dialog is open. */
213
+ open: boolean;
214
+ /** Called when the user confirms; receives the email-safe button HTML. */
215
+ onConfirm: (html: string, config: EmailButtonConfig) => void;
216
+ /** Called when the user dismisses the dialog (Esc / click-outside / cancel). */
217
+ onClose: () => void;
218
+ /** Optional initial values for the form. */
219
+ initial?: Partial<EmailButtonConfig>;
220
+ };
221
+ /**
222
+ * Modal dialog for configuring an email CTA button. Supports Esc to close,
223
+ * body-scroll lock while open, click-outside to dismiss, and a live preview of
224
+ * the rendered button.
225
+ */
226
+ declare function EmailButtonDialog({ open, onConfirm, onClose, initial, }: EmailButtonDialogProps): react_jsx_runtime.JSX.Element | null;
227
+
228
+ /**
229
+ * A TipTap extension that preserves the inline `style` attribute on every node
230
+ * and mark in the schema.
231
+ *
232
+ * TipTap (ProseMirror) strips any HTML attribute a node/mark does not declare.
233
+ * For article editing that is fine; for **email** HTML it is lossy — buttons
234
+ * lose their padding/background, headings lose their color, and so on. This
235
+ * extension declares a global `style` attribute so arbitrary inline-styled
236
+ * email HTML round-trips.
237
+ *
238
+ * It preserves `style` only on elements that map to a known node or mark
239
+ * (paragraph, heading, list, blockquote, link, image, hr, …). Raw
240
+ * `<table>`/`<td>` layouts are still flattened by ProseMirror's schema — those
241
+ * survive via the document-shell bridge, not here.
242
+ */
243
+ declare const PreserveStyles: Extension<any, any>;
244
+
245
+ /**
246
+ * The canonical extension set for the email editor.
247
+ *
248
+ * Notes / watch-outs baked in here:
249
+ * - StarterKit v3 already bundles Link, Underline, lists, blockquote, code and
250
+ * headings — we do NOT add @tiptap/extension-link or -underline separately
251
+ * (that throws duplicate-extension warnings). Link is configured through
252
+ * StarterKit.configure({ link: {...} }).
253
+ * - Image is block-level and base64 is disabled (uploads should resolve to a
254
+ * hosted URL, which is what email clients can render).
255
+ * - PreserveStyles must be present so inline `style` survives the round-trip.
256
+ * - Placeholder is NOT bundled by StarterKit v3, so it is added explicitly to
257
+ * power the empty-state hint (it sets the `is-editor-empty` class and the
258
+ * `data-placeholder` attribute that styles.css renders via `::before`).
259
+ *
260
+ * Exported as a factory so the React component and the headless tests build an
261
+ * identical extension set. Pass the empty-state placeholder text in.
262
+ */
263
+ declare function createEmailExtensions(placeholder?: string): Extensions;
264
+
265
+ /**
266
+ * Build email-safe HTML for a call-to-action button.
267
+ *
268
+ * Two non-obvious decisions, both load-bearing:
269
+ *
270
+ * 1. The anchor is wrapped in a `<p style="...;text-align:X">`, **never** a
271
+ * `<div>`. TipTap has no `div` node and unwraps a `<div>` into a paragraph,
272
+ * losing the `text-align`. A paragraph is a real node whose `text-align`
273
+ * the TextAlign extension preserves — so the alignment round-trips.
274
+ *
275
+ * 2. The button is an `<a style="display:inline-block;...">` (or
276
+ * `display:block` when full-width), not a `<button>`, because email clients
277
+ * need bulletproof, inline-styled markup.
278
+ *
279
+ * Both the label and the href are HTML-escaped.
280
+ */
281
+ declare function buildButtonHtml(config: EmailButtonConfig): string;
282
+
283
+ /**
284
+ * Escape a string for safe insertion into HTML text or a double-quoted
285
+ * attribute value. Used by `buildButtonHtml` for the button label and href.
286
+ */
287
+ declare function escapeHtml(value: string): string;
288
+
289
+ /**
290
+ * Split a full email HTML document into `{ prefix, body, suffix }`.
291
+ *
292
+ * - `prefix` = everything up to and including the opening `<body ...>` tag.
293
+ * - `body` = the inner body HTML (the editable fragment).
294
+ * - `suffix` = the closing `</body>` tag plus everything after it.
295
+ *
296
+ * If the input has no `<body>` tag it is treated as a **bare fragment**: the
297
+ * whole input becomes `body`, and `prefix`/`suffix` are empty strings.
298
+ */
299
+ declare function splitEmailHtml(html: string): EmailHtmlDocument;
300
+ /**
301
+ * Reassemble a document from a shell and a (possibly edited) body fragment.
302
+ *
303
+ * If the shell has no surrounding markup (a bare fragment was originally
304
+ * supplied) the body is returned as-is, so `onChange` emits in the same shape
305
+ * the consumer provided `value`.
306
+ */
307
+ declare function joinEmailHtml(shell: EmailHtmlDocument, newBody: string): string;
308
+ /**
309
+ * Convenience: extract just the editable body fragment from a full document
310
+ * (or return the input unchanged if it is already a bare fragment).
311
+ */
312
+ declare function extractEmailBody(html: string): string;
313
+
314
+ /**
315
+ * Preset block snippets — small, inline-styled, email-safe HTML fragments
316
+ * inserted at the caret via `editor.chain().focus().insertContent(snippet)`.
317
+ *
318
+ * Every snippet uses inline styles only (no classes) so it survives both the
319
+ * editor schema (via PreserveStyles) and downstream email clients.
320
+ */
321
+ declare const PRESET_PARAGRAPH = "<p style=\"margin:0 0 12px;color:#4b5563;line-height:1.6;\">Write your message here.</p>";
322
+ declare const PRESET_DIVIDER = "<hr style=\"border:none;border-top:1px solid #e5e7eb;margin:20px 0;\" />";
323
+ declare const PRESET_FOOTER = "<p style=\"margin:20px 0 0;color:#9ca3af;font-size:12px;\">\u00A9 Your Company</p>";
324
+ declare const PRESET_HEADING = "<h2 style=\"margin:0 0 12px;color:#111827;font-size:24px;line-height:1.3;\">Heading</h2>";
325
+ declare const presets: {
326
+ readonly paragraph: "<p style=\"margin:0 0 12px;color:#4b5563;line-height:1.6;\">Write your message here.</p>";
327
+ readonly divider: "<hr style=\"border:none;border-top:1px solid #e5e7eb;margin:20px 0;\" />";
328
+ readonly footer: "<p style=\"margin:20px 0 0;color:#9ca3af;font-size:12px;\">© Your Company</p>";
329
+ readonly heading: "<h2 style=\"margin:0 0 12px;color:#111827;font-size:24px;line-height:1.3;\">Heading</h2>";
330
+ };
331
+
332
+ declare function resolveToolbarConfig(config?: ToolbarConfig): ResolvedToolbarConfig;
333
+
334
+ export { type AlignButtons, type BlockButtons, type EmailButtonConfig, EmailButtonDialog, type EmailButtonDialogProps, EmailEditor, type EmailEditorProps, type EmailHtmlDocument, type EmailPlaceholder, type HeadingButtons, type InlineButtons, type ListButtons, PRESET_DIVIDER, PRESET_FOOTER, PRESET_HEADING, PRESET_PARAGRAPH, PreserveStyles, type ResolvedToolbarConfig, RichTextEditor, type RichTextEditorHandle, type RichTextEditorProps, type ToolbarConfig, type ToolbarGroup, buildButtonHtml, createEmailExtensions, EmailEditor as default, escapeHtml, extractEmailBody, joinEmailHtml, presets, resolveToolbarConfig, splitEmailHtml };