@emailens/engine 0.3.1 → 0.5.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/clients.ts","../src/transform.ts","../src/rules/css-support.ts","../src/fix-snippets.ts","../src/style-utils.ts","../src/analyze.ts","../src/dark-mode.ts","../src/diff.ts","../src/export-prompt.ts","../src/token-utils.ts","../src/ai-fix.ts","../src/spam-scorer.ts","../src/link-validator.ts","../src/accessibility-checker.ts","../src/image-analyzer.ts"],"sourcesContent":["import type { EmailClient } from \"./types\";\r\n\r\nexport const EMAIL_CLIENTS: EmailClient[] = [\r\n {\r\n id: \"gmail-web\",\r\n name: \"Gmail\",\r\n category: \"webmail\",\r\n engine: \"Gmail Web\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"gmail-android\",\r\n name: \"Gmail Android\",\r\n category: \"mobile\",\r\n engine: \"Gmail Mobile\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"gmail-ios\",\r\n name: \"Gmail iOS\",\r\n category: \"mobile\",\r\n engine: \"Gmail Mobile\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"outlook-web\",\r\n name: \"Outlook 365\",\r\n category: \"webmail\",\r\n engine: \"Outlook Web\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"outlook-windows\",\r\n name: \"Outlook Windows\",\r\n category: \"desktop\",\r\n engine: \"Microsoft Word\",\r\n darkModeSupport: false,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"apple-mail-macos\",\r\n name: \"Apple Mail\",\r\n category: \"desktop\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"apple-mail-ios\",\r\n name: \"Apple Mail iOS\",\r\n category: \"mobile\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"yahoo-mail\",\r\n name: \"Yahoo Mail\",\r\n category: \"webmail\",\r\n engine: \"Yahoo\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"samsung-mail\",\r\n name: \"Samsung Mail\",\r\n category: \"mobile\",\r\n engine: \"Samsung\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"thunderbird\",\r\n name: \"Thunderbird\",\r\n category: \"desktop\",\r\n engine: \"Gecko\",\r\n darkModeSupport: false,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"hey-mail\",\r\n name: \"HEY Mail\",\r\n category: \"webmail\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"superhuman\",\r\n name: \"Superhuman\",\r\n category: \"desktop\",\r\n engine: \"Blink\",\r\n darkModeSupport: true,\r\n icon: \"monitor\",\r\n },\r\n];\r\n\r\nexport function getClient(id: string): EmailClient | undefined {\r\n return EMAIL_CLIENTS.find((c) => c.id === id);\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport * as csstree from \"css-tree\";\r\nimport type { CSSWarning, Framework, TransformResult } from \"./types\";\r\nimport {\r\n CSS_SUPPORT,\r\n GMAIL_STRIPPED_PROPERTIES,\r\n OUTLOOK_WORD_UNSUPPORTED,\r\n STRUCTURAL_FIX_PROPERTIES,\r\n} from \"./rules/css-support\";\r\nimport { getCodeFix, getSuggestion, isCodeFixGenericFallback } from \"./fix-snippets\";\r\nimport { parseInlineStyle, serializeStyle } from \"./style-utils\";\r\n\r\n// --- Inline <style> blocks into elements using css-tree ---\r\nfunction inlineStyles($: cheerio.CheerioAPI): void {\r\n const styleBlocks: string[] = [];\r\n $(\"style\").each((_, el) => {\r\n styleBlocks.push($(el).text());\r\n });\r\n\r\n if (styleBlocks.length === 0) return;\r\n\r\n for (const block of styleBlocks) {\r\n let ast: csstree.CssNode;\r\n try {\r\n ast = csstree.parse(block, { parseCustomProperty: true });\r\n } catch {\r\n // If css-tree can't parse it, skip this block\r\n continue;\r\n }\r\n\r\n csstree.walk(ast, {\r\n visit: \"Rule\",\r\n enter(node: csstree.CssNode) {\r\n if (node.type !== \"Rule\" || node.prelude.type !== \"SelectorList\") return;\r\n\r\n // Generate the declarations string from the block\r\n const declarations = csstree.generate(node.block);\r\n // Strip the outer braces: \"{ color: red; }\" -> \"color: red;\"\r\n const declText = declarations.slice(1, -1).trim();\r\n if (!declText) return;\r\n\r\n // Get the selector text\r\n const selectorText = csstree.generate(node.prelude);\r\n\r\n // Skip pseudo-selectors for inlining (can't be applied as inline styles)\r\n if (selectorText.includes(\":hover\") ||\r\n selectorText.includes(\":focus\") ||\r\n selectorText.includes(\":active\") ||\r\n selectorText.includes(\"::\")) {\r\n return;\r\n }\r\n\r\n try {\r\n $(selectorText).each((_, el) => {\r\n const existing = $(el).attr(\"style\") || \"\";\r\n $(el).attr(\"style\", existing ? `${existing}; ${declText}` : declText);\r\n });\r\n } catch {\r\n // Invalid selector for cheerio, skip\r\n }\r\n },\r\n });\r\n }\r\n}\r\n\r\n/** Build a CSSWarning with framework-aware suggestion + fix + fallback flag. */\r\nfunction makeWarning(\r\n base: Omit<CSSWarning, \"suggestion\" | \"fix\" | \"fixIsGenericFallback\" | \"fixType\">,\r\n prop: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): CSSWarning {\r\n const sug = getSuggestion(prop, clientId, framework);\r\n const fix = getCodeFix(prop, clientId, framework);\r\n const isFallback = framework && (\r\n (sug?.isGenericFallback) ||\r\n (fix && isCodeFixGenericFallback(prop, clientId, framework))\r\n );\r\n return {\r\n ...base,\r\n ...(sug ? { suggestion: sug.text } : {}),\r\n ...(fix ? { fix } : {}),\r\n ...(isFallback ? { fixIsGenericFallback: true } : {}),\r\n fixType: STRUCTURAL_FIX_PROPERTIES.has(prop) ? \"structural\" : \"css\",\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Per-Client Transformers\r\n// =============================================================================\r\n\r\nfunction transformGmail(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Check for @font-face BEFORE removing <style> blocks\r\n let hasAtFontFace = false;\r\n $(\"style\").each((_, el) => {\r\n try {\r\n const ast = csstree.parse($(el).text());\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Atrule\" && node.name === \"font-face\") {\r\n hasAtFontFace = true;\r\n }\r\n },\r\n });\r\n } catch { /* skip unparseable CSS */ }\r\n });\r\n if (hasAtFontFace) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"@font-face\",\r\n message: \"Gmail does not support custom web fonts.\",\r\n }, \"@font-face\", clientId, framework));\r\n }\r\n\r\n // Gmail strips <style> blocks — inline them first\r\n inlineStyles($);\r\n $(\"style\").remove();\r\n $(\"link[rel='stylesheet']\").remove();\r\n\r\n // Gmail also strips MSO conditional comments (<!--[if mso]>...<![endif]-->)\r\n // Cheerio treats these as HTML comments; remove any that contain <style>\r\n $(\"*\")\r\n .contents()\r\n .filter(function () {\r\n return this.type === \"comment\";\r\n })\r\n .each(function () {\r\n const commentText = (this as unknown as { data: string }).data || \"\";\r\n if (commentText.includes(\"<style\") || commentText.includes(\"[if mso]\") || commentText.includes(\"[if gte mso\")) {\r\n $(this).remove();\r\n }\r\n });\r\n\r\n const styleSug = getSuggestion(\"<style>:partial\", clientId, framework);\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"<style>\",\r\n message: \"Gmail partially supports <style> blocks (head only, 16KB limit). Inlining recommended for safety.\",\r\n suggestion: styleSug.text,\r\n });\r\n\r\n // Strip unsupported CSS properties from inline styles\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n props.forEach((value, prop) => {\r\n if (GMAIL_STRIPPED_PROPERTIES.has(prop)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n // Gmail supports display:flex but NOT display:grid\r\n if (prop === \"display\" && value.includes(\"grid\")) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n // Gmail strips gradient values from background shorthand\r\n if ((prop === \"background\") &&\r\n (value.includes(\"linear-gradient\") || value.includes(\"radial-gradient\"))) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `Gmail strips \"${prop}\" from inline styles.`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n\r\n // Gmail removes <svg> elements\r\n if ($(\"svg\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<svg>\",\r\n message: \"Gmail does not support inline SVG elements.\",\r\n }, \"<svg>\", clientId, framework));\r\n $(\"svg\").each((_, el) => {\r\n $(el).replaceWith('<img alt=\"[SVG not supported]\" />');\r\n });\r\n }\r\n\r\n // Gmail removes <form> elements\r\n if ($(\"form\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<form>\",\r\n message: \"Gmail strips all form elements.\",\r\n }, \"<form>\", clientId, framework));\r\n $(\"form\").each((_, el) => {\r\n $(el).replaceWith($(el).html() || \"\");\r\n });\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformOutlookWindows(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Outlook Windows uses Word rendering — keep <style> blocks but strip unsupported properties\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n props.forEach((value, prop) => {\r\n if (OUTLOOK_WORD_UNSUPPORTED.has(prop)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n // Outlook doesn't support gradient values in background shorthand\r\n if ((prop === \"background\" || prop === \"background-image\") &&\r\n (value.includes(\"linear-gradient\") || value.includes(\"radial-gradient\"))) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `Outlook Windows (Word engine) does not support \"${prop}\".`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n\r\n // Outlook doesn't support border-radius\r\n const borderRadiusElements = $(\"[style*='border-radius']\");\r\n if (borderRadiusElements.length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"border-radius\",\r\n message:\r\n \"Outlook Windows ignores border-radius. Buttons and containers will have sharp corners.\",\r\n }, \"border-radius\", clientId, framework));\r\n }\r\n\r\n // Outlook doesn't support max-width\r\n const maxWidthElements = $(\"[style*='max-width']\");\r\n if (maxWidthElements.length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"max-width\",\r\n message: \"Outlook Windows ignores max-width.\",\r\n }, \"max-width\", clientId, framework));\r\n }\r\n\r\n // Check for div-based layouts (Outlook prefers tables)\r\n const hasDivLayout =\r\n $(\"div[style*='display']\").length > 0 ||\r\n $(\"div[style*='flex']\").length > 0 ||\r\n $(\"div[style*='grid']\").length > 0;\r\n\r\n if (hasDivLayout) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"display:flex\",\r\n message:\r\n \"Outlook Windows uses Microsoft Word for rendering. Flexbox and Grid layouts will break.\",\r\n }, \"display:flex\", clientId, framework));\r\n }\r\n\r\n // Warn about background images needing VML\r\n if (\r\n $(\"[style*='background-image']\").length > 0 ||\r\n $(\"[style*='background:']\").filter((_, el) =>\r\n ($(el).attr(\"style\") || \"\").includes(\"url(\")\r\n ).length > 0\r\n ) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"background-image\",\r\n message:\r\n \"Outlook Windows requires VML for background images.\",\r\n }, \"background-image\", clientId, framework));\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformOutlookWeb(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Outlook.com supports <style> blocks but strips some properties\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n const outlookWebUnsupported = new Set([\r\n \"position\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n ]);\r\n\r\n props.forEach((_, prop) => {\r\n if (outlookWebUnsupported.has(prop)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `Outlook 365 Web does not support \"${prop}\".`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformAppleMail(\r\n html: string,\r\n clientId: string,\r\n _framework?: Framework,\r\n): TransformResult {\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Apple Mail is the most standards-compliant — minimal transformations needed\r\n // Just flag potential dark mode issues\r\n const $ = cheerio.load(html);\r\n\r\n // Check for dark mode issues\r\n const imgsWithTransparentBg = $(\"img\").filter((_, el) => {\r\n const src = $(el).attr(\"src\") || \"\";\r\n return src.endsWith(\".png\") || src.endsWith(\".svg\");\r\n });\r\n\r\n if (imgsWithTransparentBg.length > 0) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"PNG/SVG images with transparent backgrounds may become invisible in Apple Mail dark mode.\",\r\n suggestion:\r\n \"Add a white background or padding around images, or use dark-mode-friendly image variants.\",\r\n });\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformYahooMail(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Yahoo rewrites CSS class names by prefixing them\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"class\",\r\n message:\r\n \"Yahoo Mail rewrites CSS class names with a prefix. Class-based selectors in <style> blocks will still work but the names change.\",\r\n });\r\n\r\n // Yahoo has limited support for background shorthand with images\r\n if (\r\n $(\"[style*='background']\").filter((_, el) =>\r\n ($(el).attr(\"style\") || \"\").includes(\"url(\")\r\n ).length > 0\r\n ) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"background-image\",\r\n message:\r\n \"Yahoo Mail has inconsistent support for CSS background images.\",\r\n }, \"background-image\", clientId, framework));\r\n }\r\n\r\n // Yahoo strips position, box-shadow, transform\r\n const yahooStripped = new Set([\r\n \"position\",\r\n \"box-shadow\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"opacity\",\r\n ]);\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n props.forEach((_, prop) => {\r\n if (yahooStripped.has(prop)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `Yahoo Mail strips \"${prop}\" from styles.`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformSamsungMail(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Samsung Mail supports @media queries which is useful\r\n // But has some quirks with certain CSS properties\r\n const samsungPartial = new Set([\r\n \"box-shadow\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"opacity\",\r\n ]);\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n\r\n props.forEach((_, prop) => {\r\n if (samsungPartial.has(prop)) {\r\n warnings.push(makeWarning({\r\n severity: \"info\",\r\n client: clientId,\r\n property: prop,\r\n message: `Samsung Mail has limited support for \"${prop}\".`,\r\n }, prop, clientId, framework));\r\n }\r\n });\r\n });\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformThunderbird(\r\n html: string,\r\n clientId: string,\r\n _framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Thunderbird is Gecko-based, very standards-compliant\r\n // Check for animation/transition in inline styles and <style> blocks\r\n let hasAnimationOrTransition = false;\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n props.forEach((_, prop) => {\r\n if (\r\n prop === \"animation\" ||\r\n prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") ||\r\n prop.startsWith(\"transition-\")\r\n ) {\r\n hasAnimationOrTransition = true;\r\n }\r\n });\r\n });\r\n\r\n if (!hasAnimationOrTransition) {\r\n $(\"style\").each((_, el) => {\r\n try {\r\n const ast = csstree.parse($(el).text());\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Declaration\") {\r\n const prop = node.property.toLowerCase();\r\n if (prop === \"animation\" || prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") || prop.startsWith(\"transition-\")) {\r\n hasAnimationOrTransition = true;\r\n }\r\n }\r\n },\r\n });\r\n } catch { /* skip unparseable CSS */ }\r\n });\r\n }\r\n\r\n if (hasAnimationOrTransition) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"animation\",\r\n message:\r\n \"Thunderbird does not support CSS animations or transitions.\",\r\n });\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformHeyMail(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // HEY Mail uses WebKit rendering and is standards-compliant\r\n // but strips certain CSS properties and elements for security\r\n const heyStripped = new Set([\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n ]);\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n props.forEach((value, prop) => {\r\n if (heyStripped.has(prop)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n // HEY strips fixed/sticky positioning\r\n if (prop === \"position\" && (value.includes(\"fixed\") || value.includes(\"sticky\"))) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `HEY Mail strips \"${prop}\" for security and rendering consistency.`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n\r\n // HEY strips <form> elements\r\n if ($(\"form\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<form>\",\r\n message: \"HEY Mail removes form elements for security.\",\r\n }, \"<form>\", clientId, framework));\r\n $(\"form\").each((_, el) => {\r\n $(el).replaceWith($(el).html() || \"\");\r\n });\r\n }\r\n\r\n // HEY strips external stylesheets\r\n if ($(\"link[rel='stylesheet']\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<link>\",\r\n message: \"HEY Mail does not load external stylesheets.\",\r\n }, \"<link>\", clientId, framework));\r\n $(\"link[rel='stylesheet']\").remove();\r\n }\r\n\r\n // HEY supports dark mode — warn if no dark mode styles present\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message: \"HEY Mail supports @media (prefers-color-scheme: dark). Consider adding dark mode styles.\",\r\n suggestion: \"Add a @media (prefers-color-scheme: dark) block to optimize for HEY's audience.\",\r\n });\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\nfunction transformSuperhuman(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Superhuman uses Blink/Chromium — very strong modern CSS support\r\n // It strips <form> elements and external stylesheets for security\r\n\r\n if ($(\"form\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<form>\",\r\n message: \"Superhuman removes form elements.\",\r\n }, \"<form>\", clientId, framework));\r\n $(\"form\").each((_, el) => {\r\n $(el).replaceWith($(el).html() || \"\");\r\n });\r\n }\r\n\r\n if ($(\"link[rel='stylesheet']\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<link>\",\r\n message: \"Superhuman does not load external stylesheets.\",\r\n }, \"<link>\", clientId, framework));\r\n $(\"link[rel='stylesheet']\").remove();\r\n }\r\n\r\n // Check for animation usage — may be disabled per OS accessibility settings.\r\n // Both shorthand (\"animation\", \"transition\") and sub-properties\r\n // (\"animation-duration\", \"transition-delay\", etc.) are checked so that\r\n // elements using only sub-properties don't silently bypass the warning.\r\n let hasAnimation = false;\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n props.forEach((_, prop) => {\r\n if (\r\n prop === \"animation\" ||\r\n prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") ||\r\n prop.startsWith(\"transition-\")\r\n ) {\r\n hasAnimation = true;\r\n }\r\n });\r\n });\r\n\r\n if (!hasAnimation) {\r\n $(\"style\").each((_, el) => {\r\n try {\r\n const ast = csstree.parse($(el).text());\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Declaration\") {\r\n const prop = node.property.toLowerCase();\r\n if (prop === \"animation\" || prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") || prop.startsWith(\"transition-\")) {\r\n hasAnimation = true;\r\n }\r\n }\r\n },\r\n });\r\n } catch { /* skip unparseable CSS */ }\r\n });\r\n }\r\n\r\n if (hasAnimation) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"animation\",\r\n message: \"Superhuman may honor OS-level 'reduce motion' preferences, disabling animations.\",\r\n suggestion: \"Use @media (prefers-reduced-motion: reduce) to provide static fallbacks.\",\r\n });\r\n }\r\n\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"<style>\",\r\n message: \"Superhuman uses Chromium rendering with excellent CSS support. Flexbox, Grid, CSS variables, and modern properties all work.\",\r\n });\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\n// =============================================================================\r\n// Main transform dispatcher\r\n// =============================================================================\r\n\r\nconst TRANSFORMERS: Record<\r\n string,\r\n (html: string, clientId: string, framework?: Framework) => TransformResult\r\n> = {\r\n \"gmail-web\": transformGmail,\r\n \"gmail-android\": transformGmail,\r\n \"gmail-ios\": transformGmail,\r\n \"outlook-web\": transformOutlookWeb,\r\n \"outlook-windows\": transformOutlookWindows,\r\n \"apple-mail-macos\": transformAppleMail,\r\n \"apple-mail-ios\": transformAppleMail,\r\n \"yahoo-mail\": transformYahooMail,\r\n \"samsung-mail\": transformSamsungMail,\r\n \"thunderbird\": transformThunderbird,\r\n \"hey-mail\": transformHeyMail,\r\n \"superhuman\": transformSuperhuman,\r\n};\r\n\r\nexport function transformForClient(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n if (!html || !html.trim()) {\r\n return { clientId, html: html || \"\", warnings: [] };\r\n }\r\n\r\n const transformer = TRANSFORMERS[clientId];\r\n if (!transformer) {\r\n return {\r\n clientId,\r\n html,\r\n warnings: [\r\n {\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"unknown\",\r\n message: `No transformation rules available for client \"${clientId}\".`,\r\n },\r\n ],\r\n };\r\n }\r\n return transformer(html, clientId, framework);\r\n}\r\n\r\nexport function transformForAllClients(html: string, framework?: Framework): TransformResult[] {\r\n return Object.keys(TRANSFORMERS).map((clientId) =>\r\n transformForClient(html, clientId, framework)\r\n );\r\n}\r\n","import type { SupportLevel } from \"../types\";\r\n\r\n/**\r\n * CSS property support matrix.\r\n * Last validated: 2026-02-22\r\n *\r\n * Each entry maps a CSS property to its support level per email client.\r\n *\r\n * Support levels:\r\n * - \"supported\": fully supported\r\n * - \"partial\": partially supported (with caveats)\r\n * - \"unsupported\": not supported at all\r\n * - \"unknown\": no data available\r\n *\r\n * Data sources by client:\r\n * - gmail-*, outlook-*, apple-mail-*, yahoo-mail, samsung-mail, thunderbird:\r\n * Verified against caniemail.com; inline comments cite specific notes\r\n * using caniemail's \"#N\" notation (e.g. \"caniemail: a #2\").\r\n * - hey-mail: NOT in caniemail. Values are inferred from HEY's WebKit-based\r\n * rendering engine and documented stripping behaviours (forms, external\r\n * stylesheets, fixed/sticky positioning). Treat as best-effort estimates;\r\n * empirical verification is recommended before relying on these values.\r\n * - superhuman: NOT in caniemail. Values are inferred from Superhuman's\r\n * Chromium/Blink rendering engine and its documented stripping behaviours\r\n * (forms, external stylesheets). Treat as best-effort estimates.\r\n */\r\nexport const CSS_SUPPORT: Record<\r\n string,\r\n Record<string, SupportLevel>\r\n> = {\r\n // --- Layout ---\r\n \"display\": {\r\n \"gmail-web\": \"supported\", // block, inline, inline-block, none all work\r\n \"gmail-android\": \"partial\", // basic values only\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // limited values\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"display:flex\": {\r\n \"gmail-web\": \"supported\", // caniemail: y\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (non-Google accounts)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1\r\n \"outlook-web\": \"supported\", // caniemail: y\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\", // caniemail: y #2 (no inline-flex)\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"display:grid\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"float\": {\r\n \"gmail-web\": \"supported\", // caniemail: y\r\n \"gmail-android\": \"partial\", // caniemail: a #2 (no logical values)\r\n \"gmail-ios\": \"partial\", // caniemail: a #2\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // caniemail: n #1\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2 (no logical values)\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"position\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"partial\", // caniemail: a #2 (sticky only)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"partial\", // caniemail: a #1 (no sticky/fixed)\r\n \"apple-mail-ios\": \"partial\", // caniemail: a #1\r\n \"yahoo-mail\": \"partial\", // caniemail: a #3 (relative only)\r\n \"samsung-mail\": \"partial\", // caniemail: a #1\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"partial\", // relative only; HEY strips fixed/sticky positioning\r\n \"superhuman\": \"partial\", // relative/absolute supported; fixed/sticky stripped\r\n },\r\n\r\n // --- Box Model ---\r\n \"margin\": {\r\n \"gmail-web\": \"partial\", // no negative margins\r\n \"gmail-android\": \"partial\",\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // no auto margins\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"padding\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // not on <p>, <div>\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"max-width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #1 (only on <table> elements)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"box-sizing\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Typography ---\r\n \"font-family\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"font-size\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"partial\", // may auto-resize small text\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"partial\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"font-weight\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"line-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // ignores on some elements\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"letter-spacing\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-align\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-decoration\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-transform\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"@font-face\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #4 (declaration kept, remote fonts ignored)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\", // caniemail: y #8\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\", // HEY uses WebKit — web fonts work\r\n \"superhuman\": \"supported\", // Superhuman uses Blink — web fonts work\r\n },\r\n\r\n // --- Colors & Backgrounds ---\r\n \"color\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-color\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-image\": {\r\n \"gmail-web\": \"supported\", // caniemail: y (restored 2023-08)\r\n \"gmail-android\": \"supported\", // caniemail: y\r\n \"gmail-ios\": \"supported\", // caniemail: y\r\n \"outlook-web\": \"supported\", // caniemail: y\r\n \"outlook-windows\": \"partial\", // caniemail: n #5 (VML workaround)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #3 (no multiple values)\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background\": {\r\n \"gmail-web\": \"partial\", // color only, no shorthand with images\r\n \"gmail-android\": \"partial\",\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"partial\",\r\n \"outlook-windows\": \"partial\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"linear-gradient\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Borders ---\r\n \"border\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // no shorthand on some elements\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-radius\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2 (no elliptical slash notation)\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-collapse\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"box-shadow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (non-Google accounts)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1\r\n \"outlook-web\": \"supported\", // caniemail: y (since 2023-12)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Transforms & Animation ---\r\n \"transform\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips transform for security\r\n \"superhuman\": \"partial\", // Superhuman (Blink) allows some transforms\r\n },\r\n \"animation\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"partial\", // Superhuman allows CSS animations but they may be disabled by user settings\r\n },\r\n \"transition\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"unsupported\",\r\n },\r\n\r\n // --- Media & Responsive ---\r\n \"@media\": {\r\n \"gmail-web\": \"partial\", // caniemail: a #7 (no height-based, no nested)\r\n \"gmail-android\": \"partial\", // caniemail: a #6 #7\r\n \"gmail-ios\": \"partial\", // caniemail: a #6 #7\r\n \"outlook-web\": \"partial\", // caniemail: a #1 #10 (no nested)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2\r\n \"samsung-mail\": \"partial\", // caniemail: a #9\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\", // HEY is WebKit-based, full @media support\r\n \"superhuman\": \"supported\", // Superhuman is Blink-based, full @media support\r\n },\r\n\r\n // --- HTML Elements ---\r\n \"<style>\": {\r\n \"gmail-web\": \"partial\", // caniemail: a #1 #6 (head only, 16KB limit)\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (head only)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1 #2\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #4 (must declare before use)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"<link>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips external stylesheets\r\n \"superhuman\": \"unsupported\", // Superhuman strips external stylesheets\r\n },\r\n \"<video>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"partial\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"unsupported\",\r\n },\r\n \"<svg>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"partial\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"partial\", // HEY allows SVG but strips some attributes\r\n \"superhuman\": \"supported\", // Superhuman (Blink) renders SVG well\r\n },\r\n \"<form>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips forms for security\r\n \"superhuman\": \"unsupported\", // Superhuman strips forms for security\r\n },\r\n\r\n // --- Text Wrapping ---\r\n \"word-break\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores word-break entirely\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // break-all partially works; break-word unreliable\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"overflow-wrap\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores overflow-wrap\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // inconsistent support\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"white-space\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // only normal and nowrap\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-overflow\": {\r\n \"gmail-web\": \"unsupported\", // Gmail strips overflow, so text-overflow is useless\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Table Layout ---\r\n \"vertical-align\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // only on <td> elements via valign attribute\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-spacing\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // use cellspacing attribute instead\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Sizing ---\r\n \"min-width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores min-width\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"min-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores min-height\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"max-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Shadows ---\r\n \"text-shadow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Background Sub-properties ---\r\n \"background-size\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-position\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Misc ---\r\n \"opacity\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"overflow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"visibility\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"gap\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"object-fit\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n};\r\n\r\n/**\r\n * CSS properties that Gmail strips from inline styles.\r\n * Updated per caniemail.com data — Gmail keeps float and display (basic values).\r\n */\r\nexport const GMAIL_STRIPPED_PROPERTIES = new Set([\r\n \"position\",\r\n \"overflow\",\r\n \"visibility\",\r\n \"opacity\",\r\n \"box-shadow\",\r\n \"text-shadow\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"box-sizing\",\r\n \"object-fit\",\r\n \"gap\",\r\n]);\r\n\r\n/** CSS properties that Outlook Word engine ignores */\r\nexport const OUTLOOK_WORD_UNSUPPORTED = new Set([\r\n \"border-radius\",\r\n \"box-shadow\",\r\n \"text-shadow\",\r\n \"max-width\",\r\n \"max-height\",\r\n \"min-width\",\r\n \"min-height\",\r\n \"float\",\r\n \"position\",\r\n \"display\",\r\n \"overflow\",\r\n \"opacity\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"background-size\",\r\n \"background-position\",\r\n \"box-sizing\",\r\n \"object-fit\",\r\n \"gap\",\r\n \"word-break\",\r\n \"overflow-wrap\",\r\n \"text-overflow\",\r\n \"border-spacing\",\r\n]);\r\n\r\n/**\r\n * Properties that require HTML structural changes (not just CSS swaps)\r\n * to fix. These cannot be solved by replacing one CSS value with another.\r\n */\r\nexport const STRUCTURAL_FIX_PROPERTIES = new Set([\r\n \"display:flex\",\r\n \"display:grid\",\r\n \"word-break\",\r\n \"overflow-wrap\",\r\n \"text-overflow\",\r\n \"position\",\r\n \"float\",\r\n \"gap\",\r\n \"max-width\",\r\n \"border-radius\",\r\n \"background-image\",\r\n \"background-size\",\r\n \"background-position\",\r\n \"<svg>\",\r\n \"<video>\",\r\n \"<form>\",\r\n \"object-fit\",\r\n]);\r\n","import type { CodeFix, Framework } from \"./types\";\r\n\r\n/**\r\n * Inline code fix snippets — real, paste-ready code that turns\r\n * \"here's your problem\" into \"here's your solution.\"\r\n *\r\n * Keyed by property, with optional client-specific overrides.\r\n * Client-specific keys use the format \"property::clientPrefix\"\r\n * (e.g. \"border-radius::outlook\").\r\n */\r\nconst FIX_DATABASE: Record<string, CodeFix> = {\r\n // ── border-radius (Outlook VML fallback) ──────────────────────────────\r\n \"border-radius::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML to render rounded buttons in Outlook\",\r\n before: `<a href=\"https://example.com\"\r\n style=\"background-color: #6d28d9; color: #fff;\r\n padding: 12px 32px; border-radius: 6px;\r\n text-decoration: none; display: inline-block;\">\r\n Click Here\r\n</a>`,\r\n after: `<!--[if mso]>\r\n<v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\"\r\n href=\"https://example.com\"\r\n style=\"height:44px; v-text-anchor:middle; width:200px;\"\r\n arcsize=\"14%\" strokecolor=\"#6d28d9\" fillcolor=\"#6d28d9\">\r\n <w:anchorlock/>\r\n <center style=\"color:#fff; font-family:Arial,sans-serif;\r\n font-size:14px; font-weight:bold;\">Click Here</center>\r\n</v:roundrect>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<a href=\"https://example.com\"\r\n style=\"background-color: #6d28d9; color: #fff;\r\n padding: 12px 32px; border-radius: 6px;\r\n text-decoration: none; display: inline-block;\">\r\n Click Here\r\n</a>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n // ── background-image (Outlook VML) ────────────────────────────────────\r\n \"background-image::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML for background images in Outlook\",\r\n before: `<td style=\"background-image: url('hero.jpg');\r\n background-size: cover; padding: 40px;\">\r\n <h1 style=\"color: #fff;\">Hello World</h1>\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px; height:300px;\">\r\n <v:fill type=\"frame\" src=\"hero.jpg\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background-image: url('hero.jpg');\r\n background-size: cover; padding: 40px;\">\r\n <h1 style=\"color: #fff;\">Hello World</h1>\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── display:flex → table layout ───────────────────────────────────────\r\n \"display:flex::outlook\": {\r\n language: \"html\",\r\n description: \"Use table layout as fallback for flexbox in Outlook\",\r\n before: `<div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\"><tr>\r\n <td width=\"50%\" valign=\"top\">Column 1</td>\r\n <td width=\"50%\" valign=\"top\">Column 2</td>\r\n</tr></table>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n</div>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n // ── display:grid → table layout ───────────────────────────────────────\r\n \"display:grid\": {\r\n language: \"html\",\r\n description: \"Replace CSS Grid with table layout for email compatibility\",\r\n before: `<div style=\"display: grid;\r\n grid-template-columns: 1fr 1fr; gap: 16px;\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n</div>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td width=\"50%\" style=\"padding: 8px;\" valign=\"top\">\r\n Item 1\r\n </td>\r\n <td width=\"50%\" style=\"padding: 8px;\" valign=\"top\">\r\n Item 2\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── linear-gradient fallback ──────────────────────────────────────────\r\n \"linear-gradient\": {\r\n language: \"html\",\r\n description: \"Add solid color fallback for gradient backgrounds\",\r\n before: `<td style=\"background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n after: `<!-- Always declare a solid background-color before the gradient.\r\n Clients that strip gradients will show the fallback color. -->\r\n<td style=\"background-color: #667eea;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n },\r\n\r\n \"linear-gradient::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML to render gradients in Outlook\",\r\n before: `<td style=\"background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px;\">\r\n <v:fill type=\"gradient\" color=\"#667eea\" color2=\"#764ba2\"\r\n angle=\"135\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background-color: #667eea;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── <style> stripped by Gmail ──────────────────────────────────────────\r\n \"<style>::gmail\": {\r\n language: \"html\",\r\n description: \"Inline CSS for Gmail compatibility\",\r\n before: `<style>\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n .title { color: #fff; font-size: 24px; }\r\n</style>\r\n<div class=\"header\">\r\n <h1 class=\"title\">Hello</h1>\r\n</div>`,\r\n after: `<div style=\"background-color: #6d28d9; padding: 32px;\">\r\n <h1 style=\"color: #fff; font-size: 24px;\r\n font-family: Arial, sans-serif; margin: 0;\">\r\n Hello\r\n </h1>\r\n</div>`,\r\n },\r\n\r\n // ── <svg> replaced with image ─────────────────────────────────────────\r\n \"<svg>\": {\r\n language: \"html\",\r\n description: \"Convert inline SVG to an image for email compatibility\",\r\n before: `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" fill=\"#6d28d9\"/>\r\n</svg>`,\r\n after: `<img src=\"https://example.com/icon.png\"\r\n width=\"24\" height=\"24\" alt=\"Icon\"\r\n style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── <form> → link to web form ─────────────────────────────────────────\r\n \"<form>\": {\r\n language: \"html\",\r\n description: \"Replace embedded form with a link to a hosted form\",\r\n before: `<form action=\"/subscribe\" method=\"POST\">\r\n <input type=\"email\" placeholder=\"Email\" />\r\n <button type=\"submit\">Subscribe</button>\r\n</form>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\">\r\n <tr>\r\n <td style=\"background-color: #6d28d9; border-radius: 6px;\r\n padding: 12px 32px;\">\r\n <a href=\"https://example.com/subscribe\"\r\n style=\"color: #fff; text-decoration: none;\r\n font-family: Arial, sans-serif;\r\n font-weight: bold;\">\r\n Subscribe Now\r\n </a>\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── <video> → GIF + play button ───────────────────────────────────────\r\n \"<video>\": {\r\n language: \"html\",\r\n description: \"Replace video with animated GIF or linked thumbnail\",\r\n before: `<video width=\"600\" autoplay muted>\r\n <source src=\"demo.mp4\" type=\"video/mp4\">\r\n</video>`,\r\n after: `<a href=\"https://example.com/watch\" target=\"_blank\">\r\n <img src=\"https://example.com/video-thumb.gif\"\r\n width=\"600\" alt=\"Watch the video\"\r\n style=\"display: block; border: 0; max-width: 100%;\" />\r\n</a>`,\r\n },\r\n\r\n // ── @font-face → web-safe stack ───────────────────────────────────────\r\n \"@font-face\": {\r\n language: \"css\",\r\n description: \"Add web-safe font fallback stack\",\r\n before: `@font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n}\r\nh1 { font-family: 'CustomFont'; }`,\r\n after: `/* Keep @font-face for clients that support it */\r\n@font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n}\r\nh1 {\r\n font-family: 'CustomFont', Arial, Helvetica, sans-serif;\r\n}`,\r\n },\r\n\r\n // ── @media → mobile-first layout ──────────────────────────────────────\r\n \"@media\": {\r\n language: \"html\",\r\n description: \"Design mobile-first for clients without media query support\",\r\n before: `<table width=\"800\">\r\n <tr>\r\n <td width=\"400\">Left Column</td>\r\n <td width=\"400\">Right Column</td>\r\n </tr>\r\n</table>\r\n<style>\r\n @media (max-width: 600px) {\r\n table { width: 100% !important; }\r\n td { display: block !important; width: 100% !important; }\r\n }\r\n</style>`,\r\n after: `<!-- Single-column layout that works without @media -->\r\n<table role=\"presentation\" width=\"100%\"\r\n style=\"max-width: 600px;\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"padding: 16px;\">Left Column</td>\r\n </tr>\r\n <tr>\r\n <td style=\"padding: 16px;\">Right Column</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── box-shadow → border alternative ───────────────────────────────────\r\n \"box-shadow\": {\r\n language: \"css\",\r\n description: \"Use border as a fallback for box-shadow\",\r\n before: `.card {\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n}`,\r\n after: `.card {\r\n border: 1px solid #e0e0e0;\r\n /* box-shadow as progressive enhancement */\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n}`,\r\n },\r\n\r\n // ── max-width (Outlook) → fixed width table ───────────────────────────\r\n \"max-width::outlook\": {\r\n language: \"html\",\r\n description: \"Use a fixed-width table wrapper for Outlook\",\r\n before: `<div style=\"max-width: 600px; margin: 0 auto;\">\r\n Content here\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"600\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\" align=\"center\"><tr><td>\r\n<![endif]-->\r\n<div style=\"max-width: 600px; margin: 0 auto;\">\r\n Content here\r\n</div>\r\n<!--[if mso]>\r\n</td></tr></table>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── float (Outlook) → table align ─────────────────────────────────────\r\n \"float::outlook\": {\r\n language: \"html\",\r\n description: \"Use table align attribute instead of CSS float\",\r\n before: `<img src=\"photo.jpg\" style=\"float: left;\r\n margin-right: 16px;\" width=\"200\" />\r\n<p>Text wraps around the image.</p>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td width=\"200\" valign=\"top\" style=\"padding-right: 16px;\">\r\n <img src=\"photo.jpg\" width=\"200\"\r\n style=\"display: block; border: 0;\" />\r\n </td>\r\n <td valign=\"top\">\r\n <p>Text next to the image.</p>\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── gap → padding on children ─────────────────────────────────────────\r\n \"gap\": {\r\n language: \"html\",\r\n description: \"Use padding or margin on child elements instead of gap\",\r\n before: `<div style=\"display: flex; gap: 16px;\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n <div>Item 3</div>\r\n</div>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"padding-right: 16px;\">Item 1</td>\r\n <td style=\"padding-right: 16px;\">Item 2</td>\r\n <td>Item 3</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── opacity → solid colors ────────────────────────────────────────────\r\n \"opacity\": {\r\n language: \"css\",\r\n description: \"Replace opacity with solid color equivalents\",\r\n before: `.overlay {\r\n background-color: #000;\r\n opacity: 0.5;\r\n}`,\r\n after: `.overlay {\r\n /* Use a semi-transparent color instead of opacity */\r\n background-color: #808080;\r\n /* Or for modern clients: rgba(0, 0, 0, 0.5) */\r\n}`,\r\n },\r\n\r\n // ── position → table layout ───────────────────────────────────────────\r\n \"position\": {\r\n language: \"html\",\r\n description: \"Use table-based positioning instead of CSS position\",\r\n before: `<div style=\"position: relative;\">\r\n <div style=\"position: absolute; top: 0; right: 0;\">\r\n Badge\r\n </div>\r\n <p>Content</p>\r\n</div>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td valign=\"top\">Content</td>\r\n <td width=\"80\" valign=\"top\" align=\"right\">Badge</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── box-sizing → nested padding ───────────────────────────────────────\r\n \"box-sizing\": {\r\n language: \"html\",\r\n description: \"Account for padding in width manually (no box-sizing)\",\r\n before: `<div style=\"width: 300px; padding: 20px;\r\n box-sizing: border-box;\">\r\n Content — total width stays 300px\r\n</div>`,\r\n after: `<!-- Set width to content-width (300 - 40 = 260px) -->\r\n<div style=\"width: 300px;\">\r\n <div style=\"padding: 20px;\">\r\n Content — padding on inner element\r\n </div>\r\n</div>`,\r\n },\r\n\r\n // ── <link> → inline styles ────────────────────────────────────────────\r\n \"<link>\": {\r\n language: \"html\",\r\n description: \"Inline all CSS instead of using external stylesheets\",\r\n before: `<head>\r\n <link rel=\"stylesheet\" href=\"styles.css\" />\r\n</head>`,\r\n after: `<head>\r\n <style>\r\n /* Paste your CSS here, or use a build tool like\r\n juice/inline-css to inline automatically */\r\n .container { max-width: 600px; margin: 0 auto; }\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n </style>\r\n</head>`,\r\n },\r\n\r\n // ── dark-mode (Apple Mail) ────────────────────────────────────────────\r\n \"dark-mode::apple\": {\r\n language: \"css\",\r\n description: \"Add prefers-color-scheme dark mode styles for Apple Mail\",\r\n before: `<style>\r\n .header { background-color: #6d28d9; }\r\n .content { background-color: #ffffff; color: #333; }\r\n</style>`,\r\n after: `<style>\r\n .header { background-color: #6d28d9; }\r\n .content { background-color: #ffffff; color: #333; }\r\n\r\n @media (prefers-color-scheme: dark) {\r\n .content {\r\n background-color: #1a1a2e !important;\r\n color: #e0e0e0 !important;\r\n }\r\n /* Force images to stay visible */\r\n img { opacity: 1 !important; }\r\n }\r\n</style>`,\r\n },\r\n\r\n // ── object-fit → width/height attributes ──────────────────────────────\r\n \"object-fit\": {\r\n language: \"html\",\r\n description: \"Use width/height attributes instead of object-fit\",\r\n before: `<img src=\"photo.jpg\" style=\"width: 300px; height: 200px;\r\n object-fit: cover;\" />`,\r\n after: `<!-- Crop/resize image server-side to exact dimensions -->\r\n<img src=\"photo-300x200.jpg\" width=\"300\" height=\"200\"\r\n alt=\"Photo\" style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── transform (not supported) ─────────────────────────────────────────\r\n \"transform\": {\r\n language: \"html\",\r\n description: \"Pre-render transformed states as images or use table layout\",\r\n before: `<div style=\"transform: rotate(45deg);\">\r\n Rotated content\r\n</div>`,\r\n after: `<!-- Pre-render rotated content as an image -->\r\n<img src=\"rotated-content.png\" width=\"200\" height=\"200\"\r\n alt=\"Rotated content\"\r\n style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── animation → animated GIF ──────────────────────────────────────────\r\n \"animation\": {\r\n language: \"html\",\r\n description: \"Replace CSS animation with an animated GIF\",\r\n before: `<style>\r\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\r\n .badge { animation: pulse 2s infinite; }\r\n</style>\r\n<span class=\"badge\">New!</span>`,\r\n after: `<!-- Use an animated GIF for the effect -->\r\n<img src=\"https://example.com/badge-animated.gif\"\r\n width=\"60\" height=\"24\" alt=\"New!\"\r\n style=\"display: inline-block; border: 0;\" />`,\r\n },\r\n\r\n // ── transition (not supported) ────────────────────────────────────────\r\n \"transition\": {\r\n language: \"css\",\r\n description: \"Transitions don't work in email — style the default state well\",\r\n before: `.button {\r\n background-color: #6d28d9;\r\n transition: background-color 0.2s;\r\n}\r\n.button:hover {\r\n background-color: #5b21b6;\r\n}`,\r\n after: `.button {\r\n /* Use the most visually appealing state as default.\r\n :hover is only supported in a few clients. */\r\n background-color: #6d28d9;\r\n color: #ffffff;\r\n text-decoration: none;\r\n font-weight: bold;\r\n}`,\r\n },\r\n\r\n // ── background-size (Outlook) → VML ───────────────────────────────────\r\n \"background-size\": {\r\n language: \"html\",\r\n description: \"Outlook ignores background-size — use VML or sized images\",\r\n before: `<td style=\"background: url('bg.jpg') center/cover no-repeat;\">\r\n Content\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px; height:400px;\">\r\n <v:fill type=\"frame\" src=\"bg.jpg\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background: url('bg.jpg') center/cover no-repeat;\r\n background-color: #333;\">\r\n Content\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── overflow (Gmail strips it) ────────────────────────────────────────\r\n \"overflow\": {\r\n language: \"html\",\r\n description: \"Content will always be visible — design for full content display\",\r\n before: `<div style=\"max-height: 200px; overflow: hidden;\">\r\n Long content that gets clipped...\r\n</div>`,\r\n after: `<!-- Show full content, or truncate server-side -->\r\n<div style=\"max-height: 200px;\">\r\n Shortened content that fits...\r\n <a href=\"https://example.com/full\">Read more</a>\r\n</div>`,\r\n },\r\n\r\n // ── visibility (Gmail strips it) ──────────────────────────────────────\r\n \"visibility\": {\r\n language: \"html\",\r\n description: \"Use conditional comments or remove hidden content\",\r\n before: `<div style=\"visibility: hidden;\">\r\n Hidden content for screen readers\r\n</div>`,\r\n after: `<!-- For screen readers, use font-size: 0 trick -->\r\n<div style=\"font-size: 0; max-height: 0; overflow: hidden;\r\n mso-hide: all;\" aria-hidden=\"true\">\r\n Preheader text\r\n</div>`,\r\n },\r\n\r\n // ── word-break → table cell wrapping ─────────────────────────────────────\r\n \"word-break\": {\r\n language: \"html\",\r\n description: \"Wrap long text in a table cell to force line breaks without word-break\",\r\n before: `<span style=\"word-break: break-all;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n</span>`,\r\n after: `<!-- Table cells force text wrapping in all clients including Outlook -->\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"word-break: break-all; overflow-wrap: break-word;\r\n word-wrap: break-word;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n \"word-break::jsx\": {\r\n language: \"jsx\",\r\n description: \"Wrap long text in a table cell for Outlook-safe word breaking\",\r\n before: `<span style={{ wordBreak: \"break-all\" }}>{url}</span>`,\r\n after: `{/* Table cells force text wrapping in Outlook and Yahoo */}\r\n<table width=\"100%\" cellPadding={0} cellSpacing={0}\r\n role=\"presentation\" style={{ borderCollapse: \"collapse\" }}>\r\n <tr>\r\n <td style={{\r\n wordBreak: \"break-all\" as const,\r\n overflowWrap: \"break-word\" as const,\r\n wordWrap: \"break-word\" as const,\r\n }}>\r\n {url}\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n \"word-break::mjml\": {\r\n language: \"mjml\",\r\n description: \"MJML renders text in table cells by default — word-break works via mj-text\",\r\n before: `<mj-text>\r\n <span style=\"word-break: break-all;\">Long URL here</span>\r\n</mj-text>`,\r\n after: `<!-- mj-text already renders inside a <td>, so add word-break\r\n to the mj-text css-class or inline style -->\r\n<mj-text css-class=\"break-words\"\r\n padding=\"0\">\r\n Long URL here\r\n</mj-text>\r\n<mj-style>\r\n .break-words td { word-break: break-all; word-wrap: break-word; }\r\n</mj-style>`,\r\n },\r\n\r\n // ── overflow-wrap → table cell wrapping ────────────────────────────────\r\n \"overflow-wrap\": {\r\n language: \"html\",\r\n description: \"Use a table cell to force word wrapping without overflow-wrap\",\r\n before: `<p style=\"overflow-wrap: break-word;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n</p>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"overflow-wrap: break-word; word-wrap: break-word;\r\n word-break: break-all;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n \"overflow-wrap::jsx\": {\r\n language: \"jsx\",\r\n description: \"Wrap text in a table cell for Outlook-safe overflow wrapping\",\r\n before: `<p style={{ overflowWrap: \"break-word\" }}>{longText}</p>`,\r\n after: `<table width=\"100%\" cellPadding={0} cellSpacing={0}\r\n role=\"presentation\" style={{ borderCollapse: \"collapse\" }}>\r\n <tr>\r\n <td style={{\r\n overflowWrap: \"break-word\" as const,\r\n wordWrap: \"break-word\" as const,\r\n wordBreak: \"break-all\" as const,\r\n }}>\r\n {longText}\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── text-shadow → border/font-weight alternative ───────────────────────\r\n \"text-shadow\": {\r\n language: \"css\",\r\n description: \"Use font-weight or border-bottom as alternatives to text-shadow\",\r\n before: `.glow {\r\n text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);\r\n}`,\r\n after: `.glow {\r\n /* text-shadow is not supported in Gmail, Outlook, Yahoo.\r\n Use font-weight or letter-spacing for emphasis instead. */\r\n font-weight: bold;\r\n letter-spacing: 0.5px;\r\n}`,\r\n },\r\n\r\n // ── border-spacing → cellspacing attribute ─────────────────────────────\r\n \"border-spacing\": {\r\n language: \"html\",\r\n description: \"Use the cellspacing HTML attribute instead of border-spacing CSS\",\r\n before: `<table style=\"border-spacing: 8px; border-collapse: separate;\">\r\n <tr><td>Cell</td></tr>\r\n</table>`,\r\n after: `<table cellspacing=\"8\" style=\"border-collapse: separate;\">\r\n <tr><td>Cell</td></tr>\r\n</table>`,\r\n },\r\n\r\n // ── min-width → fixed width ────────────────────────────────────────────\r\n \"min-width\": {\r\n language: \"html\",\r\n description: \"Use a fixed width instead of min-width for Outlook compatibility\",\r\n before: `<td style=\"min-width: 200px;\">Content</td>`,\r\n after: `<!-- Outlook ignores min-width. Use a fixed width or a spacer. -->\r\n<td width=\"200\" style=\"width: 200px;\">Content</td>`,\r\n },\r\n\r\n // ── min-height → fixed height ──────────────────────────────────────────\r\n \"min-height\": {\r\n language: \"html\",\r\n description: \"Use a fixed height or spacer instead of min-height\",\r\n before: `<td style=\"min-height: 100px;\">Content</td>`,\r\n after: `<!-- Outlook ignores min-height. Use height or a spacer image. -->\r\n<td height=\"100\" style=\"height: 100px;\">Content</td>`,\r\n },\r\n\r\n // ── max-height → fixed height ──────────────────────────────────────────\r\n \"max-height\": {\r\n language: \"html\",\r\n description: \"Outlook ignores max-height — truncate content server-side\",\r\n before: `<div style=\"max-height: 200px; overflow: hidden;\">\r\n Long content...\r\n</div>`,\r\n after: `<!-- Outlook ignores max-height. Truncate content server-side. -->\r\n<div style=\"height: 200px;\">\r\n Shortened content...\r\n <a href=\"https://example.com/full\">Read more</a>\r\n</div>`,\r\n },\r\n\r\n // ── REACT EMAIL (jsx) framework-specific fixes ────────────────────────────\r\n\r\n \"display:flex::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row + Column components instead of flexbox (Outlook-safe)\",\r\n before: `<div style={{ display: \"flex\", gap: \"16px\" }}>\r\n <div style={{ flex: 1 }}>Column 1</div>\r\n <div style={{ flex: 1 }}>Column 2</div>\r\n</div>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"50%\", verticalAlign: \"top\" }}>\r\n Column 1\r\n </Column>\r\n <Column style={{ width: \"50%\", verticalAlign: \"top\" }}>\r\n Column 2\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n \"display:grid::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace CSS Grid with React Email Row + Column components\",\r\n before: `<div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: \"16px\" }}>\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n</div>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"50%\", padding: \"8px\", verticalAlign: \"top\" }}>\r\n Item 1\r\n </Column>\r\n <Column style={{ width: \"50%\", padding: \"8px\", verticalAlign: \"top\" }}>\r\n Item 2\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n \"max-width::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Container component for Outlook-safe max-width centering\",\r\n before: `<div style={{ maxWidth: \"600px\", margin: \"0 auto\" }}>\r\n Content here\r\n</div>`,\r\n after: `import { Container } from \"@react-email/components\";\r\n\r\n<Container style={{ maxWidth: \"600px\" }}>\r\n Content here\r\n</Container>`,\r\n },\r\n\r\n \"@font-face::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Font component instead of @font-face in <style>\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n @font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n }\r\n \\`}</style>\r\n</Head>\r\n<h1 style={{ fontFamily: \"'CustomFont'\" }}>Hello</h1>`,\r\n after: `import { Head, Font } from \"@react-email/components\";\r\n\r\n<Head>\r\n <Font\r\n fontFamily=\"CustomFont\"\r\n fallbackFontFamily=\"Arial\"\r\n webFont={{ url: \"https://example.com/custom.woff2\", format: \"woff2\" }}\r\n fontWeight={400}\r\n fontStyle=\"normal\"\r\n />\r\n</Head>\r\n<h1 style={{ fontFamily: \"'CustomFont', Arial, sans-serif\" }}>Hello</h1>`,\r\n },\r\n\r\n \"<svg>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace inline SVG with React Email Img component\",\r\n before: `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" fill=\"#6d28d9\"/>\r\n</svg>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n<Img\r\n src=\"https://example.com/icon.png\"\r\n width={24}\r\n height={24}\r\n alt=\"Icon\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n \"<video>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace video with a linked thumbnail using React Email Img + Link\",\r\n before: `<video width=\"600\" autoPlay muted>\r\n <source src=\"demo.mp4\" type=\"video/mp4\" />\r\n</video>`,\r\n after: `import { Img, Link } from \"@react-email/components\";\r\n\r\n<Link href=\"https://example.com/watch\">\r\n <Img\r\n src=\"https://example.com/video-thumb.gif\"\r\n width={600}\r\n alt=\"Watch the video\"\r\n style={{ display: \"block\", border: \"0\", maxWidth: \"100%\" }}\r\n />\r\n</Link>`,\r\n },\r\n\r\n \"border-radius::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Render rounded buttons with VML via JSX dangerouslySetInnerHTML (Outlook workaround)\",\r\n before: `<a\r\n href=\"https://example.com\"\r\n style={{ backgroundColor: \"#6d28d9\", color: \"#fff\",\r\n padding: \"12px 32px\", borderRadius: \"6px\",\r\n textDecoration: \"none\", display: \"inline-block\" }}>\r\n Click Here\r\n</a>`,\r\n after: `{/* Use dangerouslySetInnerHTML to inject VML for Outlook rounded corners */}\r\n<div\r\n dangerouslySetInnerHTML={{\r\n __html: \\`\r\n<!--[if mso]>\r\n<v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\"\r\n href=\"https://example.com\"\r\n style=\"height:44px; v-text-anchor:middle; width:200px;\"\r\n arcsize=\"14%\" strokecolor=\"#6d28d9\" fillcolor=\"#6d28d9\">\r\n <w:anchorlock/>\r\n <center style=\"color:#fff; font-family:Arial,sans-serif;\r\n font-size:14px; font-weight:bold;\">Click Here</center>\r\n</v:roundrect>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<a href=\"https://example.com\"\r\n style=\"background-color:#6d28d9; color:#fff; padding:12px 32px;\r\n border-radius:6px; text-decoration:none; display:inline-block;\">\r\n Click Here\r\n</a>\r\n<!--<![endif]-->\r\n\\`,\r\n }}\r\n/>`,\r\n },\r\n\r\n \"gap::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use padding style prop on Column instead of gap (email-safe spacing)\",\r\n before: `<Row style={{ gap: \"16px\" }}>\r\n <Column>Item 1</Column>\r\n <Column>Item 2</Column>\r\n <Column>Item 3</Column>\r\n</Row>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n{/* Use padding on Column — gap is not supported in email clients */}\r\n<Row>\r\n <Column style={{ paddingRight: \"16px\" }}>Item 1</Column>\r\n <Column style={{ paddingRight: \"16px\" }}>Item 2</Column>\r\n <Column>Item 3</Column>\r\n</Row>`,\r\n },\r\n\r\n \"<style>::gmail::jsx\": {\r\n language: \"jsx\",\r\n description: \"React Email inlines styles via style props — manual <style> blocks won't survive Gmail\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n .title { color: #fff; font-size: 24px; }\r\n \\`}</style>\r\n</Head>\r\n<div className=\"header\">\r\n <h1 className=\"title\">Hello</h1>\r\n</div>`,\r\n after: `{/* Gmail strips <style> blocks. Move styles to inline style props: */}\r\n<div style={{ backgroundColor: \"#6d28d9\", padding: \"32px\" }}>\r\n <h1 style={{ color: \"#fff\", fontSize: \"24px\", margin: 0 }}>Hello</h1>\r\n</div>`,\r\n },\r\n\r\n \"<link>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Head component instead of <link> for stylesheet references\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <link rel=\"stylesheet\" href=\"styles.css\" />\r\n</Head>`,\r\n after: `import { Head } from \"@react-email/components\";\r\n\r\n{/* External stylesheets are stripped by most email clients.\r\n Place styles inline via style props, or use Head for font imports only. */}\r\n<Head>\r\n {/* Inline your CSS here or use Font component for web fonts */}\r\n</Head>`,\r\n },\r\n\r\n // ── MJML framework-specific fixes ─────────────────────────────────────────\r\n\r\n \"@font-face::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-font in mj-head instead of @font-face\",\r\n before: `<mj-style>\r\n @font-face {\r\n font-family: 'CustomFont';\r\n src: url('https://example.com/custom.woff2') format('woff2');\r\n }\r\n</mj-style>`,\r\n after: `<mjml>\r\n <mj-head>\r\n <mj-font name=\"CustomFont\"\r\n href=\"https://fonts.googleapis.com/css2?family=CustomFont\" />\r\n <mj-attributes>\r\n <mj-all font-family=\"CustomFont, Arial, Helvetica, sans-serif\" />\r\n </mj-attributes>\r\n </mj-head>\r\n</mjml>`,\r\n },\r\n\r\n \"<style>::gmail::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-style inline='inline' to force style inlining for Gmail\",\r\n before: `<mj-head>\r\n <mj-style>\r\n .custom { color: #6d28d9; }\r\n </mj-style>\r\n</mj-head>`,\r\n after: `<mj-head>\r\n <!-- Use inline=\"inline\" to force MJML to inline these styles.\r\n Class-based styles in a plain mj-style block will be stripped by Gmail. -->\r\n <mj-style inline=\"inline\">\r\n .custom { color: #6d28d9; }\r\n </mj-style>\r\n</mj-head>`,\r\n },\r\n\r\n \"border-radius::outlook::mjml\": {\r\n language: \"mjml\",\r\n description: \"MJML limitation: border-radius is unsupported in Outlook — MJML does not generate VML\",\r\n before: `<mj-button border-radius=\"6px\" background-color=\"#6d28d9\">\r\n Click Here\r\n</mj-button>`,\r\n after: `<!-- Known MJML limitation: MJML does not generate VML for rounded corners.\r\n Options: accept flat corners, use mj-raw for VML, or set border-radius=\"0\". -->\r\n<mj-button border-radius=\"0\" background-color=\"#6d28d9\">\r\n Click Here\r\n</mj-button>`,\r\n },\r\n\r\n \"background-image::outlook::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-section background-url for Outlook-compatible background images\",\r\n before: `<mj-section>\r\n <mj-column>\r\n <mj-image src=\"hero.jpg\" />\r\n </mj-column>\r\n</mj-section>`,\r\n after: `<!-- MJML generates VML-compatible markup automatically via background-url on mj-section. -->\r\n<mj-section background-url=\"https://example.com/hero.jpg\"\r\n background-size=\"cover\"\r\n background-repeat=\"no-repeat\"\r\n background-color=\"#333333\">\r\n <mj-column>\r\n <mj-text color=\"#ffffff\">Your content here</mj-text>\r\n </mj-column>\r\n</mj-section>`,\r\n },\r\n\r\n \"display:flex::mjml\": {\r\n language: \"mjml\",\r\n description: \"Replace flexbox (from mj-raw or inline styles) with mj-section and mj-column\",\r\n before: `<mj-raw>\r\n <div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n </div>\r\n</mj-raw>`,\r\n after: `<!-- Flexbox in MJML is not Outlook-compatible.\r\n Use mj-section and mj-column — MJML compiles these to table-based layouts. -->\r\n<mj-section>\r\n <mj-column width=\"50%\">\r\n <mj-text>Column 1</mj-text>\r\n </mj-column>\r\n <mj-column width=\"50%\">\r\n <mj-text>Column 2</mj-text>\r\n </mj-column>\r\n</mj-section>`,\r\n },\r\n\r\n \"@media::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use MJML responsive attributes and breakpoints instead of hand-written @media\",\r\n before: `<mj-style>\r\n @media (max-width: 600px) {\r\n .mobile-stack { display: block !important; width: 100% !important; }\r\n }\r\n</mj-style>`,\r\n after: `<!-- MJML generates responsive @media queries automatically.\r\n Use mj-breakpoint and mj-column widths to control responsive behavior. -->\r\n<mj-head>\r\n <mj-breakpoint width=\"600px\" />\r\n</mj-head>\r\n<mj-body>\r\n <mj-section>\r\n <mj-column width=\"50%\"><mj-text>Left</mj-text></mj-column>\r\n <mj-column width=\"50%\"><mj-text>Right</mj-text></mj-column>\r\n </mj-section>\r\n</mj-body>`,\r\n },\r\n\r\n // ── MAIZZLE framework-specific fixes ──────────────────────────────────────\r\n\r\n \"display:flex::outlook::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Replace Tailwind flex classes with HTML table + MSO conditional comments\",\r\n before: `<div class=\"flex gap-4\">\r\n <div class=\"flex-1\">Column 1</div>\r\n <div class=\"flex-1\">Column 2</div>\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\"><tr>\r\n <td class=\"w-1/2\" valign=\"top\">Column 1</td>\r\n <td class=\"w-1/2\" valign=\"top\">Column 2</td>\r\n</tr></table>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<div class=\"flex gap-4\">\r\n <div class=\"flex-1\">Column 1</div>\r\n <div class=\"flex-1\">Column 2</div>\r\n</div>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n \"@font-face::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Add fonts via the googleFonts key in config.js — Maizzle injects the Google Fonts link tag automatically. Set googleFonts: \\\"Inter:ital,wght@0,400;0,700\\\" in your environment config, then reference the font family in your template.\",\r\n before: `<style>\r\n @font-face {\r\n font-family: 'Inter';\r\n src: url('https://fonts.gstatic.com/...') format('woff2');\r\n }\r\n</style>`,\r\n after: `<!-- config.js: googleFonts: \"Inter:ital,wght@0,400;0,700\" -->\r\n<p class=\"font-['Inter',Arial,sans-serif]\">Hello</p>`,\r\n },\r\n\r\n \"<style>::gmail::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Maizzle automatically inlines CSS via juice during build (inlineCSS: true in config.js). Manual <style> blocks bypass juice and will be stripped by Gmail — prefer Tailwind utility classes instead.\",\r\n before: `<style>\r\n .custom { color: #6d28d9; }\r\n</style>\r\n<div class=\"custom\">Hello</div>`,\r\n after: `<!-- Prefer Tailwind classes — Maizzle inlines them automatically during build -->\r\n<div class=\"text-[#6d28d9]\">Hello</div>`,\r\n },\r\n\r\n \"max-width::outlook::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Wrap max-width containers with MSO conditional table for Outlook\",\r\n before: `<div class=\"max-w-[600px] mx-auto\">\r\n Content here\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"600\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\" align=\"center\"><tr><td>\r\n<![endif]-->\r\n<div class=\"max-w-[600px] mx-auto\">\r\n Content here\r\n</div>\r\n<!--[if mso]>\r\n</td></tr></table>\r\n<![endif]-->`,\r\n },\r\n\r\n \"gap::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Use padding Tailwind classes on child elements instead of gap\",\r\n before: `<div class=\"flex gap-4\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n <div>Item 3</div>\r\n</div>`,\r\n after: `<!-- gap is not supported in Outlook or many email clients.\r\n Use padding classes on child elements instead. -->\r\n<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td class=\"pr-4\">Item 1</td>\r\n <td class=\"pr-4\">Item 2</td>\r\n <td>Item 3</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── JSX fixes for remaining common properties ─────────────────────────────\r\n\r\n \"<form>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace embedded form with a React Email Button linking to a hosted form\",\r\n before: `<form action=\"/subscribe\" method=\"POST\">\r\n <input type=\"email\" placeholder=\"Email\" />\r\n <button type=\"submit\">Subscribe</button>\r\n</form>`,\r\n after: `import { Button } from \"@react-email/components\";\r\n\r\n<Button\r\n href=\"https://example.com/subscribe\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n padding: \"12px 32px\",\r\n borderRadius: \"6px\",\r\n textDecoration: \"none\",\r\n fontWeight: \"bold\",\r\n }}\r\n>\r\n Subscribe Now\r\n</Button>`,\r\n },\r\n\r\n \"@media::jsx\": {\r\n language: \"jsx\",\r\n description: \"Design mobile-first — @media queries are stripped by many clients\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n @media (max-width: 600px) {\r\n .cols { display: block !important; }\r\n .col { width: 100% !important; }\r\n }\r\n \\`}</style>\r\n</Head>\r\n<Row>\r\n <Column className=\"col\" style={{ width: \"50%\" }}>Left</Column>\r\n <Column className=\"col\" style={{ width: \"50%\" }}>Right</Column>\r\n</Row>`,\r\n after: `import { Container, Section, Text } from \"@react-email/components\";\r\n\r\n{/* Single-column stacked layout works without @media.\r\n Stack content vertically so it reads well on all clients. */}\r\n<Container style={{ maxWidth: \"600px\" }}>\r\n <Section style={{ padding: \"16px\" }}>\r\n <Text>Left</Text>\r\n </Section>\r\n <Section style={{ padding: \"16px\" }}>\r\n <Text>Right</Text>\r\n </Section>\r\n</Container>`,\r\n },\r\n\r\n \"position::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row and Column for layout instead of CSS position\",\r\n before: `<div style={{ position: \"relative\" }}>\r\n <div style={{ position: \"absolute\", top: 0, right: 0 }}>\r\n Badge\r\n </div>\r\n <p>Content</p>\r\n</div>`,\r\n after: `import { Row, Column, Text } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ verticalAlign: \"top\" }}>\r\n <Text>Content</Text>\r\n </Column>\r\n <Column style={{ width: \"80px\", verticalAlign: \"top\", textAlign: \"right\" }}>\r\n <Text>Badge</Text>\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n \"float::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row and Column instead of float for side-by-side layout\",\r\n before: `<img src=\"photo.jpg\" style={{ float: \"left\", marginRight: \"16px\" }} width={200} />\r\n<p>Text wraps around the image.</p>`,\r\n after: `import { Row, Column, Img, Text } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"200px\", paddingRight: \"16px\", verticalAlign: \"top\" }}>\r\n <Img src=\"photo.jpg\" width={200} style={{ display: \"block\", border: \"0\" }} />\r\n </Column>\r\n <Column style={{ verticalAlign: \"top\" }}>\r\n <Text>Text next to the image.</Text>\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n \"background-image::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use VML for Outlook background images in JSX via dangerouslySetInnerHTML\",\r\n before: `<td style={{ backgroundImage: \"url('hero.jpg')\",\r\n backgroundSize: \"cover\", padding: \"40px\" }}>\r\n <h1 style={{ color: \"#fff\" }}>Hello World</h1>\r\n</td>`,\r\n after: `<div\r\n dangerouslySetInnerHTML={{\r\n __html: \\`\r\n<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px; height:300px;\">\r\n <v:fill type=\"frame\" src=\"hero.jpg\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<div style=\"background-image:url('hero.jpg'); background-size:cover; padding:40px;\">\r\n <h1 style=\"color:#fff;\">Hello World</h1>\r\n</div>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->\r\n\\`,\r\n }}\r\n/>`,\r\n },\r\n\r\n \"opacity::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use solid colors instead of opacity in style objects\",\r\n before: `<div style={{\r\n backgroundColor: \"#000\",\r\n opacity: 0.5,\r\n}}>\r\n Overlay content\r\n</div>`,\r\n after: `<div style={{\r\n /* Use a pre-mixed solid color instead of opacity */\r\n backgroundColor: \"#808080\",\r\n}}>\r\n Overlay content\r\n</div>`,\r\n },\r\n\r\n \"box-shadow::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use border as a fallback for boxShadow in style objects\",\r\n before: `<div style={{\r\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.15)\",\r\n padding: \"24px\",\r\n}}>\r\n Card content\r\n</div>`,\r\n after: `import { Section } from \"@react-email/components\";\r\n\r\n<Section style={{\r\n border: \"1px solid #e0e0e0\",\r\n padding: \"24px\",\r\n}}>\r\n Card content\r\n</Section>`,\r\n },\r\n\r\n \"linear-gradient::jsx\": {\r\n language: \"jsx\",\r\n description: \"Add a solid backgroundColor fallback before the gradient\",\r\n before: `<div style={{\r\n background: \"linear-gradient(135deg, #667eea, #764ba2)\",\r\n padding: \"40px\",\r\n color: \"#fff\",\r\n}}>\r\n Content here\r\n</div>`,\r\n after: `<div style={{\r\n /* Solid fallback for clients that strip gradients */\r\n backgroundColor: \"#667eea\",\r\n background: \"linear-gradient(135deg, #667eea, #764ba2)\",\r\n padding: \"40px\",\r\n color: \"#fff\",\r\n}}>\r\n Content here\r\n</div>`,\r\n },\r\n\r\n \"transform::jsx\": {\r\n language: \"jsx\",\r\n description: \"Pre-render transformed content as an image using React Email Img\",\r\n before: `<div style={{ transform: \"rotate(45deg)\" }}>\r\n Rotated content\r\n</div>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* CSS transforms are not supported in email — pre-render as an image */}\r\n<Img\r\n src=\"https://example.com/rotated-content.png\"\r\n width={200}\r\n height={200}\r\n alt=\"Rotated content\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n \"animation::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace CSS animation with a React Email Img using an animated GIF\",\r\n before: `<span style={{ animation: \"pulse 2s infinite\" }}>New!</span>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* CSS animations are not supported — use an animated GIF */}\r\n<Img\r\n src=\"https://example.com/badge-animated.gif\"\r\n width={60}\r\n height={24}\r\n alt=\"New!\"\r\n style={{ display: \"inline-block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n \"transition::jsx\": {\r\n language: \"jsx\",\r\n description: \"Transitions don't work in email — style the default state well\",\r\n before: `<a\r\n href=\"#\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n>\r\n Click\r\n</a>`,\r\n after: `import { Button } from \"@react-email/components\";\r\n\r\n{/* Transitions are not supported — style the default state: */}\r\n<Button\r\n href=\"https://example.com\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n textDecoration: \"none\",\r\n fontWeight: \"bold\",\r\n padding: \"12px 32px\",\r\n }}\r\n>\r\n Click\r\n</Button>`,\r\n },\r\n\r\n \"overflow::jsx\": {\r\n language: \"jsx\",\r\n description: \"Content will always be visible — design for full content display\",\r\n before: `<div style={{ maxHeight: \"200px\", overflow: \"hidden\" }}>\r\n Long content that gets clipped...\r\n</div>`,\r\n after: `import { Link, Text } from \"@react-email/components\";\r\n\r\n{/* overflow:hidden is stripped — show full content or truncate server-side */}\r\n<div>\r\n <Text>Shortened content that fits...</Text>\r\n <Link href=\"https://example.com/full\">Read more</Link>\r\n</div>`,\r\n },\r\n\r\n \"visibility::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use font-size/max-height trick instead of visibility:hidden\",\r\n before: `<div style={{ visibility: \"hidden\" }}>\r\n Hidden preheader text\r\n</div>`,\r\n after: `{/* visibility:hidden is stripped by most clients — use the preheader trick.\r\n msoHide is non-standard but needed to hide content in Outlook. */}\r\n<div\r\n style={{\r\n fontSize: \"0px\",\r\n lineHeight: \"0px\",\r\n maxHeight: \"0px\",\r\n overflow: \"hidden\",\r\n display: \"none\",\r\n msoHide: \"all\",\r\n } as React.CSSProperties}\r\n aria-hidden=\"true\"\r\n>\r\n Preheader text\r\n</div>`,\r\n },\r\n\r\n \"object-fit::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Img with explicit width/height instead of object-fit\",\r\n before: `<img\r\n src=\"photo.jpg\"\r\n style={{ width: \"300px\", height: \"200px\", objectFit: \"cover\" }}\r\n/>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* Crop/resize image server-side to exact dimensions */}\r\n<Img\r\n src=\"https://example.com/photo-300x200.jpg\"\r\n width={300}\r\n height={200}\r\n alt=\"Photo\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n \"background-size::jsx\": {\r\n language: \"jsx\",\r\n description: \"Outlook ignores background-size — use sized images instead\",\r\n before: `<div style={{\r\n background: \"url('bg.jpg') center/cover no-repeat\",\r\n}}>\r\n Content\r\n</div>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* background-size is not supported in most email clients.\r\n Use a full-width image instead of a background: */}\r\n<Img\r\n src=\"https://example.com/bg-600x400.jpg\"\r\n width={600}\r\n alt=\"\"\r\n style={{ display: \"block\", width: \"100%\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n \"box-sizing::jsx\": {\r\n language: \"jsx\",\r\n description: \"Account for padding in width manually (no box-sizing support)\",\r\n before: `<div style={{\r\n width: \"300px\",\r\n padding: \"20px\",\r\n boxSizing: \"border-box\",\r\n}}>\r\n Content — total width stays 300px\r\n</div>`,\r\n after: `{/* Set outer width, use inner element for padding */}\r\n<div style={{ width: \"300px\" }}>\r\n <div style={{ padding: \"20px\" }}>\r\n Content — padding on inner element\r\n </div>\r\n</div>`,\r\n },\r\n};\r\n\r\n/**\r\n * Look up a code fix for a given property, client, and optional framework.\r\n * Returns undefined if no fix snippet exists.\r\n *\r\n * Resolution order (most specific to least specific):\r\n * 1. property::clientPrefix::framework (e.g. \"display:flex::outlook::jsx\")\r\n * 2. property::framework (e.g. \"display:grid::jsx\")\r\n * 3. property::clientPrefix (e.g. \"border-radius::outlook\")\r\n * 4. property (generic fix)\r\n */\r\nexport function getCodeFix(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): CodeFix | undefined {\r\n const clientPrefix = getClientPrefix(clientId);\r\n\r\n // Tier 1: property::clientPrefix::framework\r\n if (framework && clientPrefix) {\r\n const tier1 = FIX_DATABASE[`${property}::${clientPrefix}::${framework}`];\r\n if (tier1) return tier1;\r\n }\r\n\r\n // Tier 2: property::framework\r\n if (framework) {\r\n const tier2 = FIX_DATABASE[`${property}::${framework}`];\r\n if (tier2) return tier2;\r\n }\r\n\r\n // Tier 3: property::clientPrefix (existing behavior)\r\n if (clientPrefix) {\r\n const tier3 = FIX_DATABASE[`${property}::${clientPrefix}`];\r\n if (tier3) return tier3;\r\n }\r\n\r\n // Tier 4: generic fix (existing behavior)\r\n return FIX_DATABASE[property];\r\n}\r\n\r\n/**\r\n * Returns true if a framework was specified but the code fix resolved to\r\n * a client-specific or fully generic entry (tiers 3–4) rather than a\r\n * framework-aware entry (tiers 1–2).\r\n */\r\nexport function isCodeFixGenericFallback(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): boolean {\r\n if (!framework) return false;\r\n const clientPrefix = getClientPrefix(clientId);\r\n if (clientPrefix && FIX_DATABASE[`${property}::${clientPrefix}::${framework}`]) return false;\r\n if (FIX_DATABASE[`${property}::${framework}`]) return false;\r\n return true;\r\n}\r\n\r\nfunction getClientPrefix(clientId: string): string | null {\r\n if (clientId.startsWith(\"outlook-windows\")) return \"outlook\";\r\n if (clientId.startsWith(\"outlook\")) return null; // Outlook web is more standards-compliant\r\n if (clientId.startsWith(\"gmail\")) return \"gmail\";\r\n if (clientId.startsWith(\"apple-mail\")) return \"apple\";\r\n if (clientId === \"yahoo-mail\") return \"yahoo\";\r\n if (clientId === \"samsung-mail\") return \"samsung\";\r\n return null;\r\n}\r\n\r\n// =============================================================================\r\n// Suggestion Database — framework-aware suggestion text for warnings\r\n// =============================================================================\r\n\r\n/**\r\n * Human-readable suggestion strings attached to CSSWarning.suggestion.\r\n *\r\n * Key format mirrors FIX_DATABASE:\r\n * property → generic HTML advice\r\n * property::clientPrefix → client-specific advice\r\n * property::framework → framework-specific advice\r\n * property::clientPrefix::framework → most-specific advice\r\n *\r\n * Use `getSuggestion()` to resolve the best match via tiered lookup.\r\n */\r\nconst SUGGESTION_DATABASE: Record<string, string> = {\r\n // ── <style> ───────────────────────────────────────────────────────────\r\n \"<style>\":\r\n \"Use a CSS inliner tool (like juice) to move styles to inline attributes.\",\r\n \"<style>:partial\":\r\n \"Use inline styles as the primary approach, with <style> in <head> as progressive enhancement.\",\r\n \"<style>::jsx\":\r\n \"Move styles to inline style props — React Email components accept style objects directly.\",\r\n \"<style>:partial::jsx\":\r\n \"Use inline style props on React Email components. Reserve <style> in <Head> for progressive enhancement only.\",\r\n \"<style>::mjml\":\r\n 'Use mj-style inline=\"inline\" to force MJML to inline styles for Gmail compatibility.',\r\n \"<style>:partial::mjml\":\r\n 'Use mj-style inline=\"inline\" for critical styles; plain mj-style for progressive enhancement.',\r\n \"<style>::maizzle\":\r\n \"Prefer Tailwind utility classes — Maizzle inlines CSS via juice during build (inlineCSS: true in config.js).\",\r\n \"<style>:partial::maizzle\":\r\n \"Use Tailwind utility classes for critical styles. Maizzle automatically inlines them at build time.\",\r\n\r\n // ── <link> ────────────────────────────────────────────────────────────\r\n \"<link>\": \"Inline all CSS directly in the HTML.\",\r\n \"<link>::jsx\":\r\n \"Use the React Email <Head> component for font imports; place all other styles inline via style props.\",\r\n \"<link>::mjml\":\r\n \"MJML does not support external stylesheets. Use mj-style or inline attributes.\",\r\n \"<link>::maizzle\":\r\n \"External stylesheets are stripped. Use Tailwind CSS classes — Maizzle inlines them at build time.\",\r\n\r\n // ── <svg> ─────────────────────────────────────────────────────────────\r\n \"<svg>\": \"Convert SVGs to PNG/JPG images.\",\r\n \"<svg>::jsx\":\r\n \"Replace inline SVG with the React Email <Img> component pointing to a hosted PNG.\",\r\n \"<svg>::mjml\":\r\n \"Replace inline SVG with an mj-image component pointing to a hosted PNG.\",\r\n \"<svg>::maizzle\":\r\n \"Replace inline SVG with an <img> tag pointing to a hosted PNG.\",\r\n\r\n // ── <video> ───────────────────────────────────────────────────────────\r\n \"<video>\":\r\n \"Use an animated GIF or a static image with a play button linking to the video.\",\r\n \"<video>::jsx\":\r\n \"Replace <video> with a React Email <Link> wrapping an <Img> thumbnail.\",\r\n \"<video>::mjml\":\r\n \"Replace <video> with an mj-image linking to a video thumbnail.\",\r\n \"<video>::maizzle\":\r\n \"Replace <video> with a linked image thumbnail.\",\r\n\r\n // ── <form> ────────────────────────────────────────────────────────────\r\n \"<form>\": \"Use links to a web form instead of embedding forms in email.\",\r\n \"<form>::jsx\":\r\n \"Replace the form with a React Email <Button> or <Link> component pointing to a hosted form page.\",\r\n \"<form>::mjml\":\r\n \"Replace the form with an mj-button linking to a hosted form page.\",\r\n \"<form>::maizzle\":\r\n \"Replace the form with a CTA link/button pointing to a hosted form page.\",\r\n\r\n // ── @font-face ────────────────────────────────────────────────────────\r\n \"@font-face\":\r\n \"Always include a web-safe font stack as fallback (e.g., Arial, Helvetica, sans-serif).\",\r\n \"@font-face::jsx\":\r\n \"Use the React Email <Font> component in <Head> with a fallbackFontFamily prop.\",\r\n \"@font-face::mjml\":\r\n \"Use mj-font in mj-head instead of @font-face in mj-style.\",\r\n \"@font-face::maizzle\":\r\n \"Use the googleFonts key in config.js — Maizzle injects the Google Fonts link tag automatically.\",\r\n\r\n // ── @media ────────────────────────────────────────────────────────────\r\n \"@media\":\r\n \"Design emails mobile-first with a single-column layout that works without media queries.\",\r\n \"@media::jsx\":\r\n \"Use a single-column layout with React Email <Container> and <Section>. Avoid relying on @media queries.\",\r\n \"@media::mjml\":\r\n \"MJML generates responsive @media queries automatically. Use mj-breakpoint and mj-column widths.\",\r\n \"@media::maizzle\":\r\n \"Use Tailwind responsive utility classes and Maizzle's breakpoints config instead of hand-written @media.\",\r\n\r\n // ── display:flex ──────────────────────────────────────────────────────\r\n \"display:flex\":\r\n \"Use <table> layouts for email client compatibility.\",\r\n \"display:flex::outlook\":\r\n \"Use <table> layouts with <!--[if mso]> conditional comments for Outlook's Word engine.\",\r\n \"display:flex::jsx\":\r\n \"Use React Email <Row> and <Column> components instead of flexbox.\",\r\n \"display:flex::mjml\":\r\n \"Use mj-section and mj-column — MJML compiles these to table-based layouts.\",\r\n \"display:flex::maizzle\":\r\n \"Replace Tailwind flex classes with HTML table + MSO conditional comments for Outlook.\",\r\n\r\n // ── display:grid ──────────────────────────────────────────────────────\r\n \"display:grid\":\r\n \"Replace CSS Grid with table layout for email compatibility.\",\r\n \"display:grid::jsx\":\r\n \"Use React Email <Row> and <Column> components instead of CSS Grid.\",\r\n \"display:grid::mjml\":\r\n \"Use mj-section and mj-column for grid-like layouts.\",\r\n \"display:grid::maizzle\":\r\n \"Replace Tailwind grid classes with HTML table layout for email compatibility.\",\r\n\r\n // ── linear-gradient ───────────────────────────────────────────────────\r\n \"linear-gradient\":\r\n \"Add a solid background-color fallback before the gradient.\",\r\n \"linear-gradient::jsx\":\r\n \"Add a solid backgroundColor style prop as fallback before the gradient.\",\r\n \"linear-gradient::mjml\":\r\n \"Add a background-color attribute on mj-section as a fallback.\",\r\n \"linear-gradient::maizzle\":\r\n \"Add a bg-[color] Tailwind class as a fallback before the gradient.\",\r\n\r\n // ── box-shadow ────────────────────────────────────────────────────────\r\n \"box-shadow\":\r\n \"Use border styling as an alternative to box-shadow.\",\r\n \"box-shadow::jsx\":\r\n \"Use a border style prop as an alternative to boxShadow.\",\r\n \"box-shadow::mjml\":\r\n \"Use a border attribute on mj-section or mj-column as an alternative.\",\r\n \"box-shadow::maizzle\":\r\n \"Use Tailwind border classes as an alternative to shadow classes.\",\r\n\r\n // ── border-radius ─────────────────────────────────────────────────────\r\n \"border-radius\":\r\n \"Use VML for rounded corners in Outlook, or accept square corners.\",\r\n \"border-radius::outlook\":\r\n \"Use VML (Vector Markup Language) for rounded buttons in Outlook.\",\r\n \"border-radius::jsx\":\r\n \"Outlook ignores borderRadius. Use dangerouslySetInnerHTML with VML for rounded buttons if needed.\",\r\n \"border-radius::mjml\":\r\n 'MJML does not generate VML — border-radius will not render in Outlook. Set border-radius=\"0\" or accept flat corners.',\r\n \"border-radius::maizzle\":\r\n \"Outlook ignores border-radius. Accept flat corners or use MSO conditional VML.\",\r\n\r\n // ── max-width ─────────────────────────────────────────────────────────\r\n \"max-width\":\r\n \"Use a fixed-width table wrapper for maximum compatibility.\",\r\n \"max-width::outlook\":\r\n \"Use a fixed width on table cells instead of max-width.\",\r\n \"max-width::jsx\":\r\n \"Use the React Email <Container> component which handles max-width across clients.\",\r\n \"max-width::mjml\":\r\n \"Set the width attribute on mj-body or mj-section for maximum compatibility.\",\r\n \"max-width::maizzle\":\r\n \"Wrap max-w containers with MSO conditional table for Outlook.\",\r\n\r\n // ── gap ───────────────────────────────────────────────────────────────\r\n \"gap\":\r\n \"Use padding/margin on child elements instead of gap.\",\r\n \"gap::outlook\":\r\n \"Use cellpadding/cellspacing on tables, or padding on cells.\",\r\n \"gap::jsx\":\r\n \"Use padding style props on <Column> components instead of gap.\",\r\n \"gap::mjml\":\r\n \"Use padding attribute on mj-column or mj-text for spacing.\",\r\n \"gap::maizzle\":\r\n \"Use Tailwind padding classes on child elements instead of gap.\",\r\n\r\n // ── float ─────────────────────────────────────────────────────────────\r\n \"float\":\r\n \"Use table cells with align attribute for side-by-side content.\",\r\n \"float::outlook\":\r\n 'Use table cells with align=\"left\" or align=\"right\".',\r\n \"float::jsx\":\r\n \"Use React Email <Row> and <Column> components for side-by-side layout.\",\r\n \"float::mjml\":\r\n \"Use mj-section with multiple mj-column elements for side-by-side layout.\",\r\n \"float::maizzle\":\r\n \"Use HTML tables for side-by-side layout instead of Tailwind float classes.\",\r\n\r\n // ── background-image ──────────────────────────────────────────────────\r\n \"background-image\":\r\n \"Use VML for background images in clients that require it.\",\r\n \"background-image::outlook\":\r\n \"Use <!--[if gte mso 9]> with <v:background> VML for Outlook background images.\",\r\n \"background-image::jsx\":\r\n \"Use VML via dangerouslySetInnerHTML for Outlook background images.\",\r\n \"background-image::mjml\":\r\n \"Use background-url attribute on mj-section — MJML generates VML automatically.\",\r\n \"background-image::maizzle\":\r\n \"Use MSO conditional VML for Outlook background images.\",\r\n\r\n // ── position ──────────────────────────────────────────────────────────\r\n \"position\":\r\n \"Use table-based positioning instead of CSS position.\",\r\n \"position::jsx\":\r\n \"Use React Email <Row> and <Column> components for positioning.\",\r\n \"position::mjml\":\r\n \"Use mj-section and mj-column for layout positioning.\",\r\n \"position::maizzle\":\r\n \"Use HTML table layout instead of Tailwind position classes.\",\r\n\r\n // ── opacity ───────────────────────────────────────────────────────────\r\n \"opacity\":\r\n \"Use solid colors instead of opacity.\",\r\n \"opacity::jsx\":\r\n \"Use solid colors. Opacity is not supported in many email clients.\",\r\n \"opacity::mjml\":\r\n \"Use solid colors. Most email clients don't support opacity.\",\r\n \"opacity::maizzle\":\r\n \"Use solid Tailwind color classes instead of opacity.\",\r\n\r\n // ── word-break ──────────────────────────────────────────────────────\r\n \"word-break\":\r\n \"Wrap long text in a <table><td> to force wrapping in clients that don't support word-break.\",\r\n \"word-break::outlook\":\r\n \"Outlook's Word engine ignores word-break. Place text inside a <td> with a constrained width — tables always wrap.\",\r\n \"word-break::jsx\":\r\n \"Wrap long text in a <table><tr><td> element. Outlook ignores wordBreak but respects table cell widths.\",\r\n \"word-break::mjml\":\r\n \"mj-text renders inside a <td>, which helps. Add word-wrap: break-word to the td via mj-style.\",\r\n\r\n // ── overflow-wrap ──────────────────────────────────────────────────\r\n \"overflow-wrap\":\r\n \"Wrap text in a <table><td> to force wrapping. overflow-wrap is ignored by Outlook and unreliable in Yahoo.\",\r\n \"overflow-wrap::jsx\":\r\n \"Wrap text in a <table><tr><td> element. Outlook ignores overflowWrap but respects table cell widths.\",\r\n\r\n // ── white-space ────────────────────────────────────────────────────\r\n \"white-space\":\r\n \"Outlook only supports 'normal' and 'nowrap'. Use &nbsp; for non-breaking spaces.\",\r\n\r\n // ── text-overflow ──────────────────────────────────────────────────\r\n \"text-overflow\":\r\n \"text-overflow requires overflow:hidden which is stripped by Gmail. Truncate content server-side.\",\r\n\r\n // ── vertical-align ─────────────────────────────────────────────────\r\n \"vertical-align\":\r\n 'Use the valign HTML attribute on <td> elements for Outlook (e.g., valign=\"top\").',\r\n\r\n // ── border-spacing ─────────────────────────────────────────────────\r\n \"border-spacing\":\r\n 'Use the cellspacing HTML attribute instead (e.g., <table cellspacing=\"8\">).',\r\n\r\n // ── min-width / min-height ─────────────────────────────────────────\r\n \"min-width\":\r\n \"Outlook ignores min-width. Use a fixed width attribute on <td> or <table>.\",\r\n \"min-height\":\r\n \"Outlook ignores min-height. Use a fixed height or a spacer image.\",\r\n \"max-height\":\r\n \"Outlook ignores max-height. Truncate content server-side or use a fixed height.\",\r\n\r\n // ── text-shadow ────────────────────────────────────────────────────\r\n \"text-shadow\":\r\n \"text-shadow is stripped by Gmail, Outlook, and Yahoo. Use font-weight for emphasis.\",\r\n\r\n // ── background-size / background-position ──────────────────────────\r\n \"background-size\":\r\n \"Not supported in many clients. Set image dimensions directly.\",\r\n \"background-position\":\r\n \"Not supported in many clients. Use VML for positioning.\",\r\n\r\n // ── Additional properties covered by transform helpers ────────────────\r\n \"overflow\":\r\n \"Content will always be visible. Design accordingly.\",\r\n \"visibility\":\r\n \"Remove the element or use display:none as an alternative.\",\r\n \"transform\":\r\n \"CSS transforms are not supported in email. Pre-render the effect as an image.\",\r\n \"animation\":\r\n \"CSS animations are not supported. Use animated GIFs instead.\",\r\n \"transition\":\r\n \"CSS transitions are not supported in email.\",\r\n \"box-sizing\":\r\n \"Account for padding in your width calculations (use padding on a nested element).\",\r\n \"object-fit\":\r\n \"Use width/height attributes on <img> directly.\",\r\n \"display\":\r\n \"Use tables for layout in email clients.\",\r\n};\r\n\r\n/**\r\n * Look up a suggestion string for a given property, client, and optional framework.\r\n *\r\n * Resolution order mirrors `getCodeFix()`:\r\n * 1. property::clientPrefix::framework\r\n * 2. property::framework\r\n * 3. property::clientPrefix\r\n * 4. property (generic)\r\n *\r\n * `isGenericFallback` is true when a framework was specified but no\r\n * framework-specific entry was found (resolution fell through to tiers 3–4).\r\n */\r\nexport function getSuggestion(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): { text: string; isGenericFallback: boolean } {\r\n const clientPrefix = getClientPrefix(clientId);\r\n\r\n // Tier 1: property::clientPrefix::framework\r\n if (framework && clientPrefix) {\r\n const tier1 = SUGGESTION_DATABASE[`${property}::${clientPrefix}::${framework}`];\r\n if (tier1) return { text: tier1, isGenericFallback: false };\r\n }\r\n\r\n // Tier 2: property::framework\r\n if (framework) {\r\n const tier2 = SUGGESTION_DATABASE[`${property}::${framework}`];\r\n if (tier2) return { text: tier2, isGenericFallback: false };\r\n }\r\n\r\n // Tier 3: property::clientPrefix\r\n if (clientPrefix) {\r\n const tier3 = SUGGESTION_DATABASE[`${property}::${clientPrefix}`];\r\n if (tier3) return { text: tier3, isGenericFallback: !!framework };\r\n }\r\n\r\n // Tier 4: generic\r\n const tier4 = SUGGESTION_DATABASE[property];\r\n if (tier4) return { text: tier4, isGenericFallback: !!framework };\r\n\r\n // No entry — return a default\r\n return {\r\n text: `\"${property}\" is not supported in this email client.`,\r\n isGenericFallback: !!framework,\r\n };\r\n}\r\n","/**\r\n * Shared utilities for parsing inline CSS style declarations.\r\n *\r\n * Used by both the analyzer and the per-client transformers to avoid\r\n * duplicating the semicolon-aware splitting logic.\r\n */\r\n\r\n/**\r\n * Split a CSS style string on semicolons while respecting\r\n * quoted strings and url()/function parentheses.\r\n */\r\nexport function splitStyleDeclarations(style: string): string[] {\r\n const parts: string[] = [];\r\n let current = \"\";\r\n let inSingleQuote = false;\r\n let inDoubleQuote = false;\r\n let parenDepth = 0;\r\n\r\n for (let i = 0; i < style.length; i++) {\r\n const ch = style[i];\r\n\r\n if (ch === \"'\" && !inDoubleQuote) {\r\n inSingleQuote = !inSingleQuote;\r\n } else if (ch === '\"' && !inSingleQuote) {\r\n inDoubleQuote = !inDoubleQuote;\r\n } else if (ch === \"(\" && !inSingleQuote && !inDoubleQuote) {\r\n parenDepth++;\r\n } else if (ch === \")\" && !inSingleQuote && !inDoubleQuote) {\r\n parenDepth = Math.max(0, parenDepth - 1);\r\n }\r\n\r\n if (ch === \";\" && !inSingleQuote && !inDoubleQuote && parenDepth === 0) {\r\n if (current.trim()) {\r\n parts.push(current.trim());\r\n }\r\n current = \"\";\r\n } else {\r\n current += ch;\r\n }\r\n }\r\n\r\n if (current.trim()) {\r\n parts.push(current.trim());\r\n }\r\n return parts;\r\n}\r\n\r\n/**\r\n * Parse an inline style string into a list of property names.\r\n */\r\nexport function parseStyleProperties(style: string): string[] {\r\n const props: string[] = [];\r\n const parts = splitStyleDeclarations(style);\r\n for (const part of parts) {\r\n const colonIndex = part.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = part.slice(0, colonIndex).trim().toLowerCase();\r\n if (prop) props.push(prop);\r\n }\r\n return props;\r\n}\r\n\r\n/**\r\n * Extract the value of a specific property from an inline style string.\r\n */\r\nexport function getStyleValue(style: string, property: string): string | null {\r\n const parts = splitStyleDeclarations(style);\r\n for (const part of parts) {\r\n const colonIndex = part.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = part.slice(0, colonIndex).trim().toLowerCase();\r\n if (prop === property) {\r\n return part.slice(colonIndex + 1).trim();\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Parse an inline style string into a Map of property → value.\r\n */\r\nexport function parseInlineStyle(style: string): Map<string, string> {\r\n const map = new Map<string, string>();\r\n const declarations = splitStyleDeclarations(style);\r\n for (const decl of declarations) {\r\n const colonIndex = decl.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = decl.slice(0, colonIndex).trim().toLowerCase();\r\n const value = decl.slice(colonIndex + 1).trim();\r\n if (prop && value) {\r\n map.set(prop, value);\r\n }\r\n }\r\n return map;\r\n}\r\n\r\n/**\r\n * Serialize a Map of property → value back to an inline style string.\r\n */\r\nexport function serializeStyle(map: Map<string, string>): string {\r\n const parts: string[] = [];\r\n map.forEach((value, prop) => {\r\n parts.push(`${prop}: ${value}`);\r\n });\r\n return parts.join(\"; \");\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport * as csstree from \"css-tree\";\r\nimport { CSS_SUPPORT, STRUCTURAL_FIX_PROPERTIES } from \"./rules/css-support\";\r\nimport { EMAIL_CLIENTS } from \"./clients\";\r\nimport { getCodeFix, getSuggestion, isCodeFixGenericFallback } from \"./fix-snippets\";\r\nimport { parseStyleProperties, getStyleValue } from \"./style-utils\";\r\nimport type { CSSWarning, FixType, Framework, SupportLevel } from \"./types\";\r\n\r\n/**\r\n * Analyze an HTML email and return CSS compatibility warnings\r\n * for all target email clients.\r\n *\r\n * The `framework` parameter controls which fix snippets are attached\r\n * to warnings — it does NOT change which warnings fire. Analysis always\r\n * runs on compiled HTML (what email clients actually receive). Fix\r\n * snippets reference source-level constructs so users know how to\r\n * modify their framework source code.\r\n */\r\nexport function analyzeEmail(html: string, framework?: Framework): CSSWarning[] {\r\n if (!html || !html.trim()) {\r\n return [];\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n const seenWarnings = new Set<string>();\r\n\r\n function addWarning(w: CSSWarning) {\r\n const key = `${w.client}:${w.property}:${w.severity}`;\r\n if (!seenWarnings.has(key)) {\r\n seenWarnings.add(key);\r\n warnings.push(w);\r\n }\r\n }\r\n\r\n // 1. Check for <style> block usage\r\n if ($(\"style\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<style>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<style>\", client.id, framework);\r\n const fix = getCodeFix(\"<style>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<style>\",\r\n message: `${client.name} strips <style> blocks. Styles must be inlined.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<style>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<style>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n } else if (support === \"partial\") {\r\n const sug = getSuggestion(\"<style>:partial\", client.id, framework);\r\n const fix = getCodeFix(\"<style>\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"<style>\",\r\n message: `${client.name} has partial <style> support (head only, with limitations). Inline styles recommended.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<style>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<style>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 2. Check for <link> stylesheet usage\r\n if ($(\"link[rel='stylesheet']\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<link>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<link>\", client.id, framework);\r\n const fix = getCodeFix(\"<link>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<link>\",\r\n message: `${client.name} does not support external stylesheets.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<link>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<link>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 3. Check for SVG usage\r\n if ($(\"svg\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<svg>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<svg>\", client.id, framework);\r\n const fix = getCodeFix(\"<svg>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<svg>\",\r\n message: `${client.name} does not support inline SVG.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<svg>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<svg>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 4. Check for video\r\n if ($(\"video\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<video>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<video>\", client.id, framework);\r\n const fix = getCodeFix(\"<video>\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"<video>\",\r\n message: `${client.name} does not support <video> elements.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<video>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<video>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 5. Check for form elements\r\n if ($(\"form\").length > 0 || $(\"input\").length > 0 || $(\"button[type='submit']\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<form>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<form>\", client.id, framework);\r\n const fix = getCodeFix(\"<form>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<form>\",\r\n message: `${client.name} strips form elements.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<form>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<form>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 6-7. Parse <style> blocks with css-tree for accurate at-rule and property detection\r\n const parsedAtRules = new Set<string>();\r\n const parsedProperties = new Set<string>();\r\n\r\n $(\"style\").each((_, el) => {\r\n const cssText = $(el).text();\r\n try {\r\n const ast = csstree.parse(cssText, { parseCustomProperty: true });\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Atrule\") {\r\n parsedAtRules.add(`@${node.name}`);\r\n }\r\n if (node.type === \"Declaration\") {\r\n parsedProperties.add(node.property.toLowerCase());\r\n // Check for display:flex / display:grid values\r\n if (node.property.toLowerCase() === \"display\") {\r\n const value = csstree.generate(node.value);\r\n if (value.includes(\"flex\")) parsedProperties.add(\"display:flex\");\r\n if (value.includes(\"grid\")) parsedProperties.add(\"display:grid\");\r\n }\r\n // Check for gradient values\r\n const valueStr = csstree.generate(node.value);\r\n if (valueStr.includes(\"linear-gradient\") || valueStr.includes(\"radial-gradient\")) {\r\n parsedProperties.add(\"linear-gradient\");\r\n }\r\n }\r\n },\r\n });\r\n } catch {\r\n // If css-tree can't parse, skip\r\n }\r\n });\r\n\r\n // Check @font-face\r\n if (parsedAtRules.has(\"@font-face\")) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"@font-face\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"@font-face\", client.id, framework);\r\n const fix = getCodeFix(\"@font-face\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"@font-face\",\r\n message: `${client.name} does not support web fonts (@font-face).`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"@font-face\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"@font-face\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Check @media queries\r\n if (parsedAtRules.has(\"@media\")) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"@media\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"@media\", client.id, framework);\r\n const fix = getCodeFix(\"@media\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"@media\",\r\n message: `${client.name} does not support @media queries.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"@media\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"@media\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 8. Scan inline styles for unsupported CSS properties\r\n const cssPropertiesToCheck = Object.keys(CSS_SUPPORT).filter(\r\n (k) => !k.startsWith(\"<\") && !k.startsWith(\"@\")\r\n );\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseStyleProperties(style);\r\n\r\n for (const prop of props) {\r\n // Check for flex/grid in display value\r\n if (prop === \"display\") {\r\n const value = getStyleValue(style, \"display\");\r\n if (value?.includes(\"flex\")) {\r\n checkPropertySupport(\"display:flex\", addWarning, framework);\r\n } else if (value?.includes(\"grid\")) {\r\n checkPropertySupport(\"display:grid\", addWarning, framework);\r\n }\r\n }\r\n\r\n // Check the property itself\r\n if (cssPropertiesToCheck.includes(prop)) {\r\n checkPropertySupport(prop, addWarning, framework);\r\n }\r\n\r\n // Check for gradient values in the property value\r\n const value = getStyleValue(style, prop);\r\n if (value && (value.includes(\"linear-gradient\") || value.includes(\"radial-gradient\"))) {\r\n checkPropertySupport(\"linear-gradient\", addWarning, framework);\r\n }\r\n }\r\n });\r\n\r\n // 9. Check CSS properties found in <style> blocks (via css-tree parsing)\r\n for (const prop of parsedProperties) {\r\n if (prop.includes(\":\")) continue; // skip compound like display:flex (handled separately)\r\n if (!cssPropertiesToCheck.includes(prop)) continue;\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[prop]?.[client.id];\r\n if (support === \"unsupported\") {\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: prop,\r\n message: `${client.name} does not support \"${prop}\" in <style> blocks.`,\r\n fixType: getFixType(prop),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Check compound properties from <style> blocks\r\n for (const compound of [\"display:flex\", \"display:grid\", \"linear-gradient\"]) {\r\n if (parsedProperties.has(compound)) {\r\n checkPropertySupport(compound, addWarning, framework);\r\n }\r\n }\r\n\r\n // Sort: errors first, then warnings, then info\r\n const severityOrder: Record<string, number> = { error: 0, warning: 1, info: 2 };\r\n warnings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);\r\n\r\n return warnings;\r\n}\r\n\r\nfunction getFixType(prop: string): FixType {\r\n return STRUCTURAL_FIX_PROPERTIES.has(prop) ? \"structural\" : \"css\";\r\n}\r\n\r\nfunction checkPropertySupport(\r\n prop: string,\r\n addWarning: (w: CSSWarning) => void,\r\n framework?: Framework\r\n) {\r\n const supportData = CSS_SUPPORT[prop];\r\n if (!supportData) return;\r\n\r\n const fixType = getFixType(prop);\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const support: SupportLevel = supportData[client.id] || \"unknown\";\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(prop, client.id, framework);\r\n const fix = getCodeFix(prop, client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: prop,\r\n message: `${client.name} does not support \"${prop}\".`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType,\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(prop, client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n } else if (support === \"partial\") {\r\n const sug = getSuggestion(prop, client.id, framework);\r\n const fix = getCodeFix(prop, client.id, framework);\r\n addWarning({\r\n severity: \"info\",\r\n client: client.id,\r\n property: prop,\r\n message: `${client.name} has partial support for \"${prop}\".`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType,\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(prop, client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Generate a summary of CSS compatibility for the email.\r\n */\r\nexport function generateCompatibilityScore(\r\n warnings: CSSWarning[]\r\n): Record<string, { score: number; errors: number; warnings: number; info: number }> {\r\n const result: Record<string, { score: number; errors: number; warnings: number; info: number }> = {};\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const clientWarnings = warnings.filter((w) => w.client === client.id);\r\n const errors = clientWarnings.filter((w) => w.severity === \"error\").length;\r\n const warns = clientWarnings.filter((w) => w.severity === \"warning\").length;\r\n const info = clientWarnings.filter((w) => w.severity === \"info\").length;\r\n\r\n // Score: 100 minus penalties, clamped to 0-100\r\n const score = Math.max(0, Math.min(100, 100 - errors * 15 - warns * 5 - info * 1));\r\n\r\n result[client.id] = { score, errors, warnings: warns, info };\r\n }\r\n\r\n return result;\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { CSSWarning } from \"./types\";\r\n\r\n/**\r\n * Simulate dark mode rendering for an email.\r\n * Email clients apply dark mode differently:\r\n * - Some invert colors (Gmail Android)\r\n * - Some use prefers-color-scheme media query (Apple Mail)\r\n * - Some do partial inversion (Outlook.com)\r\n */\r\nexport function simulateDarkMode(\r\n html: string,\r\n clientId: string\r\n): { html: string; warnings: CSSWarning[] } {\r\n if (!html || !html.trim()) {\r\n return { html: html || \"\", warnings: [] };\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Check for images with transparent backgrounds\r\n $(\"img\").each((_, el) => {\r\n const src = $(el).attr(\"src\") || \"\";\r\n if (src.endsWith(\".png\") || src.endsWith(\".svg\") || src.endsWith(\".webp\")) {\r\n warnings.push({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message: \"Image with potentially transparent background may disappear in dark mode.\",\r\n suggestion:\r\n 'Add a background-color to the parent element, or use a non-transparent image format.',\r\n });\r\n }\r\n });\r\n\r\n // Determine dark mode behavior based on client\r\n switch (clientId) {\r\n case \"gmail-web\":\r\n case \"gmail-android\":\r\n case \"gmail-ios\":\r\n // Gmail does full color inversion on Android,\r\n // partial on web/iOS\r\n applyColorInversion($, clientId === \"gmail-android\" ? \"full\" : \"partial\");\r\n break;\r\n\r\n case \"outlook-web\":\r\n // Outlook.com applies its own dark mode with partial inversion\r\n applyColorInversion($, \"partial\");\r\n break;\r\n\r\n case \"apple-mail-macos\":\r\n case \"apple-mail-ios\":\r\n // Apple Mail respects prefers-color-scheme\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"Apple Mail supports @media (prefers-color-scheme: dark). Consider adding dark mode styles.\",\r\n suggestion:\r\n \"Add a @media (prefers-color-scheme: dark) block with inverted colors for the best dark mode experience.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"yahoo-mail\":\r\n applyColorInversion($, \"partial\");\r\n break;\r\n\r\n case \"samsung-mail\":\r\n applyColorInversion($, \"full\");\r\n break;\r\n\r\n case \"hey-mail\":\r\n // HEY Mail respects prefers-color-scheme; simulate partial inversion\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"HEY Mail supports @media (prefers-color-scheme: dark). Add dark mode styles for the best experience.\",\r\n suggestion:\r\n \"Add a @media (prefers-color-scheme: dark) block with inverted colors.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"superhuman\":\r\n // Superhuman uses Blink and respects prefers-color-scheme\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"Superhuman respects @media (prefers-color-scheme: dark). Many Superhuman users run in dark mode.\",\r\n suggestion:\r\n \"Add @media (prefers-color-scheme: dark) styles — Superhuman's power-user audience often prefers dark mode.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"outlook-windows\":\r\n case \"thunderbird\":\r\n // These don't have dark mode\r\n break;\r\n }\r\n\r\n // Add dark mode wrapper styling\r\n $(\"body\").css(\"background-color\", \"#1a1a1a\");\r\n $(\"body\").css(\"color\", \"#e0e0e0\");\r\n\r\n return { html: $.html(), warnings };\r\n}\r\n\r\nfunction applyColorInversion(\r\n $: cheerio.CheerioAPI,\r\n mode: \"full\" | \"partial\"\r\n): void {\r\n // Simulate dark mode by inverting light background colors\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n\r\n if (mode === \"full\") {\r\n // Full inversion: swap all light backgrounds to dark\r\n const updated = style\r\n .replace(/background-color:\\s*(#fff|#ffffff|white|#fafafa|#f5f5f5|#f0f0f0|#fefefe)/gi, \"background-color: #1a1a1a\")\r\n .replace(/background:\\s*(#fff|#ffffff|white|#fafafa|#f5f5f5|#f0f0f0|#fefefe)/gi, \"background: #1a1a1a\")\r\n .replace(/color:\\s*(#000|#000000|black|#111|#222|#333)/gi, \"color: #e0e0e0\")\r\n .replace(/color:\\s*(#fff|#ffffff|white)/gi, \"color: #e0e0e0\")\r\n .replace(/border(?:-[a-z]+)?:\\s*[^;]*(?:#000|#111|#222|#333|black)/gi, (match) =>\r\n match.replace(/#000|#111|#222|#333|black/gi, \"#555\")\r\n );\r\n $(el).attr(\"style\", updated);\r\n } else {\r\n // Partial inversion: only invert very light backgrounds\r\n const updated = style\r\n .replace(/background-color:\\s*(#fff|#ffffff|white)/gi, \"background-color: #2d2d2d\")\r\n .replace(/background:\\s*(#fff|#ffffff|white)/gi, \"background: #2d2d2d\")\r\n .replace(/color:\\s*(#000|#000000|black)/gi, \"color: #d4d4d4\");\r\n $(el).attr(\"style\", updated);\r\n }\r\n });\r\n\r\n // Also handle bgcolor attributes on table elements\r\n $(\"[bgcolor]\").each((_, el) => {\r\n const bgcolor = ($(el).attr(\"bgcolor\") || \"\").toLowerCase();\r\n if (\r\n bgcolor === \"#ffffff\" ||\r\n bgcolor === \"#fff\" ||\r\n bgcolor === \"white\" ||\r\n bgcolor === \"#fafafa\" ||\r\n bgcolor === \"#f5f5f5\"\r\n ) {\r\n $(el).attr(\"bgcolor\", mode === \"full\" ? \"#1a1a1a\" : \"#2d2d2d\");\r\n }\r\n });\r\n}\r\n","import type { CSSWarning, DiffResult } from \"./types\";\r\nimport { EMAIL_CLIENTS } from \"./clients\";\r\n\r\n/**\r\n * Compare two sets of analysis results to show what improved,\r\n * what regressed, and what stayed the same.\r\n */\r\nexport function diffResults(\r\n before: {\r\n scores: Record<string, { score: number; errors: number; warnings: number; info: number }>;\r\n warnings: CSSWarning[];\r\n },\r\n after: {\r\n scores: Record<string, { score: number; errors: number; warnings: number; info: number }>;\r\n warnings: CSSWarning[];\r\n }\r\n): DiffResult[] {\r\n const results: DiffResult[] = [];\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const scoreBefore = before.scores[client.id]?.score ?? 100;\r\n const scoreAfter = after.scores[client.id]?.score ?? 100;\r\n\r\n const beforeWarnings = before.warnings.filter((w) => w.client === client.id);\r\n const afterWarnings = after.warnings.filter((w) => w.client === client.id);\r\n\r\n // Key warnings by property+severity for comparison\r\n const beforeKeys = new Set(beforeWarnings.map(warningKey));\r\n const afterKeys = new Set(afterWarnings.map(warningKey));\r\n\r\n const fixed = beforeWarnings.filter((w) => !afterKeys.has(warningKey(w)));\r\n const introduced = afterWarnings.filter((w) => !beforeKeys.has(warningKey(w)));\r\n const unchanged = afterWarnings.filter((w) => beforeKeys.has(warningKey(w)));\r\n\r\n results.push({\r\n clientId: client.id,\r\n scoreBefore,\r\n scoreAfter,\r\n scoreDelta: scoreAfter - scoreBefore,\r\n fixed,\r\n introduced,\r\n unchanged,\r\n });\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction warningKey(w: CSSWarning): string {\r\n return `${w.property}:${w.severity}`;\r\n}\r\n","import type { CSSWarning } from \"./types\";\r\nimport { getClient, EMAIL_CLIENTS } from \"./clients\";\r\n\r\nexport type ExportScope = \"all\" | \"current\";\r\n\r\nexport interface ExportPromptOptions {\r\n originalHtml: string;\r\n warnings: CSSWarning[];\r\n scores: Record<\r\n string,\r\n { score: number; errors: number; warnings: number; info: number }\r\n >;\r\n scope: ExportScope;\r\n selectedClientId?: string;\r\n format?: \"html\" | \"jsx\" | \"mjml\" | \"maizzle\";\r\n}\r\n\r\nexport function generateFixPrompt(options: ExportPromptOptions): string {\r\n const {\r\n originalHtml,\r\n warnings,\r\n scores,\r\n scope,\r\n selectedClientId,\r\n format = \"html\",\r\n } = options;\r\n\r\n const filteredWarnings =\r\n scope === \"current\" && selectedClientId\r\n ? warnings.filter((w) => w.client === selectedClientId)\r\n : warnings;\r\n\r\n const filteredScores =\r\n scope === \"current\" && selectedClientId\r\n ? { [selectedClientId]: scores[selectedClientId] }\r\n : scores;\r\n\r\n const clientCount = Object.keys(filteredScores).length;\r\n const errorCount = filteredWarnings.filter(\r\n (w) => w.severity === \"error\"\r\n ).length;\r\n const warnCount = filteredWarnings.filter(\r\n (w) => w.severity === \"warning\"\r\n ).length;\r\n const infoCount = filteredWarnings.filter(\r\n (w) => w.severity === \"info\"\r\n ).length;\r\n\r\n const clientLabel =\r\n scope === \"current\" && selectedClientId\r\n ? getClient(selectedClientId)?.name ?? selectedClientId\r\n : `${clientCount} email clients`;\r\n\r\n const sections: string[] = [];\r\n\r\n // 1. Context\r\n sections.push(\r\n `# Email Compatibility Fix Request\\n\\n` +\r\n `- **Format:** ${format.toUpperCase()}\\n` +\r\n `- **Scope:** ${clientLabel}\\n` +\r\n `- **Issues found:** ${errorCount} error${errorCount !== 1 ? \"s\" : \"\"}, ` +\r\n `${warnCount} warning${warnCount !== 1 ? \"s\" : \"\"}, ` +\r\n `${infoCount} info`\r\n );\r\n\r\n // 2. Original email code\r\n sections.push(\r\n `## Original Email Code\\n\\n` + `\\`\\`\\`${format}\\n${originalHtml}\\n\\`\\`\\``\r\n );\r\n\r\n // 3. Compatibility scores table\r\n const scoreEntries = Object.entries(filteredScores).filter(\r\n ([, v]) => v != null\r\n );\r\n if (scoreEntries.length > 0) {\r\n let table = `## Compatibility Scores\\n\\n`;\r\n table += `| Client | Score | Errors | Warnings | Info |\\n`;\r\n table += `|--------|------:|-------:|---------:|-----:|\\n`;\r\n for (const [clientId, data] of scoreEntries) {\r\n const name = getClient(clientId)?.name ?? clientId;\r\n table += `| ${name} | ${data.score} | ${data.errors} | ${data.warnings} | ${data.info} |\\n`;\r\n }\r\n sections.push(table.trimEnd());\r\n }\r\n\r\n // 4. Detected issues grouped by severity\r\n if (filteredWarnings.length > 0) {\r\n let issueSection = `## Detected Issues\\n`;\r\n\r\n const groups: [string, CSSWarning[]][] = [\r\n [\"Errors\", filteredWarnings.filter((w) => w.severity === \"error\")],\r\n [\"Warnings\", filteredWarnings.filter((w) => w.severity === \"warning\")],\r\n [\"Info\", filteredWarnings.filter((w) => w.severity === \"info\")],\r\n ];\r\n\r\n for (const [label, group] of groups) {\r\n if (group.length === 0) continue;\r\n issueSection += `\\n### ${label}\\n`;\r\n\r\n for (const w of group) {\r\n const clientName = getClient(w.client)?.name ?? w.client;\r\n const fixTypeLabel = w.fixType === \"structural\" ? \" [STRUCTURAL]\" : \"\";\r\n issueSection += `\\n- **${w.property}** (${clientName})${fixTypeLabel}: ${w.message}`;\r\n if (w.fixType === \"structural\") {\r\n issueSection += `\\n - **Fix type: structural** — CSS-only changes will NOT work. HTML restructuring required.`;\r\n }\r\n if (w.suggestion) {\r\n issueSection += `\\n - Suggestion: ${w.suggestion}`;\r\n }\r\n if (w.fix) {\r\n const fixLabel = w.fixIsGenericFallback && format !== \"html\"\r\n ? `Fix (generic HTML — adapt to ${format.toUpperCase()} syntax)`\r\n : \"Fix\";\r\n issueSection += `\\n - ${fixLabel}:`;\r\n issueSection += `\\n - Before: \\`${w.fix.before}\\``;\r\n issueSection += `\\n - After: \\`${w.fix.after}\\``;\r\n }\r\n }\r\n }\r\n\r\n sections.push(issueSection);\r\n }\r\n\r\n // 5. Instructions\r\n const formatInstructions: Record<string, string> = {\r\n jsx:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return complete fixed JSX code using @react-email/components. ` +\r\n `Use Row, Column, Container, Font, Img, Head, and Link components from ` +\r\n `@react-email/components wherever the fix suggests them. ` +\r\n `Keep all style values as camelCase JavaScript object properties (e.g. { backgroundColor: \"#fff\" }). ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the JSX structure and style props needed to fix the issues.`,\r\n mjml:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return complete fixed MJML markup. ` +\r\n `Use MJML-native elements (mj-section, mj-column, mj-text, mj-button, mj-font, mj-style, mj-raw) ` +\r\n `as indicated in the fixes. ` +\r\n `Ensure the result is valid MJML that compiles without errors. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the MJML structure and attributes needed to fix the issues.`,\r\n maizzle:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return the complete fixed Maizzle template. ` +\r\n `Use Tailwind CSS utility classes and Maizzle config settings as indicated in the fixes. ` +\r\n `Add MSO conditional comment table wrappers where needed for Outlook compatibility. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the Tailwind classes and HTML structure needed to fix the issues.`,\r\n html:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return the complete fixed HTML code. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the CSS and HTML attributes needed to fix the issues.`,\r\n };\r\n\r\n sections.push(\r\n `## Instructions\\n\\n` + (formatInstructions[format] ?? formatInstructions.html)\r\n );\r\n\r\n return sections.join(\"\\n\\n\");\r\n}\r\n","import { generateFixPrompt } from \"./export-prompt\";\r\nimport type { ExportPromptOptions } from \"./export-prompt\";\r\nimport type { CSSWarning } from \"./types\";\r\n\r\n/**\r\n * Heuristic token estimate: ~3.5 characters per token for mixed\r\n * HTML/CSS/code content. This is conservative (slightly over-counts)\r\n * to avoid surprise overruns.\r\n *\r\n * For precise counts, use Anthropic's `messages.countTokens()` API\r\n * or pass a custom `tokenCounter` callback to `estimateAiFixTokens()`.\r\n */\r\nconst CHARS_PER_TOKEN = 3.5;\r\n\r\n/**\r\n * Rough output-to-input ratio. The AI returns a fixed version of the\r\n * email, which is typically similar in size to the input HTML plus\r\n * some overhead for VML/table wrappers.\r\n */\r\nconst OUTPUT_RATIO = 1.3;\r\n\r\n/**\r\n * Default token overhead for the AI_FIX_SYSTEM_PROMPT exported from ai-fix.ts.\r\n * The system prompt is ~800 chars ≈ ~230 tokens. We use 250 as a safe default.\r\n * Consumers using a custom system prompt can override via `systemPromptTokens`.\r\n */\r\nconst DEFAULT_SYSTEM_PROMPT_TOKENS = 250;\r\n\r\nexport interface TokenEstimate {\r\n /** Estimated input tokens (prompt + system prompt) */\r\n inputTokens: number;\r\n /** Estimated output tokens (fixed code response) */\r\n estimatedOutputTokens: number;\r\n /** Raw character count of the prompt */\r\n promptCharacters: number;\r\n /** Character count of just the HTML being fixed */\r\n htmlCharacters: number;\r\n /** Total warnings included in the prompt */\r\n warningCount: number;\r\n /** How many warnings are structural (need HTML changes) */\r\n structuralCount: number;\r\n /** Whether warnings were truncated to fit within limits */\r\n truncated: boolean;\r\n /** Number of warnings removed during truncation */\r\n warningsRemoved: number;\r\n}\r\n\r\n/**\r\n * Extended return type from `estimateAiFixTokens()` that includes both the\r\n * token metrics AND the (potentially truncated) warnings list. The `warnings`\r\n * field is used internally by `generateAiFix()` to build the prompt with the\r\n * truncated set, but is NOT exposed in `AiFixResult.tokenEstimate` to keep\r\n * the public API clean.\r\n */\r\nexport interface TokenEstimateWithWarnings extends TokenEstimate {\r\n /** The warnings after smart truncation (may be shorter than the input list) */\r\n warnings: CSSWarning[];\r\n}\r\n\r\nexport interface EstimateOptions extends Omit<ExportPromptOptions, \"warnings\"> {\r\n warnings: CSSWarning[];\r\n /**\r\n * Maximum input tokens to target. If the estimated prompt exceeds\r\n * this, warnings will be truncated (info first, then duplicates).\r\n * Defaults to 16000 (~56KB of prompt text).\r\n */\r\n maxInputTokens?: number;\r\n /**\r\n * Optional precise token counter. If provided, it will be called\r\n * with the final prompt text for an exact count. Consumers can wire\r\n * this to `anthropic.messages.countTokens()`.\r\n */\r\n tokenCounter?: (text: string) => Promise<number> | number;\r\n /**\r\n * Token count for the system prompt. Added to the input token estimate\r\n * since the system prompt counts against the context window. Defaults\r\n * to 250 (matching the built-in AI_FIX_SYSTEM_PROMPT). Set to 0 if\r\n * not using a system prompt, or override for custom system prompts.\r\n */\r\n systemPromptTokens?: number;\r\n}\r\n\r\n/**\r\n * Estimate tokens for an AI fix prompt BEFORE making the API call.\r\n * Use this to show cost estimates, check limits, and decide whether\r\n * to proceed.\r\n *\r\n * @example\r\n * ```ts\r\n * const estimate = await estimateAiFixTokens({\r\n * originalHtml: html,\r\n * warnings,\r\n * scores,\r\n * scope: \"all\",\r\n * format: \"jsx\",\r\n * });\r\n *\r\n * console.log(`~${estimate.inputTokens} input tokens`);\r\n * console.log(`~${estimate.estimatedOutputTokens} output tokens`);\r\n * console.log(`Truncated: ${estimate.truncated}`);\r\n * ```\r\n */\r\nexport async function estimateAiFixTokens(\r\n options: EstimateOptions,\r\n): Promise<TokenEstimateWithWarnings> {\r\n const {\r\n maxInputTokens = 16000,\r\n tokenCounter,\r\n systemPromptTokens = DEFAULT_SYSTEM_PROMPT_TOKENS,\r\n ...rest\r\n } = options;\r\n\r\n // Reserve space for the system prompt when truncating\r\n const effectiveMaxTokens = maxInputTokens - systemPromptTokens;\r\n\r\n // Truncate warnings if needed to stay within token limits\r\n const { warnings: finalWarnings, truncated, removed } = truncateWarnings(\r\n rest.warnings,\r\n rest.originalHtml,\r\n effectiveMaxTokens,\r\n rest.scope,\r\n rest.selectedClientId,\r\n );\r\n\r\n const prompt = generateFixPrompt({ ...rest, warnings: finalWarnings });\r\n const promptChars = prompt.length;\r\n\r\n // Use precise counter if available, otherwise heuristic\r\n let promptTokens: number;\r\n if (tokenCounter) {\r\n const count = tokenCounter(prompt);\r\n promptTokens = count instanceof Promise ? await count : count;\r\n } else {\r\n promptTokens = heuristicTokenCount(prompt);\r\n }\r\n\r\n // Total input = user prompt + system prompt\r\n const inputTokens = promptTokens + systemPromptTokens;\r\n\r\n const estimatedOutputTokens = Math.ceil(\r\n heuristicTokenCount(rest.originalHtml) * OUTPUT_RATIO,\r\n );\r\n\r\n const structuralCount = finalWarnings.filter(\r\n (w) => w.fixType === \"structural\",\r\n ).length;\r\n\r\n return {\r\n inputTokens,\r\n estimatedOutputTokens,\r\n promptCharacters: promptChars,\r\n htmlCharacters: rest.originalHtml.length,\r\n warningCount: finalWarnings.length,\r\n structuralCount,\r\n truncated,\r\n warningsRemoved: removed,\r\n warnings: finalWarnings,\r\n };\r\n}\r\n\r\n/**\r\n * Quick synchronous heuristic token count. No deps, no API calls.\r\n * Accuracy: within ~10-15% of real Claude tokenizer for code/HTML.\r\n */\r\nexport function heuristicTokenCount(text: string): number {\r\n return Math.ceil(text.length / CHARS_PER_TOKEN);\r\n}\r\n\r\n/**\r\n * Intelligently trim warnings to keep the prompt within token limits.\r\n *\r\n * Truncation priority (least important removed first):\r\n * 1. Remove duplicate warnings (same property across different clients)\r\n * 2. Remove `info`-level warnings\r\n * 3. Remove `warning`-level warnings with CSS-only fixes (not structural)\r\n * 4. Trim long fix snippet before/after strings\r\n */\r\nfunction truncateWarnings(\r\n warnings: CSSWarning[],\r\n html: string,\r\n maxTokens: number,\r\n scope: string,\r\n selectedClientId?: string,\r\n): { warnings: CSSWarning[]; truncated: boolean; removed: number } {\r\n const originalCount = warnings.length;\r\n\r\n // Quick check: will the full prompt fit?\r\n const fullPromptEstimate = heuristicTokenCount(html) + heuristicTokenCount(\r\n JSON.stringify(warnings),\r\n ) + 500; // overhead for markdown structure\r\n\r\n if (fullPromptEstimate <= maxTokens) {\r\n return { warnings, truncated: false, removed: 0 };\r\n }\r\n\r\n let result = [...warnings];\r\n\r\n // Step 1: Deduplicate — keep one warning per property per severity,\r\n // preferring the most relevant client\r\n const seen = new Set<string>();\r\n result = result.filter((w) => {\r\n const key = `${w.property}:${w.severity}`;\r\n if (seen.has(key)) return false;\r\n seen.add(key);\r\n return true;\r\n });\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 2: Remove info-level warnings\r\n result = result.filter((w) => w.severity !== \"info\");\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 3: Remove CSS-only warnings (keep structural ones)\r\n result = result.filter((w) => w.fixType === \"structural\" || w.severity === \"error\");\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 4: Trim fix snippets to just descriptions\r\n result = result.map((w) => ({\r\n ...w,\r\n fix: w.fix\r\n ? {\r\n ...w.fix,\r\n before: w.fix.before.length > 200\r\n ? w.fix.before.slice(0, 200) + \"\\n/* ... truncated ... */\"\r\n : w.fix.before,\r\n after: w.fix.after.length > 200\r\n ? w.fix.after.slice(0, 200) + \"\\n/* ... truncated ... */\"\r\n : w.fix.after,\r\n }\r\n : undefined,\r\n }));\r\n\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n}\r\n\r\nfunction estimateFits(\r\n warnings: CSSWarning[],\r\n html: string,\r\n maxTokens: number,\r\n): boolean {\r\n const estimate =\r\n heuristicTokenCount(html) +\r\n heuristicTokenCount(JSON.stringify(warnings)) +\r\n 500;\r\n return estimate <= maxTokens;\r\n}\r\n","import { generateFixPrompt } from \"./export-prompt\";\r\nimport type { ExportPromptOptions } from \"./export-prompt\";\r\nimport type { AiProvider, AiFixResult, CSSWarning } from \"./types\";\r\nimport { estimateAiFixTokens } from \"./token-utils\";\r\n\r\nexport interface GenerateAiFixOptions extends ExportPromptOptions {\r\n /** Callback that sends a prompt to an LLM and returns the response text. */\r\n provider: AiProvider;\r\n /**\r\n * Maximum input tokens for the prompt. If the estimated prompt exceeds\r\n * this, warnings are intelligently truncated (info first, then CSS-only\r\n * duplicates). Defaults to 16000.\r\n */\r\n maxInputTokens?: number;\r\n}\r\n\r\n/**\r\n * Generate an AI-powered fix for email compatibility issues.\r\n *\r\n * This uses the deterministic engine's analysis (warnings, scores, fix snippets)\r\n * to build a structured prompt, then delegates to an LLM for context-aware\r\n * structural fixes that static snippets cannot handle.\r\n *\r\n * The engine stays provider-agnostic — consumers pass their own `AiProvider`\r\n * callback (Anthropic SDK, Vercel AI SDK, OpenRouter, etc.).\r\n *\r\n * @example\r\n * ```ts\r\n * import Anthropic from \"@anthropic-ai/sdk\";\r\n * import { analyzeEmail, generateCompatibilityScore, generateAiFix } from \"@emailens/engine\";\r\n *\r\n * const anthropic = new Anthropic();\r\n * const warnings = analyzeEmail(html, \"jsx\");\r\n * const scores = generateCompatibilityScore(warnings);\r\n *\r\n * // 1. Check cost before calling\r\n * const estimate = await estimateAiFixTokens({\r\n * originalHtml: html, warnings, scores, scope: \"all\", format: \"jsx\",\r\n * });\r\n * console.log(`~${estimate.inputTokens} input tokens`);\r\n *\r\n * // 2. Generate the fix\r\n * const result = await generateAiFix({\r\n * originalHtml: html, warnings, scores, scope: \"all\", format: \"jsx\",\r\n * provider: async (prompt) => {\r\n * const msg = await anthropic.messages.create({\r\n * model: \"claude-sonnet-4-6\",\r\n * max_tokens: 8192,\r\n * system: AI_FIX_SYSTEM_PROMPT,\r\n * messages: [{ role: \"user\", content: prompt }],\r\n * });\r\n * return msg.content[0].type === \"text\" ? msg.content[0].text : \"\";\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport async function generateAiFix(\r\n options: GenerateAiFixOptions,\r\n): Promise<AiFixResult> {\r\n const { provider, maxInputTokens = 16000, ...promptOptions } = options;\r\n\r\n // Estimate tokens and apply smart truncation if needed\r\n const estimate = await estimateAiFixTokens({\r\n ...promptOptions,\r\n maxInputTokens,\r\n });\r\n\r\n // Use the truncated warnings from the estimate (not the original list)\r\n const truncatedWarnings = estimate.warnings;\r\n\r\n // Build the final prompt with the truncated warnings\r\n const prompt = generateFixPrompt({ ...promptOptions, warnings: truncatedWarnings });\r\n\r\n const structuralCount = countStructuralWarnings(\r\n truncatedWarnings,\r\n promptOptions.scope,\r\n promptOptions.selectedClientId,\r\n );\r\n\r\n // Strip the warnings list from the estimate to keep the public API clean\r\n const { warnings: _discarded, ...tokenEstimate } = estimate;\r\n\r\n const response = await provider(prompt);\r\n\r\n // Extract code from the response — the LLM may wrap it in a code fence\r\n const code = extractCode(response);\r\n\r\n return {\r\n code,\r\n prompt,\r\n targetedWarnings: tokenEstimate.warningCount,\r\n structuralCount,\r\n tokenEstimate,\r\n };\r\n}\r\n\r\n/**\r\n * System prompt for the AI fix provider. Consumers should pass this as\r\n * the `system` parameter to their LLM call for best results.\r\n */\r\nexport const AI_FIX_SYSTEM_PROMPT = `You are an expert email developer specializing in cross-client HTML email compatibility. You fix emails to render correctly across all email clients.\r\n\r\nRules:\r\n- Return ONLY the fixed code inside a single code fence. No explanations before or after.\r\n- Preserve all existing content, text, links, and visual design.\r\n- For structural issues (fixType: \"structural\"), you MUST restructure the HTML — CSS-only changes will not work.\r\n- Common structural patterns:\r\n - word-break/overflow-wrap unsupported → wrap text in <table><tr><td> with constrained width\r\n - display:flex/grid → convert to <table> layout (match the original column count and proportions)\r\n - border-radius in Outlook → use VML <v:roundrect> with <!--[if mso]> conditionals\r\n - background-image in Outlook → use VML <v:rect> with <v:fill>\r\n - max-width in Outlook → wrap in <!--[if mso]><table width=\"N\"> conditional\r\n - position:absolute → use <table> cells for layout\r\n - <svg> → replace with <img> pointing to a hosted PNG\r\n- For CSS-only issues (fixType: \"css\"), swap properties or add fallbacks.\r\n- Apply ALL fixes from the issues list — do not skip any.\r\n- Use the framework syntax specified (JSX/MJML/Maizzle/HTML).\r\n- For JSX: use camelCase style props, React Email components, and proper TypeScript types.\r\n- For MJML: use mj-* elements and attributes.\r\n- For Maizzle: use Tailwind CSS classes.`;\r\n\r\nfunction countStructuralWarnings(\r\n warnings: CSSWarning[],\r\n scope: string,\r\n selectedClientId?: string,\r\n): number {\r\n const filtered =\r\n scope === \"current\" && selectedClientId\r\n ? warnings.filter((w) => w.client === selectedClientId)\r\n : warnings;\r\n return filtered.filter((w) => w.fixType === \"structural\").length;\r\n}\r\n\r\n/**\r\n * Extract code from an LLM response that may contain markdown code fences.\r\n * If multiple fences exist, returns the largest one (most likely the full\r\n * fixed email rather than a small snippet).\r\n */\r\nfunction extractCode(response: string): string {\r\n // Find all code fences and pick the largest one\r\n const fencePattern = /```(?:[\\w]*)\\n([\\s\\S]*?)```/g;\r\n let largest: string | null = null;\r\n\r\n let match: RegExpExecArray | null;\r\n while ((match = fencePattern.exec(response)) !== null) {\r\n const content = match[1].trim();\r\n if (largest === null || content.length > largest.length) {\r\n largest = content;\r\n }\r\n }\r\n\r\n if (largest !== null) {\r\n return largest;\r\n }\r\n\r\n // If no code fence, return the whole response trimmed\r\n return response.trim();\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { SpamAnalysisOptions, SpamIssue, SpamReport } from \"./types\";\r\n\r\nconst SPAM_TRIGGER_PHRASES = [\r\n \"act now\", \"limited time\", \"click here\", \"buy now\", \"order now\",\r\n \"don't miss\", \"don't delete\", \"urgent\", \"congratulations\",\r\n \"you've been selected\", \"you've won\", \"winner\", \"free gift\",\r\n \"risk free\", \"no obligation\", \"no cost\", \"no fees\",\r\n \"100% free\", \"100% satisfied\", \"double your money\",\r\n \"earn extra cash\", \"make money\", \"cash bonus\",\r\n \"as seen on\", \"incredible deal\", \"lowest price\",\r\n \"once in a lifetime\", \"special promotion\", \"this isn't spam\",\r\n \"what are you waiting for\", \"apply now\", \"sign up free\",\r\n \"cancel anytime\", \"no strings attached\", \"no questions asked\",\r\n];\r\n\r\n// Fix #6: Pre-compiled word-boundary regexes for spam phrases\r\nfunction escapeRegex(s: string): string {\r\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\nconst SPAM_PHRASE_PATTERNS: Map<string, RegExp> = new Map(\r\n SPAM_TRIGGER_PHRASES.map((phrase) => [\r\n phrase,\r\n new RegExp(\"\\\\b\" + escapeRegex(phrase) + \"\\\\b\"),\r\n ]),\r\n);\r\n\r\nconst URL_SHORTENERS = [\r\n \"bit.ly\", \"tinyurl.com\", \"t.co\", \"goo.gl\", \"ow.ly\",\r\n \"is.gd\", \"buff.ly\", \"rebrand.ly\", \"bl.ink\", \"short.io\",\r\n \"cutt.ly\", \"rb.gy\",\r\n];\r\n\r\n// Fix #2: Known ESP tracking domains\r\nconst ESP_TRACKING_DOMAINS = [\r\n \"mailchi.mp\", \"list-manage.com\", \"click.mailchimp.com\",\r\n \"sendgrid.net\", \"click.sendgrid.net\", \"ct.sendgrid.net\",\r\n \"click.klaviyomail.com\", \"trk.klaviyo.com\",\r\n \"click.hubspotemail.net\",\r\n \"links.iterable.com\", \"track.customer.io\",\r\n \"go.pardot.com\", \"mailgun.org\",\r\n \"em.salesforce.com\", \"click.marketingcloud.com\",\r\n \"r.mail.yahoo.com\", \"t.dripemail2.com\",\r\n];\r\n\r\n// Fix #4: Transactional email signal phrases\r\nconst TRANSACTIONAL_SIGNALS = [\r\n \"reset your password\", \"password reset\",\r\n \"verify your email\", \"email verification\",\r\n \"order confirmation\", \"your order\",\r\n \"your receipt\", \"purchase confirmation\",\r\n \"verification code\", \"confirm your account\",\r\n \"your invoice\", \"shipping confirmation\",\r\n \"account activation\", \"security alert\",\r\n];\r\n\r\nconst TRANSACTIONAL_SIGNAL_PATTERN = new RegExp(\r\n TRANSACTIONAL_SIGNALS.map(escapeRegex).join(\"|\"),\r\n \"i\",\r\n);\r\n\r\n// OTP-like pattern: standalone 4-8 digit codes\r\nconst OTP_PATTERN = /\\b\\d{4,8}\\b/;\r\n\r\nconst WEIGHTS: Record<string, number> = {\r\n \"caps-ratio\": 15,\r\n \"excessive-punctuation\": 10,\r\n \"spam-phrases\": 5,\r\n \"missing-unsubscribe\": 15,\r\n \"hidden-text\": 20,\r\n \"url-shortener\": 10,\r\n \"image-only\": 20,\r\n \"high-image-ratio\": 10,\r\n \"deceptive-link\": 15,\r\n \"all-caps-subject\": 10,\r\n};\r\n\r\nfunction extractVisibleText($: cheerio.CheerioAPI): string {\r\n const clone = $.root().clone();\r\n clone.find(\"script, style, head\").remove();\r\n return clone.text().replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nfunction checkCapsRatio(text: string): SpamIssue | null {\r\n const words = text.split(/\\s+/).filter((w) => w.length >= 3);\r\n if (words.length < 5) return null;\r\n\r\n const capsWords = words.filter((w) => w === w.toUpperCase() && /[A-Z]/.test(w));\r\n const ratio = capsWords.length / words.length;\r\n\r\n if (ratio > 0.2) {\r\n return {\r\n rule: \"caps-ratio\",\r\n severity: \"warning\",\r\n message: `${Math.round(ratio * 100)}% of words are ALL CAPS — spam filters flag excessive capitalization.`,\r\n detail: `Found ${capsWords.length} of ${words.length} words in all caps.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #5: Scale punctuation threshold by text length, exclude $digit patterns\r\nfunction checkExcessivePunctuation(text: string): SpamIssue | null {\r\n const exclamations = (text.match(/!/g) || []).length;\r\n // Only count $ NOT followed by a digit (skip price patterns like $29)\r\n const dollars = (text.match(/\\$(?!\\d)/g) || []).length;\r\n const total = exclamations + dollars;\r\n\r\n const threshold = Math.max(5, Math.floor(text.length / 200));\r\n\r\n if (total > threshold) {\r\n return {\r\n rule: \"excessive-punctuation\",\r\n severity: \"warning\",\r\n message: `Excessive special characters detected (${exclamations} \"!\", ${dollars} \"$\") — common spam trigger.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #6: Word-boundary matching for spam phrases\r\nfunction checkSpamPhrases(text: string): SpamIssue[] {\r\n const lower = text.toLowerCase();\r\n const found: SpamIssue[] = [];\r\n\r\n for (const [phrase, pattern] of SPAM_PHRASE_PATTERNS) {\r\n if (pattern.test(lower)) {\r\n found.push({\r\n rule: \"spam-phrases\",\r\n severity: \"info\",\r\n message: `Contains spam trigger phrase: \"${phrase}\"`,\r\n });\r\n }\r\n }\r\n return found;\r\n}\r\n\r\n// Fix #4: Transactional exemption + options API for unsubscribe\r\nfunction checkUnsubscribe(\r\n $: cheerio.CheerioAPI,\r\n text: string,\r\n options?: SpamAnalysisOptions,\r\n): SpamIssue | null {\r\n // Explicit transactional type → skip entirely\r\n if (options?.emailType === \"transactional\") return null;\r\n\r\n // List-Unsubscribe header provided → satisfied\r\n if (options?.listUnsubscribeHeader?.trim()) return null;\r\n\r\n let hasUnsubscribe = false;\r\n\r\n $(\"a\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const linkText = $(el).text().toLowerCase();\r\n if (\r\n linkText.includes(\"unsubscribe\") ||\r\n href.toLowerCase().includes(\"unsubscribe\") ||\r\n linkText.includes(\"opt out\") ||\r\n linkText.includes(\"opt-out\") ||\r\n href.toLowerCase().includes(\"opt-out\") ||\r\n href.toLowerCase().includes(\"optout\")\r\n ) {\r\n hasUnsubscribe = true;\r\n }\r\n });\r\n\r\n if (!hasUnsubscribe) {\r\n // Auto-detect transactional signals\r\n const lower = text.toLowerCase();\r\n const signalMatches = TRANSACTIONAL_SIGNALS.filter((s) =>\r\n lower.includes(s.toLowerCase()),\r\n );\r\n const hasOtp = OTP_PATTERN.test(text);\r\n const signalCount = signalMatches.length + (hasOtp ? 1 : 0);\r\n\r\n if (signalCount >= 2) {\r\n // Looks transactional — downgrade to info instead of error\r\n return {\r\n rule: \"missing-unsubscribe\",\r\n severity: \"info\",\r\n message:\r\n \"No unsubscribe link found, but email appears transactional — may not be required.\",\r\n detail: `Detected transactional signals: ${signalMatches.join(\", \")}${hasOtp ? \", OTP code\" : \"\"}`,\r\n };\r\n }\r\n\r\n return {\r\n rule: \"missing-unsubscribe\",\r\n severity: \"error\",\r\n message:\r\n \"No unsubscribe link found — required by CAN-SPAM and GDPR. Most spam filters penalize this.\",\r\n detail: 'Add an <a> link with \"unsubscribe\" text or href.',\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #1: Preheader exemption for hidden text\r\nconst PREHEADER_ACCESSORY_PATTERNS = [\r\n /max-height\\s*:\\s*0/,\r\n /overflow\\s*:\\s*hidden/,\r\n /mso-hide\\s*:\\s*all/,\r\n /opacity\\s*:\\s*0/,\r\n /color\\s*:\\s*transparent/,\r\n /line-height\\s*:\\s*0/,\r\n];\r\n\r\nfunction isLikelyPreheader(\r\n style: string,\r\n text: string,\r\n): boolean {\r\n if (text.length > 200) return false;\r\n\r\n // Exempt only when preheader-specific accessory patterns are present\r\n return PREHEADER_ACCESSORY_PATTERNS.some((p) => p.test(style));\r\n}\r\n\r\nfunction checkHiddenText($: cheerio.CheerioAPI): SpamIssue | null {\r\n let found = false;\r\n let detail = \"\";\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = ($(el).attr(\"style\") || \"\").toLowerCase();\r\n const text = $(el).text().trim();\r\n if (!text) return;\r\n\r\n // visibility:hidden is always flagged — no legitimate preheader use\r\n if (/visibility\\s*:\\s*hidden/.test(style)) {\r\n found = true;\r\n detail = \"visibility:hidden on element with text content\";\r\n return false;\r\n }\r\n\r\n if (/font-size\\s*:\\s*0(?:px|em|rem|pt)?(?:\\s|;|$)/.test(style)) {\r\n if (!isLikelyPreheader(style, text)) {\r\n found = true;\r\n detail = \"font-size:0 on element with text content\";\r\n return false;\r\n }\r\n }\r\n\r\n if (/display\\s*:\\s*none/.test(style)) {\r\n if (!isLikelyPreheader(style, text)) {\r\n found = true;\r\n detail = \"display:none on element with text content\";\r\n return false;\r\n }\r\n }\r\n });\r\n\r\n if (found) {\r\n return {\r\n rule: \"hidden-text\",\r\n severity: \"error\",\r\n message: \"Hidden text detected — major spam filter red flag.\",\r\n detail,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #3: Hostname matching instead of substring for URL shorteners\r\nfunction checkUrlShorteners($: cheerio.CheerioAPI): SpamIssue[] {\r\n const issues: SpamIssue[] = [];\r\n const seen = new Set<string>();\r\n\r\n $(\"a[href]\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n let hostname: string;\r\n try {\r\n hostname = new URL(href).hostname.toLowerCase();\r\n } catch {\r\n return; // malformed URL, skip\r\n }\r\n\r\n for (const shortener of URL_SHORTENERS) {\r\n if (\r\n (hostname === shortener || hostname.endsWith(\".\" + shortener)) &&\r\n !seen.has(shortener)\r\n ) {\r\n seen.add(shortener);\r\n issues.push({\r\n rule: \"url-shortener\",\r\n severity: \"warning\",\r\n message: `URL shortener detected (${shortener}) — spam filters distrust shortened links.`,\r\n detail: href,\r\n });\r\n }\r\n }\r\n });\r\n return issues;\r\n}\r\n\r\n// Fix #7: Accept pre-extracted text to avoid duplicate extractVisibleText call\r\nfunction checkImageToTextRatio(\r\n $: cheerio.CheerioAPI,\r\n text: string,\r\n): SpamIssue | null {\r\n const images = $(\"img\").length;\r\n if (images === 0) return null;\r\n\r\n if (text.length < 50 && images > 0) {\r\n return {\r\n rule: \"image-only\",\r\n severity: \"error\",\r\n message: `Image-heavy email with almost no text (${text.length} chars, ${images} images) — likely to be flagged as spam or clipped.`,\r\n };\r\n }\r\n\r\n const ratio = images / (text.length / 100);\r\n if (ratio > 0.5 && images > 3) {\r\n return {\r\n rule: \"high-image-ratio\",\r\n severity: \"warning\",\r\n message: `High image-to-text ratio (${images} images for ${text.length} chars of text) — consider adding more text content.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #2: ESP tracking domain allowlist + encoded destination heuristic\r\nfunction isEspTrackingDomain(hostname: string): boolean {\r\n return ESP_TRACKING_DOMAINS.some(\r\n (esp) => hostname === esp || hostname.endsWith(\".\" + esp),\r\n );\r\n}\r\n\r\nfunction hasEncodedDestination(href: string, textDomain: string): boolean {\r\n // Check if the href path/query contains the text domain URL-encoded\r\n const encoded = encodeURIComponent(textDomain);\r\n return href.includes(encoded) || href.includes(textDomain);\r\n}\r\n\r\nfunction checkDeceptiveLinks($: cheerio.CheerioAPI): SpamIssue[] {\r\n const issues: SpamIssue[] = [];\r\n\r\n $(\"a[href]\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const text = $(el).text().trim();\r\n\r\n if (/^https?:\\/\\/\\S+/i.test(text) || /^www\\.\\S+/i.test(text)) {\r\n try {\r\n const textDomain = new URL(\r\n text.startsWith(\"www.\") ? `https://${text}` : text,\r\n ).hostname.replace(/^www\\./, \"\");\r\n const hrefDomain = new URL(href).hostname.replace(/^www\\./, \"\");\r\n\r\n if (textDomain !== hrefDomain) {\r\n // Skip known ESP tracking domains\r\n if (isEspTrackingDomain(hrefDomain)) return;\r\n\r\n // Skip if href contains encoded destination (redirect wrapper)\r\n if (hasEncodedDestination(href, textDomain)) return;\r\n\r\n issues.push({\r\n rule: \"deceptive-link\",\r\n severity: \"error\",\r\n message: `Link text shows \"${textDomain}\" but links to \"${hrefDomain}\" — phishing red flag.`,\r\n detail: `Text: ${text}\\nHref: ${href}`,\r\n });\r\n }\r\n } catch {\r\n // Malformed URL, skip\r\n }\r\n }\r\n });\r\n return issues;\r\n}\r\n\r\nfunction checkAllCapsTitle($: cheerio.CheerioAPI): SpamIssue | null {\r\n const title = $(\"title\").text().trim();\r\n if (title.length > 5 && title === title.toUpperCase() && /[A-Z]/.test(title)) {\r\n return {\r\n rule: \"all-caps-subject\",\r\n severity: \"warning\",\r\n message: \"Email title/subject is ALL CAPS — common spam indicator.\",\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Analyze an HTML email for spam indicators.\r\n *\r\n * Returns a 0-100 score (100 = clean, 0 = very spammy) and an array\r\n * of issues found. Uses heuristic rules modeled after common spam\r\n * filter triggers (CAN-SPAM, GDPR, SpamAssassin patterns).\r\n */\r\nexport function analyzeSpam(\r\n html: string,\r\n options?: SpamAnalysisOptions,\r\n): SpamReport {\r\n if (!html || !html.trim()) {\r\n return { score: 100, level: \"low\", issues: [] };\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const text = extractVisibleText($);\r\n const issues: SpamIssue[] = [];\r\n\r\n const capsIssue = checkCapsRatio(text);\r\n if (capsIssue) issues.push(capsIssue);\r\n\r\n const punctIssue = checkExcessivePunctuation(text);\r\n if (punctIssue) issues.push(punctIssue);\r\n\r\n issues.push(...checkSpamPhrases(text));\r\n\r\n const unsubIssue = checkUnsubscribe($, text, options);\r\n if (unsubIssue) issues.push(unsubIssue);\r\n\r\n const hiddenIssue = checkHiddenText($);\r\n if (hiddenIssue) issues.push(hiddenIssue);\r\n\r\n issues.push(...checkUrlShorteners($));\r\n\r\n // Fix #7: pass pre-extracted text instead of calling extractVisibleText again\r\n const imageRatioIssue = checkImageToTextRatio($, text);\r\n if (imageRatioIssue) issues.push(imageRatioIssue);\r\n\r\n issues.push(...checkDeceptiveLinks($));\r\n\r\n const capsTitle = checkAllCapsTitle($);\r\n if (capsTitle) issues.push(capsTitle);\r\n\r\n // Calculate score\r\n let penalty = 0;\r\n const seenRules = new Map<string, number>();\r\n\r\n for (const issue of issues) {\r\n const count = (seenRules.get(issue.rule) || 0) + 1;\r\n seenRules.set(issue.rule, count);\r\n const weight = WEIGHTS[issue.rule] || 5;\r\n\r\n if (issue.rule === \"spam-phrases\") {\r\n if (count <= 5) penalty += weight;\r\n } else if (issue.rule === \"url-shortener\" || issue.rule === \"deceptive-link\") {\r\n if (count <= 2) penalty += weight;\r\n } else {\r\n // info-level issues don't penalize score\r\n if (issue.severity !== \"info\") {\r\n penalty += weight;\r\n }\r\n }\r\n }\r\n\r\n const score = Math.max(0, Math.min(100, 100 - penalty));\r\n const level: SpamReport[\"level\"] =\r\n score >= 70 ? \"low\" : score >= 40 ? \"medium\" : \"high\";\r\n\r\n return { score, level, issues };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { LinkIssue, LinkReport } from \"./types\";\r\n\r\nconst GENERIC_LINK_TEXT = new Set([\r\n \"click here\", \"here\", \"read more\", \"learn more\", \"more\",\r\n \"link\", \"this link\", \"click\", \"tap here\", \"this\",\r\n]);\r\n\r\nfunction classifyHref(href: string): string {\r\n if (!href || !href.trim()) return \"empty\";\r\n const h = href.trim().toLowerCase();\r\n if (h.startsWith(\"https://\")) return \"https\";\r\n if (h.startsWith(\"http://\")) return \"http\";\r\n if (h.startsWith(\"mailto:\")) return \"mailto\";\r\n if (h.startsWith(\"tel:\")) return \"tel\";\r\n if (h.startsWith(\"#\")) return \"anchor\";\r\n if (h.startsWith(\"javascript:\")) return \"javascript\";\r\n if (h.startsWith(\"//\")) return \"protocol-relative\";\r\n return \"other\";\r\n}\r\n\r\nfunction isPlaceholderHref(href: string): boolean {\r\n const h = href.trim().toLowerCase();\r\n return h === \"#\" || h === \"\" || h === \"javascript:void(0)\" || h === \"javascript:;\";\r\n}\r\n\r\n/**\r\n * Extract and validate all links from an HTML email.\r\n *\r\n * Performs static analysis only (no network requests). Checks for\r\n * empty/placeholder hrefs, javascript: protocol, insecure HTTP,\r\n * generic link text, accessibility issues, and more.\r\n */\r\nexport function validateLinks(html: string): LinkReport {\r\n if (!html || !html.trim()) {\r\n return {\r\n totalLinks: 0,\r\n issues: [],\r\n breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, other: 0 },\r\n };\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: LinkIssue[] = [];\r\n const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, other: 0 };\r\n\r\n const links = $(\"a\");\r\n const totalLinks = links.length;\r\n\r\n if (totalLinks === 0) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"no-links\",\r\n message: \"Email contains no links\",\r\n });\r\n return { totalLinks: 0, issues, breakdown };\r\n }\r\n\r\n links.each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const text = $(el).text().trim();\r\n const category = classifyHref(href);\r\n\r\n // Count breakdown\r\n switch (category) {\r\n case \"https\": breakdown.https++; break;\r\n case \"http\": breakdown.http++; break;\r\n case \"mailto\": breakdown.mailto++; break;\r\n case \"tel\": breakdown.tel++; break;\r\n case \"anchor\": breakdown.anchor++; break;\r\n default: breakdown.other++; break;\r\n }\r\n\r\n // Empty or missing href\r\n if (!href || !href.trim()) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-href\",\r\n message: \"Link has no href attribute\",\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // javascript: protocol (not placeholder)\r\n if (category === \"javascript\" && !isPlaceholderHref(href)) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"javascript-href\",\r\n message: \"Link uses javascript: protocol\",\r\n href: href.slice(0, 100),\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // Placeholder href\r\n if (isPlaceholderHref(href)) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"placeholder-href\",\r\n message: \"Link has a placeholder href (# or javascript:void)\",\r\n href,\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // HTTP instead of HTTPS\r\n if (category === \"http\") {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"insecure-link\",\r\n message: \"Link uses HTTP instead of HTTPS\",\r\n href: href.slice(0, 120),\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // Generic link text\r\n if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"generic-link-text\",\r\n message: `Link text \"${text}\" is vague — use descriptive text for accessibility and engagement`,\r\n href: href.slice(0, 120),\r\n text,\r\n });\r\n }\r\n\r\n // Link with no text and no aria-label\r\n if (!text && !$(el).attr(\"aria-label\") && !$(el).find(\"img[alt]\").length) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-link-text\",\r\n message: \"Link has no visible text or aria-label\",\r\n href: href.slice(0, 120),\r\n });\r\n }\r\n\r\n // mailto without address\r\n if (category === \"mailto\" && href.trim().toLowerCase() === \"mailto:\") {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-mailto\",\r\n message: \"mailto: link has no email address\",\r\n href,\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // Very long URL\r\n if (href.length > 2000) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"long-url\",\r\n message: \"URL exceeds 2000 characters — may be truncated by some email clients\",\r\n href: href.slice(0, 120) + \"...\",\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n });\r\n\r\n return { totalLinks, issues, breakdown };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { AccessibilityIssue, AccessibilityReport } from \"./types\";\r\n\r\nconst GENERIC_LINK_TEXT = new Set([\r\n \"click here\", \"here\", \"read more\", \"learn more\", \"more\",\r\n \"link\", \"this link\", \"click\", \"tap here\", \"this\",\r\n]);\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction describeElement($: cheerio.CheerioAPI, el: any): string {\r\n const tag = (el.tagName as string)?.toLowerCase() || \"unknown\";\r\n const src = $(el).attr(\"src\");\r\n const href = $(el).attr(\"href\");\r\n if (src) return `<${tag} src=\"${src.slice(0, 60)}${src.length > 60 ? \"...\" : \"\"}\">`;\r\n if (href) return `<${tag} href=\"${href.slice(0, 60)}${href.length > 60 ? \"...\" : \"\"}\">`;\r\n const text = $(el).text().trim().slice(0, 40);\r\n if (text) return `<${tag}>${text}${$(el).text().trim().length > 40 ? \"...\" : \"\"}</${tag}>`;\r\n return `<${tag}>`;\r\n}\r\n\r\nfunction checkLangAttribute($: cheerio.CheerioAPI): AccessibilityIssue | null {\r\n const lang = $(\"html\").attr(\"lang\");\r\n if (!lang || !lang.trim()) {\r\n return {\r\n severity: \"error\",\r\n rule: \"missing-lang\",\r\n message: \"Missing lang attribute on <html> element\",\r\n details: 'Screen readers use the lang attribute to determine pronunciation. Add lang=\"en\" (or appropriate language code).',\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nfunction checkTitle($: cheerio.CheerioAPI): AccessibilityIssue | null {\r\n const title = $(\"title\").text().trim();\r\n if (!title) {\r\n return {\r\n severity: \"warning\",\r\n rule: \"missing-title\",\r\n message: \"Missing or empty <title> element\",\r\n details: \"The <title> helps screen readers identify the email content.\",\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nfunction checkImageAlt($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"img\").each((_, el) => {\r\n const alt = $(el).attr(\"alt\");\r\n const src = $(el).attr(\"src\") || \"\";\r\n const role = $(el).attr(\"role\");\r\n\r\n if (role === \"presentation\" || role === \"none\") return;\r\n\r\n if (alt === undefined) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"img-missing-alt\",\r\n message: \"Image missing alt attribute\",\r\n element: describeElement($, el),\r\n details: 'Every image must have an alt attribute. Use alt=\"\" for decorative images.',\r\n });\r\n } else if (alt.trim() === \"\") {\r\n const isLikelyContent =\r\n !src.includes(\"spacer\") &&\r\n !src.includes(\"pixel\") &&\r\n !src.includes(\"tracking\") &&\r\n !src.includes(\"1x1\") &&\r\n !src.includes(\"transparent\");\r\n\r\n if (isLikelyContent && ($(el).attr(\"width\") || \"0\") !== \"1\") {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"img-empty-alt\",\r\n message: \"Image has empty alt text — verify it is decorative\",\r\n element: describeElement($, el),\r\n details: \"Empty alt is correct for decorative images, but content images need descriptive alt text.\",\r\n });\r\n }\r\n } else if (/\\.(png|jpg|jpeg|gif|svg|webp|bmp)$/i.test(alt)) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"img-filename-alt\",\r\n message: \"Image alt text is a filename, not a description\",\r\n element: describeElement($, el),\r\n details: `Alt \"${alt}\" should describe the image content, not the file name.`,\r\n });\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkLinkAccessibility($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"a\").each((_, el) => {\r\n const text = $(el).text().trim().toLowerCase();\r\n const ariaLabel = $(el).attr(\"aria-label\");\r\n const title = $(el).attr(\"title\");\r\n const imgAlt = $(el).find(\"img\").attr(\"alt\");\r\n\r\n if (!text && !ariaLabel && !title && !imgAlt) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"link-no-accessible-name\",\r\n message: \"Link has no accessible name\",\r\n element: describeElement($, el),\r\n details: \"Links need visible text, aria-label, or an image with alt text.\",\r\n });\r\n return;\r\n }\r\n\r\n if (text && GENERIC_LINK_TEXT.has(text) && !ariaLabel) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"link-generic-text\",\r\n message: `Link text \"${$(el).text().trim()}\" is not descriptive`,\r\n element: describeElement($, el),\r\n details: \"Screen readers often list links out of context. Use text that describes the destination.\",\r\n });\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkTableAccessibility($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"table\").each((_, el) => {\r\n const role = $(el).attr(\"role\");\r\n const hasHeaders = $(el).find(\"th\").length > 0;\r\n const looksLikeLayout = !hasHeaders;\r\n\r\n if (looksLikeLayout && role !== \"presentation\" && role !== \"none\") {\r\n const nestedTables = $(el).find(\"table\").length;\r\n if (nestedTables > 0 || $(el).find(\"td\").length > 2) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"table-missing-role\",\r\n message: 'Layout table missing role=\"presentation\"',\r\n element: `<table> with ${$(el).find(\"td\").length} cells`,\r\n details: 'Add role=\"presentation\" to tables used for layout so screen readers don\\'t announce them as data tables.',\r\n });\r\n }\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkColorContrast($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n let smallTextCount = 0;\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n\r\n const fontSizeMatch = style.match(/font-size\\s*:\\s*(\\d+(?:\\.\\d+)?)(px|pt)/i);\r\n if (fontSizeMatch) {\r\n const size = parseFloat(fontSizeMatch[1]);\r\n const unit = fontSizeMatch[2].toLowerCase();\r\n const pxSize = unit === \"pt\" ? size * 1.333 : size;\r\n\r\n if (pxSize < 10 && pxSize > 0) {\r\n smallTextCount++;\r\n if (smallTextCount <= 3) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"small-text\",\r\n message: `Very small text (${fontSizeMatch[0].trim()})`,\r\n element: describeElement($, el),\r\n details: \"Text smaller than 10px is difficult to read, especially on mobile devices.\",\r\n });\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (smallTextCount > 3) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"small-text-multiple\",\r\n message: `${smallTextCount} elements with text smaller than 10px`,\r\n details: \"Consider using a minimum font size of 12-14px for readability.\",\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkSemanticStructure($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n const headings: { level: number; text: string }[] = [];\r\n $(\"h1, h2, h3, h4, h5, h6\").each((_, el) => {\r\n const level = parseInt(el.tagName.replace(/h/i, \"\"), 10);\r\n headings.push({ level, text: $(el).text().trim().slice(0, 60) });\r\n });\r\n\r\n for (let i = 1; i < headings.length; i++) {\r\n const gap = headings[i].level - headings[i - 1].level;\r\n if (gap > 1) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"heading-skip\",\r\n message: `Heading level skipped: h${headings[i - 1].level} to h${headings[i].level}`,\r\n details: \"Skipped heading levels can confuse screen readers. Use sequential heading levels.\",\r\n });\r\n break;\r\n }\r\n }\r\n\r\n return issues;\r\n}\r\n\r\n/**\r\n * Audit an HTML email for accessibility issues.\r\n *\r\n * Checks for missing lang attributes, image alt text, small fonts,\r\n * layout table roles, link accessibility, heading hierarchy, and\r\n * color contrast. Returns a 0–100 score and detailed issues.\r\n */\r\nexport function checkAccessibility(html: string): AccessibilityReport {\r\n if (!html || !html.trim()) {\r\n return { score: 100, issues: [] };\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n const langIssue = checkLangAttribute($);\r\n if (langIssue) issues.push(langIssue);\r\n\r\n const titleIssue = checkTitle($);\r\n if (titleIssue) issues.push(titleIssue);\r\n\r\n issues.push(...checkImageAlt($));\r\n issues.push(...checkLinkAccessibility($));\r\n issues.push(...checkTableAccessibility($));\r\n issues.push(...checkColorContrast($));\r\n issues.push(...checkSemanticStructure($));\r\n\r\n let penalty = 0;\r\n for (const issue of issues) {\r\n switch (issue.severity) {\r\n case \"error\": penalty += 12; break;\r\n case \"warning\": penalty += 6; break;\r\n case \"info\": penalty += 2; break;\r\n }\r\n }\r\n\r\n const score = Math.max(0, 100 - penalty);\r\n return { score, issues };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { ImageIssue, ImageInfo, ImageReport } from \"./types\";\r\n\r\nconst DATA_URI_WARN_BYTES = 100 * 1024;\r\nconst TOTAL_DATA_URI_WARN_BYTES = 500 * 1024;\r\nconst HIGH_IMAGE_COUNT = 10;\r\n\r\nfunction estimateBase64Bytes(dataUri: string): number {\r\n const commaIdx = dataUri.indexOf(\",\");\r\n if (commaIdx === -1) return 0;\r\n const payload = dataUri.slice(commaIdx + 1);\r\n return Math.floor((payload.length * 3) / 4);\r\n}\r\n\r\nfunction isTrackingPixel(\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n el: cheerio.Cheerio<any>,\r\n): boolean {\r\n const width = el.attr(\"width\");\r\n const height = el.attr(\"height\");\r\n const style = (el.attr(\"style\") || \"\").toLowerCase();\r\n\r\n if (width === \"1\" && height === \"1\") return true;\r\n if (width === \"0\" || height === \"0\") return true;\r\n\r\n if (\r\n style.includes(\"display:none\") ||\r\n style.includes(\"display: none\") ||\r\n style.includes(\"visibility:hidden\") ||\r\n style.includes(\"visibility: hidden\")\r\n ) {\r\n return true;\r\n }\r\n\r\n if (/width\\s*:\\s*1px/.test(style) && /height\\s*:\\s*1px/.test(style)) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction truncateSrc(src: string, max = 60): string {\r\n if (src.startsWith(\"data:\")) {\r\n const semi = src.indexOf(\";\");\r\n return semi > 0 ? src.slice(0, semi + 1) + \"base64,...\" : \"data:...\";\r\n }\r\n return src.length > max ? src.slice(0, max - 3) + \"...\" : src;\r\n}\r\n\r\n/**\r\n * Analyze images in an HTML email for best practices.\r\n *\r\n * Checks for missing dimensions, oversized data URIs, missing alt\r\n * attributes, unsupported formats (WebP, SVG), tracking pixels,\r\n * missing display:block, and overall image heaviness.\r\n */\r\nexport function analyzeImages(html: string): ImageReport {\r\n if (!html || !html.trim()) {\r\n return { total: 0, totalDataUriBytes: 0, issues: [], images: [] };\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: ImageIssue[] = [];\r\n const images: ImageInfo[] = [];\r\n let totalDataUriBytes = 0;\r\n\r\n $(\"img\").each((_, el) => {\r\n const img = $(el);\r\n const src = img.attr(\"src\") || \"\";\r\n const alt = img.attr(\"alt\") ?? null;\r\n const width = img.attr(\"width\") ?? null;\r\n const height = img.attr(\"height\") ?? null;\r\n const style = (img.attr(\"style\") || \"\").toLowerCase();\r\n const imgIssues: string[] = [];\r\n\r\n const tracking = isTrackingPixel(img);\r\n\r\n let dataUriBytes = 0;\r\n if (src.startsWith(\"data:\")) {\r\n dataUriBytes = estimateBase64Bytes(src);\r\n totalDataUriBytes += dataUriBytes;\r\n }\r\n\r\n // Skip detailed checks for tracking pixels\r\n if (tracking) {\r\n images.push({\r\n src: truncateSrc(src),\r\n alt, width, height,\r\n isTrackingPixel: true,\r\n dataUriBytes,\r\n issues: [\"tracking-pixel\"],\r\n });\r\n return;\r\n }\r\n\r\n // Missing width/height\r\n if (!width && !height) {\r\n const hasStyleWidth = /width\\s*:/.test(style);\r\n const hasStyleHeight = /height\\s*:/.test(style);\r\n if (!hasStyleWidth && !hasStyleHeight) {\r\n imgIssues.push(\"missing-dimensions\");\r\n issues.push({\r\n rule: \"missing-dimensions\",\r\n severity: \"warning\",\r\n message: \"Image missing width/height attributes — causes layout shifts and Outlook rendering issues.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n }\r\n\r\n // Large data URI\r\n if (dataUriBytes > DATA_URI_WARN_BYTES) {\r\n const kb = Math.round(dataUriBytes / 1024);\r\n imgIssues.push(\"large-data-uri\");\r\n issues.push({\r\n rule: \"large-data-uri\",\r\n severity: \"warning\",\r\n message: `Data URI is ${kb}KB — consider hosting the image externally to reduce email size.`,\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // Missing alt\r\n if (alt === null) {\r\n imgIssues.push(\"missing-alt\");\r\n issues.push({\r\n rule: \"missing-alt\",\r\n severity: \"warning\",\r\n message: \"Image missing alt attribute — hurts deliverability and accessibility.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // WebP format\r\n if (src.toLowerCase().endsWith(\".webp\") || src.includes(\"image/webp\")) {\r\n imgIssues.push(\"webp-format\");\r\n issues.push({\r\n rule: \"webp-format\",\r\n severity: \"info\",\r\n message: \"WebP format detected — not supported by all email clients. Consider PNG or JPEG.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // SVG format\r\n if (src.toLowerCase().endsWith(\".svg\") || src.includes(\"image/svg\")) {\r\n imgIssues.push(\"svg-format\");\r\n issues.push({\r\n rule: \"svg-format\",\r\n severity: \"info\",\r\n message: \"SVG format detected — not supported by most email clients. Use PNG instead.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // Missing display:block\r\n if (!style.includes(\"display:block\") && !style.includes(\"display: block\")) {\r\n imgIssues.push(\"missing-display-block\");\r\n issues.push({\r\n rule: \"missing-display-block\",\r\n severity: \"info\",\r\n message: \"Image without display:block — may cause unwanted gaps in Outlook.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n images.push({\r\n src: truncateSrc(src),\r\n alt, width, height,\r\n isTrackingPixel: false,\r\n dataUriBytes,\r\n issues: imgIssues,\r\n });\r\n });\r\n\r\n // Aggregate checks\r\n const nonTrackingImages = images.filter((i) => !i.isTrackingPixel);\r\n\r\n if (nonTrackingImages.length > HIGH_IMAGE_COUNT) {\r\n issues.push({\r\n rule: \"high-image-count\",\r\n severity: \"info\",\r\n message: `Email contains ${nonTrackingImages.length} images — heavy emails may be clipped or load slowly.`,\r\n });\r\n }\r\n\r\n const trackingPixels = images.filter((i) => i.isTrackingPixel);\r\n if (trackingPixels.length > 0) {\r\n issues.push({\r\n rule: \"tracking-pixel\",\r\n severity: \"info\",\r\n message: `${trackingPixels.length} tracking pixel${trackingPixels.length > 1 ? \"s\" : \"\"} detected.`,\r\n });\r\n }\r\n\r\n if (totalDataUriBytes > TOTAL_DATA_URI_WARN_BYTES) {\r\n const kb = Math.round(totalDataUriBytes / 1024);\r\n issues.push({\r\n rule: \"total-data-uri-size\",\r\n severity: \"warning\",\r\n message: `Total data URI size is ${kb}KB — consider hosting images externally to reduce email size.`,\r\n });\r\n }\r\n\r\n return { total: images.length, totalDataUriBytes, issues, images };\r\n}\r\n"],"mappings":"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","names":["_","cheerio","csstree","value","cheerio","cheerio","cheerio","cheerio","GENERIC_LINK_TEXT","cheerio"]}
1
+ {"version":3,"sources":["../src/clients.ts","../src/transform.ts","../src/rules/css-support.ts","../src/fix-snippets/html-fixes.ts","../src/fix-snippets/jsx-fixes.ts","../src/fix-snippets/mjml-fixes.ts","../src/fix-snippets/maizzle-fixes.ts","../src/fix-snippets/html-suggestions.ts","../src/fix-snippets/jsx-suggestions.ts","../src/fix-snippets/mjml-suggestions.ts","../src/fix-snippets/maizzle-suggestions.ts","../src/fix-snippets/index.ts","../src/style-utils.ts","../src/constants.ts","../src/analyze.ts","../src/dark-mode.ts","../src/color-utils.ts","../src/diff.ts","../src/export-prompt.ts","../src/token-utils.ts","../src/ai-fix.ts","../src/spam-scorer.ts","../src/link-validator.ts","../src/accessibility-checker.ts","../src/image-analyzer.ts","../src/audit.ts"],"sourcesContent":["import type { EmailClient } from \"./types\";\r\n\r\nexport const EMAIL_CLIENTS: EmailClient[] = [\r\n {\r\n id: \"gmail-web\",\r\n name: \"Gmail\",\r\n category: \"webmail\",\r\n engine: \"Gmail Web\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"gmail-android\",\r\n name: \"Gmail Android\",\r\n category: \"mobile\",\r\n engine: \"Gmail Mobile\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"gmail-ios\",\r\n name: \"Gmail iOS\",\r\n category: \"mobile\",\r\n engine: \"Gmail Mobile\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"outlook-web\",\r\n name: \"Outlook 365\",\r\n category: \"webmail\",\r\n engine: \"Outlook Web\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"outlook-windows\",\r\n name: \"Outlook Windows\",\r\n category: \"desktop\",\r\n engine: \"Microsoft Word\",\r\n darkModeSupport: false,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"apple-mail-macos\",\r\n name: \"Apple Mail\",\r\n category: \"desktop\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"apple-mail-ios\",\r\n name: \"Apple Mail iOS\",\r\n category: \"mobile\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"yahoo-mail\",\r\n name: \"Yahoo Mail\",\r\n category: \"webmail\",\r\n engine: \"Yahoo\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"samsung-mail\",\r\n name: \"Samsung Mail\",\r\n category: \"mobile\",\r\n engine: \"Samsung\",\r\n darkModeSupport: true,\r\n icon: \"smartphone\",\r\n },\r\n {\r\n id: \"thunderbird\",\r\n name: \"Thunderbird\",\r\n category: \"desktop\",\r\n engine: \"Gecko\",\r\n darkModeSupport: false,\r\n icon: \"monitor\",\r\n },\r\n {\r\n id: \"hey-mail\",\r\n name: \"HEY Mail\",\r\n category: \"webmail\",\r\n engine: \"WebKit\",\r\n darkModeSupport: true,\r\n icon: \"mail\",\r\n },\r\n {\r\n id: \"superhuman\",\r\n name: \"Superhuman\",\r\n category: \"desktop\",\r\n engine: \"Blink\",\r\n darkModeSupport: true,\r\n icon: \"monitor\",\r\n },\r\n];\r\n\r\nexport function getClient(id: string): EmailClient | undefined {\r\n return EMAIL_CLIENTS.find((c) => c.id === id);\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport * as csstree from \"css-tree\";\r\nimport type { CSSWarning, Framework, TransformResult } from \"./types\";\r\nimport {\r\n CSS_SUPPORT,\r\n GMAIL_STRIPPED_PROPERTIES,\r\n OUTLOOK_WORD_UNSUPPORTED,\r\n STRUCTURAL_FIX_PROPERTIES,\r\n} from \"./rules/css-support\";\r\nimport { getCodeFix, getSuggestion, isCodeFixGenericFallback } from \"./fix-snippets\";\r\nimport { parseInlineStyle, serializeStyle } from \"./style-utils\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\n\r\n// =============================================================================\r\n// Shared helpers\r\n// =============================================================================\r\n\r\n/** Inline <style> blocks into elements using css-tree. */\r\nfunction inlineStyles($: cheerio.CheerioAPI): void {\r\n const styleBlocks: string[] = [];\r\n $(\"style\").each((_, el) => {\r\n styleBlocks.push($(el).text());\r\n });\r\n\r\n if (styleBlocks.length === 0) return;\r\n\r\n for (const block of styleBlocks) {\r\n let ast: csstree.CssNode;\r\n try {\r\n ast = csstree.parse(block, { parseCustomProperty: true });\r\n } catch {\r\n continue;\r\n }\r\n\r\n csstree.walk(ast, {\r\n visit: \"Rule\",\r\n enter(node: csstree.CssNode) {\r\n if (node.type !== \"Rule\" || node.prelude.type !== \"SelectorList\") return;\r\n\r\n const declarations = csstree.generate(node.block);\r\n const declText = declarations.slice(1, -1).trim();\r\n if (!declText) return;\r\n\r\n const selectorText = csstree.generate(node.prelude);\r\n\r\n if (selectorText.includes(\":hover\") ||\r\n selectorText.includes(\":focus\") ||\r\n selectorText.includes(\":active\") ||\r\n selectorText.includes(\"::\")) {\r\n return;\r\n }\r\n\r\n try {\r\n $(selectorText).each((_, el) => {\r\n const existing = $(el).attr(\"style\") || \"\";\r\n $(el).attr(\"style\", existing ? `${existing}; ${declText}` : declText);\r\n });\r\n } catch {\r\n // Invalid selector for cheerio, skip\r\n }\r\n },\r\n });\r\n }\r\n}\r\n\r\n/** Build a CSSWarning with framework-aware suggestion + fix + fallback flag. */\r\nfunction makeWarning(\r\n base: Omit<CSSWarning, \"suggestion\" | \"fix\" | \"fixIsGenericFallback\" | \"fixType\">,\r\n prop: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): CSSWarning {\r\n const sug = getSuggestion(prop, clientId, framework);\r\n const fix = getCodeFix(prop, clientId, framework);\r\n const isFallback = framework && (\r\n (sug?.isGenericFallback) ||\r\n (fix && isCodeFixGenericFallback(prop, clientId, framework))\r\n );\r\n return {\r\n ...base,\r\n ...(sug ? { suggestion: sug.text } : {}),\r\n ...(fix ? { fix } : {}),\r\n ...(isFallback ? { fixIsGenericFallback: true } : {}),\r\n fixType: STRUCTURAL_FIX_PROPERTIES.has(prop) ? \"structural\" : \"css\",\r\n };\r\n}\r\n\r\n/** Detect animation/transition usage in inline styles and <style> blocks. */\r\nfunction detectAnimations($: cheerio.CheerioAPI): boolean {\r\n let found = false;\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n props.forEach((_, prop) => {\r\n if (\r\n prop === \"animation\" || prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") || prop.startsWith(\"transition-\")\r\n ) {\r\n found = true;\r\n }\r\n });\r\n });\r\n\r\n if (!found) {\r\n $(\"style\").each((_, el) => {\r\n try {\r\n const ast = csstree.parse($(el).text());\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Declaration\") {\r\n const prop = node.property.toLowerCase();\r\n if (prop === \"animation\" || prop === \"transition\" ||\r\n prop.startsWith(\"animation-\") || prop.startsWith(\"transition-\")) {\r\n found = true;\r\n }\r\n }\r\n },\r\n });\r\n } catch { /* skip unparseable CSS */ }\r\n });\r\n }\r\n\r\n return found;\r\n}\r\n\r\n// =============================================================================\r\n// Data-driven client config\r\n// =============================================================================\r\n\r\ninterface ClientTransformConfig {\r\n id: string;\r\n /** CSS properties stripped from inline styles */\r\n strippedProperties: Set<string>;\r\n /** How stripped properties are treated: \"strip\" removes them, \"info\" just warns */\r\n stripMode: \"strip\" | \"info\";\r\n /** Custom value-level checks (e.g., display:grid but not display:flex) */\r\n valueStrips?: Array<{ prop: string; pattern: RegExp }>;\r\n /** Whether to inline <style> blocks and remove them */\r\n inlineAndStripStyles: boolean;\r\n /** Whether to strip <link rel=\"stylesheet\"> */\r\n stripExternalStylesheets: boolean;\r\n /** Whether to strip <form> elements */\r\n stripForms: boolean;\r\n /** Whether to strip <svg> elements */\r\n stripSvg: boolean;\r\n /** Additional checks to run (animation detection, dark mode hints, etc.) */\r\n additionalChecks?: (\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n html: string,\r\n framework?: Framework,\r\n ) => CSSWarning[];\r\n}\r\n\r\n// -- Shared additional check functions --\r\n\r\nfunction gmailAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n _html: string,\r\n framework?: Framework,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Check for @font-face (must happen before style removal — called pre-strip)\r\n let hasAtFontFace = false;\r\n $(\"style\").each((_, el) => {\r\n try {\r\n const ast = csstree.parse($(el).text());\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Atrule\" && node.name === \"font-face\") {\r\n hasAtFontFace = true;\r\n }\r\n },\r\n });\r\n } catch { /* skip unparseable CSS */ }\r\n });\r\n if (hasAtFontFace) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"@font-face\",\r\n message: \"Gmail does not support custom web fonts.\",\r\n }, \"@font-face\", clientId, framework));\r\n }\r\n\r\n return warnings;\r\n}\r\n\r\nfunction gmailPostChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n _html: string,\r\n framework?: Framework,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Remove MSO conditional comments\r\n $(\"*\")\r\n .contents()\r\n .filter(function () {\r\n return this.type === \"comment\";\r\n })\r\n .each(function () {\r\n const commentText = (this as unknown as { data: string }).data || \"\";\r\n if (commentText.includes(\"<style\") || commentText.includes(\"[if mso]\") || commentText.includes(\"[if gte mso\")) {\r\n $(this).remove();\r\n }\r\n });\r\n\r\n const styleSug = getSuggestion(\"<style>:partial\", clientId, framework);\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"<style>\",\r\n message: \"Gmail partially supports <style> blocks (head only, 16KB limit). Inlining recommended for safety.\",\r\n suggestion: styleSug.text,\r\n });\r\n\r\n return warnings;\r\n}\r\n\r\nfunction outlookWindowsAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n _html: string,\r\n framework?: Framework,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n if ($(\"[style*='border-radius']\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"border-radius\",\r\n message: \"Outlook Windows ignores border-radius. Buttons and containers will have sharp corners.\",\r\n }, \"border-radius\", clientId, framework));\r\n }\r\n\r\n if ($(\"[style*='max-width']\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"max-width\",\r\n message: \"Outlook Windows ignores max-width.\",\r\n }, \"max-width\", clientId, framework));\r\n }\r\n\r\n const hasDivLayout =\r\n $(\"div[style*='display']\").length > 0 ||\r\n $(\"div[style*='flex']\").length > 0 ||\r\n $(\"div[style*='grid']\").length > 0;\r\n\r\n if (hasDivLayout) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"display:flex\",\r\n message: \"Outlook Windows uses Microsoft Word for rendering. Flexbox and Grid layouts will break.\",\r\n }, \"display:flex\", clientId, framework));\r\n }\r\n\r\n if (\r\n $(\"[style*='background-image']\").length > 0 ||\r\n $(\"[style*='background:']\").filter((_, el) =>\r\n ($(el).attr(\"style\") || \"\").includes(\"url(\")\r\n ).length > 0\r\n ) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"background-image\",\r\n message: \"Outlook Windows requires VML for background images.\",\r\n }, \"background-image\", clientId, framework));\r\n }\r\n\r\n return warnings;\r\n}\r\n\r\nfunction appleMailAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n const imgsWithTransparentBg = $(\"img\").filter((_, el) => {\r\n const src = $(el).attr(\"src\") || \"\";\r\n return src.endsWith(\".png\") || src.endsWith(\".svg\");\r\n });\r\n\r\n if (imgsWithTransparentBg.length > 0) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message: \"PNG/SVG images with transparent backgrounds may become invisible in Apple Mail dark mode.\",\r\n suggestion: \"Add a white background or padding around images, or use dark-mode-friendly image variants.\",\r\n });\r\n }\r\n\r\n return warnings;\r\n}\r\n\r\nfunction yahooAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n _html: string,\r\n framework?: Framework,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"class\",\r\n message: \"Yahoo Mail rewrites CSS class names with a prefix. Class-based selectors in <style> blocks will still work but the names change.\",\r\n });\r\n\r\n if (\r\n $(\"[style*='background']\").filter((_, el) =>\r\n ($(el).attr(\"style\") || \"\").includes(\"url(\")\r\n ).length > 0\r\n ) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"background-image\",\r\n message: \"Yahoo Mail has inconsistent support for CSS background images.\",\r\n }, \"background-image\", clientId, framework));\r\n }\r\n\r\n return warnings;\r\n}\r\n\r\nfunction thunderbirdAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n): CSSWarning[] {\r\n if (detectAnimations($)) {\r\n return [{\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"animation\",\r\n message: \"Thunderbird does not support CSS animations or transitions.\",\r\n }];\r\n }\r\n return [];\r\n}\r\n\r\nfunction heyAdditionalChecks(\r\n _$: cheerio.CheerioAPI,\r\n clientId: string,\r\n html: string,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message: \"HEY Mail supports @media (prefers-color-scheme: dark). Consider adding dark mode styles.\",\r\n suggestion: \"Add a @media (prefers-color-scheme: dark) block to optimize for HEY's audience.\",\r\n });\r\n }\r\n\r\n return warnings;\r\n}\r\n\r\nfunction superhumanAdditionalChecks(\r\n $: cheerio.CheerioAPI,\r\n clientId: string,\r\n html: string,\r\n): CSSWarning[] {\r\n const warnings: CSSWarning[] = [];\r\n\r\n if (detectAnimations($)) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"animation\",\r\n message: \"Superhuman may honor OS-level 'reduce motion' preferences, disabling animations.\",\r\n suggestion: \"Use @media (prefers-reduced-motion: reduce) to provide static fallbacks.\",\r\n });\r\n }\r\n\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"<style>\",\r\n message: \"Superhuman uses Chromium rendering with excellent CSS support. Flexbox, Grid, CSS variables, and modern properties all work.\",\r\n });\r\n\r\n return warnings;\r\n}\r\n\r\n// =============================================================================\r\n// Per-client configurations (declarative data)\r\n// =============================================================================\r\n\r\nconst EMPTY_SET: Set<string> = new Set();\r\n\r\nconst CLIENT_CONFIGS: Record<string, ClientTransformConfig> = {\r\n \"gmail-web\": {\r\n id: \"gmail-web\",\r\n strippedProperties: GMAIL_STRIPPED_PROPERTIES,\r\n stripMode: \"strip\",\r\n valueStrips: [\r\n { prop: \"display\", pattern: /grid/ },\r\n { prop: \"background\", pattern: /linear-gradient|radial-gradient/ },\r\n ],\r\n inlineAndStripStyles: true,\r\n stripExternalStylesheets: true,\r\n stripForms: true,\r\n stripSvg: true,\r\n additionalChecks: gmailAdditionalChecks,\r\n },\r\n \"gmail-android\": {\r\n id: \"gmail-android\",\r\n strippedProperties: GMAIL_STRIPPED_PROPERTIES,\r\n stripMode: \"strip\",\r\n valueStrips: [\r\n { prop: \"display\", pattern: /grid/ },\r\n { prop: \"background\", pattern: /linear-gradient|radial-gradient/ },\r\n ],\r\n inlineAndStripStyles: true,\r\n stripExternalStylesheets: true,\r\n stripForms: true,\r\n stripSvg: true,\r\n additionalChecks: gmailAdditionalChecks,\r\n },\r\n \"gmail-ios\": {\r\n id: \"gmail-ios\",\r\n strippedProperties: GMAIL_STRIPPED_PROPERTIES,\r\n stripMode: \"strip\",\r\n valueStrips: [\r\n { prop: \"display\", pattern: /grid/ },\r\n { prop: \"background\", pattern: /linear-gradient|radial-gradient/ },\r\n ],\r\n inlineAndStripStyles: true,\r\n stripExternalStylesheets: true,\r\n stripForms: true,\r\n stripSvg: true,\r\n additionalChecks: gmailAdditionalChecks,\r\n },\r\n \"outlook-windows\": {\r\n id: \"outlook-windows\",\r\n strippedProperties: OUTLOOK_WORD_UNSUPPORTED,\r\n stripMode: \"strip\",\r\n valueStrips: [\r\n { prop: \"background\", pattern: /linear-gradient|radial-gradient/ },\r\n { prop: \"background-image\", pattern: /linear-gradient|radial-gradient/ },\r\n ],\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n additionalChecks: outlookWindowsAdditionalChecks,\r\n },\r\n \"outlook-web\": {\r\n id: \"outlook-web\",\r\n strippedProperties: new Set([\"position\", \"transform\", \"animation\", \"transition\"]),\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n },\r\n \"apple-mail-macos\": {\r\n id: \"apple-mail-macos\",\r\n strippedProperties: EMPTY_SET,\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n additionalChecks: appleMailAdditionalChecks,\r\n },\r\n \"apple-mail-ios\": {\r\n id: \"apple-mail-ios\",\r\n strippedProperties: EMPTY_SET,\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n additionalChecks: appleMailAdditionalChecks,\r\n },\r\n \"yahoo-mail\": {\r\n id: \"yahoo-mail\",\r\n strippedProperties: new Set([\"position\", \"box-shadow\", \"transform\", \"animation\", \"transition\", \"opacity\"]),\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n additionalChecks: yahooAdditionalChecks,\r\n },\r\n \"samsung-mail\": {\r\n id: \"samsung-mail\",\r\n strippedProperties: new Set([\"box-shadow\", \"transform\", \"animation\", \"transition\", \"opacity\"]),\r\n stripMode: \"info\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n },\r\n \"thunderbird\": {\r\n id: \"thunderbird\",\r\n strippedProperties: EMPTY_SET,\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: false,\r\n stripForms: false,\r\n stripSvg: false,\r\n additionalChecks: thunderbirdAdditionalChecks,\r\n },\r\n \"hey-mail\": {\r\n id: \"hey-mail\",\r\n strippedProperties: new Set([\"transform\", \"animation\", \"transition\"]),\r\n stripMode: \"strip\",\r\n valueStrips: [\r\n { prop: \"position\", pattern: /fixed|sticky/ },\r\n ],\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: true,\r\n stripForms: true,\r\n stripSvg: false,\r\n additionalChecks: heyAdditionalChecks,\r\n },\r\n \"superhuman\": {\r\n id: \"superhuman\",\r\n strippedProperties: EMPTY_SET,\r\n stripMode: \"strip\",\r\n inlineAndStripStyles: false,\r\n stripExternalStylesheets: true,\r\n stripForms: true,\r\n stripSvg: false,\r\n additionalChecks: superhumanAdditionalChecks,\r\n },\r\n};\r\n\r\n// =============================================================================\r\n// Shared transform engine\r\n// =============================================================================\r\n\r\nfunction applyTransform(\r\n html: string,\r\n config: ClientTransformConfig,\r\n framework?: Framework,\r\n): TransformResult {\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n const clientId = config.id;\r\n\r\n // 0. Pre-strip additional checks (e.g., Gmail @font-face detection)\r\n if (config.additionalChecks && config.inlineAndStripStyles) {\r\n warnings.push(...config.additionalChecks($, clientId, html, framework));\r\n }\r\n\r\n // 1. Inline + strip <style> if needed\r\n if (config.inlineAndStripStyles) {\r\n inlineStyles($);\r\n $(\"style\").remove();\r\n }\r\n\r\n // 2. Strip external stylesheets\r\n if (config.stripExternalStylesheets) {\r\n if ($(\"link[rel='stylesheet']\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<link>\",\r\n message: `${clientId} does not load external stylesheets.`,\r\n }, \"<link>\", clientId, framework));\r\n $(\"link[rel='stylesheet']\").remove();\r\n }\r\n }\r\n\r\n // 3. Strip forms\r\n if (config.stripForms && $(\"form\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<form>\",\r\n message: `${clientId} removes form elements.`,\r\n }, \"<form>\", clientId, framework));\r\n $(\"form\").each((_, el) => {\r\n $(el).replaceWith($(el).html() || \"\");\r\n });\r\n }\r\n\r\n // 4. Strip SVG\r\n if (config.stripSvg && $(\"svg\").length > 0) {\r\n warnings.push(makeWarning({\r\n severity: \"error\",\r\n client: clientId,\r\n property: \"<svg>\",\r\n message: `${clientId} does not support inline SVG elements.`,\r\n }, \"<svg>\", clientId, framework));\r\n $(\"svg\").each((_, el) => {\r\n $(el).replaceWith('<img alt=\"[SVG not supported]\" />');\r\n });\r\n }\r\n\r\n // 5. Strip/warn about unsupported CSS properties (shared loop)\r\n if (config.strippedProperties.size > 0 || (config.valueStrips && config.valueStrips.length > 0)) {\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n const removed: string[] = [];\r\n\r\n props.forEach((value, prop) => {\r\n // Check stripped properties\r\n if (config.strippedProperties.has(prop)) {\r\n if (config.stripMode === \"strip\") {\r\n removed.push(prop);\r\n props.delete(prop);\r\n } else {\r\n warnings.push(makeWarning({\r\n severity: \"info\",\r\n client: clientId,\r\n property: prop,\r\n message: `${clientId} has limited support for \"${prop}\".`,\r\n }, prop, clientId, framework));\r\n }\r\n return;\r\n }\r\n\r\n // Check value-level strips\r\n for (const vs of config.valueStrips ?? []) {\r\n if (prop === vs.prop && vs.pattern.test(value)) {\r\n removed.push(prop);\r\n props.delete(prop);\r\n return;\r\n }\r\n }\r\n });\r\n\r\n if (removed.length > 0) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n for (const prop of removed) {\r\n warnings.push(makeWarning({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: prop,\r\n message: `${clientId} strips \"${prop}\" from styles.`,\r\n }, prop, clientId, framework));\r\n }\r\n }\r\n });\r\n }\r\n\r\n // 6. Gmail-specific post-strip checks (MSO comments, partial style info)\r\n if (config.inlineAndStripStyles) {\r\n warnings.push(...gmailPostChecks($, clientId, html, framework));\r\n }\r\n\r\n // 7. Run additional checks (non-Gmail, or non-pre-strip)\r\n if (config.additionalChecks && !config.inlineAndStripStyles) {\r\n warnings.push(...config.additionalChecks($, clientId, html, framework));\r\n }\r\n\r\n return { clientId, html: $.html(), warnings };\r\n}\r\n\r\n// =============================================================================\r\n// Public API\r\n// =============================================================================\r\n\r\nexport function transformForClient(\r\n html: string,\r\n clientId: string,\r\n framework?: Framework,\r\n): TransformResult {\r\n if (!html || !html.trim()) {\r\n return { clientId, html: html || \"\", warnings: [] };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const config = CLIENT_CONFIGS[clientId];\r\n if (!config) {\r\n return {\r\n clientId,\r\n html,\r\n warnings: [\r\n {\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"unknown\",\r\n message: `No transformation rules available for client \"${clientId}\".`,\r\n },\r\n ],\r\n };\r\n }\r\n return applyTransform(html, config, framework);\r\n}\r\n\r\nexport function transformForAllClients(html: string, framework?: Framework): TransformResult[] {\r\n return Object.keys(CLIENT_CONFIGS).map((clientId) =>\r\n transformForClient(html, clientId, framework)\r\n );\r\n}\r\n","import type { SupportLevel } from \"../types\";\r\n\r\n/**\r\n * CSS property support matrix.\r\n * Last validated: 2026-02-22\r\n *\r\n * Each entry maps a CSS property to its support level per email client.\r\n *\r\n * Support levels:\r\n * - \"supported\": fully supported\r\n * - \"partial\": partially supported (with caveats)\r\n * - \"unsupported\": not supported at all\r\n * - \"unknown\": no data available\r\n *\r\n * Data sources by client:\r\n * - gmail-*, outlook-*, apple-mail-*, yahoo-mail, samsung-mail, thunderbird:\r\n * Verified against caniemail.com; inline comments cite specific notes\r\n * using caniemail's \"#N\" notation (e.g. \"caniemail: a #2\").\r\n * - hey-mail: NOT in caniemail. Values are inferred from HEY's WebKit-based\r\n * rendering engine and documented stripping behaviours (forms, external\r\n * stylesheets, fixed/sticky positioning). Treat as best-effort estimates;\r\n * empirical verification is recommended before relying on these values.\r\n * - superhuman: NOT in caniemail. Values are inferred from Superhuman's\r\n * Chromium/Blink rendering engine and its documented stripping behaviours\r\n * (forms, external stylesheets). Treat as best-effort estimates.\r\n */\r\nexport const CSS_SUPPORT: Record<\r\n string,\r\n Record<string, SupportLevel>\r\n> = {\r\n // --- Layout ---\r\n \"display\": {\r\n \"gmail-web\": \"supported\", // block, inline, inline-block, none all work\r\n \"gmail-android\": \"partial\", // basic values only\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // limited values\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"display:flex\": {\r\n \"gmail-web\": \"supported\", // caniemail: y\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (non-Google accounts)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1\r\n \"outlook-web\": \"supported\", // caniemail: y\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\", // caniemail: y #2 (no inline-flex)\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"display:grid\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"float\": {\r\n \"gmail-web\": \"supported\", // caniemail: y\r\n \"gmail-android\": \"partial\", // caniemail: a #2 (no logical values)\r\n \"gmail-ios\": \"partial\", // caniemail: a #2\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // caniemail: n #1\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2 (no logical values)\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"position\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"partial\", // caniemail: a #2 (sticky only)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"partial\", // caniemail: a #1 (no sticky/fixed)\r\n \"apple-mail-ios\": \"partial\", // caniemail: a #1\r\n \"yahoo-mail\": \"partial\", // caniemail: a #3 (relative only)\r\n \"samsung-mail\": \"partial\", // caniemail: a #1\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"partial\", // relative only; HEY strips fixed/sticky positioning\r\n \"superhuman\": \"partial\", // relative/absolute supported; fixed/sticky stripped\r\n },\r\n\r\n // --- Box Model ---\r\n \"margin\": {\r\n \"gmail-web\": \"partial\", // no negative margins\r\n \"gmail-android\": \"partial\",\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // no auto margins\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"padding\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // not on <p>, <div>\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"max-width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #1 (only on <table> elements)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"box-sizing\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Typography ---\r\n \"font-family\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"font-size\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"partial\", // may auto-resize small text\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"partial\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"font-weight\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"line-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // ignores on some elements\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"letter-spacing\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-align\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-decoration\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-transform\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"@font-face\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #4 (declaration kept, remote fonts ignored)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\", // caniemail: y #8\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\", // HEY uses WebKit — web fonts work\r\n \"superhuman\": \"supported\", // Superhuman uses Blink — web fonts work\r\n },\r\n\r\n // --- Colors & Backgrounds ---\r\n \"color\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-color\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-image\": {\r\n \"gmail-web\": \"supported\", // caniemail: y (restored 2023-08)\r\n \"gmail-android\": \"supported\", // caniemail: y\r\n \"gmail-ios\": \"supported\", // caniemail: y\r\n \"outlook-web\": \"supported\", // caniemail: y\r\n \"outlook-windows\": \"partial\", // caniemail: n #5 (VML workaround)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #3 (no multiple values)\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background\": {\r\n \"gmail-web\": \"partial\", // color only, no shorthand with images\r\n \"gmail-android\": \"partial\",\r\n \"gmail-ios\": \"partial\",\r\n \"outlook-web\": \"partial\",\r\n \"outlook-windows\": \"partial\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"linear-gradient\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Borders ---\r\n \"border\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // no shorthand on some elements\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-radius\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2 (no elliptical slash notation)\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-collapse\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"supported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"box-shadow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (non-Google accounts)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1\r\n \"outlook-web\": \"supported\", // caniemail: y (since 2023-12)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\", // caniemail: y\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Transforms & Animation ---\r\n \"transform\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips transform for security\r\n \"superhuman\": \"partial\", // Superhuman (Blink) allows some transforms\r\n },\r\n \"animation\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"partial\", // Superhuman allows CSS animations but they may be disabled by user settings\r\n },\r\n \"transition\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"unsupported\",\r\n },\r\n\r\n // --- Media & Responsive ---\r\n \"@media\": {\r\n \"gmail-web\": \"partial\", // caniemail: a #7 (no height-based, no nested)\r\n \"gmail-android\": \"partial\", // caniemail: a #6 #7\r\n \"gmail-ios\": \"partial\", // caniemail: a #6 #7\r\n \"outlook-web\": \"partial\", // caniemail: a #1 #10 (no nested)\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // caniemail: a #2\r\n \"samsung-mail\": \"partial\", // caniemail: a #9\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\", // HEY is WebKit-based, full @media support\r\n \"superhuman\": \"supported\", // Superhuman is Blink-based, full @media support\r\n },\r\n\r\n // --- HTML Elements ---\r\n \"<style>\": {\r\n \"gmail-web\": \"partial\", // caniemail: a #1 #6 (head only, 16KB limit)\r\n \"gmail-android\": \"partial\", // caniemail: a #1 (head only)\r\n \"gmail-ios\": \"partial\", // caniemail: a #1 #2\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // caniemail: a #4 (must declare before use)\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"<link>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips external stylesheets\r\n \"superhuman\": \"unsupported\", // Superhuman strips external stylesheets\r\n },\r\n \"<video>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"partial\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"unsupported\",\r\n \"hey-mail\": \"unsupported\",\r\n \"superhuman\": \"unsupported\",\r\n },\r\n \"<svg>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"partial\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"partial\", // HEY allows SVG but strips some attributes\r\n \"superhuman\": \"supported\", // Superhuman (Blink) renders SVG well\r\n },\r\n \"<form>\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"unsupported\", // HEY strips forms for security\r\n \"superhuman\": \"unsupported\", // Superhuman strips forms for security\r\n },\r\n\r\n // --- Text Wrapping ---\r\n \"word-break\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores word-break entirely\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // break-all partially works; break-word unreliable\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"overflow-wrap\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores overflow-wrap\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\", // inconsistent support\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"white-space\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // only normal and nowrap\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"text-overflow\": {\r\n \"gmail-web\": \"unsupported\", // Gmail strips overflow, so text-overflow is useless\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Table Layout ---\r\n \"vertical-align\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"partial\", // only on <td> elements via valign attribute\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"border-spacing\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // use cellspacing attribute instead\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Sizing ---\r\n \"min-width\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores min-width\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"min-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\", // Word engine ignores min-height\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"max-height\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Shadows ---\r\n \"text-shadow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Background Sub-properties ---\r\n \"background-size\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"background-position\": {\r\n \"gmail-web\": \"supported\",\r\n \"gmail-android\": \"supported\",\r\n \"gmail-ios\": \"supported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"partial\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n\r\n // --- Misc ---\r\n \"opacity\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"overflow\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"partial\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"visibility\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"supported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"supported\",\r\n \"samsung-mail\": \"supported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"gap\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n \"object-fit\": {\r\n \"gmail-web\": \"unsupported\",\r\n \"gmail-android\": \"unsupported\",\r\n \"gmail-ios\": \"unsupported\",\r\n \"outlook-web\": \"unsupported\",\r\n \"outlook-windows\": \"unsupported\",\r\n \"apple-mail-macos\": \"supported\",\r\n \"apple-mail-ios\": \"supported\",\r\n \"yahoo-mail\": \"unsupported\",\r\n \"samsung-mail\": \"unsupported\",\r\n \"thunderbird\": \"supported\",\r\n \"hey-mail\": \"supported\",\r\n \"superhuman\": \"supported\",\r\n },\r\n};\r\n\r\n/**\r\n * CSS properties that Gmail strips from inline styles.\r\n * Updated per caniemail.com data — Gmail keeps float and display (basic values).\r\n */\r\nexport const GMAIL_STRIPPED_PROPERTIES = new Set([\r\n \"position\",\r\n \"overflow\",\r\n \"visibility\",\r\n \"opacity\",\r\n \"box-shadow\",\r\n \"text-shadow\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"box-sizing\",\r\n \"object-fit\",\r\n \"gap\",\r\n]);\r\n\r\n/** CSS properties that Outlook Word engine ignores */\r\nexport const OUTLOOK_WORD_UNSUPPORTED = new Set([\r\n \"border-radius\",\r\n \"box-shadow\",\r\n \"text-shadow\",\r\n \"max-width\",\r\n \"max-height\",\r\n \"min-width\",\r\n \"min-height\",\r\n \"float\",\r\n \"position\",\r\n \"display\",\r\n \"overflow\",\r\n \"opacity\",\r\n \"transform\",\r\n \"animation\",\r\n \"transition\",\r\n \"background-size\",\r\n \"background-position\",\r\n \"box-sizing\",\r\n \"object-fit\",\r\n \"gap\",\r\n \"word-break\",\r\n \"overflow-wrap\",\r\n \"text-overflow\",\r\n \"border-spacing\",\r\n]);\r\n\r\n/**\r\n * Properties that require HTML structural changes (not just CSS swaps)\r\n * to fix. These cannot be solved by replacing one CSS value with another.\r\n */\r\nexport const STRUCTURAL_FIX_PROPERTIES = new Set([\r\n \"display:flex\",\r\n \"display:grid\",\r\n \"word-break\",\r\n \"overflow-wrap\",\r\n \"text-overflow\",\r\n \"position\",\r\n \"float\",\r\n \"gap\",\r\n \"max-width\",\r\n \"border-radius\",\r\n \"background-image\",\r\n \"background-size\",\r\n \"background-position\",\r\n \"<svg>\",\r\n \"<video>\",\r\n \"<form>\",\r\n \"object-fit\",\r\n]);\r\n","import type { CodeFix } from \"../types\";\r\n\r\n/**\r\n * HTML/generic code fix snippets — entries that do NOT have a\r\n * ::jsx, ::mjml, or ::maizzle suffix in their key.\r\n */\r\nexport const HTML_FIX_DATABASE: Record<string, CodeFix> = {\r\n // ── border-radius (Outlook VML fallback) ──────────────────────────────\r\n \"border-radius::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML to render rounded buttons in Outlook\",\r\n before: `<a href=\"https://example.com\"\r\n style=\"background-color: #6d28d9; color: #fff;\r\n padding: 12px 32px; border-radius: 6px;\r\n text-decoration: none; display: inline-block;\">\r\n Click Here\r\n</a>`,\r\n after: `<!--[if mso]>\r\n<v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\"\r\n href=\"https://example.com\"\r\n style=\"height:44px; v-text-anchor:middle; width:200px;\"\r\n arcsize=\"14%\" strokecolor=\"#6d28d9\" fillcolor=\"#6d28d9\">\r\n <w:anchorlock/>\r\n <center style=\"color:#fff; font-family:Arial,sans-serif;\r\n font-size:14px; font-weight:bold;\">Click Here</center>\r\n</v:roundrect>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<a href=\"https://example.com\"\r\n style=\"background-color: #6d28d9; color: #fff;\r\n padding: 12px 32px; border-radius: 6px;\r\n text-decoration: none; display: inline-block;\">\r\n Click Here\r\n</a>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n // ── background-image (Outlook VML) ────────────────────────────────────\r\n \"background-image::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML for background images in Outlook\",\r\n before: `<td style=\"background-image: url('hero.jpg');\r\n background-size: cover; padding: 40px;\">\r\n <h1 style=\"color: #fff;\">Hello World</h1>\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px; height:300px;\">\r\n <v:fill type=\"frame\" src=\"hero.jpg\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background-image: url('hero.jpg');\r\n background-size: cover; padding: 40px;\">\r\n <h1 style=\"color: #fff;\">Hello World</h1>\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── display:flex → table layout ───────────────────────────────────────\r\n \"display:flex::outlook\": {\r\n language: \"html\",\r\n description: \"Use table layout as fallback for flexbox in Outlook\",\r\n before: `<div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\"><tr>\r\n <td width=\"50%\" valign=\"top\">Column 1</td>\r\n <td width=\"50%\" valign=\"top\">Column 2</td>\r\n</tr></table>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n</div>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n // ── display:grid → table layout ───────────────────────────────────────\r\n \"display:grid\": {\r\n language: \"html\",\r\n description: \"Replace CSS Grid with table layout for email compatibility\",\r\n before: `<div style=\"display: grid;\r\n grid-template-columns: 1fr 1fr; gap: 16px;\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n</div>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td width=\"50%\" style=\"padding: 8px;\" valign=\"top\">\r\n Item 1\r\n </td>\r\n <td width=\"50%\" style=\"padding: 8px;\" valign=\"top\">\r\n Item 2\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── linear-gradient fallback ──────────────────────────────────────────\r\n \"linear-gradient\": {\r\n language: \"html\",\r\n description: \"Add solid color fallback for gradient backgrounds\",\r\n before: `<td style=\"background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n after: `<!-- Always declare a solid background-color before the gradient.\r\n Clients that strip gradients will show the fallback color. -->\r\n<td style=\"background-color: #667eea;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n },\r\n\r\n \"linear-gradient::outlook\": {\r\n language: \"html\",\r\n description: \"Use VML to render gradients in Outlook\",\r\n before: `<td style=\"background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px;\">\r\n <v:fill type=\"gradient\" color=\"#667eea\" color2=\"#764ba2\"\r\n angle=\"135\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background-color: #667eea;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n padding: 40px; color: #fff;\">\r\n Content here\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── <style> stripped by Gmail ──────────────────────────────────────────\r\n \"<style>::gmail\": {\r\n language: \"html\",\r\n description: \"Inline CSS for Gmail compatibility\",\r\n before: `<style>\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n .title { color: #fff; font-size: 24px; }\r\n</style>\r\n<div class=\"header\">\r\n <h1 class=\"title\">Hello</h1>\r\n</div>`,\r\n after: `<div style=\"background-color: #6d28d9; padding: 32px;\">\r\n <h1 style=\"color: #fff; font-size: 24px;\r\n font-family: Arial, sans-serif; margin: 0;\">\r\n Hello\r\n </h1>\r\n</div>`,\r\n },\r\n\r\n // ── <svg> replaced with image ─────────────────────────────────────────\r\n \"<svg>\": {\r\n language: \"html\",\r\n description: \"Convert inline SVG to an image for email compatibility\",\r\n before: `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" fill=\"#6d28d9\"/>\r\n</svg>`,\r\n after: `<img src=\"https://example.com/icon.png\"\r\n width=\"24\" height=\"24\" alt=\"Icon\"\r\n style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── <form> → link to web form ─────────────────────────────────────────\r\n \"<form>\": {\r\n language: \"html\",\r\n description: \"Replace embedded form with a link to a hosted form\",\r\n before: `<form action=\"/subscribe\" method=\"POST\">\r\n <input type=\"email\" placeholder=\"Email\" />\r\n <button type=\"submit\">Subscribe</button>\r\n</form>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\">\r\n <tr>\r\n <td style=\"background-color: #6d28d9; border-radius: 6px;\r\n padding: 12px 32px;\">\r\n <a href=\"https://example.com/subscribe\"\r\n style=\"color: #fff; text-decoration: none;\r\n font-family: Arial, sans-serif;\r\n font-weight: bold;\">\r\n Subscribe Now\r\n </a>\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── <video> → GIF + play button ───────────────────────────────────────\r\n \"<video>\": {\r\n language: \"html\",\r\n description: \"Replace video with animated GIF or linked thumbnail\",\r\n before: `<video width=\"600\" autoplay muted>\r\n <source src=\"demo.mp4\" type=\"video/mp4\">\r\n</video>`,\r\n after: `<a href=\"https://example.com/watch\" target=\"_blank\">\r\n <img src=\"https://example.com/video-thumb.gif\"\r\n width=\"600\" alt=\"Watch the video\"\r\n style=\"display: block; border: 0; max-width: 100%;\" />\r\n</a>`,\r\n },\r\n\r\n // ── @font-face → web-safe stack ───────────────────────────────────────\r\n \"@font-face\": {\r\n language: \"css\",\r\n description: \"Add web-safe font fallback stack\",\r\n before: `@font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n}\r\nh1 { font-family: 'CustomFont'; }`,\r\n after: `/* Keep @font-face for clients that support it */\r\n@font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n}\r\nh1 {\r\n font-family: 'CustomFont', Arial, Helvetica, sans-serif;\r\n}`,\r\n },\r\n\r\n // ── @media → mobile-first layout ──────────────────────────────────────\r\n \"@media\": {\r\n language: \"html\",\r\n description: \"Design mobile-first for clients without media query support\",\r\n before: `<table width=\"800\">\r\n <tr>\r\n <td width=\"400\">Left Column</td>\r\n <td width=\"400\">Right Column</td>\r\n </tr>\r\n</table>\r\n<style>\r\n @media (max-width: 600px) {\r\n table { width: 100% !important; }\r\n td { display: block !important; width: 100% !important; }\r\n }\r\n</style>`,\r\n after: `<!-- Single-column layout that works without @media -->\r\n<table role=\"presentation\" width=\"100%\"\r\n style=\"max-width: 600px;\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"padding: 16px;\">Left Column</td>\r\n </tr>\r\n <tr>\r\n <td style=\"padding: 16px;\">Right Column</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── box-shadow → border alternative ───────────────────────────────────\r\n \"box-shadow\": {\r\n language: \"css\",\r\n description: \"Use border as a fallback for box-shadow\",\r\n before: `.card {\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n}`,\r\n after: `.card {\r\n border: 1px solid #e0e0e0;\r\n /* box-shadow as progressive enhancement */\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n}`,\r\n },\r\n\r\n // ── max-width (Outlook) → fixed width table ───────────────────────────\r\n \"max-width::outlook\": {\r\n language: \"html\",\r\n description: \"Use a fixed-width table wrapper for Outlook\",\r\n before: `<div style=\"max-width: 600px; margin: 0 auto;\">\r\n Content here\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"600\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\" align=\"center\"><tr><td>\r\n<![endif]-->\r\n<div style=\"max-width: 600px; margin: 0 auto;\">\r\n Content here\r\n</div>\r\n<!--[if mso]>\r\n</td></tr></table>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── float (Outlook) → table align ─────────────────────────────────────\r\n \"float::outlook\": {\r\n language: \"html\",\r\n description: \"Use table align attribute instead of CSS float\",\r\n before: `<img src=\"photo.jpg\" style=\"float: left;\r\n margin-right: 16px;\" width=\"200\" />\r\n<p>Text wraps around the image.</p>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td width=\"200\" valign=\"top\" style=\"padding-right: 16px;\">\r\n <img src=\"photo.jpg\" width=\"200\"\r\n style=\"display: block; border: 0;\" />\r\n </td>\r\n <td valign=\"top\">\r\n <p>Text next to the image.</p>\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── gap → padding on children ─────────────────────────────────────────\r\n \"gap\": {\r\n language: \"html\",\r\n description: \"Use padding or margin on child elements instead of gap\",\r\n before: `<div style=\"display: flex; gap: 16px;\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n <div>Item 3</div>\r\n</div>`,\r\n after: `<table role=\"presentation\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"padding-right: 16px;\">Item 1</td>\r\n <td style=\"padding-right: 16px;\">Item 2</td>\r\n <td>Item 3</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── opacity → solid colors ────────────────────────────────────────────\r\n \"opacity\": {\r\n language: \"css\",\r\n description: \"Replace opacity with solid color equivalents\",\r\n before: `.overlay {\r\n background-color: #000;\r\n opacity: 0.5;\r\n}`,\r\n after: `.overlay {\r\n /* Use a semi-transparent color instead of opacity */\r\n background-color: #808080;\r\n /* Or for modern clients: rgba(0, 0, 0, 0.5) */\r\n}`,\r\n },\r\n\r\n // ── position → table layout ───────────────────────────────────────────\r\n \"position\": {\r\n language: \"html\",\r\n description: \"Use table-based positioning instead of CSS position\",\r\n before: `<div style=\"position: relative;\">\r\n <div style=\"position: absolute; top: 0; right: 0;\">\r\n Badge\r\n </div>\r\n <p>Content</p>\r\n</div>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td valign=\"top\">Content</td>\r\n <td width=\"80\" valign=\"top\" align=\"right\">Badge</td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── box-sizing → nested padding ───────────────────────────────────────\r\n \"box-sizing\": {\r\n language: \"html\",\r\n description: \"Account for padding in width manually (no box-sizing)\",\r\n before: `<div style=\"width: 300px; padding: 20px;\r\n box-sizing: border-box;\">\r\n Content — total width stays 300px\r\n</div>`,\r\n after: `<!-- Set width to content-width (300 - 40 = 260px) -->\r\n<div style=\"width: 300px;\">\r\n <div style=\"padding: 20px;\">\r\n Content — padding on inner element\r\n </div>\r\n</div>`,\r\n },\r\n\r\n // ── <link> → inline styles ────────────────────────────────────────────\r\n \"<link>\": {\r\n language: \"html\",\r\n description: \"Inline all CSS instead of using external stylesheets\",\r\n before: `<head>\r\n <link rel=\"stylesheet\" href=\"styles.css\" />\r\n</head>`,\r\n after: `<head>\r\n <style>\r\n /* Paste your CSS here, or use a build tool like\r\n juice/inline-css to inline automatically */\r\n .container { max-width: 600px; margin: 0 auto; }\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n </style>\r\n</head>`,\r\n },\r\n\r\n // ── dark-mode (Apple Mail) ────────────────────────────────────────────\r\n \"dark-mode::apple\": {\r\n language: \"css\",\r\n description: \"Add prefers-color-scheme dark mode styles for Apple Mail\",\r\n before: `<style>\r\n .header { background-color: #6d28d9; }\r\n .content { background-color: #ffffff; color: #333; }\r\n</style>`,\r\n after: `<style>\r\n .header { background-color: #6d28d9; }\r\n .content { background-color: #ffffff; color: #333; }\r\n\r\n @media (prefers-color-scheme: dark) {\r\n .content {\r\n background-color: #1a1a2e !important;\r\n color: #e0e0e0 !important;\r\n }\r\n /* Force images to stay visible */\r\n img { opacity: 1 !important; }\r\n }\r\n</style>`,\r\n },\r\n\r\n // ── object-fit → width/height attributes ──────────────────────────────\r\n \"object-fit\": {\r\n language: \"html\",\r\n description: \"Use width/height attributes instead of object-fit\",\r\n before: `<img src=\"photo.jpg\" style=\"width: 300px; height: 200px;\r\n object-fit: cover;\" />`,\r\n after: `<!-- Crop/resize image server-side to exact dimensions -->\r\n<img src=\"photo-300x200.jpg\" width=\"300\" height=\"200\"\r\n alt=\"Photo\" style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── transform (not supported) ─────────────────────────────────────────\r\n \"transform\": {\r\n language: \"html\",\r\n description: \"Pre-render transformed states as images or use table layout\",\r\n before: `<div style=\"transform: rotate(45deg);\">\r\n Rotated content\r\n</div>`,\r\n after: `<!-- Pre-render rotated content as an image -->\r\n<img src=\"rotated-content.png\" width=\"200\" height=\"200\"\r\n alt=\"Rotated content\"\r\n style=\"display: block; border: 0;\" />`,\r\n },\r\n\r\n // ── animation → animated GIF ──────────────────────────────────────────\r\n \"animation\": {\r\n language: \"html\",\r\n description: \"Replace CSS animation with an animated GIF\",\r\n before: `<style>\r\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\r\n .badge { animation: pulse 2s infinite; }\r\n</style>\r\n<span class=\"badge\">New!</span>`,\r\n after: `<!-- Use an animated GIF for the effect -->\r\n<img src=\"https://example.com/badge-animated.gif\"\r\n width=\"60\" height=\"24\" alt=\"New!\"\r\n style=\"display: inline-block; border: 0;\" />`,\r\n },\r\n\r\n // ── transition (not supported) ────────────────────────────────────────\r\n \"transition\": {\r\n language: \"css\",\r\n description: \"Transitions don't work in email — style the default state well\",\r\n before: `.button {\r\n background-color: #6d28d9;\r\n transition: background-color 0.2s;\r\n}\r\n.button:hover {\r\n background-color: #5b21b6;\r\n}`,\r\n after: `.button {\r\n /* Use the most visually appealing state as default.\r\n :hover is only supported in a few clients. */\r\n background-color: #6d28d9;\r\n color: #ffffff;\r\n text-decoration: none;\r\n font-weight: bold;\r\n}`,\r\n },\r\n\r\n // ── background-size (Outlook) → VML ───────────────────────────────────\r\n \"background-size\": {\r\n language: \"html\",\r\n description: \"Outlook ignores background-size — use VML or sized images\",\r\n before: `<td style=\"background: url('bg.jpg') center/cover no-repeat;\">\r\n Content\r\n</td>`,\r\n after: `<!--[if gte mso 9]>\r\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\r\n stroke=\"false\" style=\"width:600px; height:400px;\">\r\n <v:fill type=\"frame\" src=\"bg.jpg\" />\r\n <v:textbox inset=\"0,0,0,0\">\r\n<![endif]-->\r\n<td style=\"background: url('bg.jpg') center/cover no-repeat;\r\n background-color: #333;\">\r\n Content\r\n</td>\r\n<!--[if gte mso 9]>\r\n </v:textbox>\r\n</v:rect>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── overflow (Gmail strips it) ────────────────────────────────────────\r\n \"overflow\": {\r\n language: \"html\",\r\n description: \"Content will always be visible — design for full content display\",\r\n before: `<div style=\"max-height: 200px; overflow: hidden;\">\r\n Long content that gets clipped...\r\n</div>`,\r\n after: `<!-- Show full content, or truncate server-side -->\r\n<div style=\"max-height: 200px;\">\r\n Shortened content that fits...\r\n <a href=\"https://example.com/full\">Read more</a>\r\n</div>`,\r\n },\r\n\r\n // ── visibility (Gmail strips it) ──────────────────────────────────────\r\n \"visibility\": {\r\n language: \"html\",\r\n description: \"Use conditional comments or remove hidden content\",\r\n before: `<div style=\"visibility: hidden;\">\r\n Hidden content for screen readers\r\n</div>`,\r\n after: `<!-- For screen readers, use font-size: 0 trick -->\r\n<div style=\"font-size: 0; max-height: 0; overflow: hidden;\r\n mso-hide: all;\" aria-hidden=\"true\">\r\n Preheader text\r\n</div>`,\r\n },\r\n\r\n // ── word-break → table cell wrapping ──────────────────────────────────\r\n \"word-break\": {\r\n language: \"html\",\r\n description: \"Wrap long text in a table cell to force line breaks without word-break\",\r\n before: `<span style=\"word-break: break-all;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n</span>`,\r\n after: `<!-- Table cells force text wrapping in all clients including Outlook -->\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"word-break: break-all; overflow-wrap: break-word;\r\n word-wrap: break-word;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── overflow-wrap → table cell wrapping ───────────────────────────────\r\n \"overflow-wrap\": {\r\n language: \"html\",\r\n description: \"Use a table cell to force word wrapping without overflow-wrap\",\r\n before: `<p style=\"overflow-wrap: break-word;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n</p>`,\r\n after: `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td style=\"overflow-wrap: break-word; word-wrap: break-word;\r\n word-break: break-all;\">\r\n https://example.com/very/long/url?token=abc123def456\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── text-shadow → border/font-weight alternative ──────────────────────\r\n \"text-shadow\": {\r\n language: \"css\",\r\n description: \"Use font-weight or border-bottom as alternatives to text-shadow\",\r\n before: `.glow {\r\n text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);\r\n}`,\r\n after: `.glow {\r\n /* text-shadow is not supported in Gmail, Outlook, Yahoo.\r\n Use font-weight or letter-spacing for emphasis instead. */\r\n font-weight: bold;\r\n letter-spacing: 0.5px;\r\n}`,\r\n },\r\n\r\n // ── border-spacing → cellspacing attribute ────────────────────────────\r\n \"border-spacing\": {\r\n language: \"html\",\r\n description: \"Use the cellspacing HTML attribute instead of border-spacing CSS\",\r\n before: `<table style=\"border-spacing: 8px; border-collapse: separate;\">\r\n <tr><td>Cell</td></tr>\r\n</table>`,\r\n after: `<table cellspacing=\"8\" style=\"border-collapse: separate;\">\r\n <tr><td>Cell</td></tr>\r\n</table>`,\r\n },\r\n\r\n // ── min-width → fixed width ──────────────────────────────────────────\r\n \"min-width\": {\r\n language: \"html\",\r\n description: \"Use a fixed width instead of min-width for Outlook compatibility\",\r\n before: `<td style=\"min-width: 200px;\">Content</td>`,\r\n after: `<!-- Outlook ignores min-width. Use a fixed width or a spacer. -->\r\n<td width=\"200\" style=\"width: 200px;\">Content</td>`,\r\n },\r\n\r\n // ── min-height → fixed height ────────────────────────────────────────\r\n \"min-height\": {\r\n language: \"html\",\r\n description: \"Use a fixed height or spacer instead of min-height\",\r\n before: `<td style=\"min-height: 100px;\">Content</td>`,\r\n after: `<!-- Outlook ignores min-height. Use height or a spacer image. -->\r\n<td height=\"100\" style=\"height: 100px;\">Content</td>`,\r\n },\r\n\r\n // ── max-height → fixed height ────────────────────────────────────────\r\n \"max-height\": {\r\n language: \"html\",\r\n description: \"Outlook ignores max-height — truncate content server-side\",\r\n before: `<div style=\"max-height: 200px; overflow: hidden;\">\r\n Long content...\r\n</div>`,\r\n after: `<!-- Outlook ignores max-height. Truncate content server-side. -->\r\n<div style=\"height: 200px;\">\r\n Shortened content...\r\n <a href=\"https://example.com/full\">Read more</a>\r\n</div>`,\r\n },\r\n};\r\n","import type { CodeFix } from \"../types\";\r\n\r\n/**\r\n * JSX (React Email) framework-specific code fix snippets.\r\n * All keys have a ::jsx suffix in the original FIX_DATABASE.\r\n */\r\nexport const JSX_FIX_DATABASE: Record<string, CodeFix> = {\r\n // ── word-break (JSX) ─────────────────────────────────────────────────\r\n \"word-break::jsx\": {\r\n language: \"jsx\",\r\n description: \"Wrap long text in a table cell for Outlook-safe word breaking\",\r\n before: `<span style={{ wordBreak: \"break-all\" }}>{url}</span>`,\r\n after: `{/* Table cells force text wrapping in Outlook and Yahoo */}\r\n<table width=\"100%\" cellPadding={0} cellSpacing={0}\r\n role=\"presentation\" style={{ borderCollapse: \"collapse\" }}>\r\n <tr>\r\n <td style={{\r\n wordBreak: \"break-all\" as const,\r\n overflowWrap: \"break-word\" as const,\r\n wordWrap: \"break-word\" as const,\r\n }}>\r\n {url}\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── overflow-wrap (JSX) ──────────────────────────────────────────────\r\n \"overflow-wrap::jsx\": {\r\n language: \"jsx\",\r\n description: \"Wrap text in a table cell for Outlook-safe overflow wrapping\",\r\n before: `<p style={{ overflowWrap: \"break-word\" }}>{longText}</p>`,\r\n after: `<table width=\"100%\" cellPadding={0} cellSpacing={0}\r\n role=\"presentation\" style={{ borderCollapse: \"collapse\" }}>\r\n <tr>\r\n <td style={{\r\n overflowWrap: \"break-word\" as const,\r\n wordWrap: \"break-word\" as const,\r\n wordBreak: \"break-all\" as const,\r\n }}>\r\n {longText}\r\n </td>\r\n </tr>\r\n</table>`,\r\n },\r\n\r\n // ── display:flex (Outlook JSX) ───────────────────────────────────────\r\n \"display:flex::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row + Column components instead of flexbox (Outlook-safe)\",\r\n before: `<div style={{ display: \"flex\", gap: \"16px\" }}>\r\n <div style={{ flex: 1 }}>Column 1</div>\r\n <div style={{ flex: 1 }}>Column 2</div>\r\n</div>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"50%\", verticalAlign: \"top\" }}>\r\n Column 1\r\n </Column>\r\n <Column style={{ width: \"50%\", verticalAlign: \"top\" }}>\r\n Column 2\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n // ── display:grid (JSX) ──────────────────────────────────────────────\r\n \"display:grid::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace CSS Grid with React Email Row + Column components\",\r\n before: `<div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: \"16px\" }}>\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n</div>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"50%\", padding: \"8px\", verticalAlign: \"top\" }}>\r\n Item 1\r\n </Column>\r\n <Column style={{ width: \"50%\", padding: \"8px\", verticalAlign: \"top\" }}>\r\n Item 2\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n // ── max-width (Outlook JSX) ─────────────────────────────────────────\r\n \"max-width::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Container component for Outlook-safe max-width centering\",\r\n before: `<div style={{ maxWidth: \"600px\", margin: \"0 auto\" }}>\r\n Content here\r\n</div>`,\r\n after: `import { Container } from \"@react-email/components\";\r\n\r\n<Container style={{ maxWidth: \"600px\" }}>\r\n Content here\r\n</Container>`,\r\n },\r\n\r\n // ── @font-face (JSX) ────────────────────────────────────────────────\r\n \"@font-face::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Font component instead of @font-face in <style>\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n @font-face {\r\n font-family: 'CustomFont';\r\n src: url('custom.woff2') format('woff2');\r\n }\r\n \\`}</style>\r\n</Head>\r\n<h1 style={{ fontFamily: \"'CustomFont'\" }}>Hello</h1>`,\r\n after: `import { Head, Font } from \"@react-email/components\";\r\n\r\n<Head>\r\n <Font\r\n fontFamily=\"CustomFont\"\r\n fallbackFontFamily=\"Arial\"\r\n webFont={{ url: \"https://example.com/custom.woff2\", format: \"woff2\" }}\r\n fontWeight={400}\r\n fontStyle=\"normal\"\r\n />\r\n</Head>\r\n<h1 style={{ fontFamily: \"'CustomFont', Arial, sans-serif\" }}>Hello</h1>`,\r\n },\r\n\r\n // ── <svg> (JSX) ─────────────────────────────────────────────────────\r\n \"<svg>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace inline SVG with React Email Img component\",\r\n before: `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" fill=\"#6d28d9\"/>\r\n</svg>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n<Img\r\n src=\"https://example.com/icon.png\"\r\n width={24}\r\n height={24}\r\n alt=\"Icon\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n // ── <video> (JSX) ───────────────────────────────────────────────────\r\n \"<video>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace video with a linked thumbnail using React Email Img + Link\",\r\n before: `<video width=\"600\" autoPlay muted>\r\n <source src=\"demo.mp4\" type=\"video/mp4\" />\r\n</video>`,\r\n after: `import { Img, Link } from \"@react-email/components\";\r\n\r\n<Link href=\"https://example.com/watch\">\r\n <Img\r\n src=\"https://example.com/video-thumb.gif\"\r\n width={600}\r\n alt=\"Watch the video\"\r\n style={{ display: \"block\", border: \"0\", maxWidth: \"100%\" }}\r\n />\r\n</Link>`,\r\n },\r\n\r\n // ── border-radius (Outlook JSX) ─────────────────────────────────────\r\n \"border-radius::outlook::jsx\": {\r\n language: \"jsx\",\r\n description:\r\n \"Render rounded buttons with VML via JSX dangerouslySetInnerHTML (Outlook workaround)\",\r\n before: `<a\r\n href=\"https://example.com\"\r\n style={{ backgroundColor: \"#6d28d9\", color: \"#fff\",\r\n padding: \"12px 32px\", borderRadius: \"6px\",\r\n textDecoration: \"none\", display: \"inline-block\" }}>\r\n Click Here\r\n</a>`,\r\n after:\r\n '{/* Use dangerouslySetInnerHTML to inject VML for Outlook rounded corners */}\\n<div\\n dangerouslySetInnerHTML={{\\n __html: `\\n<!--[if mso]>\\n<v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\"\\n href=\"https://example.com\"\\n style=\"height:44px; v-text-anchor:middle; width:200px;\"\\n arcsize=\"14%\" strokecolor=\"#6d28d9\" fillcolor=\"#6d28d9\">\\n <w:anchorlock/>\\n <center style=\"color:#fff; font-family:Arial,sans-serif;\\n font-size:14px; font-weight:bold;\">Click Here</center>\\n</v:roundrect>\\n<![endif]-->\\n<!--[if !mso]><!-->\\n<a href=\"https://example.com\"\\n style=\"background-color:#6d28d9; color:#fff; padding:12px 32px;\\n border-radius:6px; text-decoration:none; display:inline-block;\">\\n Click Here\\n</a>\\n<!--<![endif]-->\\n`,\\n }}\\n/>',\r\n },\r\n\r\n // ── gap (JSX) ───────────────────────────────────────────────────────\r\n \"gap::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use padding style prop on Column instead of gap (email-safe spacing)\",\r\n before: `<Row style={{ gap: \"16px\" }}>\r\n <Column>Item 1</Column>\r\n <Column>Item 2</Column>\r\n <Column>Item 3</Column>\r\n</Row>`,\r\n after: `import { Row, Column } from \"@react-email/components\";\r\n\r\n{/* Use padding on Column — gap is not supported in email clients */}\r\n<Row>\r\n <Column style={{ paddingRight: \"16px\" }}>Item 1</Column>\r\n <Column style={{ paddingRight: \"16px\" }}>Item 2</Column>\r\n <Column>Item 3</Column>\r\n</Row>`,\r\n },\r\n\r\n // ── <style> (Gmail JSX) ─────────────────────────────────────────────\r\n \"<style>::gmail::jsx\": {\r\n language: \"jsx\",\r\n description:\r\n \"React Email inlines styles via style props — manual <style> blocks won't survive Gmail\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n .header { background-color: #6d28d9; padding: 32px; }\r\n .title { color: #fff; font-size: 24px; }\r\n \\`}</style>\r\n</Head>\r\n<div className=\"header\">\r\n <h1 className=\"title\">Hello</h1>\r\n</div>`,\r\n after: `{/* Gmail strips <style> blocks. Move styles to inline style props: */}\r\n<div style={{ backgroundColor: \"#6d28d9\", padding: \"32px\" }}>\r\n <h1 style={{ color: \"#fff\", fontSize: \"24px\", margin: 0 }}>Hello</h1>\r\n</div>`,\r\n },\r\n\r\n // ── <link> (JSX) ────────────────────────────────────────────────────\r\n \"<link>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Head component instead of <link> for stylesheet references\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <link rel=\"stylesheet\" href=\"styles.css\" />\r\n</Head>`,\r\n after: `import { Head } from \"@react-email/components\";\r\n\r\n{/* External stylesheets are stripped by most email clients.\r\n Place styles inline via style props, or use Head for font imports only. */}\r\n<Head>\r\n {/* Inline your CSS here or use Font component for web fonts */}\r\n</Head>`,\r\n },\r\n\r\n // ── <form> (JSX) ───────────────────────────────────────────────────\r\n \"<form>::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace embedded form with a React Email Button linking to a hosted form\",\r\n before: `<form action=\"/subscribe\" method=\"POST\">\r\n <input type=\"email\" placeholder=\"Email\" />\r\n <button type=\"submit\">Subscribe</button>\r\n</form>`,\r\n after: `import { Button } from \"@react-email/components\";\r\n\r\n<Button\r\n href=\"https://example.com/subscribe\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n padding: \"12px 32px\",\r\n borderRadius: \"6px\",\r\n textDecoration: \"none\",\r\n fontWeight: \"bold\",\r\n }}\r\n>\r\n Subscribe Now\r\n</Button>`,\r\n },\r\n\r\n // ── @media (JSX) ────────────────────────────────────────────────────\r\n \"@media::jsx\": {\r\n language: \"jsx\",\r\n description: \"Design mobile-first — @media queries are stripped by many clients\",\r\n before: `import { Head } from \"@react-email/components\";\r\n\r\n<Head>\r\n <style>{\\`\r\n @media (max-width: 600px) {\r\n .cols { display: block !important; }\r\n .col { width: 100% !important; }\r\n }\r\n \\`}</style>\r\n</Head>\r\n<Row>\r\n <Column className=\"col\" style={{ width: \"50%\" }}>Left</Column>\r\n <Column className=\"col\" style={{ width: \"50%\" }}>Right</Column>\r\n</Row>`,\r\n after: `import { Container, Section, Text } from \"@react-email/components\";\r\n\r\n{/* Single-column stacked layout works without @media.\r\n Stack content vertically so it reads well on all clients. */}\r\n<Container style={{ maxWidth: \"600px\" }}>\r\n <Section style={{ padding: \"16px\" }}>\r\n <Text>Left</Text>\r\n </Section>\r\n <Section style={{ padding: \"16px\" }}>\r\n <Text>Right</Text>\r\n </Section>\r\n</Container>`,\r\n },\r\n\r\n // ── position (JSX) ─────────────────────────────────────────────────\r\n \"position::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row and Column for layout instead of CSS position\",\r\n before: `<div style={{ position: \"relative\" }}>\r\n <div style={{ position: \"absolute\", top: 0, right: 0 }}>\r\n Badge\r\n </div>\r\n <p>Content</p>\r\n</div>`,\r\n after: `import { Row, Column, Text } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ verticalAlign: \"top\" }}>\r\n <Text>Content</Text>\r\n </Column>\r\n <Column style={{ width: \"80px\", verticalAlign: \"top\", textAlign: \"right\" }}>\r\n <Text>Badge</Text>\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n // ── float (JSX) ────────────────────────────────────────────────────\r\n \"float::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Row and Column instead of float for side-by-side layout\",\r\n before: `<img src=\"photo.jpg\" style={{ float: \"left\", marginRight: \"16px\" }} width={200} />\r\n<p>Text wraps around the image.</p>`,\r\n after: `import { Row, Column, Img, Text } from \"@react-email/components\";\r\n\r\n<Row>\r\n <Column style={{ width: \"200px\", paddingRight: \"16px\", verticalAlign: \"top\" }}>\r\n <Img src=\"photo.jpg\" width={200} style={{ display: \"block\", border: \"0\" }} />\r\n </Column>\r\n <Column style={{ verticalAlign: \"top\" }}>\r\n <Text>Text next to the image.</Text>\r\n </Column>\r\n</Row>`,\r\n },\r\n\r\n // ── background-image (Outlook JSX) ──────────────────────────────────\r\n \"background-image::outlook::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use VML for Outlook background images in JSX via dangerouslySetInnerHTML\",\r\n before: `<td style={{ backgroundImage: \"url('hero.jpg')\",\r\n backgroundSize: \"cover\", padding: \"40px\" }}>\r\n <h1 style={{ color: \"#fff\" }}>Hello World</h1>\r\n</td>`,\r\n after:\r\n '<div\\n dangerouslySetInnerHTML={{\\n __html: `\\n<!--[if gte mso 9]>\\n<v:rect xmlns:v=\"urn:schemas-microsoft-com:vml\" fill=\"true\"\\n stroke=\"false\" style=\"width:600px; height:300px;\">\\n <v:fill type=\"frame\" src=\"hero.jpg\" />\\n <v:textbox inset=\"0,0,0,0\">\\n<![endif]-->\\n<div style=\"background-image:url(\\'hero.jpg\\'); background-size:cover; padding:40px;\">\\n <h1 style=\"color:#fff;\">Hello World</h1>\\n</div>\\n<!--[if gte mso 9]>\\n </v:textbox>\\n</v:rect>\\n<![endif]-->\\n`,\\n }}\\n/>',\r\n },\r\n\r\n // ── opacity (JSX) ──────────────────────────────────────────────────\r\n \"opacity::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use solid colors instead of opacity in style objects\",\r\n before: `<div style={{\r\n backgroundColor: \"#000\",\r\n opacity: 0.5,\r\n}}>\r\n Overlay content\r\n</div>`,\r\n after: `<div style={{\r\n /* Use a pre-mixed solid color instead of opacity */\r\n backgroundColor: \"#808080\",\r\n}}>\r\n Overlay content\r\n</div>`,\r\n },\r\n\r\n // ── box-shadow (JSX) ──────────────────────────────────────────────\r\n \"box-shadow::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use border as a fallback for boxShadow in style objects\",\r\n before: `<div style={{\r\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.15)\",\r\n padding: \"24px\",\r\n}}>\r\n Card content\r\n</div>`,\r\n after: `import { Section } from \"@react-email/components\";\r\n\r\n<Section style={{\r\n border: \"1px solid #e0e0e0\",\r\n padding: \"24px\",\r\n}}>\r\n Card content\r\n</Section>`,\r\n },\r\n\r\n // ── linear-gradient (JSX) ─────────────────────────────────────────\r\n \"linear-gradient::jsx\": {\r\n language: \"jsx\",\r\n description: \"Add a solid backgroundColor fallback before the gradient\",\r\n before: `<div style={{\r\n background: \"linear-gradient(135deg, #667eea, #764ba2)\",\r\n padding: \"40px\",\r\n color: \"#fff\",\r\n}}>\r\n Content here\r\n</div>`,\r\n after: `<div style={{\r\n /* Solid fallback for clients that strip gradients */\r\n backgroundColor: \"#667eea\",\r\n background: \"linear-gradient(135deg, #667eea, #764ba2)\",\r\n padding: \"40px\",\r\n color: \"#fff\",\r\n}}>\r\n Content here\r\n</div>`,\r\n },\r\n\r\n // ── transform (JSX) ──────────────────────────────────────────────\r\n \"transform::jsx\": {\r\n language: \"jsx\",\r\n description: \"Pre-render transformed content as an image using React Email Img\",\r\n before: `<div style={{ transform: \"rotate(45deg)\" }}>\r\n Rotated content\r\n</div>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* CSS transforms are not supported in email — pre-render as an image */}\r\n<Img\r\n src=\"https://example.com/rotated-content.png\"\r\n width={200}\r\n height={200}\r\n alt=\"Rotated content\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n // ── animation (JSX) ──────────────────────────────────────────────\r\n \"animation::jsx\": {\r\n language: \"jsx\",\r\n description: \"Replace CSS animation with a React Email Img using an animated GIF\",\r\n before: `<span style={{ animation: \"pulse 2s infinite\" }}>New!</span>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* CSS animations are not supported — use an animated GIF */}\r\n<Img\r\n src=\"https://example.com/badge-animated.gif\"\r\n width={60}\r\n height={24}\r\n alt=\"New!\"\r\n style={{ display: \"inline-block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n // ── transition (JSX) ─────────────────────────────────────────────\r\n \"transition::jsx\": {\r\n language: \"jsx\",\r\n description: \"Transitions don't work in email — style the default state well\",\r\n before: `<a\r\n href=\"#\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n>\r\n Click\r\n</a>`,\r\n after: `import { Button } from \"@react-email/components\";\r\n\r\n{/* Transitions are not supported — style the default state: */}\r\n<Button\r\n href=\"https://example.com\"\r\n style={{\r\n backgroundColor: \"#6d28d9\",\r\n color: \"#fff\",\r\n textDecoration: \"none\",\r\n fontWeight: \"bold\",\r\n padding: \"12px 32px\",\r\n }}\r\n>\r\n Click\r\n</Button>`,\r\n },\r\n\r\n // ── overflow (JSX) ───────────────────────────────────────────────\r\n \"overflow::jsx\": {\r\n language: \"jsx\",\r\n description: \"Content will always be visible — design for full content display\",\r\n before: `<div style={{ maxHeight: \"200px\", overflow: \"hidden\" }}>\r\n Long content that gets clipped...\r\n</div>`,\r\n after: `import { Link, Text } from \"@react-email/components\";\r\n\r\n{/* overflow:hidden is stripped — show full content or truncate server-side */}\r\n<div>\r\n <Text>Shortened content that fits...</Text>\r\n <Link href=\"https://example.com/full\">Read more</Link>\r\n</div>`,\r\n },\r\n\r\n // ── visibility (JSX) ─────────────────────────────────────────────\r\n \"visibility::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use font-size/max-height trick instead of visibility:hidden\",\r\n before: `<div style={{ visibility: \"hidden\" }}>\r\n Hidden preheader text\r\n</div>`,\r\n after: `{/* visibility:hidden is stripped by most clients — use the preheader trick.\r\n msoHide is non-standard but needed to hide content in Outlook. */}\r\n<div\r\n style={{\r\n fontSize: \"0px\",\r\n lineHeight: \"0px\",\r\n maxHeight: \"0px\",\r\n overflow: \"hidden\",\r\n display: \"none\",\r\n msoHide: \"all\",\r\n } as React.CSSProperties}\r\n aria-hidden=\"true\"\r\n>\r\n Preheader text\r\n</div>`,\r\n },\r\n\r\n // ── object-fit (JSX) ─────────────────────────────────────────────\r\n \"object-fit::jsx\": {\r\n language: \"jsx\",\r\n description: \"Use React Email Img with explicit width/height instead of object-fit\",\r\n before: `<img\r\n src=\"photo.jpg\"\r\n style={{ width: \"300px\", height: \"200px\", objectFit: \"cover\" }}\r\n/>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* Crop/resize image server-side to exact dimensions */}\r\n<Img\r\n src=\"https://example.com/photo-300x200.jpg\"\r\n width={300}\r\n height={200}\r\n alt=\"Photo\"\r\n style={{ display: \"block\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n // ── background-size (JSX) ────────────────────────────────────────\r\n \"background-size::jsx\": {\r\n language: \"jsx\",\r\n description: \"Outlook ignores background-size — use sized images instead\",\r\n before: `<div style={{\r\n background: \"url('bg.jpg') center/cover no-repeat\",\r\n}}>\r\n Content\r\n</div>`,\r\n after: `import { Img } from \"@react-email/components\";\r\n\r\n{/* background-size is not supported in most email clients.\r\n Use a full-width image instead of a background: */}\r\n<Img\r\n src=\"https://example.com/bg-600x400.jpg\"\r\n width={600}\r\n alt=\"\"\r\n style={{ display: \"block\", width: \"100%\", border: \"0\" }}\r\n/>`,\r\n },\r\n\r\n // ── box-sizing (JSX) ─────────────────────────────────────────────\r\n \"box-sizing::jsx\": {\r\n language: \"jsx\",\r\n description: \"Account for padding in width manually (no box-sizing support)\",\r\n before: `<div style={{\r\n width: \"300px\",\r\n padding: \"20px\",\r\n boxSizing: \"border-box\",\r\n}}>\r\n Content — total width stays 300px\r\n</div>`,\r\n after: `{/* Set outer width, use inner element for padding */}\r\n<div style={{ width: \"300px\" }}>\r\n <div style={{ padding: \"20px\" }}>\r\n Content — padding on inner element\r\n </div>\r\n</div>`,\r\n },\r\n};\r\n","import type { CodeFix } from \"../types\";\r\n\r\n/**\r\n * MJML framework-specific code fix snippets.\r\n * All keys have a ::mjml suffix in the original FIX_DATABASE.\r\n */\r\nexport const MJML_FIX_DATABASE: Record<string, CodeFix> = {\r\n // ── word-break (MJML) ────────────────────────────────────────────────\r\n \"word-break::mjml\": {\r\n language: \"mjml\",\r\n description: \"MJML renders text in table cells by default — word-break works via mj-text\",\r\n before: `<mj-text>\r\n <span style=\"word-break: break-all;\">Long URL here</span>\r\n</mj-text>`,\r\n after: `<!-- mj-text already renders inside a <td>, so add word-break\r\n to the mj-text css-class or inline style -->\r\n<mj-text css-class=\"break-words\"\r\n padding=\"0\">\r\n Long URL here\r\n</mj-text>\r\n<mj-style>\r\n .break-words td { word-break: break-all; word-wrap: break-word; }\r\n</mj-style>`,\r\n },\r\n\r\n // ── @font-face (MJML) ───────────────────────────────────────────────\r\n \"@font-face::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-font in mj-head instead of @font-face\",\r\n before: `<mj-style>\r\n @font-face {\r\n font-family: 'CustomFont';\r\n src: url('https://example.com/custom.woff2') format('woff2');\r\n }\r\n</mj-style>`,\r\n after: `<mjml>\r\n <mj-head>\r\n <mj-font name=\"CustomFont\"\r\n href=\"https://fonts.googleapis.com/css2?family=CustomFont\" />\r\n <mj-attributes>\r\n <mj-all font-family=\"CustomFont, Arial, Helvetica, sans-serif\" />\r\n </mj-attributes>\r\n </mj-head>\r\n</mjml>`,\r\n },\r\n\r\n // ── <style> (Gmail MJML) ────────────────────────────────────────────\r\n \"<style>::gmail::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-style inline='inline' to force style inlining for Gmail\",\r\n before: `<mj-head>\r\n <mj-style>\r\n .custom { color: #6d28d9; }\r\n </mj-style>\r\n</mj-head>`,\r\n after: `<mj-head>\r\n <!-- Use inline=\"inline\" to force MJML to inline these styles.\r\n Class-based styles in a plain mj-style block will be stripped by Gmail. -->\r\n <mj-style inline=\"inline\">\r\n .custom { color: #6d28d9; }\r\n </mj-style>\r\n</mj-head>`,\r\n },\r\n\r\n // ── border-radius (Outlook MJML) ────────────────────────────────────\r\n \"border-radius::outlook::mjml\": {\r\n language: \"mjml\",\r\n description: \"MJML limitation: border-radius is unsupported in Outlook — MJML does not generate VML\",\r\n before: `<mj-button border-radius=\"6px\" background-color=\"#6d28d9\">\r\n Click Here\r\n</mj-button>`,\r\n after: `<!-- Known MJML limitation: MJML does not generate VML for rounded corners.\r\n Options: accept flat corners, use mj-raw for VML, or set border-radius=\"0\". -->\r\n<mj-button border-radius=\"0\" background-color=\"#6d28d9\">\r\n Click Here\r\n</mj-button>`,\r\n },\r\n\r\n // ── background-image (Outlook MJML) ─────────────────────────────────\r\n \"background-image::outlook::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use mj-section background-url for Outlook-compatible background images\",\r\n before: `<mj-section>\r\n <mj-column>\r\n <mj-image src=\"hero.jpg\" />\r\n </mj-column>\r\n</mj-section>`,\r\n after: `<!-- MJML generates VML-compatible markup automatically via background-url on mj-section. -->\r\n<mj-section background-url=\"https://example.com/hero.jpg\"\r\n background-size=\"cover\"\r\n background-repeat=\"no-repeat\"\r\n background-color=\"#333333\">\r\n <mj-column>\r\n <mj-text color=\"#ffffff\">Your content here</mj-text>\r\n </mj-column>\r\n</mj-section>`,\r\n },\r\n\r\n // ── display:flex (MJML) ─────────────────────────────────────────────\r\n \"display:flex::mjml\": {\r\n language: \"mjml\",\r\n description: \"Replace flexbox (from mj-raw or inline styles) with mj-section and mj-column\",\r\n before: `<mj-raw>\r\n <div style=\"display: flex; gap: 16px;\">\r\n <div style=\"flex: 1;\">Column 1</div>\r\n <div style=\"flex: 1;\">Column 2</div>\r\n </div>\r\n</mj-raw>`,\r\n after: `<!-- Flexbox in MJML is not Outlook-compatible.\r\n Use mj-section and mj-column — MJML compiles these to table-based layouts. -->\r\n<mj-section>\r\n <mj-column width=\"50%\">\r\n <mj-text>Column 1</mj-text>\r\n </mj-column>\r\n <mj-column width=\"50%\">\r\n <mj-text>Column 2</mj-text>\r\n </mj-column>\r\n</mj-section>`,\r\n },\r\n\r\n // ── @media (MJML) ──────────────────────────────────────────────────\r\n \"@media::mjml\": {\r\n language: \"mjml\",\r\n description: \"Use MJML responsive attributes and breakpoints instead of hand-written @media\",\r\n before: `<mj-style>\r\n @media (max-width: 600px) {\r\n .mobile-stack { display: block !important; width: 100% !important; }\r\n }\r\n</mj-style>`,\r\n after: `<!-- MJML generates responsive @media queries automatically.\r\n Use mj-breakpoint and mj-column widths to control responsive behavior. -->\r\n<mj-head>\r\n <mj-breakpoint width=\"600px\" />\r\n</mj-head>\r\n<mj-body>\r\n <mj-section>\r\n <mj-column width=\"50%\"><mj-text>Left</mj-text></mj-column>\r\n <mj-column width=\"50%\"><mj-text>Right</mj-text></mj-column>\r\n </mj-section>\r\n</mj-body>`,\r\n },\r\n};\r\n","import type { CodeFix } from \"../types\";\r\n\r\n/**\r\n * Maizzle framework-specific code fix snippets.\r\n * All keys have a ::maizzle suffix in the original FIX_DATABASE.\r\n */\r\nexport const MAIZZLE_FIX_DATABASE: Record<string, CodeFix> = {\r\n // ── display:flex (Outlook Maizzle) ──────────────────────────────────\r\n \"display:flex::outlook::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Replace Tailwind flex classes with HTML table + MSO conditional comments\",\r\n before: `<div class=\"flex gap-4\">\r\n <div class=\"flex-1\">Column 1</div>\r\n <div class=\"flex-1\">Column 2</div>\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\"><tr>\r\n <td class=\"w-1/2\" valign=\"top\">Column 1</td>\r\n <td class=\"w-1/2\" valign=\"top\">Column 2</td>\r\n</tr></table>\r\n<![endif]-->\r\n<!--[if !mso]><!-->\r\n<div class=\"flex gap-4\">\r\n <div class=\"flex-1\">Column 1</div>\r\n <div class=\"flex-1\">Column 2</div>\r\n</div>\r\n<!--<![endif]-->`,\r\n },\r\n\r\n // ── @font-face (Maizzle) ────────────────────────────────────────────\r\n \"@font-face::maizzle\": {\r\n language: \"maizzle\",\r\n description:\r\n 'Add fonts via the googleFonts key in config.js — Maizzle injects the Google Fonts link tag automatically. Set googleFonts: \"Inter:ital,wght@0,400;0,700\" in your environment config, then reference the font family in your template.',\r\n before: `<style>\r\n @font-face {\r\n font-family: 'Inter';\r\n src: url('https://fonts.gstatic.com/...') format('woff2');\r\n }\r\n</style>`,\r\n after: `<!-- config.js: googleFonts: \"Inter:ital,wght@0,400;0,700\" -->\r\n<p class=\"font-['Inter',Arial,sans-serif]\">Hello</p>`,\r\n },\r\n\r\n // ── <style> (Gmail Maizzle) ─────────────────────────────────────────\r\n \"<style>::gmail::maizzle\": {\r\n language: \"maizzle\",\r\n description:\r\n \"Maizzle automatically inlines CSS via juice during build (inlineCSS: true in config.js). Manual <style> blocks bypass juice and will be stripped by Gmail — prefer Tailwind utility classes instead.\",\r\n before: `<style>\r\n .custom { color: #6d28d9; }\r\n</style>\r\n<div class=\"custom\">Hello</div>`,\r\n after: `<!-- Prefer Tailwind classes — Maizzle inlines them automatically during build -->\r\n<div class=\"text-[#6d28d9]\">Hello</div>`,\r\n },\r\n\r\n // ── max-width (Outlook Maizzle) ─────────────────────────────────────\r\n \"max-width::outlook::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Wrap max-width containers with MSO conditional table for Outlook\",\r\n before: `<div class=\"max-w-[600px] mx-auto\">\r\n Content here\r\n</div>`,\r\n after: `<!--[if mso]>\r\n<table role=\"presentation\" width=\"600\" cellpadding=\"0\"\r\n cellspacing=\"0\" border=\"0\" align=\"center\"><tr><td>\r\n<![endif]-->\r\n<div class=\"max-w-[600px] mx-auto\">\r\n Content here\r\n</div>\r\n<!--[if mso]>\r\n</td></tr></table>\r\n<![endif]-->`,\r\n },\r\n\r\n // ── gap (Maizzle) ──────────────────────────────────────────────────\r\n \"gap::maizzle\": {\r\n language: \"maizzle\",\r\n description: \"Use padding Tailwind classes on child elements instead of gap\",\r\n before: `<div class=\"flex gap-4\">\r\n <div>Item 1</div>\r\n <div>Item 2</div>\r\n <div>Item 3</div>\r\n</div>`,\r\n after: `<!-- gap is not supported in Outlook or many email clients.\r\n Use padding classes on child elements instead. -->\r\n<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\r\n <tr>\r\n <td class=\"pr-4\">Item 1</td>\r\n <td class=\"pr-4\">Item 2</td>\r\n <td>Item 3</td>\r\n </tr>\r\n</table>`,\r\n },\r\n};\r\n","/**\r\n * HTML/generic suggestion strings — entries that do NOT have a\r\n * ::jsx, ::mjml, or ::maizzle suffix in their key.\r\n */\r\nexport const HTML_SUGGESTION_DATABASE: Record<string, string> = {\r\n // ── <style> ───────────────────────────────────────────────────────────\r\n \"<style>\":\r\n \"Use a CSS inliner tool (like juice) to move styles to inline attributes.\",\r\n \"<style>:partial\":\r\n \"Use inline styles as the primary approach, with <style> in <head> as progressive enhancement.\",\r\n\r\n // ── <link> ────────────────────────────────────────────────────────────\r\n \"<link>\": \"Inline all CSS directly in the HTML.\",\r\n\r\n // ── <svg> ─────────────────────────────────────────────────────────────\r\n \"<svg>\": \"Convert SVGs to PNG/JPG images.\",\r\n\r\n // ── <video> ───────────────────────────────────────────────────────────\r\n \"<video>\":\r\n \"Use an animated GIF or a static image with a play button linking to the video.\",\r\n\r\n // ── <form> ────────────────────────────────────────────────────────────\r\n \"<form>\": \"Use links to a web form instead of embedding forms in email.\",\r\n\r\n // ── @font-face ────────────────────────────────────────────────────────\r\n \"@font-face\":\r\n \"Always include a web-safe font stack as fallback (e.g., Arial, Helvetica, sans-serif).\",\r\n\r\n // ── @media ────────────────────────────────────────────────────────────\r\n \"@media\":\r\n \"Design emails mobile-first with a single-column layout that works without media queries.\",\r\n\r\n // ── display:flex ──────────────────────────────────────────────────────\r\n \"display:flex\":\r\n \"Use <table> layouts for email client compatibility.\",\r\n \"display:flex::outlook\":\r\n \"Use <table> layouts with <!--[if mso]> conditional comments for Outlook's Word engine.\",\r\n\r\n // ── display:grid ──────────────────────────────────────────────────────\r\n \"display:grid\":\r\n \"Replace CSS Grid with table layout for email compatibility.\",\r\n\r\n // ── linear-gradient ───────────────────────────────────────────────────\r\n \"linear-gradient\":\r\n \"Add a solid background-color fallback before the gradient.\",\r\n\r\n // ── box-shadow ────────────────────────────────────────────────────────\r\n \"box-shadow\":\r\n \"Use border styling as an alternative to box-shadow.\",\r\n\r\n // ── border-radius ─────────────────────────────────────────────────────\r\n \"border-radius\":\r\n \"Use VML for rounded corners in Outlook, or accept square corners.\",\r\n \"border-radius::outlook\":\r\n \"Use VML (Vector Markup Language) for rounded buttons in Outlook.\",\r\n\r\n // ── max-width ─────────────────────────────────────────────────────────\r\n \"max-width\":\r\n \"Use a fixed-width table wrapper for maximum compatibility.\",\r\n \"max-width::outlook\":\r\n \"Use a fixed width on table cells instead of max-width.\",\r\n\r\n // ── gap ───────────────────────────────────────────────────────────────\r\n \"gap\":\r\n \"Use padding/margin on child elements instead of gap.\",\r\n \"gap::outlook\":\r\n \"Use cellpadding/cellspacing on tables, or padding on cells.\",\r\n\r\n // ── float ─────────────────────────────────────────────────────────────\r\n \"float\":\r\n \"Use table cells with align attribute for side-by-side content.\",\r\n \"float::outlook\":\r\n 'Use table cells with align=\"left\" or align=\"right\".',\r\n\r\n // ── background-image ──────────────────────────────────────────────────\r\n \"background-image\":\r\n \"Use VML for background images in clients that require it.\",\r\n \"background-image::outlook\":\r\n \"Use <!--[if gte mso 9]> with <v:background> VML for Outlook background images.\",\r\n\r\n // ── position ──────────────────────────────────────────────────────────\r\n \"position\":\r\n \"Use table-based positioning instead of CSS position.\",\r\n\r\n // ── opacity ───────────────────────────────────────────────────────────\r\n \"opacity\":\r\n \"Use solid colors instead of opacity.\",\r\n\r\n // ── word-break ────────────────────────────────────────────────────────\r\n \"word-break\":\r\n \"Wrap long text in a <table><td> to force wrapping in clients that don't support word-break.\",\r\n \"word-break::outlook\":\r\n \"Outlook's Word engine ignores word-break. Place text inside a <td> with a constrained width — tables always wrap.\",\r\n\r\n // ── overflow-wrap ─────────────────────────────────────────────────────\r\n \"overflow-wrap\":\r\n \"Wrap text in a <table><td> to force wrapping. overflow-wrap is ignored by Outlook and unreliable in Yahoo.\",\r\n\r\n // ── white-space ───────────────────────────────────────────────────────\r\n \"white-space\":\r\n \"Outlook only supports 'normal' and 'nowrap'. Use &nbsp; for non-breaking spaces.\",\r\n\r\n // ── text-overflow ─────────────────────────────────────────────────────\r\n \"text-overflow\":\r\n \"text-overflow requires overflow:hidden which is stripped by Gmail. Truncate content server-side.\",\r\n\r\n // ── vertical-align ────────────────────────────────────────────────────\r\n \"vertical-align\":\r\n 'Use the valign HTML attribute on <td> elements for Outlook (e.g., valign=\"top\").',\r\n\r\n // ── border-spacing ────────────────────────────────────────────────────\r\n \"border-spacing\":\r\n 'Use the cellspacing HTML attribute instead (e.g., <table cellspacing=\"8\">).',\r\n\r\n // ── min-width / min-height ────────────────────────────────────────────\r\n \"min-width\":\r\n \"Outlook ignores min-width. Use a fixed width attribute on <td> or <table>.\",\r\n \"min-height\":\r\n \"Outlook ignores min-height. Use a fixed height or a spacer image.\",\r\n \"max-height\":\r\n \"Outlook ignores max-height. Truncate content server-side or use a fixed height.\",\r\n\r\n // ── text-shadow ───────────────────────────────────────────────────────\r\n \"text-shadow\":\r\n \"text-shadow is stripped by Gmail, Outlook, and Yahoo. Use font-weight for emphasis.\",\r\n\r\n // ── background-size / background-position ─────────────────────────────\r\n \"background-size\":\r\n \"Not supported in many clients. Set image dimensions directly.\",\r\n \"background-position\":\r\n \"Not supported in many clients. Use VML for positioning.\",\r\n\r\n // ── Additional properties covered by transform helpers ────────────────\r\n \"overflow\":\r\n \"Content will always be visible. Design accordingly.\",\r\n \"visibility\":\r\n \"Remove the element or use display:none as an alternative.\",\r\n \"transform\":\r\n \"CSS transforms are not supported in email. Pre-render the effect as an image.\",\r\n \"animation\":\r\n \"CSS animations are not supported. Use animated GIFs instead.\",\r\n \"transition\":\r\n \"CSS transitions are not supported in email.\",\r\n \"box-sizing\":\r\n \"Account for padding in your width calculations (use padding on a nested element).\",\r\n \"object-fit\":\r\n \"Use width/height attributes on <img> directly.\",\r\n \"display\":\r\n \"Use tables for layout in email clients.\",\r\n};\r\n","/**\r\n * JSX (React Email) framework-specific suggestion strings.\r\n * All keys have a ::jsx suffix in the original SUGGESTION_DATABASE.\r\n */\r\nexport const JSX_SUGGESTION_DATABASE: Record<string, string> = {\r\n // ── <style> ───────────────────────────────────────────────────────────\r\n \"<style>::jsx\":\r\n \"Move styles to inline style props — React Email components accept style objects directly.\",\r\n \"<style>:partial::jsx\":\r\n \"Use inline style props on React Email components. Reserve <style> in <Head> for progressive enhancement only.\",\r\n\r\n // ── <link> ────────────────────────────────────────────────────────────\r\n \"<link>::jsx\":\r\n \"Use the React Email <Head> component for font imports; place all other styles inline via style props.\",\r\n\r\n // ── <svg> ─────────────────────────────────────────────────────────────\r\n \"<svg>::jsx\":\r\n \"Replace inline SVG with the React Email <Img> component pointing to a hosted PNG.\",\r\n\r\n // ── <video> ───────────────────────────────────────────────────────────\r\n \"<video>::jsx\":\r\n \"Replace <video> with a React Email <Link> wrapping an <Img> thumbnail.\",\r\n\r\n // ── <form> ────────────────────────────────────────────────────────────\r\n \"<form>::jsx\":\r\n \"Replace the form with a React Email <Button> or <Link> component pointing to a hosted form page.\",\r\n\r\n // ── @font-face ────────────────────────────────────────────────────────\r\n \"@font-face::jsx\":\r\n \"Use the React Email <Font> component in <Head> with a fallbackFontFamily prop.\",\r\n\r\n // ── @media ────────────────────────────────────────────────────────────\r\n \"@media::jsx\":\r\n \"Use a single-column layout with React Email <Container> and <Section>. Avoid relying on @media queries.\",\r\n\r\n // ── display:flex ──────────────────────────────────────────────────────\r\n \"display:flex::jsx\":\r\n \"Use React Email <Row> and <Column> components instead of flexbox.\",\r\n\r\n // ── display:grid ──────────────────────────────────────────────────────\r\n \"display:grid::jsx\":\r\n \"Use React Email <Row> and <Column> components instead of CSS Grid.\",\r\n\r\n // ── linear-gradient ───────────────────────────────────────────────────\r\n \"linear-gradient::jsx\":\r\n \"Add a solid backgroundColor style prop as fallback before the gradient.\",\r\n\r\n // ── box-shadow ────────────────────────────────────────────────────────\r\n \"box-shadow::jsx\":\r\n \"Use a border style prop as an alternative to boxShadow.\",\r\n\r\n // ── border-radius ─────────────────────────────────────────────────────\r\n \"border-radius::jsx\":\r\n \"Outlook ignores borderRadius. Use dangerouslySetInnerHTML with VML for rounded buttons if needed.\",\r\n\r\n // ── max-width ─────────────────────────────────────────────────────────\r\n \"max-width::jsx\":\r\n \"Use the React Email <Container> component which handles max-width across clients.\",\r\n\r\n // ── gap ───────────────────────────────────────────────────────────────\r\n \"gap::jsx\":\r\n \"Use padding style props on <Column> components instead of gap.\",\r\n\r\n // ── float ─────────────────────────────────────────────────────────────\r\n \"float::jsx\":\r\n \"Use React Email <Row> and <Column> components for side-by-side layout.\",\r\n\r\n // ── background-image ──────────────────────────────────────────────────\r\n \"background-image::jsx\":\r\n \"Use VML via dangerouslySetInnerHTML for Outlook background images.\",\r\n\r\n // ── position ──────────────────────────────────────────────────────────\r\n \"position::jsx\":\r\n \"Use React Email <Row> and <Column> components for positioning.\",\r\n\r\n // ── opacity ───────────────────────────────────────────────────────────\r\n \"opacity::jsx\":\r\n \"Use solid colors. Opacity is not supported in many email clients.\",\r\n\r\n // ── word-break ────────────────────────────────────────────────────────\r\n \"word-break::jsx\":\r\n \"Wrap long text in a <table><tr><td> element. Outlook ignores wordBreak but respects table cell widths.\",\r\n\r\n // ── overflow-wrap ─────────────────────────────────────────────────────\r\n \"overflow-wrap::jsx\":\r\n \"Wrap text in a <table><tr><td> element. Outlook ignores overflowWrap but respects table cell widths.\",\r\n};\r\n","/**\r\n * MJML framework-specific suggestion strings.\r\n * All keys have a ::mjml suffix in the original SUGGESTION_DATABASE.\r\n */\r\nexport const MJML_SUGGESTION_DATABASE: Record<string, string> = {\r\n // ── <style> ───────────────────────────────────────────────────────────\r\n \"<style>::mjml\":\r\n 'Use mj-style inline=\"inline\" to force MJML to inline styles for Gmail compatibility.',\r\n \"<style>:partial::mjml\":\r\n 'Use mj-style inline=\"inline\" for critical styles; plain mj-style for progressive enhancement.',\r\n\r\n // ── <link> ────────────────────────────────────────────────────────────\r\n \"<link>::mjml\":\r\n \"MJML does not support external stylesheets. Use mj-style or inline attributes.\",\r\n\r\n // ── <svg> ─────────────────────────────────────────────────────────────\r\n \"<svg>::mjml\":\r\n \"Replace inline SVG with an mj-image component pointing to a hosted PNG.\",\r\n\r\n // ── <video> ───────────────────────────────────────────────────────────\r\n \"<video>::mjml\":\r\n \"Replace <video> with an mj-image linking to a video thumbnail.\",\r\n\r\n // ── <form> ────────────────────────────────────────────────────────────\r\n \"<form>::mjml\":\r\n \"Replace the form with an mj-button linking to a hosted form page.\",\r\n\r\n // ── @font-face ────────────────────────────────────────────────────────\r\n \"@font-face::mjml\":\r\n \"Use mj-font in mj-head instead of @font-face in mj-style.\",\r\n\r\n // ── @media ────────────────────────────────────────────────────────────\r\n \"@media::mjml\":\r\n \"MJML generates responsive @media queries automatically. Use mj-breakpoint and mj-column widths.\",\r\n\r\n // ── display:flex ──────────────────────────────────────────────────────\r\n \"display:flex::mjml\":\r\n \"Use mj-section and mj-column — MJML compiles these to table-based layouts.\",\r\n\r\n // ── display:grid ──────────────────────────────────────────────────────\r\n \"display:grid::mjml\":\r\n \"Use mj-section and mj-column for grid-like layouts.\",\r\n\r\n // ── linear-gradient ───────────────────────────────────────────────────\r\n \"linear-gradient::mjml\":\r\n \"Add a background-color attribute on mj-section as a fallback.\",\r\n\r\n // ── box-shadow ────────────────────────────────────────────────────────\r\n \"box-shadow::mjml\":\r\n \"Use a border attribute on mj-section or mj-column as an alternative.\",\r\n\r\n // ── border-radius ─────────────────────────────────────────────────────\r\n \"border-radius::mjml\":\r\n 'MJML does not generate VML — border-radius will not render in Outlook. Set border-radius=\"0\" or accept flat corners.',\r\n\r\n // ── max-width ─────────────────────────────────────────────────────────\r\n \"max-width::mjml\":\r\n \"Set the width attribute on mj-body or mj-section for maximum compatibility.\",\r\n\r\n // ── gap ───────────────────────────────────────────────────────────────\r\n \"gap::mjml\":\r\n \"Use padding attribute on mj-column or mj-text for spacing.\",\r\n\r\n // ── float ─────────────────────────────────────────────────────────────\r\n \"float::mjml\":\r\n \"Use mj-section with multiple mj-column elements for side-by-side layout.\",\r\n\r\n // ── background-image ──────────────────────────────────────────────────\r\n \"background-image::mjml\":\r\n \"Use background-url attribute on mj-section — MJML generates VML automatically.\",\r\n\r\n // ── position ──────────────────────────────────────────────────────────\r\n \"position::mjml\":\r\n \"Use mj-section and mj-column for layout positioning.\",\r\n\r\n // ── opacity ───────────────────────────────────────────────────────────\r\n \"opacity::mjml\":\r\n \"Use solid colors. Most email clients don't support opacity.\",\r\n\r\n // ── word-break ────────────────────────────────────────────────────────\r\n \"word-break::mjml\":\r\n \"mj-text renders inside a <td>, which helps. Add word-wrap: break-word to the td via mj-style.\",\r\n};\r\n","/**\r\n * Maizzle framework-specific suggestion strings.\r\n * All keys have a ::maizzle suffix in the original SUGGESTION_DATABASE.\r\n */\r\nexport const MAIZZLE_SUGGESTION_DATABASE: Record<string, string> = {\r\n // ── <style> ───────────────────────────────────────────────────────────\r\n \"<style>::maizzle\":\r\n \"Prefer Tailwind utility classes — Maizzle inlines CSS via juice during build (inlineCSS: true in config.js).\",\r\n \"<style>:partial::maizzle\":\r\n \"Use Tailwind utility classes for critical styles. Maizzle automatically inlines them at build time.\",\r\n\r\n // ── <link> ────────────────────────────────────────────────────────────\r\n \"<link>::maizzle\":\r\n \"External stylesheets are stripped. Use Tailwind CSS classes — Maizzle inlines them at build time.\",\r\n\r\n // ── <svg> ─────────────────────────────────────────────────────────────\r\n \"<svg>::maizzle\":\r\n \"Replace inline SVG with an <img> tag pointing to a hosted PNG.\",\r\n\r\n // ── <video> ───────────────────────────────────────────────────────────\r\n \"<video>::maizzle\":\r\n \"Replace <video> with a linked image thumbnail.\",\r\n\r\n // ── <form> ────────────────────────────────────────────────────────────\r\n \"<form>::maizzle\":\r\n \"Replace the form with a CTA link/button pointing to a hosted form page.\",\r\n\r\n // ── @font-face ────────────────────────────────────────────────────────\r\n \"@font-face::maizzle\":\r\n \"Use the googleFonts key in config.js — Maizzle injects the Google Fonts link tag automatically.\",\r\n\r\n // ── @media ────────────────────────────────────────────────────────────\r\n \"@media::maizzle\":\r\n \"Use Tailwind responsive utility classes and Maizzle's breakpoints config instead of hand-written @media.\",\r\n\r\n // ── display:flex ──────────────────────────────────────────────────────\r\n \"display:flex::maizzle\":\r\n \"Replace Tailwind flex classes with HTML table + MSO conditional comments for Outlook.\",\r\n\r\n // ── display:grid ──────────────────────────────────────────────────────\r\n \"display:grid::maizzle\":\r\n \"Replace Tailwind grid classes with HTML table layout for email compatibility.\",\r\n\r\n // ── linear-gradient ───────────────────────────────────────────────────\r\n \"linear-gradient::maizzle\":\r\n \"Add a bg-[color] Tailwind class as a fallback before the gradient.\",\r\n\r\n // ── box-shadow ────────────────────────────────────────────────────────\r\n \"box-shadow::maizzle\":\r\n \"Use Tailwind border classes as an alternative to shadow classes.\",\r\n\r\n // ── border-radius ─────────────────────────────────────────────────────\r\n \"border-radius::maizzle\":\r\n \"Outlook ignores border-radius. Accept flat corners or use MSO conditional VML.\",\r\n\r\n // ── max-width ─────────────────────────────────────────────────────────\r\n \"max-width::maizzle\":\r\n \"Wrap max-w containers with MSO conditional table for Outlook.\",\r\n\r\n // ── gap ───────────────────────────────────────────────────────────────\r\n \"gap::maizzle\":\r\n \"Use Tailwind padding classes on child elements instead of gap.\",\r\n\r\n // ── float ─────────────────────────────────────────────────────────────\r\n \"float::maizzle\":\r\n \"Use HTML tables for side-by-side layout instead of Tailwind float classes.\",\r\n\r\n // ── background-image ──────────────────────────────────────────────────\r\n \"background-image::maizzle\":\r\n \"Use MSO conditional VML for Outlook background images.\",\r\n\r\n // ── position ──────────────────────────────────────────────────────────\r\n \"position::maizzle\":\r\n \"Use HTML table layout instead of Tailwind position classes.\",\r\n\r\n // ── opacity ───────────────────────────────────────────────────────────\r\n \"opacity::maizzle\":\r\n \"Use solid Tailwind color classes instead of opacity.\",\r\n};\r\n","import type { CodeFix, Framework } from \"../types\";\r\n\r\nimport { HTML_FIX_DATABASE } from \"./html-fixes\";\r\nimport { JSX_FIX_DATABASE } from \"./jsx-fixes\";\r\nimport { MJML_FIX_DATABASE } from \"./mjml-fixes\";\r\nimport { MAIZZLE_FIX_DATABASE } from \"./maizzle-fixes\";\r\n\r\nimport { HTML_SUGGESTION_DATABASE } from \"./html-suggestions\";\r\nimport { JSX_SUGGESTION_DATABASE } from \"./jsx-suggestions\";\r\nimport { MJML_SUGGESTION_DATABASE } from \"./mjml-suggestions\";\r\nimport { MAIZZLE_SUGGESTION_DATABASE } from \"./maizzle-suggestions\";\r\n\r\n/**\r\n * Inline code fix snippets — real, paste-ready code that turns\r\n * \"here's your problem\" into \"here's your solution.\"\r\n *\r\n * Keyed by property, with optional client-specific overrides.\r\n * Client-specific keys use the format \"property::clientPrefix\"\r\n * (e.g. \"border-radius::outlook\").\r\n */\r\nconst FIX_DATABASE: Record<string, CodeFix> = {\r\n ...HTML_FIX_DATABASE,\r\n ...JSX_FIX_DATABASE,\r\n ...MJML_FIX_DATABASE,\r\n ...MAIZZLE_FIX_DATABASE,\r\n};\r\n\r\n/**\r\n * Human-readable suggestion strings attached to CSSWarning.suggestion.\r\n *\r\n * Key format mirrors FIX_DATABASE:\r\n * property → generic HTML advice\r\n * property::clientPrefix → client-specific advice\r\n * property::framework → framework-specific advice\r\n * property::clientPrefix::framework → most-specific advice\r\n *\r\n * Use `getSuggestion()` to resolve the best match via tiered lookup.\r\n */\r\nconst SUGGESTION_DATABASE: Record<string, string> = {\r\n ...HTML_SUGGESTION_DATABASE,\r\n ...JSX_SUGGESTION_DATABASE,\r\n ...MJML_SUGGESTION_DATABASE,\r\n ...MAIZZLE_SUGGESTION_DATABASE,\r\n};\r\n\r\n/**\r\n * Look up a code fix for a given property, client, and optional framework.\r\n * Returns undefined if no fix snippet exists.\r\n *\r\n * Resolution order (most specific to least specific):\r\n * 1. property::clientPrefix::framework (e.g. \"display:flex::outlook::jsx\")\r\n * 2. property::framework (e.g. \"display:grid::jsx\")\r\n * 3. property::clientPrefix (e.g. \"border-radius::outlook\")\r\n * 4. property (generic fix)\r\n */\r\nexport function getCodeFix(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): CodeFix | undefined {\r\n const clientPrefix = getClientPrefix(clientId);\r\n\r\n // Tier 1: property::clientPrefix::framework\r\n if (framework && clientPrefix) {\r\n const tier1 = FIX_DATABASE[`${property}::${clientPrefix}::${framework}`];\r\n if (tier1) return tier1;\r\n }\r\n\r\n // Tier 2: property::framework\r\n if (framework) {\r\n const tier2 = FIX_DATABASE[`${property}::${framework}`];\r\n if (tier2) return tier2;\r\n }\r\n\r\n // Tier 3: property::clientPrefix (existing behavior)\r\n if (clientPrefix) {\r\n const tier3 = FIX_DATABASE[`${property}::${clientPrefix}`];\r\n if (tier3) return tier3;\r\n }\r\n\r\n // Tier 4: generic fix (existing behavior)\r\n return FIX_DATABASE[property];\r\n}\r\n\r\n/**\r\n * Returns true if a framework was specified but the code fix resolved to\r\n * a client-specific or fully generic entry (tiers 3–4) rather than a\r\n * framework-aware entry (tiers 1–2).\r\n */\r\nexport function isCodeFixGenericFallback(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): boolean {\r\n if (!framework) return false;\r\n const clientPrefix = getClientPrefix(clientId);\r\n if (clientPrefix && FIX_DATABASE[`${property}::${clientPrefix}::${framework}`]) return false;\r\n if (FIX_DATABASE[`${property}::${framework}`]) return false;\r\n return true;\r\n}\r\n\r\nfunction getClientPrefix(clientId: string): string | null {\r\n if (clientId.startsWith(\"outlook-windows\")) return \"outlook\";\r\n if (clientId.startsWith(\"outlook\")) return null; // Outlook web is more standards-compliant\r\n if (clientId.startsWith(\"gmail\")) return \"gmail\";\r\n if (clientId.startsWith(\"apple-mail\")) return \"apple\";\r\n if (clientId === \"yahoo-mail\") return \"yahoo\";\r\n if (clientId === \"samsung-mail\") return \"samsung\";\r\n return null;\r\n}\r\n\r\n/**\r\n * Look up a suggestion string for a given property, client, and optional framework.\r\n *\r\n * Resolution order mirrors `getCodeFix()`:\r\n * 1. property::clientPrefix::framework\r\n * 2. property::framework\r\n * 3. property::clientPrefix\r\n * 4. property (generic)\r\n *\r\n * `isGenericFallback` is true when a framework was specified but no\r\n * framework-specific entry was found (resolution fell through to tiers 3–4).\r\n */\r\nexport function getSuggestion(\r\n property: string,\r\n clientId: string,\r\n framework?: Framework\r\n): { text: string; isGenericFallback: boolean } {\r\n const clientPrefix = getClientPrefix(clientId);\r\n\r\n // Tier 1: property::clientPrefix::framework\r\n if (framework && clientPrefix) {\r\n const tier1 = SUGGESTION_DATABASE[`${property}::${clientPrefix}::${framework}`];\r\n if (tier1) return { text: tier1, isGenericFallback: false };\r\n }\r\n\r\n // Tier 2: property::framework\r\n if (framework) {\r\n const tier2 = SUGGESTION_DATABASE[`${property}::${framework}`];\r\n if (tier2) return { text: tier2, isGenericFallback: false };\r\n }\r\n\r\n // Tier 3: property::clientPrefix\r\n if (clientPrefix) {\r\n const tier3 = SUGGESTION_DATABASE[`${property}::${clientPrefix}`];\r\n if (tier3) return { text: tier3, isGenericFallback: !!framework };\r\n }\r\n\r\n // Tier 4: generic\r\n const tier4 = SUGGESTION_DATABASE[property];\r\n if (tier4) return { text: tier4, isGenericFallback: !!framework };\r\n\r\n // No entry — return a default\r\n return {\r\n text: `\"${property}\" is not supported in this email client.`,\r\n isGenericFallback: !!framework,\r\n };\r\n}\r\n","/**\r\n * Shared utilities for parsing inline CSS style declarations.\r\n *\r\n * Used by both the analyzer and the per-client transformers to avoid\r\n * duplicating the semicolon-aware splitting logic.\r\n */\r\n\r\n/**\r\n * Split a CSS style string on semicolons while respecting\r\n * quoted strings and url()/function parentheses.\r\n */\r\nexport function splitStyleDeclarations(style: string): string[] {\r\n const parts: string[] = [];\r\n let current = \"\";\r\n let inSingleQuote = false;\r\n let inDoubleQuote = false;\r\n let parenDepth = 0;\r\n\r\n for (let i = 0; i < style.length; i++) {\r\n const ch = style[i];\r\n\r\n if (ch === \"'\" && !inDoubleQuote) {\r\n inSingleQuote = !inSingleQuote;\r\n } else if (ch === '\"' && !inSingleQuote) {\r\n inDoubleQuote = !inDoubleQuote;\r\n } else if (ch === \"(\" && !inSingleQuote && !inDoubleQuote) {\r\n parenDepth++;\r\n } else if (ch === \")\" && !inSingleQuote && !inDoubleQuote) {\r\n parenDepth = Math.max(0, parenDepth - 1);\r\n }\r\n\r\n if (ch === \";\" && !inSingleQuote && !inDoubleQuote && parenDepth === 0) {\r\n if (current.trim()) {\r\n parts.push(current.trim());\r\n }\r\n current = \"\";\r\n } else {\r\n current += ch;\r\n }\r\n }\r\n\r\n if (current.trim()) {\r\n parts.push(current.trim());\r\n }\r\n return parts;\r\n}\r\n\r\n/**\r\n * Parse an inline style string into a list of property names.\r\n */\r\nexport function parseStyleProperties(style: string): string[] {\r\n const props: string[] = [];\r\n const parts = splitStyleDeclarations(style);\r\n for (const part of parts) {\r\n const colonIndex = part.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = part.slice(0, colonIndex).trim().toLowerCase();\r\n if (prop) props.push(prop);\r\n }\r\n return props;\r\n}\r\n\r\n/**\r\n * Extract the value of a specific property from an inline style string.\r\n */\r\nexport function getStyleValue(style: string, property: string): string | null {\r\n const parts = splitStyleDeclarations(style);\r\n for (const part of parts) {\r\n const colonIndex = part.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = part.slice(0, colonIndex).trim().toLowerCase();\r\n if (prop === property) {\r\n return part.slice(colonIndex + 1).trim();\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Parse an inline style string into a Map of property → value.\r\n */\r\nexport function parseInlineStyle(style: string): Map<string, string> {\r\n const map = new Map<string, string>();\r\n const declarations = splitStyleDeclarations(style);\r\n for (const decl of declarations) {\r\n const colonIndex = decl.indexOf(\":\");\r\n if (colonIndex === -1) continue;\r\n const prop = decl.slice(0, colonIndex).trim().toLowerCase();\r\n const value = decl.slice(colonIndex + 1).trim();\r\n if (prop && value) {\r\n map.set(prop, value);\r\n }\r\n }\r\n return map;\r\n}\r\n\r\n/**\r\n * Serialize a Map of property → value back to an inline style string.\r\n */\r\nexport function serializeStyle(map: Map<string, string>): string {\r\n const parts: string[] = [];\r\n map.forEach((value, prop) => {\r\n parts.push(`${prop}: ${value}`);\r\n });\r\n return parts.join(\"; \");\r\n}\r\n","/**\r\n * Shared constants used across the engine.\r\n */\r\n\r\n/** Maximum HTML input size: 2MB. Inputs exceeding this are rejected early. */\r\nexport const MAX_HTML_SIZE = 2 * 1024 * 1024;\r\n\r\nexport const GENERIC_LINK_TEXT = new Set([\r\n \"click here\", \"here\", \"read more\", \"learn more\", \"more\",\r\n \"link\", \"this link\", \"click\", \"tap here\", \"this\",\r\n]);\r\n","import * as cheerio from \"cheerio\";\r\nimport * as csstree from \"css-tree\";\r\nimport { CSS_SUPPORT, STRUCTURAL_FIX_PROPERTIES } from \"./rules/css-support\";\r\nimport { EMAIL_CLIENTS } from \"./clients\";\r\nimport { getCodeFix, getSuggestion, isCodeFixGenericFallback } from \"./fix-snippets\";\r\nimport { parseStyleProperties, getStyleValue } from \"./style-utils\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\nimport type { CSSWarning, FixType, Framework, SupportLevel } from \"./types\";\r\n\r\n/**\r\n * Analyze an HTML email and return CSS compatibility warnings\r\n * for all target email clients.\r\n *\r\n * The `framework` parameter controls which fix snippets are attached\r\n * to warnings — it does NOT change which warnings fire. Analysis always\r\n * runs on compiled HTML (what email clients actually receive). Fix\r\n * snippets reference source-level constructs so users know how to\r\n * modify their framework source code.\r\n */\r\nexport function analyzeEmail(html: string, framework?: Framework): CSSWarning[] {\r\n if (!html || !html.trim()) {\r\n return [];\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n const seenWarnings = new Set<string>();\r\n\r\n function addWarning(w: CSSWarning) {\r\n const key = `${w.client}:${w.property}:${w.severity}:${w.selector || \"\"}`;\r\n if (!seenWarnings.has(key)) {\r\n seenWarnings.add(key);\r\n warnings.push(w);\r\n }\r\n }\r\n\r\n /** Build a selector description for an element (e.g., \"div.card\", \"a[href]\") */\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n function describeSelector(el: any): string {\r\n const $el = $(el);\r\n const tag = (el.tagName as string)?.toLowerCase() || \"\";\r\n const cls = $el.attr(\"class\");\r\n const id = $el.attr(\"id\");\r\n if (id) return `${tag}#${id}`;\r\n if (cls) return `${tag}.${cls.split(/\\s+/)[0]}`;\r\n const href = $el.attr(\"href\");\r\n if (href) return `${tag}[href]`;\r\n return tag;\r\n }\r\n\r\n // 1. Check for <style> block usage\r\n if ($(\"style\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<style>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<style>\", client.id, framework);\r\n const fix = getCodeFix(\"<style>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<style>\",\r\n message: `${client.name} strips <style> blocks. Styles must be inlined.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<style>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<style>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n } else if (support === \"partial\") {\r\n const sug = getSuggestion(\"<style>:partial\", client.id, framework);\r\n const fix = getCodeFix(\"<style>\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"<style>\",\r\n message: `${client.name} has partial <style> support (head only, with limitations). Inline styles recommended.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<style>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<style>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 2. Check for <link> stylesheet usage\r\n if ($(\"link[rel='stylesheet']\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<link>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<link>\", client.id, framework);\r\n const fix = getCodeFix(\"<link>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<link>\",\r\n message: `${client.name} does not support external stylesheets.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<link>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<link>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 3. Check for SVG usage\r\n if ($(\"svg\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<svg>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<svg>\", client.id, framework);\r\n const fix = getCodeFix(\"<svg>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<svg>\",\r\n message: `${client.name} does not support inline SVG.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<svg>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<svg>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 4. Check for video\r\n if ($(\"video\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<video>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<video>\", client.id, framework);\r\n const fix = getCodeFix(\"<video>\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"<video>\",\r\n message: `${client.name} does not support <video> elements.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<video>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<video>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 5. Check for form elements\r\n if ($(\"form\").length > 0 || $(\"input\").length > 0 || $(\"button[type='submit']\").length > 0) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"<form>\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"<form>\", client.id, framework);\r\n const fix = getCodeFix(\"<form>\", client.id, framework);\r\n addWarning({\r\n severity: \"error\",\r\n client: client.id,\r\n property: \"<form>\",\r\n message: `${client.name} strips form elements.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"<form>\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"<form>\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 6-7. Parse <style> blocks with css-tree for accurate at-rule and property detection\r\n const parsedAtRules = new Set<string>();\r\n const parsedProperties = new Set<string>();\r\n /** Track first line number for each property found in <style> blocks */\r\n const propertyLines = new Map<string, number>();\r\n\r\n $(\"style\").each((_, el) => {\r\n const cssText = $(el).text();\r\n try {\r\n const ast = csstree.parse(cssText, { parseCustomProperty: true, positions: true });\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type === \"Atrule\") {\r\n parsedAtRules.add(`@${node.name}`);\r\n }\r\n if (node.type === \"Declaration\") {\r\n const prop = node.property.toLowerCase();\r\n parsedProperties.add(prop);\r\n if (node.loc && !propertyLines.has(prop)) {\r\n propertyLines.set(prop, node.loc.start.line);\r\n }\r\n // Check for display:flex / display:grid values\r\n if (prop === \"display\") {\r\n const value = csstree.generate(node.value);\r\n if (value.includes(\"flex\")) {\r\n parsedProperties.add(\"display:flex\");\r\n if (node.loc && !propertyLines.has(\"display:flex\")) {\r\n propertyLines.set(\"display:flex\", node.loc.start.line);\r\n }\r\n }\r\n if (value.includes(\"grid\")) {\r\n parsedProperties.add(\"display:grid\");\r\n if (node.loc && !propertyLines.has(\"display:grid\")) {\r\n propertyLines.set(\"display:grid\", node.loc.start.line);\r\n }\r\n }\r\n }\r\n // Check for gradient values\r\n const valueStr = csstree.generate(node.value);\r\n if (valueStr.includes(\"linear-gradient\") || valueStr.includes(\"radial-gradient\")) {\r\n parsedProperties.add(\"linear-gradient\");\r\n if (node.loc && !propertyLines.has(\"linear-gradient\")) {\r\n propertyLines.set(\"linear-gradient\", node.loc.start.line);\r\n }\r\n }\r\n }\r\n },\r\n });\r\n } catch {\r\n // If css-tree can't parse, skip\r\n }\r\n });\r\n\r\n // Check @font-face\r\n if (parsedAtRules.has(\"@font-face\")) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"@font-face\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"@font-face\", client.id, framework);\r\n const fix = getCodeFix(\"@font-face\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"@font-face\",\r\n message: `${client.name} does not support web fonts (@font-face).`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"@font-face\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"@font-face\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Check @media queries\r\n if (parsedAtRules.has(\"@media\")) {\r\n for (const client of EMAIL_CLIENTS) {\r\n const support = CSS_SUPPORT[\"@media\"]?.[client.id];\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(\"@media\", client.id, framework);\r\n const fix = getCodeFix(\"@media\", client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: \"@media\",\r\n message: `${client.name} does not support @media queries.`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType: getFixType(\"@media\"),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(\"@media\", client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 8. Scan inline styles for unsupported CSS properties\r\n const cssPropertiesToCheck = Object.keys(CSS_SUPPORT).filter(\r\n (k) => !k.startsWith(\"<\") && !k.startsWith(\"@\")\r\n );\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseStyleProperties(style);\r\n const selector = describeSelector(el);\r\n\r\n for (const prop of props) {\r\n // Check for flex/grid in display value\r\n if (prop === \"display\") {\r\n const value = getStyleValue(style, \"display\");\r\n if (value?.includes(\"flex\")) {\r\n checkPropertySupport(\"display:flex\", addWarning, framework, selector);\r\n } else if (value?.includes(\"grid\")) {\r\n checkPropertySupport(\"display:grid\", addWarning, framework, selector);\r\n }\r\n }\r\n\r\n // Check the property itself\r\n if (cssPropertiesToCheck.includes(prop)) {\r\n checkPropertySupport(prop, addWarning, framework, selector);\r\n }\r\n\r\n // Check for gradient values in the property value\r\n const value = getStyleValue(style, prop);\r\n if (value && (value.includes(\"linear-gradient\") || value.includes(\"radial-gradient\"))) {\r\n checkPropertySupport(\"linear-gradient\", addWarning, framework, selector);\r\n }\r\n }\r\n });\r\n\r\n // 9. Check CSS properties found in <style> blocks (via css-tree parsing)\r\n for (const prop of parsedProperties) {\r\n if (prop.includes(\":\")) continue; // skip compound like display:flex (handled separately)\r\n if (!cssPropertiesToCheck.includes(prop)) continue;\r\n\r\n checkPropertySupport(prop, addWarning, framework, undefined, propertyLines.get(prop));\r\n }\r\n\r\n // Check compound properties from <style> blocks\r\n for (const compound of [\"display:flex\", \"display:grid\", \"linear-gradient\"]) {\r\n if (parsedProperties.has(compound)) {\r\n checkPropertySupport(compound, addWarning, framework, undefined, propertyLines.get(compound));\r\n }\r\n }\r\n\r\n // Sort: errors first, then warnings, then info\r\n const severityOrder: Record<string, number> = { error: 0, warning: 1, info: 2 };\r\n warnings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);\r\n\r\n return warnings;\r\n}\r\n\r\nfunction getFixType(prop: string): FixType {\r\n return STRUCTURAL_FIX_PROPERTIES.has(prop) ? \"structural\" : \"css\";\r\n}\r\n\r\nfunction checkPropertySupport(\r\n prop: string,\r\n addWarning: (w: CSSWarning) => void,\r\n framework?: Framework,\r\n selector?: string,\r\n line?: number,\r\n) {\r\n const supportData = CSS_SUPPORT[prop];\r\n if (!supportData) return;\r\n\r\n const fixType = getFixType(prop);\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const support: SupportLevel = supportData[client.id] || \"unknown\";\r\n if (support === \"unsupported\") {\r\n const sug = getSuggestion(prop, client.id, framework);\r\n const fix = getCodeFix(prop, client.id, framework);\r\n addWarning({\r\n severity: \"warning\",\r\n client: client.id,\r\n property: prop,\r\n message: `${client.name} does not support \"${prop}\".`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType,\r\n ...(selector ? { selector } : {}),\r\n ...(line !== undefined ? { line } : {}),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(prop, client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n } else if (support === \"partial\") {\r\n const sug = getSuggestion(prop, client.id, framework);\r\n const fix = getCodeFix(prop, client.id, framework);\r\n addWarning({\r\n severity: \"info\",\r\n client: client.id,\r\n property: prop,\r\n message: `${client.name} has partial support for \"${prop}\".`,\r\n suggestion: sug.text,\r\n fix,\r\n fixType,\r\n ...(selector ? { selector } : {}),\r\n ...(line !== undefined ? { line } : {}),\r\n ...(framework && (sug.isGenericFallback || (fix && isCodeFixGenericFallback(prop, client.id, framework)))\r\n ? { fixIsGenericFallback: true } : {}),\r\n });\r\n }\r\n }\r\n}\r\n\r\n\r\n/**\r\n * Generate a summary of CSS compatibility for the email.\r\n */\r\nexport function generateCompatibilityScore(\r\n warnings: CSSWarning[]\r\n): Record<string, { score: number; errors: number; warnings: number; info: number }> {\r\n const result: Record<string, { score: number; errors: number; warnings: number; info: number }> = {};\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const clientWarnings = warnings.filter((w) => w.client === client.id);\r\n const errors = clientWarnings.filter((w) => w.severity === \"error\").length;\r\n const warns = clientWarnings.filter((w) => w.severity === \"warning\").length;\r\n const info = clientWarnings.filter((w) => w.severity === \"info\").length;\r\n\r\n // Score: 100 minus penalties, clamped to 0-100\r\n const score = Math.max(0, Math.min(100, 100 - errors * 15 - warns * 5 - info * 1));\r\n\r\n result[client.id] = { score, errors, warnings: warns, info };\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/** Filter warnings for a specific client. */\r\nexport function warningsForClient(warnings: CSSWarning[], clientId: string): CSSWarning[] {\r\n return warnings.filter(w => w.client === clientId);\r\n}\r\n\r\n/** Get only error-severity warnings. */\r\nexport function errorWarnings(warnings: CSSWarning[]): CSSWarning[] {\r\n return warnings.filter(w => w.severity === \"error\");\r\n}\r\n\r\n/** Get only structural fix warnings. */\r\nexport function structuralWarnings(warnings: CSSWarning[]): CSSWarning[] {\r\n return warnings.filter(w => w.fixType === \"structural\");\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport * as csstree from \"css-tree\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\nimport { parseColor, relativeLuminance } from \"./color-utils\";\r\nimport { parseInlineStyle, serializeStyle } from \"./style-utils\";\r\nimport type { CSSWarning } from \"./types\";\r\n\r\n/** Luminance threshold — colors above this are considered \"light\" */\r\nconst LIGHT_THRESHOLD = 0.7;\r\n/** Luminance threshold — colors below this are considered \"dark\" */\r\nconst DARK_THRESHOLD = 0.15;\r\n\r\n/**\r\n * Simulate dark mode rendering for an email.\r\n * Email clients apply dark mode differently:\r\n * - Some invert colors (Gmail Android)\r\n * - Some use prefers-color-scheme media query (Apple Mail)\r\n * - Some do partial inversion (Outlook.com)\r\n */\r\nexport function simulateDarkMode(\r\n html: string,\r\n clientId: string\r\n): { html: string; warnings: CSSWarning[] } {\r\n if (!html || !html.trim()) {\r\n return { html: html || \"\", warnings: [] };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const warnings: CSSWarning[] = [];\r\n\r\n // Check for images with transparent backgrounds\r\n $(\"img\").each((_, el) => {\r\n const src = $(el).attr(\"src\") || \"\";\r\n if (src.endsWith(\".png\") || src.endsWith(\".svg\") || src.endsWith(\".webp\")) {\r\n warnings.push({\r\n severity: \"warning\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message: \"Image with potentially transparent background may disappear in dark mode.\",\r\n suggestion:\r\n 'Add a background-color to the parent element, or use a non-transparent image format.',\r\n });\r\n }\r\n });\r\n\r\n // Determine dark mode behavior based on client\r\n switch (clientId) {\r\n case \"gmail-web\":\r\n case \"gmail-android\":\r\n case \"gmail-ios\":\r\n // Gmail does full color inversion on Android,\r\n // partial on web/iOS\r\n applyColorInversion($, clientId === \"gmail-android\" ? \"full\" : \"partial\");\r\n break;\r\n\r\n case \"outlook-web\":\r\n // Outlook.com applies its own dark mode with partial inversion\r\n applyColorInversion($, \"partial\");\r\n break;\r\n\r\n case \"apple-mail-macos\":\r\n case \"apple-mail-ios\":\r\n // Apple Mail respects prefers-color-scheme\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"Apple Mail supports @media (prefers-color-scheme: dark). Consider adding dark mode styles.\",\r\n suggestion:\r\n \"Add a @media (prefers-color-scheme: dark) block with inverted colors for the best dark mode experience.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"yahoo-mail\":\r\n applyColorInversion($, \"partial\");\r\n break;\r\n\r\n case \"samsung-mail\":\r\n applyColorInversion($, \"full\");\r\n break;\r\n\r\n case \"hey-mail\":\r\n // HEY Mail respects prefers-color-scheme; simulate partial inversion\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"HEY Mail supports @media (prefers-color-scheme: dark). Add dark mode styles for the best experience.\",\r\n suggestion:\r\n \"Add a @media (prefers-color-scheme: dark) block with inverted colors.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"superhuman\":\r\n // Superhuman uses Blink and respects prefers-color-scheme\r\n applyColorInversion($, \"partial\");\r\n if (!html.includes(\"prefers-color-scheme\")) {\r\n warnings.push({\r\n severity: \"info\",\r\n client: clientId,\r\n property: \"dark-mode\",\r\n message:\r\n \"Superhuman respects @media (prefers-color-scheme: dark). Many Superhuman users run in dark mode.\",\r\n suggestion:\r\n \"Add @media (prefers-color-scheme: dark) styles — Superhuman's power-user audience often prefers dark mode.\",\r\n });\r\n }\r\n break;\r\n\r\n case \"outlook-windows\":\r\n case \"thunderbird\":\r\n // These don't have dark mode\r\n break;\r\n }\r\n\r\n // Add dark mode wrapper styling\r\n $(\"body\").css(\"background-color\", \"#1a1a1a\");\r\n $(\"body\").css(\"color\", \"#e0e0e0\");\r\n\r\n return { html: $.html(), warnings };\r\n}\r\n\r\n/**\r\n * Invert a color value for dark mode.\r\n * Returns null if the color can't be parsed or shouldn't be inverted.\r\n */\r\nfunction invertColor(value: string, mode: \"full\" | \"partial\"): string | null {\r\n const parsed = parseColor(value);\r\n if (!parsed || parsed.a === 0) return null;\r\n\r\n const lum = relativeLuminance(parsed.r, parsed.g, parsed.b);\r\n\r\n if (mode === \"full\") {\r\n // Full inversion: invert both light and dark colors\r\n if (lum > LIGHT_THRESHOLD) {\r\n // Light color → dark\r\n return \"#1a1a1a\";\r\n }\r\n if (lum < DARK_THRESHOLD) {\r\n // Dark color → light\r\n return \"#e0e0e0\";\r\n }\r\n return null; // mid-range colors left alone\r\n } else {\r\n // Partial inversion: only invert very light backgrounds/very dark text\r\n if (lum > 0.85) {\r\n return \"#2d2d2d\";\r\n }\r\n if (lum < 0.05) {\r\n return \"#d4d4d4\";\r\n }\r\n return null;\r\n }\r\n}\r\n\r\n/** Color-bearing CSS properties */\r\nconst COLOR_PROPS = new Set([\r\n \"color\", \"background-color\", \"border-color\",\r\n \"border-top-color\", \"border-right-color\", \"border-bottom-color\", \"border-left-color\",\r\n \"outline-color\",\r\n]);\r\n\r\n/** Background shorthand — extract color portion */\r\nfunction extractBackgroundColor(value: string): string | null {\r\n // Simple heuristic: if background value starts with a color, extract it\r\n const trimmed = value.trim();\r\n if (trimmed.startsWith(\"#\") || trimmed.startsWith(\"rgb\") || /^[a-z]+$/i.test(trimmed.split(/\\s/)[0])) {\r\n const firstToken = trimmed.split(/\\s/)[0];\r\n if (parseColor(firstToken)) return firstToken;\r\n }\r\n return null;\r\n}\r\n\r\nfunction applyColorInversion(\r\n $: cheerio.CheerioAPI,\r\n mode: \"full\" | \"partial\"\r\n): void {\r\n // Process inline styles using parseColor for accurate detection\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n const props = parseInlineStyle(style);\r\n let changed = false;\r\n\r\n props.forEach((value, prop) => {\r\n if (COLOR_PROPS.has(prop)) {\r\n const inverted = invertColor(value, mode);\r\n if (inverted) {\r\n props.set(prop, inverted);\r\n changed = true;\r\n }\r\n }\r\n // Handle background shorthand\r\n if (prop === \"background\") {\r\n const bgColor = extractBackgroundColor(value);\r\n if (bgColor) {\r\n const inverted = invertColor(bgColor, mode);\r\n if (inverted) {\r\n props.set(prop, value.replace(bgColor, inverted));\r\n changed = true;\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (changed) {\r\n $(el).attr(\"style\", serializeStyle(props));\r\n }\r\n });\r\n\r\n // Process <style> blocks\r\n $(\"style\").each((_, el) => {\r\n const cssText = $(el).text();\r\n try {\r\n const ast = csstree.parse(cssText, { parseCustomProperty: true });\r\n let modified = false;\r\n\r\n csstree.walk(ast, {\r\n enter(node: csstree.CssNode) {\r\n if (node.type !== \"Declaration\") return;\r\n const prop = node.property.toLowerCase();\r\n if (!COLOR_PROPS.has(prop) && prop !== \"background\") return;\r\n\r\n const valueStr = csstree.generate(node.value);\r\n if (prop === \"background\") {\r\n const bgColor = extractBackgroundColor(valueStr);\r\n if (bgColor) {\r\n const inverted = invertColor(bgColor, mode);\r\n if (inverted) {\r\n const newValue = valueStr.replace(bgColor, inverted);\r\n node.value = csstree.parse(newValue, { context: \"value\" }) as csstree.Value;\r\n modified = true;\r\n }\r\n }\r\n } else {\r\n const inverted = invertColor(valueStr, mode);\r\n if (inverted) {\r\n node.value = csstree.parse(inverted, { context: \"value\" }) as csstree.Value;\r\n modified = true;\r\n }\r\n }\r\n },\r\n });\r\n\r\n if (modified) {\r\n $(el).text(csstree.generate(ast));\r\n }\r\n } catch {\r\n // If css-tree can't parse it, skip\r\n }\r\n });\r\n\r\n // Also handle bgcolor attributes on table elements\r\n $(\"[bgcolor]\").each((_, el) => {\r\n const bgcolor = $(el).attr(\"bgcolor\") || \"\";\r\n const inverted = invertColor(bgcolor, mode);\r\n if (inverted) {\r\n $(el).attr(\"bgcolor\", inverted);\r\n }\r\n });\r\n}\r\n","/**\r\n * WCAG 2.1 color parsing, luminance, and contrast utilities.\r\n *\r\n * Handles hex (#fff, #ffffff, #rrggbbaa), rgb()/rgba(), 148 named CSS colors,\r\n * and `transparent`. Returns null for unresolvable values like var(), inherit,\r\n * currentColor.\r\n */\r\n\r\nexport interface RGBA {\r\n r: number; // 0-255\r\n g: number; // 0-255\r\n b: number; // 0-255\r\n a: number; // 0-1\r\n}\r\n\r\nexport type WcagGrade = \"AAA\" | \"AA\" | \"AA Large\" | \"Fail\";\r\n\r\n// 148 CSS named colors (lowercase → [r, g, b])\r\nconst NAMED_COLORS: Record<string, [number, number, number]> = {\r\n aliceblue: [240, 248, 255], antiquewhite: [250, 235, 215], aqua: [0, 255, 255],\r\n aquamarine: [127, 255, 212], azure: [240, 255, 255], beige: [245, 245, 220],\r\n bisque: [255, 228, 196], black: [0, 0, 0], blanchedalmond: [255, 235, 205],\r\n blue: [0, 0, 255], blueviolet: [138, 43, 226], brown: [165, 42, 42],\r\n burlywood: [222, 184, 135], cadetblue: [95, 158, 160], chartreuse: [127, 255, 0],\r\n chocolate: [210, 105, 30], coral: [255, 127, 80], cornflowerblue: [100, 149, 237],\r\n cornsilk: [255, 248, 220], crimson: [220, 20, 60], cyan: [0, 255, 255],\r\n darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgoldenrod: [184, 134, 11],\r\n darkgray: [169, 169, 169], darkgreen: [0, 100, 0], darkgrey: [169, 169, 169],\r\n darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47],\r\n darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0],\r\n darksalmon: [233, 150, 122], darkseagreen: [143, 188, 143], darkslateblue: [72, 61, 139],\r\n darkslategray: [47, 79, 79], darkslategrey: [47, 79, 79], darkturquoise: [0, 206, 209],\r\n darkviolet: [148, 0, 211], deeppink: [255, 20, 147], deepskyblue: [0, 191, 255],\r\n dimgray: [105, 105, 105], dimgrey: [105, 105, 105], dodgerblue: [30, 144, 255],\r\n firebrick: [178, 34, 34], floralwhite: [255, 250, 240], forestgreen: [34, 139, 34],\r\n fuchsia: [255, 0, 255], gainsboro: [220, 220, 220], ghostwhite: [248, 248, 255],\r\n gold: [255, 215, 0], goldenrod: [218, 165, 32], gray: [128, 128, 128],\r\n green: [0, 128, 0], greenyellow: [173, 255, 47], grey: [128, 128, 128],\r\n honeydew: [240, 255, 240], hotpink: [255, 105, 180], indianred: [205, 92, 92],\r\n indigo: [75, 0, 130], ivory: [255, 255, 240], khaki: [240, 230, 140],\r\n lavender: [230, 230, 250], lavenderblush: [255, 240, 245], lawngreen: [124, 252, 0],\r\n lemonchiffon: [255, 250, 205], lightblue: [173, 216, 230], lightcoral: [240, 128, 128],\r\n lightcyan: [224, 255, 255], lightgoldenrodyellow: [250, 250, 210], lightgray: [211, 211, 211],\r\n lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193],\r\n lightsalmon: [255, 160, 122], lightseagreen: [32, 178, 170], lightskyblue: [135, 206, 250],\r\n lightslategray: [119, 136, 153], lightslategrey: [119, 136, 153], lightsteelblue: [176, 196, 222],\r\n lightyellow: [255, 255, 224], lime: [0, 255, 0], limegreen: [50, 205, 50],\r\n linen: [250, 240, 230], magenta: [255, 0, 255], maroon: [128, 0, 0],\r\n mediumaquamarine: [102, 205, 170], mediumblue: [0, 0, 205], mediumorchid: [186, 85, 211],\r\n mediumpurple: [147, 111, 219], mediumseagreen: [60, 179, 113], mediumslateblue: [123, 104, 238],\r\n mediumspringgreen: [0, 250, 154], mediumturquoise: [72, 209, 204], mediumvioletred: [199, 21, 133],\r\n midnightblue: [25, 25, 112], mintcream: [245, 255, 250], mistyrose: [255, 228, 225],\r\n moccasin: [255, 228, 181], navajowhite: [255, 222, 173], navy: [0, 0, 128],\r\n oldlace: [253, 245, 230], olive: [128, 128, 0], olivedrab: [107, 142, 35],\r\n orange: [255, 165, 0], orangered: [255, 69, 0], orchid: [218, 112, 214],\r\n palegoldenrod: [238, 232, 170], palegreen: [152, 251, 152], paleturquoise: [175, 238, 238],\r\n palevioletred: [219, 112, 147], papayawhip: [255, 239, 213], peachpuff: [255, 218, 185],\r\n peru: [205, 133, 63], pink: [255, 192, 203], plum: [221, 160, 221],\r\n powderblue: [176, 224, 230], purple: [128, 0, 128], rebeccapurple: [102, 51, 153],\r\n red: [255, 0, 0], rosybrown: [188, 143, 143], royalblue: [65, 105, 225],\r\n saddlebrown: [139, 69, 19], salmon: [250, 128, 114], sandybrown: [244, 164, 96],\r\n seagreen: [46, 139, 87], seashell: [255, 245, 238], sienna: [160, 82, 45],\r\n silver: [192, 192, 192], skyblue: [135, 206, 235], slateblue: [106, 90, 205],\r\n slategray: [112, 128, 144], slategrey: [112, 128, 144], snow: [255, 250, 250],\r\n springgreen: [0, 255, 127], steelblue: [70, 130, 180], tan: [210, 180, 140],\r\n teal: [0, 128, 128], thistle: [216, 191, 216], tomato: [255, 99, 71],\r\n turquoise: [64, 224, 208], violet: [238, 130, 238], wheat: [245, 222, 179],\r\n white: [255, 255, 255], whitesmoke: [245, 245, 245], yellow: [255, 255, 0],\r\n yellowgreen: [154, 205, 50],\r\n};\r\n\r\n/**\r\n * Parse a CSS color value to RGBA. Returns null for unresolvable values.\r\n */\r\nexport function parseColor(value: string): RGBA | null {\r\n if (!value) return null;\r\n const v = value.trim().toLowerCase();\r\n\r\n // Unresolvable\r\n if (v === \"inherit\" || v === \"currentcolor\" || v === \"initial\" || v === \"unset\" || v.startsWith(\"var(\")) {\r\n return null;\r\n }\r\n\r\n if (v === \"transparent\") {\r\n return { r: 0, g: 0, b: 0, a: 0 };\r\n }\r\n\r\n // Named colors\r\n const named = NAMED_COLORS[v];\r\n if (named) {\r\n return { r: named[0], g: named[1], b: named[2], a: 1 };\r\n }\r\n\r\n // Hex: #rgb, #rrggbb, #rrggbbaa\r\n if (v.startsWith(\"#\")) {\r\n const hex = v.slice(1);\r\n if (hex.length === 3) {\r\n return {\r\n r: parseInt(hex[0] + hex[0], 16),\r\n g: parseInt(hex[1] + hex[1], 16),\r\n b: parseInt(hex[2] + hex[2], 16),\r\n a: 1,\r\n };\r\n }\r\n if (hex.length === 6) {\r\n return {\r\n r: parseInt(hex.slice(0, 2), 16),\r\n g: parseInt(hex.slice(2, 4), 16),\r\n b: parseInt(hex.slice(4, 6), 16),\r\n a: 1,\r\n };\r\n }\r\n if (hex.length === 8) {\r\n return {\r\n r: parseInt(hex.slice(0, 2), 16),\r\n g: parseInt(hex.slice(2, 4), 16),\r\n b: parseInt(hex.slice(4, 6), 16),\r\n a: parseInt(hex.slice(6, 8), 16) / 255,\r\n };\r\n }\r\n return null;\r\n }\r\n\r\n // rgb() / rgba()\r\n const rgbMatch = v.match(/^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)$/);\r\n if (rgbMatch) {\r\n return {\r\n r: Math.min(255, parseInt(rgbMatch[1], 10)),\r\n g: Math.min(255, parseInt(rgbMatch[2], 10)),\r\n b: Math.min(255, parseInt(rgbMatch[3], 10)),\r\n a: rgbMatch[4] !== undefined ? Math.min(1, parseFloat(rgbMatch[4])) : 1,\r\n };\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Compute WCAG 2.1 relative luminance for an sRGB color.\r\n * Input values 0-255.\r\n */\r\nexport function relativeLuminance(r: number, g: number, b: number): number {\r\n const [rs, gs, bs] = [r, g, b].map((c) => {\r\n const s = c / 255;\r\n return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);\r\n });\r\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\r\n}\r\n\r\n/**\r\n * Compute the WCAG contrast ratio between two relative luminances.\r\n */\r\nexport function contrastRatio(l1: number, l2: number): number {\r\n const lighter = Math.max(l1, l2);\r\n const darker = Math.min(l1, l2);\r\n return (lighter + 0.05) / (darker + 0.05);\r\n}\r\n\r\n/**\r\n * Determine WCAG 2.1 conformance grade for a contrast ratio.\r\n */\r\nexport function wcagGrade(ratio: number): WcagGrade {\r\n if (ratio >= 7) return \"AAA\";\r\n if (ratio >= 4.5) return \"AA\";\r\n if (ratio >= 3) return \"AA Large\";\r\n return \"Fail\";\r\n}\r\n\r\n/**\r\n * Alpha-blend a foreground RGBA onto an opaque background (r, g, b 0-255).\r\n * Returns the flattened [r, g, b] as 0-255 values.\r\n */\r\nexport function alphaBlend(fg: RGBA, bgR: number, bgG: number, bgB: number): [number, number, number] {\r\n const a = fg.a;\r\n return [\r\n Math.round(fg.r * a + bgR * (1 - a)),\r\n Math.round(fg.g * a + bgG * (1 - a)),\r\n Math.round(fg.b * a + bgB * (1 - a)),\r\n ];\r\n}\r\n","import type { CSSWarning, DiffResult } from \"./types\";\r\nimport { EMAIL_CLIENTS } from \"./clients\";\r\n\r\n/**\r\n * Compare two sets of analysis results to show what improved,\r\n * what regressed, and what stayed the same.\r\n */\r\nexport function diffResults(\r\n before: {\r\n scores: Record<string, { score: number; errors: number; warnings: number; info: number }>;\r\n warnings: CSSWarning[];\r\n },\r\n after: {\r\n scores: Record<string, { score: number; errors: number; warnings: number; info: number }>;\r\n warnings: CSSWarning[];\r\n }\r\n): DiffResult[] {\r\n const results: DiffResult[] = [];\r\n\r\n for (const client of EMAIL_CLIENTS) {\r\n const scoreBefore = before.scores[client.id]?.score ?? 100;\r\n const scoreAfter = after.scores[client.id]?.score ?? 100;\r\n\r\n const beforeWarnings = before.warnings.filter((w) => w.client === client.id);\r\n const afterWarnings = after.warnings.filter((w) => w.client === client.id);\r\n\r\n // Key warnings by property+severity for comparison\r\n const beforeKeys = new Set(beforeWarnings.map(warningKey));\r\n const afterKeys = new Set(afterWarnings.map(warningKey));\r\n\r\n const fixed = beforeWarnings.filter((w) => !afterKeys.has(warningKey(w)));\r\n const introduced = afterWarnings.filter((w) => !beforeKeys.has(warningKey(w)));\r\n const unchanged = afterWarnings.filter((w) => beforeKeys.has(warningKey(w)));\r\n\r\n results.push({\r\n clientId: client.id,\r\n scoreBefore,\r\n scoreAfter,\r\n scoreDelta: scoreAfter - scoreBefore,\r\n fixed,\r\n introduced,\r\n unchanged,\r\n });\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction warningKey(w: CSSWarning): string {\r\n return `${w.property}:${w.severity}`;\r\n}\r\n","import type { CSSWarning } from \"./types\";\r\nimport { getClient, EMAIL_CLIENTS } from \"./clients\";\r\n\r\nexport type ExportScope = \"all\" | \"current\";\r\n\r\nexport interface ExportPromptOptions {\r\n originalHtml: string;\r\n warnings: CSSWarning[];\r\n scores: Record<\r\n string,\r\n { score: number; errors: number; warnings: number; info: number }\r\n >;\r\n scope: ExportScope;\r\n selectedClientId?: string;\r\n format?: \"html\" | \"jsx\" | \"mjml\" | \"maizzle\";\r\n}\r\n\r\nexport function generateFixPrompt(options: ExportPromptOptions): string {\r\n const {\r\n originalHtml,\r\n warnings,\r\n scores,\r\n scope,\r\n selectedClientId,\r\n format = \"html\",\r\n } = options;\r\n\r\n const filteredWarnings =\r\n scope === \"current\" && selectedClientId\r\n ? warnings.filter((w) => w.client === selectedClientId)\r\n : warnings;\r\n\r\n const filteredScores =\r\n scope === \"current\" && selectedClientId\r\n ? { [selectedClientId]: scores[selectedClientId] }\r\n : scores;\r\n\r\n const clientCount = Object.keys(filteredScores).length;\r\n const errorCount = filteredWarnings.filter(\r\n (w) => w.severity === \"error\"\r\n ).length;\r\n const warnCount = filteredWarnings.filter(\r\n (w) => w.severity === \"warning\"\r\n ).length;\r\n const infoCount = filteredWarnings.filter(\r\n (w) => w.severity === \"info\"\r\n ).length;\r\n\r\n const clientLabel =\r\n scope === \"current\" && selectedClientId\r\n ? getClient(selectedClientId)?.name ?? selectedClientId\r\n : `${clientCount} email clients`;\r\n\r\n const sections: string[] = [];\r\n\r\n // 1. Context\r\n sections.push(\r\n `# Email Compatibility Fix Request\\n\\n` +\r\n `- **Format:** ${format.toUpperCase()}\\n` +\r\n `- **Scope:** ${clientLabel}\\n` +\r\n `- **Issues found:** ${errorCount} error${errorCount !== 1 ? \"s\" : \"\"}, ` +\r\n `${warnCount} warning${warnCount !== 1 ? \"s\" : \"\"}, ` +\r\n `${infoCount} info`\r\n );\r\n\r\n // 2. Original email code\r\n sections.push(\r\n `## Original Email Code\\n\\n` + `\\`\\`\\`${format}\\n${originalHtml}\\n\\`\\`\\``\r\n );\r\n\r\n // 3. Compatibility scores table\r\n const scoreEntries = Object.entries(filteredScores).filter(\r\n ([, v]) => v != null\r\n );\r\n if (scoreEntries.length > 0) {\r\n let table = `## Compatibility Scores\\n\\n`;\r\n table += `| Client | Score | Errors | Warnings | Info |\\n`;\r\n table += `|--------|------:|-------:|---------:|-----:|\\n`;\r\n for (const [clientId, data] of scoreEntries) {\r\n const name = getClient(clientId)?.name ?? clientId;\r\n table += `| ${name} | ${data.score} | ${data.errors} | ${data.warnings} | ${data.info} |\\n`;\r\n }\r\n sections.push(table.trimEnd());\r\n }\r\n\r\n // 4. Detected issues grouped by severity\r\n if (filteredWarnings.length > 0) {\r\n let issueSection = `## Detected Issues\\n`;\r\n\r\n const groups: [string, CSSWarning[]][] = [\r\n [\"Errors\", filteredWarnings.filter((w) => w.severity === \"error\")],\r\n [\"Warnings\", filteredWarnings.filter((w) => w.severity === \"warning\")],\r\n [\"Info\", filteredWarnings.filter((w) => w.severity === \"info\")],\r\n ];\r\n\r\n for (const [label, group] of groups) {\r\n if (group.length === 0) continue;\r\n issueSection += `\\n### ${label}\\n`;\r\n\r\n for (const w of group) {\r\n const clientName = getClient(w.client)?.name ?? w.client;\r\n const fixTypeLabel = w.fixType === \"structural\" ? \" [STRUCTURAL]\" : \"\";\r\n issueSection += `\\n- **${w.property}** (${clientName})${fixTypeLabel}: ${w.message}`;\r\n if (w.fixType === \"structural\") {\r\n issueSection += `\\n - **Fix type: structural** — CSS-only changes will NOT work. HTML restructuring required.`;\r\n }\r\n if (w.suggestion) {\r\n issueSection += `\\n - Suggestion: ${w.suggestion}`;\r\n }\r\n if (w.fix) {\r\n const fixLabel = w.fixIsGenericFallback && format !== \"html\"\r\n ? `Fix (generic HTML — adapt to ${format.toUpperCase()} syntax)`\r\n : \"Fix\";\r\n issueSection += `\\n - ${fixLabel}:`;\r\n issueSection += `\\n - Before: \\`${w.fix.before}\\``;\r\n issueSection += `\\n - After: \\`${w.fix.after}\\``;\r\n }\r\n }\r\n }\r\n\r\n sections.push(issueSection);\r\n }\r\n\r\n // 5. Instructions\r\n const formatInstructions: Record<string, string> = {\r\n jsx:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return complete fixed JSX code using @react-email/components. ` +\r\n `Use Row, Column, Container, Font, Img, Head, and Link components from ` +\r\n `@react-email/components wherever the fix suggests them. ` +\r\n `Keep all style values as camelCase JavaScript object properties (e.g. { backgroundColor: \"#fff\" }). ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the JSX structure and style props needed to fix the issues.`,\r\n mjml:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return complete fixed MJML markup. ` +\r\n `Use MJML-native elements (mj-section, mj-column, mj-text, mj-button, mj-font, mj-style, mj-raw) ` +\r\n `as indicated in the fixes. ` +\r\n `Ensure the result is valid MJML that compiles without errors. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the MJML structure and attributes needed to fix the issues.`,\r\n maizzle:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return the complete fixed Maizzle template. ` +\r\n `Use Tailwind CSS utility classes and Maizzle config settings as indicated in the fixes. ` +\r\n `Add MSO conditional comment table wrappers where needed for Outlook compatibility. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the Tailwind classes and HTML structure needed to fix the issues.`,\r\n html:\r\n `Apply all the fixes listed above to the original email code. ` +\r\n `Return the complete fixed HTML code. ` +\r\n `Ensure the result is compatible with ${clientLabel}. ` +\r\n `Do not remove any content — only modify the CSS and HTML attributes needed to fix the issues.`,\r\n };\r\n\r\n sections.push(\r\n `## Instructions\\n\\n` + (formatInstructions[format] ?? formatInstructions.html)\r\n );\r\n\r\n return sections.join(\"\\n\\n\");\r\n}\r\n","import { generateFixPrompt } from \"./export-prompt\";\r\nimport type { ExportPromptOptions } from \"./export-prompt\";\r\nimport type { CSSWarning } from \"./types\";\r\n\r\n/**\r\n * Heuristic token estimate: ~3.5 characters per token for mixed\r\n * HTML/CSS/code content. This is conservative (slightly over-counts)\r\n * to avoid surprise overruns.\r\n *\r\n * For precise counts, use Anthropic's `messages.countTokens()` API\r\n * or pass a custom `tokenCounter` callback to `estimateAiFixTokens()`.\r\n */\r\nconst CHARS_PER_TOKEN = 3.5;\r\n\r\n/**\r\n * Rough output-to-input ratio. The AI returns a fixed version of the\r\n * email, which is typically similar in size to the input HTML plus\r\n * some overhead for VML/table wrappers.\r\n */\r\nconst OUTPUT_RATIO = 1.3;\r\n\r\n/**\r\n * Default token overhead for the AI_FIX_SYSTEM_PROMPT exported from ai-fix.ts.\r\n * The system prompt is ~800 chars ≈ ~230 tokens. We use 250 as a safe default.\r\n * Consumers using a custom system prompt can override via `systemPromptTokens`.\r\n */\r\nconst DEFAULT_SYSTEM_PROMPT_TOKENS = 250;\r\n\r\nexport interface TokenEstimate {\r\n /** Estimated input tokens (prompt + system prompt) */\r\n inputTokens: number;\r\n /** Estimated output tokens (fixed code response) */\r\n estimatedOutputTokens: number;\r\n /** Raw character count of the prompt */\r\n promptCharacters: number;\r\n /** Character count of just the HTML being fixed */\r\n htmlCharacters: number;\r\n /** Total warnings included in the prompt */\r\n warningCount: number;\r\n /** How many warnings are structural (need HTML changes) */\r\n structuralCount: number;\r\n /** Whether warnings were truncated to fit within limits */\r\n truncated: boolean;\r\n /** Number of warnings removed during truncation */\r\n warningsRemoved: number;\r\n}\r\n\r\n/**\r\n * Extended return type from `estimateAiFixTokens()` that includes both the\r\n * token metrics AND the (potentially truncated) warnings list. The `warnings`\r\n * field is used internally by `generateAiFix()` to build the prompt with the\r\n * truncated set, but is NOT exposed in `AiFixResult.tokenEstimate` to keep\r\n * the public API clean.\r\n */\r\nexport interface TokenEstimateWithWarnings extends TokenEstimate {\r\n /** The warnings after smart truncation (may be shorter than the input list) */\r\n warnings: CSSWarning[];\r\n}\r\n\r\nexport interface EstimateOptions extends Omit<ExportPromptOptions, \"warnings\"> {\r\n warnings: CSSWarning[];\r\n /**\r\n * Maximum input tokens to target. If the estimated prompt exceeds\r\n * this, warnings will be truncated (info first, then duplicates).\r\n * Defaults to 16000 (~56KB of prompt text).\r\n */\r\n maxInputTokens?: number;\r\n /**\r\n * Optional precise token counter. If provided, it will be called\r\n * with the final prompt text for an exact count. Consumers can wire\r\n * this to `anthropic.messages.countTokens()`.\r\n */\r\n tokenCounter?: (text: string) => Promise<number> | number;\r\n /**\r\n * Token count for the system prompt. Added to the input token estimate\r\n * since the system prompt counts against the context window. Defaults\r\n * to 250 (matching the built-in AI_FIX_SYSTEM_PROMPT). Set to 0 if\r\n * not using a system prompt, or override for custom system prompts.\r\n */\r\n systemPromptTokens?: number;\r\n}\r\n\r\n/**\r\n * Estimate tokens for an AI fix prompt BEFORE making the API call.\r\n * Use this to show cost estimates, check limits, and decide whether\r\n * to proceed.\r\n *\r\n * @example\r\n * ```ts\r\n * const estimate = await estimateAiFixTokens({\r\n * originalHtml: html,\r\n * warnings,\r\n * scores,\r\n * scope: \"all\",\r\n * format: \"jsx\",\r\n * });\r\n *\r\n * console.log(`~${estimate.inputTokens} input tokens`);\r\n * console.log(`~${estimate.estimatedOutputTokens} output tokens`);\r\n * console.log(`Truncated: ${estimate.truncated}`);\r\n * ```\r\n */\r\nexport async function estimateAiFixTokens(\r\n options: EstimateOptions,\r\n): Promise<TokenEstimateWithWarnings> {\r\n const {\r\n maxInputTokens = 16000,\r\n tokenCounter,\r\n systemPromptTokens = DEFAULT_SYSTEM_PROMPT_TOKENS,\r\n ...rest\r\n } = options;\r\n\r\n // Reserve space for the system prompt when truncating\r\n const effectiveMaxTokens = maxInputTokens - systemPromptTokens;\r\n\r\n // Truncate warnings if needed to stay within token limits\r\n const { warnings: finalWarnings, truncated, removed } = truncateWarnings(\r\n rest.warnings,\r\n rest.originalHtml,\r\n effectiveMaxTokens,\r\n rest.scope,\r\n rest.selectedClientId,\r\n );\r\n\r\n const prompt = generateFixPrompt({ ...rest, warnings: finalWarnings });\r\n const promptChars = prompt.length;\r\n\r\n // Use precise counter if available, otherwise heuristic\r\n let promptTokens: number;\r\n if (tokenCounter) {\r\n const count = tokenCounter(prompt);\r\n promptTokens = count instanceof Promise ? await count : count;\r\n } else {\r\n promptTokens = heuristicTokenCount(prompt);\r\n }\r\n\r\n // Total input = user prompt + system prompt\r\n const inputTokens = promptTokens + systemPromptTokens;\r\n\r\n const estimatedOutputTokens = Math.ceil(\r\n heuristicTokenCount(rest.originalHtml) * OUTPUT_RATIO,\r\n );\r\n\r\n const structuralCount = finalWarnings.filter(\r\n (w) => w.fixType === \"structural\",\r\n ).length;\r\n\r\n return {\r\n inputTokens,\r\n estimatedOutputTokens,\r\n promptCharacters: promptChars,\r\n htmlCharacters: rest.originalHtml.length,\r\n warningCount: finalWarnings.length,\r\n structuralCount,\r\n truncated,\r\n warningsRemoved: removed,\r\n warnings: finalWarnings,\r\n };\r\n}\r\n\r\n/**\r\n * Quick synchronous heuristic token count. No deps, no API calls.\r\n * Accuracy: within ~10-15% of real Claude tokenizer for code/HTML.\r\n */\r\nexport function heuristicTokenCount(text: string): number {\r\n return Math.ceil(text.length / CHARS_PER_TOKEN);\r\n}\r\n\r\n/**\r\n * Intelligently trim warnings to keep the prompt within token limits.\r\n *\r\n * Truncation priority (least important removed first):\r\n * 1. Remove duplicate warnings (same property across different clients)\r\n * 2. Remove `info`-level warnings\r\n * 3. Remove `warning`-level warnings with CSS-only fixes (not structural)\r\n * 4. Trim long fix snippet before/after strings\r\n */\r\nfunction truncateWarnings(\r\n warnings: CSSWarning[],\r\n html: string,\r\n maxTokens: number,\r\n scope: string,\r\n selectedClientId?: string,\r\n): { warnings: CSSWarning[]; truncated: boolean; removed: number } {\r\n const originalCount = warnings.length;\r\n\r\n // Quick check: will the full prompt fit?\r\n const fullPromptEstimate = heuristicTokenCount(html) + heuristicTokenCount(\r\n JSON.stringify(warnings),\r\n ) + 500; // overhead for markdown structure\r\n\r\n if (fullPromptEstimate <= maxTokens) {\r\n return { warnings, truncated: false, removed: 0 };\r\n }\r\n\r\n let result = [...warnings];\r\n\r\n // Step 1: Deduplicate — keep one warning per property per severity,\r\n // preferring the most relevant client\r\n const seen = new Set<string>();\r\n result = result.filter((w) => {\r\n const key = `${w.property}:${w.severity}`;\r\n if (seen.has(key)) return false;\r\n seen.add(key);\r\n return true;\r\n });\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 2: Remove info-level warnings\r\n result = result.filter((w) => w.severity !== \"info\");\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 3: Remove CSS-only warnings (keep structural ones)\r\n result = result.filter((w) => w.fixType === \"structural\" || w.severity === \"error\");\r\n\r\n if (estimateFits(result, html, maxTokens)) {\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n }\r\n\r\n // Step 4: Trim fix snippets to just descriptions\r\n result = result.map((w) => ({\r\n ...w,\r\n fix: w.fix\r\n ? {\r\n ...w.fix,\r\n before: w.fix.before.length > 200\r\n ? w.fix.before.slice(0, 200) + \"\\n/* ... truncated ... */\"\r\n : w.fix.before,\r\n after: w.fix.after.length > 200\r\n ? w.fix.after.slice(0, 200) + \"\\n/* ... truncated ... */\"\r\n : w.fix.after,\r\n }\r\n : undefined,\r\n }));\r\n\r\n return { warnings: result, truncated: true, removed: originalCount - result.length };\r\n}\r\n\r\nfunction estimateFits(\r\n warnings: CSSWarning[],\r\n html: string,\r\n maxTokens: number,\r\n): boolean {\r\n const estimate =\r\n heuristicTokenCount(html) +\r\n heuristicTokenCount(JSON.stringify(warnings)) +\r\n 500;\r\n return estimate <= maxTokens;\r\n}\r\n","import { generateFixPrompt } from \"./export-prompt\";\r\nimport type { ExportPromptOptions } from \"./export-prompt\";\r\nimport type { AiProvider, AiFixResult, CSSWarning } from \"./types\";\r\nimport { estimateAiFixTokens } from \"./token-utils\";\r\n\r\nexport interface GenerateAiFixOptions extends ExportPromptOptions {\r\n /** Callback that sends a prompt to an LLM and returns the response text. */\r\n provider: AiProvider;\r\n /**\r\n * Maximum input tokens for the prompt. If the estimated prompt exceeds\r\n * this, warnings are intelligently truncated (info first, then CSS-only\r\n * duplicates). Defaults to 16000.\r\n */\r\n maxInputTokens?: number;\r\n}\r\n\r\n/**\r\n * Generate an AI-powered fix for email compatibility issues.\r\n *\r\n * This uses the deterministic engine's analysis (warnings, scores, fix snippets)\r\n * to build a structured prompt, then delegates to an LLM for context-aware\r\n * structural fixes that static snippets cannot handle.\r\n *\r\n * The engine stays provider-agnostic — consumers pass their own `AiProvider`\r\n * callback (Anthropic SDK, Vercel AI SDK, OpenRouter, etc.).\r\n *\r\n * @example\r\n * ```ts\r\n * import Anthropic from \"@anthropic-ai/sdk\";\r\n * import { analyzeEmail, generateCompatibilityScore, generateAiFix } from \"@emailens/engine\";\r\n *\r\n * const anthropic = new Anthropic();\r\n * const warnings = analyzeEmail(html, \"jsx\");\r\n * const scores = generateCompatibilityScore(warnings);\r\n *\r\n * // 1. Check cost before calling\r\n * const estimate = await estimateAiFixTokens({\r\n * originalHtml: html, warnings, scores, scope: \"all\", format: \"jsx\",\r\n * });\r\n * console.log(`~${estimate.inputTokens} input tokens`);\r\n *\r\n * // 2. Generate the fix\r\n * const result = await generateAiFix({\r\n * originalHtml: html, warnings, scores, scope: \"all\", format: \"jsx\",\r\n * provider: async (prompt) => {\r\n * const msg = await anthropic.messages.create({\r\n * model: \"claude-sonnet-4-6\",\r\n * max_tokens: 8192,\r\n * system: AI_FIX_SYSTEM_PROMPT,\r\n * messages: [{ role: \"user\", content: prompt }],\r\n * });\r\n * return msg.content[0].type === \"text\" ? msg.content[0].text : \"\";\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport async function generateAiFix(\r\n options: GenerateAiFixOptions,\r\n): Promise<AiFixResult> {\r\n const { provider, maxInputTokens = 16000, ...promptOptions } = options;\r\n\r\n // Estimate tokens and apply smart truncation if needed\r\n const estimate = await estimateAiFixTokens({\r\n ...promptOptions,\r\n maxInputTokens,\r\n });\r\n\r\n // Use the truncated warnings from the estimate (not the original list)\r\n const truncatedWarnings = estimate.warnings;\r\n\r\n // Build the final prompt with the truncated warnings\r\n const prompt = generateFixPrompt({ ...promptOptions, warnings: truncatedWarnings });\r\n\r\n const structuralCount = countStructuralWarnings(\r\n truncatedWarnings,\r\n promptOptions.scope,\r\n promptOptions.selectedClientId,\r\n );\r\n\r\n // Strip the warnings list from the estimate to keep the public API clean\r\n const { warnings: _discarded, ...tokenEstimate } = estimate;\r\n\r\n const response = await provider(prompt);\r\n\r\n // Extract code from the response — the LLM may wrap it in a code fence\r\n const code = extractCode(response);\r\n\r\n return {\r\n code,\r\n prompt,\r\n targetedWarnings: tokenEstimate.warningCount,\r\n structuralCount,\r\n tokenEstimate,\r\n };\r\n}\r\n\r\n/**\r\n * System prompt for the AI fix provider. Consumers should pass this as\r\n * the `system` parameter to their LLM call for best results.\r\n */\r\nexport const AI_FIX_SYSTEM_PROMPT = `You are an expert email developer specializing in cross-client HTML email compatibility. You fix emails to render correctly across all email clients.\r\n\r\nRules:\r\n- Return ONLY the fixed code inside a single code fence. No explanations before or after.\r\n- Preserve all existing content, text, links, and visual design.\r\n- For structural issues (fixType: \"structural\"), you MUST restructure the HTML — CSS-only changes will not work.\r\n- Common structural patterns:\r\n - word-break/overflow-wrap unsupported → wrap text in <table><tr><td> with constrained width\r\n - display:flex/grid → convert to <table> layout (match the original column count and proportions)\r\n - border-radius in Outlook → use VML <v:roundrect> with <!--[if mso]> conditionals\r\n - background-image in Outlook → use VML <v:rect> with <v:fill>\r\n - max-width in Outlook → wrap in <!--[if mso]><table width=\"N\"> conditional\r\n - position:absolute → use <table> cells for layout\r\n - <svg> → replace with <img> pointing to a hosted PNG\r\n- For CSS-only issues (fixType: \"css\"), swap properties or add fallbacks.\r\n- Apply ALL fixes from the issues list — do not skip any.\r\n- Use the framework syntax specified (JSX/MJML/Maizzle/HTML).\r\n- For JSX: use camelCase style props, React Email components, and proper TypeScript types.\r\n- For MJML: use mj-* elements and attributes.\r\n- For Maizzle: use Tailwind CSS classes.`;\r\n\r\nfunction countStructuralWarnings(\r\n warnings: CSSWarning[],\r\n scope: string,\r\n selectedClientId?: string,\r\n): number {\r\n const filtered =\r\n scope === \"current\" && selectedClientId\r\n ? warnings.filter((w) => w.client === selectedClientId)\r\n : warnings;\r\n return filtered.filter((w) => w.fixType === \"structural\").length;\r\n}\r\n\r\n/**\r\n * Extract code from an LLM response that may contain markdown code fences.\r\n * If multiple fences exist, returns the largest one (most likely the full\r\n * fixed email rather than a small snippet).\r\n */\r\nfunction extractCode(response: string): string {\r\n // Find all code fences and pick the largest one\r\n const fencePattern = /```(?:[\\w]*)\\n([\\s\\S]*?)```/g;\r\n let largest: string | null = null;\r\n\r\n let match: RegExpExecArray | null;\r\n while ((match = fencePattern.exec(response)) !== null) {\r\n const content = match[1].trim();\r\n if (largest === null || content.length > largest.length) {\r\n largest = content;\r\n }\r\n }\r\n\r\n if (largest !== null) {\r\n return largest;\r\n }\r\n\r\n // If no code fence, return the whole response trimmed\r\n return response.trim();\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\nimport type { SpamAnalysisOptions, SpamIssue, SpamReport } from \"./types\";\r\n\r\nconst SPAM_TRIGGER_PHRASES = [\r\n \"act now\", \"limited time\", \"click here\", \"buy now\", \"order now\",\r\n \"don't miss\", \"don't delete\", \"urgent\", \"congratulations\",\r\n \"you've been selected\", \"you've won\", \"winner\", \"free gift\",\r\n \"risk free\", \"no obligation\", \"no cost\", \"no fees\",\r\n \"100% free\", \"100% satisfied\", \"double your money\",\r\n \"earn extra cash\", \"make money\", \"cash bonus\",\r\n \"as seen on\", \"incredible deal\", \"lowest price\",\r\n \"once in a lifetime\", \"special promotion\", \"this isn't spam\",\r\n \"what are you waiting for\", \"apply now\", \"sign up free\",\r\n \"cancel anytime\", \"no strings attached\", \"no questions asked\",\r\n];\r\n\r\n// Fix #6: Pre-compiled word-boundary regexes for spam phrases\r\nfunction escapeRegex(s: string): string {\r\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\nconst SPAM_PHRASE_PATTERNS: Map<string, RegExp> = new Map(\r\n SPAM_TRIGGER_PHRASES.map((phrase) => [\r\n phrase,\r\n new RegExp(\"\\\\b\" + escapeRegex(phrase) + \"\\\\b\"),\r\n ]),\r\n);\r\n\r\nconst URL_SHORTENERS = [\r\n \"bit.ly\", \"tinyurl.com\", \"t.co\", \"goo.gl\", \"ow.ly\",\r\n \"is.gd\", \"buff.ly\", \"rebrand.ly\", \"bl.ink\", \"short.io\",\r\n \"cutt.ly\", \"rb.gy\",\r\n];\r\n\r\n// Fix #2: Known ESP tracking domains\r\nconst ESP_TRACKING_DOMAINS = [\r\n \"mailchi.mp\", \"list-manage.com\", \"click.mailchimp.com\",\r\n \"sendgrid.net\", \"click.sendgrid.net\", \"ct.sendgrid.net\",\r\n \"click.klaviyomail.com\", \"trk.klaviyo.com\",\r\n \"click.hubspotemail.net\",\r\n \"links.iterable.com\", \"track.customer.io\",\r\n \"go.pardot.com\", \"mailgun.org\",\r\n \"em.salesforce.com\", \"click.marketingcloud.com\",\r\n \"r.mail.yahoo.com\", \"t.dripemail2.com\",\r\n];\r\n\r\n// Fix #4: Transactional email signal phrases\r\nconst TRANSACTIONAL_SIGNALS = [\r\n \"reset your password\", \"password reset\",\r\n \"verify your email\", \"email verification\",\r\n \"order confirmation\", \"your order\",\r\n \"your receipt\", \"purchase confirmation\",\r\n \"verification code\", \"confirm your account\",\r\n \"your invoice\", \"shipping confirmation\",\r\n \"account activation\", \"security alert\",\r\n];\r\n\r\nconst TRANSACTIONAL_SIGNAL_PATTERN = new RegExp(\r\n TRANSACTIONAL_SIGNALS.map(escapeRegex).join(\"|\"),\r\n \"i\",\r\n);\r\n\r\n// OTP-like pattern: standalone 4-8 digit codes\r\nconst OTP_PATTERN = /\\b\\d{4,8}\\b/;\r\n\r\nconst WEIGHTS: Record<string, number> = {\r\n \"caps-ratio\": 15,\r\n \"excessive-punctuation\": 10,\r\n \"spam-phrases\": 5,\r\n \"missing-unsubscribe\": 15,\r\n \"hidden-text\": 20,\r\n \"url-shortener\": 10,\r\n \"image-only\": 20,\r\n \"high-image-ratio\": 10,\r\n \"deceptive-link\": 15,\r\n \"all-caps-subject\": 10,\r\n};\r\n\r\nfunction extractVisibleText($: cheerio.CheerioAPI): string {\r\n const clone = $.root().clone();\r\n clone.find(\"script, style, head\").remove();\r\n return clone.text().replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nfunction checkCapsRatio(text: string): SpamIssue | null {\r\n const words = text.split(/\\s+/).filter((w) => w.length >= 3);\r\n if (words.length < 5) return null;\r\n\r\n const capsWords = words.filter((w) => w === w.toUpperCase() && /[A-Z]/.test(w));\r\n const ratio = capsWords.length / words.length;\r\n\r\n if (ratio > 0.2) {\r\n return {\r\n rule: \"caps-ratio\",\r\n severity: \"warning\",\r\n message: `${Math.round(ratio * 100)}% of words are ALL CAPS — spam filters flag excessive capitalization.`,\r\n detail: `Found ${capsWords.length} of ${words.length} words in all caps.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #5: Scale punctuation threshold by text length, exclude $digit patterns\r\nfunction checkExcessivePunctuation(text: string): SpamIssue | null {\r\n const exclamations = (text.match(/!/g) || []).length;\r\n // Only count $ NOT followed by a digit (skip price patterns like $29)\r\n const dollars = (text.match(/\\$(?!\\d)/g) || []).length;\r\n const total = exclamations + dollars;\r\n\r\n const threshold = Math.max(5, Math.floor(text.length / 200));\r\n\r\n if (total > threshold) {\r\n return {\r\n rule: \"excessive-punctuation\",\r\n severity: \"warning\",\r\n message: `Excessive special characters detected (${exclamations} \"!\", ${dollars} \"$\") — common spam trigger.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #6: Word-boundary matching for spam phrases\r\nfunction checkSpamPhrases(text: string): SpamIssue[] {\r\n const lower = text.toLowerCase();\r\n const found: SpamIssue[] = [];\r\n\r\n for (const [phrase, pattern] of SPAM_PHRASE_PATTERNS) {\r\n if (pattern.test(lower)) {\r\n found.push({\r\n rule: \"spam-phrases\",\r\n severity: \"info\",\r\n message: `Contains spam trigger phrase: \"${phrase}\"`,\r\n });\r\n }\r\n }\r\n return found;\r\n}\r\n\r\n// Fix #4: Transactional exemption + options API for unsubscribe\r\nfunction checkUnsubscribe(\r\n $: cheerio.CheerioAPI,\r\n text: string,\r\n options?: SpamAnalysisOptions,\r\n): SpamIssue | null {\r\n // Explicit transactional type → skip entirely\r\n if (options?.emailType === \"transactional\") return null;\r\n\r\n // List-Unsubscribe header provided → satisfied\r\n if (options?.listUnsubscribeHeader?.trim()) return null;\r\n\r\n let hasUnsubscribe = false;\r\n\r\n $(\"a\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const linkText = $(el).text().toLowerCase();\r\n if (\r\n linkText.includes(\"unsubscribe\") ||\r\n href.toLowerCase().includes(\"unsubscribe\") ||\r\n linkText.includes(\"opt out\") ||\r\n linkText.includes(\"opt-out\") ||\r\n href.toLowerCase().includes(\"opt-out\") ||\r\n href.toLowerCase().includes(\"optout\")\r\n ) {\r\n hasUnsubscribe = true;\r\n }\r\n });\r\n\r\n if (!hasUnsubscribe) {\r\n // Auto-detect transactional signals\r\n const lower = text.toLowerCase();\r\n const signalMatches = TRANSACTIONAL_SIGNALS.filter((s) =>\r\n lower.includes(s.toLowerCase()),\r\n );\r\n const hasOtp = OTP_PATTERN.test(text);\r\n const signalCount = signalMatches.length + (hasOtp ? 1 : 0);\r\n\r\n if (signalCount >= 2) {\r\n // Looks transactional — downgrade to info instead of error\r\n return {\r\n rule: \"missing-unsubscribe\",\r\n severity: \"info\",\r\n message:\r\n \"No unsubscribe link found, but email appears transactional — may not be required.\",\r\n detail: `Detected transactional signals: ${signalMatches.join(\", \")}${hasOtp ? \", OTP code\" : \"\"}`,\r\n };\r\n }\r\n\r\n return {\r\n rule: \"missing-unsubscribe\",\r\n severity: \"error\",\r\n message:\r\n \"No unsubscribe link found — required by CAN-SPAM and GDPR. Most spam filters penalize this.\",\r\n detail: 'Add an <a> link with \"unsubscribe\" text or href.',\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #1: Preheader exemption for hidden text\r\nconst PREHEADER_ACCESSORY_PATTERNS = [\r\n /max-height\\s*:\\s*0/,\r\n /overflow\\s*:\\s*hidden/,\r\n /mso-hide\\s*:\\s*all/,\r\n /opacity\\s*:\\s*0/,\r\n /color\\s*:\\s*transparent/,\r\n /line-height\\s*:\\s*0/,\r\n];\r\n\r\nfunction isLikelyPreheader(\r\n style: string,\r\n text: string,\r\n): boolean {\r\n if (text.length > 200) return false;\r\n\r\n // Exempt only when preheader-specific accessory patterns are present\r\n return PREHEADER_ACCESSORY_PATTERNS.some((p) => p.test(style));\r\n}\r\n\r\nfunction checkHiddenText($: cheerio.CheerioAPI): SpamIssue | null {\r\n let found = false;\r\n let detail = \"\";\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = ($(el).attr(\"style\") || \"\").toLowerCase();\r\n const text = $(el).text().trim();\r\n if (!text) return;\r\n\r\n // visibility:hidden is always flagged — no legitimate preheader use\r\n if (/visibility\\s*:\\s*hidden/.test(style)) {\r\n found = true;\r\n detail = \"visibility:hidden on element with text content\";\r\n return false;\r\n }\r\n\r\n if (/font-size\\s*:\\s*0(?:px|em|rem|pt)?(?:\\s|;|$)/.test(style)) {\r\n if (!isLikelyPreheader(style, text)) {\r\n found = true;\r\n detail = \"font-size:0 on element with text content\";\r\n return false;\r\n }\r\n }\r\n\r\n if (/display\\s*:\\s*none/.test(style)) {\r\n if (!isLikelyPreheader(style, text)) {\r\n found = true;\r\n detail = \"display:none on element with text content\";\r\n return false;\r\n }\r\n }\r\n });\r\n\r\n if (found) {\r\n return {\r\n rule: \"hidden-text\",\r\n severity: \"error\",\r\n message: \"Hidden text detected — major spam filter red flag.\",\r\n detail,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #3: Hostname matching instead of substring for URL shorteners\r\nfunction checkUrlShorteners($: cheerio.CheerioAPI): SpamIssue[] {\r\n const issues: SpamIssue[] = [];\r\n const seen = new Set<string>();\r\n\r\n $(\"a[href]\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n let hostname: string;\r\n try {\r\n hostname = new URL(href).hostname.toLowerCase();\r\n } catch {\r\n return; // malformed URL, skip\r\n }\r\n\r\n for (const shortener of URL_SHORTENERS) {\r\n if (\r\n (hostname === shortener || hostname.endsWith(\".\" + shortener)) &&\r\n !seen.has(shortener)\r\n ) {\r\n seen.add(shortener);\r\n issues.push({\r\n rule: \"url-shortener\",\r\n severity: \"warning\",\r\n message: `URL shortener detected (${shortener}) — spam filters distrust shortened links.`,\r\n detail: href,\r\n });\r\n }\r\n }\r\n });\r\n return issues;\r\n}\r\n\r\n// Fix #7: Accept pre-extracted text to avoid duplicate extractVisibleText call\r\nfunction checkImageToTextRatio(\r\n $: cheerio.CheerioAPI,\r\n text: string,\r\n): SpamIssue | null {\r\n const images = $(\"img\").length;\r\n if (images === 0) return null;\r\n\r\n if (text.length < 50 && images > 0) {\r\n return {\r\n rule: \"image-only\",\r\n severity: \"error\",\r\n message: `Image-heavy email with almost no text (${text.length} chars, ${images} images) — likely to be flagged as spam or clipped.`,\r\n };\r\n }\r\n\r\n const ratio = images / (text.length / 100);\r\n if (ratio > 0.5 && images > 3) {\r\n return {\r\n rule: \"high-image-ratio\",\r\n severity: \"warning\",\r\n message: `High image-to-text ratio (${images} images for ${text.length} chars of text) — consider adding more text content.`,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Fix #2: ESP tracking domain allowlist + encoded destination heuristic\r\nfunction isEspTrackingDomain(hostname: string): boolean {\r\n return ESP_TRACKING_DOMAINS.some(\r\n (esp) => hostname === esp || hostname.endsWith(\".\" + esp),\r\n );\r\n}\r\n\r\nfunction hasEncodedDestination(href: string, textDomain: string): boolean {\r\n // Check if the href path/query contains the text domain URL-encoded\r\n const encoded = encodeURIComponent(textDomain);\r\n return href.includes(encoded) || href.includes(textDomain);\r\n}\r\n\r\nfunction checkDeceptiveLinks($: cheerio.CheerioAPI): SpamIssue[] {\r\n const issues: SpamIssue[] = [];\r\n\r\n $(\"a[href]\").each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const text = $(el).text().trim();\r\n\r\n if (/^https?:\\/\\/\\S+/i.test(text) || /^www\\.\\S+/i.test(text)) {\r\n try {\r\n const textDomain = new URL(\r\n text.startsWith(\"www.\") ? `https://${text}` : text,\r\n ).hostname.replace(/^www\\./, \"\");\r\n const hrefDomain = new URL(href).hostname.replace(/^www\\./, \"\");\r\n\r\n if (textDomain !== hrefDomain) {\r\n // Skip known ESP tracking domains\r\n if (isEspTrackingDomain(hrefDomain)) return;\r\n\r\n // Skip if href contains encoded destination (redirect wrapper)\r\n if (hasEncodedDestination(href, textDomain)) return;\r\n\r\n issues.push({\r\n rule: \"deceptive-link\",\r\n severity: \"error\",\r\n message: `Link text shows \"${textDomain}\" but links to \"${hrefDomain}\" — phishing red flag.`,\r\n detail: `Text: ${text}\\nHref: ${href}`,\r\n });\r\n }\r\n } catch {\r\n // Malformed URL, skip\r\n }\r\n }\r\n });\r\n return issues;\r\n}\r\n\r\nfunction checkAllCapsTitle($: cheerio.CheerioAPI): SpamIssue | null {\r\n const title = $(\"title\").text().trim();\r\n if (title.length > 5 && title === title.toUpperCase() && /[A-Z]/.test(title)) {\r\n return {\r\n rule: \"all-caps-subject\",\r\n severity: \"warning\",\r\n message: \"Email title/subject is ALL CAPS — common spam indicator.\",\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Analyze an HTML email for spam indicators.\r\n *\r\n * Returns a 0-100 score (100 = clean, 0 = very spammy) and an array\r\n * of issues found. Uses heuristic rules modeled after common spam\r\n * filter triggers (CAN-SPAM, GDPR, SpamAssassin patterns).\r\n */\r\nexport function analyzeSpam(\r\n html: string,\r\n options?: SpamAnalysisOptions,\r\n): SpamReport {\r\n if (!html || !html.trim()) {\r\n return { score: 100, level: \"low\", issues: [] };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const text = extractVisibleText($);\r\n const issues: SpamIssue[] = [];\r\n\r\n const capsIssue = checkCapsRatio(text);\r\n if (capsIssue) issues.push(capsIssue);\r\n\r\n const punctIssue = checkExcessivePunctuation(text);\r\n if (punctIssue) issues.push(punctIssue);\r\n\r\n issues.push(...checkSpamPhrases(text));\r\n\r\n const unsubIssue = checkUnsubscribe($, text, options);\r\n if (unsubIssue) issues.push(unsubIssue);\r\n\r\n const hiddenIssue = checkHiddenText($);\r\n if (hiddenIssue) issues.push(hiddenIssue);\r\n\r\n issues.push(...checkUrlShorteners($));\r\n\r\n // Fix #7: pass pre-extracted text instead of calling extractVisibleText again\r\n const imageRatioIssue = checkImageToTextRatio($, text);\r\n if (imageRatioIssue) issues.push(imageRatioIssue);\r\n\r\n issues.push(...checkDeceptiveLinks($));\r\n\r\n const capsTitle = checkAllCapsTitle($);\r\n if (capsTitle) issues.push(capsTitle);\r\n\r\n // Calculate score\r\n let penalty = 0;\r\n const seenRules = new Map<string, number>();\r\n\r\n for (const issue of issues) {\r\n const count = (seenRules.get(issue.rule) || 0) + 1;\r\n seenRules.set(issue.rule, count);\r\n const weight = WEIGHTS[issue.rule] || 5;\r\n\r\n if (issue.rule === \"spam-phrases\") {\r\n if (count <= 5) penalty += weight;\r\n } else if (issue.rule === \"url-shortener\" || issue.rule === \"deceptive-link\") {\r\n if (count <= 2) penalty += weight;\r\n } else {\r\n // info-level issues don't penalize score\r\n if (issue.severity !== \"info\") {\r\n penalty += weight;\r\n }\r\n }\r\n }\r\n\r\n const score = Math.max(0, Math.min(100, 100 - penalty));\r\n const level: SpamReport[\"level\"] =\r\n score >= 70 ? \"low\" : score >= 40 ? \"medium\" : \"high\";\r\n\r\n return { score, level, issues };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { LinkIssue, LinkReport } from \"./types\";\r\nimport { GENERIC_LINK_TEXT, MAX_HTML_SIZE } from \"./constants\";\r\n\r\nfunction classifyHref(href: string): string {\r\n if (!href || !href.trim()) return \"empty\";\r\n const h = href.trim().toLowerCase();\r\n if (h.startsWith(\"https://\")) return \"https\";\r\n if (h.startsWith(\"http://\")) return \"http\";\r\n if (h.startsWith(\"mailto:\")) return \"mailto\";\r\n if (h.startsWith(\"tel:\")) return \"tel\";\r\n if (h.startsWith(\"#\")) return \"anchor\";\r\n if (h.startsWith(\"javascript:\")) return \"javascript\";\r\n if (h.startsWith(\"//\")) return \"protocol-relative\";\r\n return \"other\";\r\n}\r\n\r\nfunction isPlaceholderHref(href: string): boolean {\r\n const h = href.trim().toLowerCase();\r\n return h === \"#\" || h === \"\" || h === \"javascript:void(0)\" || h === \"javascript:;\";\r\n}\r\n\r\n/**\r\n * Extract and validate all links from an HTML email.\r\n *\r\n * Performs static analysis only (no network requests). Checks for\r\n * empty/placeholder hrefs, javascript: protocol, insecure HTTP,\r\n * generic link text, accessibility issues, and more.\r\n */\r\nexport function validateLinks(html: string): LinkReport {\r\n if (!html || !html.trim()) {\r\n return {\r\n totalLinks: 0,\r\n issues: [],\r\n breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 },\r\n };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: LinkIssue[] = [];\r\n const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 };\r\n\r\n const links = $(\"a\");\r\n const totalLinks = links.length;\r\n\r\n if (totalLinks === 0) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"no-links\",\r\n message: \"Email contains no links\",\r\n });\r\n return { totalLinks: 0, issues, breakdown };\r\n }\r\n\r\n const hrefCounts = new Map<string, number>();\r\n\r\n links.each((_, el) => {\r\n const href = $(el).attr(\"href\") || \"\";\r\n const text = $(el).text().trim();\r\n const category = classifyHref(href);\r\n\r\n // Count breakdown\r\n switch (category) {\r\n case \"https\": breakdown.https++; break;\r\n case \"http\": breakdown.http++; break;\r\n case \"mailto\": breakdown.mailto++; break;\r\n case \"tel\": breakdown.tel++; break;\r\n case \"anchor\": breakdown.anchor++; break;\r\n case \"javascript\": breakdown.javascript++; break;\r\n case \"protocol-relative\": breakdown.protocolRelative++; break;\r\n default: breakdown.other++; break;\r\n }\r\n\r\n // Track href occurrences for duplicate detection\r\n if (href && href.trim()) {\r\n hrefCounts.set(href, (hrefCounts.get(href) || 0) + 1);\r\n }\r\n\r\n // Empty or missing href\r\n if (!href || !href.trim()) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-href\",\r\n message: \"Link has no href attribute\",\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // javascript: protocol (not placeholder)\r\n if (category === \"javascript\" && !isPlaceholderHref(href)) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"javascript-href\",\r\n message: \"Link uses javascript: protocol\",\r\n href: href.slice(0, 100),\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // Placeholder href\r\n if (isPlaceholderHref(href)) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"placeholder-href\",\r\n message: \"Link has a placeholder href (# or javascript:void)\",\r\n href,\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n return;\r\n }\r\n\r\n // HTTP instead of HTTPS\r\n if (category === \"http\") {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"insecure-link\",\r\n message: \"Link uses HTTP instead of HTTPS\",\r\n href: href.slice(0, 120),\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // Protocol-relative URL\r\n if (category === \"protocol-relative\") {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"protocol-relative\",\r\n message: \"Protocol-relative URL may break in email clients — use https:// explicitly\",\r\n href: href.slice(0, 120),\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // Generic link text\r\n if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"generic-link-text\",\r\n message: `Link text \"${text}\" is vague — use descriptive text for accessibility and engagement`,\r\n href: href.slice(0, 120),\r\n text,\r\n });\r\n }\r\n\r\n // Link with no text and no aria-label\r\n if (!text && !$(el).attr(\"aria-label\") && !$(el).find(\"img[alt]\").length) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-link-text\",\r\n message: \"Link has no visible text or aria-label\",\r\n href: href.slice(0, 120),\r\n });\r\n }\r\n\r\n // mailto without address\r\n if (category === \"mailto\" && href.trim().toLowerCase() === \"mailto:\") {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-mailto\",\r\n message: \"mailto: link has no email address\",\r\n href,\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // tel: without number\r\n if (category === \"tel\" && href.trim().toLowerCase() === \"tel:\") {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"empty-tel\",\r\n message: \"tel: link has no phone number\",\r\n href,\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n\r\n // Very long URL\r\n if (href.length > 2000) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"long-url\",\r\n message: \"URL exceeds 2000 characters — may be truncated by some email clients\",\r\n href: href.slice(0, 120) + \"...\",\r\n text: text.slice(0, 80) || \"(no text)\",\r\n });\r\n }\r\n });\r\n\r\n // Duplicate link detection\r\n for (const [href, count] of hrefCounts) {\r\n if (count > 5) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"duplicate-links\",\r\n message: `URL appears ${count} times — consider consolidating`,\r\n href: href.slice(0, 120),\r\n });\r\n }\r\n }\r\n\r\n return { totalLinks, issues, breakdown };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport type { AccessibilityIssue, AccessibilityReport } from \"./types\";\r\nimport { GENERIC_LINK_TEXT, MAX_HTML_SIZE } from \"./constants\";\r\nimport { getStyleValue } from \"./style-utils\";\r\nimport { parseColor, relativeLuminance, contrastRatio, wcagGrade, alphaBlend } from \"./color-utils\";\r\n\r\n// Per-rule penalty caps — only the score penalty is capped, all issues are still reported\r\nconst RULE_PENALTY_CAPS: Record<string, number> = {\r\n \"img-missing-alt\": 3,\r\n \"link-generic-text\": 3,\r\n \"link-no-accessible-name\": 3,\r\n \"table-missing-role\": 2,\r\n \"low-contrast\": 3,\r\n};\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction describeElement($: cheerio.CheerioAPI, el: any): string {\r\n const tag = (el.tagName as string)?.toLowerCase() || \"unknown\";\r\n const src = $(el).attr(\"src\");\r\n const href = $(el).attr(\"href\");\r\n if (src) return `<${tag} src=\"${src.slice(0, 60)}${src.length > 60 ? \"...\" : \"\"}\">`;\r\n if (href) return `<${tag} href=\"${href.slice(0, 60)}${href.length > 60 ? \"...\" : \"\"}\">`;\r\n const text = $(el).text().trim().slice(0, 40);\r\n if (text) return `<${tag}>${text}${$(el).text().trim().length > 40 ? \"...\" : \"\"}</${tag}>`;\r\n return `<${tag}>`;\r\n}\r\n\r\nfunction checkLangAttribute($: cheerio.CheerioAPI): AccessibilityIssue | null {\r\n const lang = $(\"html\").attr(\"lang\");\r\n if (!lang || !lang.trim()) {\r\n return {\r\n severity: \"error\",\r\n rule: \"missing-lang\",\r\n message: \"Missing lang attribute on <html> element\",\r\n details: 'Screen readers use the lang attribute to determine pronunciation. Add lang=\"en\" (or appropriate language code).',\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nfunction checkTitle($: cheerio.CheerioAPI): AccessibilityIssue | null {\r\n const title = $(\"title\").text().trim();\r\n if (!title) {\r\n return {\r\n severity: \"warning\",\r\n rule: \"missing-title\",\r\n message: \"Missing or empty <title> element\",\r\n details: \"The <title> helps screen readers identify the email content.\",\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nfunction checkImageAlt($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"img\").each((_, el) => {\r\n const alt = $(el).attr(\"alt\");\r\n const src = $(el).attr(\"src\") || \"\";\r\n const role = $(el).attr(\"role\");\r\n\r\n if (role === \"presentation\" || role === \"none\") return;\r\n\r\n if (alt === undefined) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"img-missing-alt\",\r\n message: \"Image missing alt attribute\",\r\n element: describeElement($, el),\r\n details: 'Every image must have an alt attribute. Use alt=\"\" for decorative images.',\r\n });\r\n } else if (alt.trim() === \"\") {\r\n const isLikelyContent =\r\n !src.includes(\"spacer\") &&\r\n !src.includes(\"pixel\") &&\r\n !src.includes(\"tracking\") &&\r\n !src.includes(\"1x1\") &&\r\n !src.includes(\"transparent\");\r\n\r\n if (isLikelyContent && ($(el).attr(\"width\") || \"0\") !== \"1\") {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"img-empty-alt\",\r\n message: \"Image has empty alt text — verify it is decorative\",\r\n element: describeElement($, el),\r\n details: \"Empty alt is correct for decorative images, but content images need descriptive alt text.\",\r\n });\r\n }\r\n } else if (/\\.(png|jpg|jpeg|gif|svg|webp|bmp)$/i.test(alt)) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"img-filename-alt\",\r\n message: \"Image alt text is a filename, not a description\",\r\n element: describeElement($, el),\r\n details: `Alt \"${alt}\" should describe the image content, not the file name.`,\r\n });\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkLinkAccessibility($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"a\").each((_, el) => {\r\n const text = $(el).text().trim().toLowerCase();\r\n const ariaLabel = $(el).attr(\"aria-label\");\r\n const title = $(el).attr(\"title\");\r\n const imgAlt = $(el).find(\"img\").attr(\"alt\");\r\n\r\n if (!text && !ariaLabel && !title && !imgAlt) {\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"link-no-accessible-name\",\r\n message: \"Link has no accessible name\",\r\n element: describeElement($, el),\r\n details: \"Links need visible text, aria-label, or an image with alt text.\",\r\n });\r\n return;\r\n }\r\n\r\n if (text && GENERIC_LINK_TEXT.has(text) && !ariaLabel) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"link-generic-text\",\r\n message: `Link text \"${$(el).text().trim()}\" is not descriptive`,\r\n element: describeElement($, el),\r\n details: \"Screen readers often list links out of context. Use text that describes the destination.\",\r\n });\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkTableAccessibility($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n $(\"table\").each((_, el) => {\r\n // Skip inner tables that are inside a presentation/none ancestor\r\n if ($(el).parents('table[role=\"presentation\"], table[role=\"none\"]').length > 0) return;\r\n\r\n const role = $(el).attr(\"role\");\r\n const hasHeaders = $(el).find(\"th\").length > 0;\r\n const looksLikeLayout = !hasHeaders;\r\n\r\n if (looksLikeLayout && role !== \"presentation\" && role !== \"none\") {\r\n const nestedTables = $(el).find(\"table\").length;\r\n if (nestedTables > 0 || $(el).find(\"td\").length > 2) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"table-missing-role\",\r\n message: 'Layout table missing role=\"presentation\"',\r\n element: `<table> with ${$(el).find(\"td\").length} cells`,\r\n details: 'Add role=\"presentation\" to tables used for layout so screen readers don\\'t announce them as data tables.',\r\n });\r\n }\r\n }\r\n });\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkTextSizeAndContrast($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n let smallTextCount = 0;\r\n\r\n $(\"[style]\").each((_, el) => {\r\n const style = $(el).attr(\"style\") || \"\";\r\n\r\n // --- Small text check (threshold lowered to 9px) ---\r\n const fontSizeMatch = style.match(/font-size\\s*:\\s*(\\d+(?:\\.\\d+)?)(px|pt)/i);\r\n if (fontSizeMatch) {\r\n const size = parseFloat(fontSizeMatch[1]);\r\n const unit = fontSizeMatch[2].toLowerCase();\r\n const pxSize = unit === \"pt\" ? size * 1.333 : size;\r\n\r\n if (pxSize < 9 && pxSize > 0) {\r\n smallTextCount++;\r\n if (smallTextCount <= 3) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"small-text\",\r\n message: `Very small text (${fontSizeMatch[0].trim()})`,\r\n element: describeElement($, el),\r\n details: \"Text smaller than 9px is difficult to read, especially on mobile devices.\",\r\n });\r\n }\r\n }\r\n }\r\n\r\n // --- Color contrast check ---\r\n const colorValue = getStyleValue(style, \"color\");\r\n if (colorValue) {\r\n const fg = parseColor(colorValue);\r\n if (fg) {\r\n // Find nearest ancestor background-color, default white\r\n let bgR = 255, bgG = 255, bgB = 255;\r\n let current = $(el);\r\n let foundBg = false;\r\n\r\n // Check own background first, then walk ancestors\r\n const elements = [current, ...$(el).parents(\"[style]\").toArray().map((p) => $(p))];\r\n for (const ancestor of elements) {\r\n const ancestorStyle = (typeof ancestor === \"function\" ? ancestor : $(ancestor)).attr(\"style\") || \"\";\r\n const bgValue = getStyleValue(ancestorStyle, \"background-color\");\r\n if (bgValue) {\r\n const bg = parseColor(bgValue);\r\n if (bg && bg.a > 0) {\r\n if (bg.a < 1) {\r\n // Semi-transparent bg — blend against white\r\n [bgR, bgG, bgB] = alphaBlend(bg, 255, 255, 255);\r\n } else {\r\n bgR = bg.r;\r\n bgG = bg.g;\r\n bgB = bg.b;\r\n }\r\n foundBg = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // Alpha-blend foreground if needed\r\n const [fR, fG, fB] = fg.a < 1 ? alphaBlend(fg, bgR, bgG, bgB) : [fg.r, fg.g, fg.b];\r\n\r\n const fgLum = relativeLuminance(fR, fG, fB);\r\n const bgLum = relativeLuminance(bgR, bgG, bgB);\r\n const ratio = contrastRatio(fgLum, bgLum);\r\n\r\n // Determine if text is \"large\" (≥18px, or ≥14px bold)\r\n let isLargeText = false;\r\n if (fontSizeMatch) {\r\n const size = parseFloat(fontSizeMatch[1]);\r\n const unit = fontSizeMatch[2].toLowerCase();\r\n const pxSize = unit === \"pt\" ? size * 1.333 : size;\r\n const fontWeight = getStyleValue(style, \"font-weight\");\r\n const isBold = fontWeight === \"bold\" || fontWeight === \"bolder\" || (fontWeight && parseInt(fontWeight, 10) >= 700);\r\n isLargeText = pxSize >= 18 || (pxSize >= 14 && !!isBold);\r\n }\r\n\r\n const grade = wcagGrade(ratio);\r\n if (grade === \"Fail\") {\r\n // ratio < 3:1 — error for all text\r\n issues.push({\r\n severity: \"error\",\r\n rule: \"low-contrast\",\r\n message: `Low contrast ratio ${ratio.toFixed(1)}:1 — fails WCAG minimum`,\r\n element: describeElement($, el),\r\n details: `Foreground ${colorValue} on background needs at least ${isLargeText ? \"3:1\" : \"4.5:1\"} contrast ratio.`,\r\n });\r\n } else if (!isLargeText && grade === \"AA Large\") {\r\n // ratio 3:1–4.5:1 — only passes for large text, so flag normal text\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"low-contrast\",\r\n message: `Low contrast ratio ${ratio.toFixed(1)}:1 — fails WCAG AA for normal text`,\r\n element: describeElement($, el),\r\n details: `Foreground ${colorValue} on background needs at least 4.5:1 for normal-sized text.`,\r\n });\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (smallTextCount > 3) {\r\n issues.push({\r\n severity: \"warning\",\r\n rule: \"small-text-multiple\",\r\n message: `${smallTextCount} elements with text smaller than 9px`,\r\n details: \"Consider using a minimum font size of 12-14px for readability.\",\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction checkSemanticStructure($: cheerio.CheerioAPI): AccessibilityIssue[] {\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n const headings: { level: number; text: string }[] = [];\r\n $(\"h1, h2, h3, h4, h5, h6\").each((_, el) => {\r\n const level = parseInt(el.tagName.replace(/h/i, \"\"), 10);\r\n headings.push({ level, text: $(el).text().trim().slice(0, 60) });\r\n });\r\n\r\n for (let i = 1; i < headings.length; i++) {\r\n const gap = headings[i].level - headings[i - 1].level;\r\n if (gap > 1) {\r\n issues.push({\r\n severity: \"info\",\r\n rule: \"heading-skip\",\r\n message: `Heading level skipped: h${headings[i - 1].level} to h${headings[i].level}`,\r\n details: \"Skipped heading levels can confuse screen readers. Use sequential heading levels.\",\r\n });\r\n break;\r\n }\r\n }\r\n\r\n return issues;\r\n}\r\n\r\n/**\r\n * Audit an HTML email for accessibility issues.\r\n *\r\n * Checks for missing lang attributes, image alt text, small fonts,\r\n * layout table roles, link accessibility, heading hierarchy, and\r\n * color contrast. Returns a 0–100 score and detailed issues.\r\n */\r\nexport function checkAccessibility(html: string): AccessibilityReport {\r\n if (!html || !html.trim()) {\r\n return { score: 100, issues: [] };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: AccessibilityIssue[] = [];\r\n\r\n const langIssue = checkLangAttribute($);\r\n if (langIssue) issues.push(langIssue);\r\n\r\n const titleIssue = checkTitle($);\r\n if (titleIssue) issues.push(titleIssue);\r\n\r\n issues.push(...checkImageAlt($));\r\n issues.push(...checkLinkAccessibility($));\r\n issues.push(...checkTableAccessibility($));\r\n issues.push(...checkTextSizeAndContrast($));\r\n issues.push(...checkSemanticStructure($));\r\n\r\n // Calculate score with per-rule penalty capping\r\n let penalty = 0;\r\n const seenRules = new Map<string, number>();\r\n\r\n for (const issue of issues) {\r\n const count = (seenRules.get(issue.rule) || 0) + 1;\r\n seenRules.set(issue.rule, count);\r\n\r\n const cap = RULE_PENALTY_CAPS[issue.rule];\r\n if (cap !== undefined && count > cap) continue; // skip penalty but keep the issue\r\n\r\n switch (issue.severity) {\r\n case \"error\": penalty += 12; break;\r\n case \"warning\": penalty += 6; break;\r\n case \"info\": penalty += 2; break;\r\n }\r\n }\r\n\r\n const score = Math.max(0, 100 - penalty);\r\n return { score, issues };\r\n}\r\n","import * as cheerio from \"cheerio\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\nimport type { ImageIssue, ImageInfo, ImageReport } from \"./types\";\r\n\r\nconst DATA_URI_WARN_BYTES = 100 * 1024;\r\nconst TOTAL_DATA_URI_WARN_BYTES = 500 * 1024;\r\nconst HIGH_IMAGE_COUNT = 10;\r\n\r\nfunction estimateBase64Bytes(dataUri: string): number {\r\n const commaIdx = dataUri.indexOf(\",\");\r\n if (commaIdx === -1) return 0;\r\n const payload = dataUri.slice(commaIdx + 1);\r\n return Math.floor((payload.length * 3) / 4);\r\n}\r\n\r\nfunction isTrackingPixel(\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n el: cheerio.Cheerio<any>,\r\n): boolean {\r\n const width = el.attr(\"width\");\r\n const height = el.attr(\"height\");\r\n const style = (el.attr(\"style\") || \"\").toLowerCase();\r\n\r\n if (width === \"1\" && height === \"1\") return true;\r\n if (width === \"0\" || height === \"0\") return true;\r\n\r\n if (\r\n style.includes(\"display:none\") ||\r\n style.includes(\"display: none\") ||\r\n style.includes(\"visibility:hidden\") ||\r\n style.includes(\"visibility: hidden\")\r\n ) {\r\n return true;\r\n }\r\n\r\n if (/width\\s*:\\s*1px/.test(style) && /height\\s*:\\s*1px/.test(style)) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction truncateSrc(src: string, max = 60): string {\r\n if (src.startsWith(\"data:\")) {\r\n const semi = src.indexOf(\";\");\r\n return semi > 0 ? src.slice(0, semi + 1) + \"base64,...\" : \"data:...\";\r\n }\r\n return src.length > max ? src.slice(0, max - 3) + \"...\" : src;\r\n}\r\n\r\n/**\r\n * Analyze images in an HTML email for best practices.\r\n *\r\n * Checks for missing dimensions, oversized data URIs, missing alt\r\n * attributes, unsupported formats (WebP, SVG), tracking pixels,\r\n * missing display:block, and overall image heaviness.\r\n */\r\nexport function analyzeImages(html: string): ImageReport {\r\n if (!html || !html.trim()) {\r\n return { total: 0, totalDataUriBytes: 0, issues: [], images: [] };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const $ = cheerio.load(html);\r\n const issues: ImageIssue[] = [];\r\n const images: ImageInfo[] = [];\r\n let totalDataUriBytes = 0;\r\n\r\n $(\"img\").each((_, el) => {\r\n const img = $(el);\r\n const src = img.attr(\"src\") || \"\";\r\n const alt = img.attr(\"alt\") ?? null;\r\n const width = img.attr(\"width\") ?? null;\r\n const height = img.attr(\"height\") ?? null;\r\n const style = (img.attr(\"style\") || \"\").toLowerCase();\r\n const imgIssues: string[] = [];\r\n\r\n const tracking = isTrackingPixel(img);\r\n\r\n let dataUriBytes = 0;\r\n if (src.startsWith(\"data:\")) {\r\n dataUriBytes = estimateBase64Bytes(src);\r\n totalDataUriBytes += dataUriBytes;\r\n }\r\n\r\n // Skip detailed checks for tracking pixels\r\n if (tracking) {\r\n images.push({\r\n src: truncateSrc(src),\r\n alt, width, height,\r\n isTrackingPixel: true,\r\n dataUriBytes,\r\n issues: [\"tracking-pixel\"],\r\n });\r\n return;\r\n }\r\n\r\n // Missing width/height\r\n if (!width && !height) {\r\n const hasStyleWidth = /width\\s*:/.test(style);\r\n const hasStyleHeight = /height\\s*:/.test(style);\r\n if (!hasStyleWidth && !hasStyleHeight) {\r\n imgIssues.push(\"missing-dimensions\");\r\n issues.push({\r\n rule: \"missing-dimensions\",\r\n severity: \"warning\",\r\n message: \"Image missing width/height attributes — causes layout shifts and Outlook rendering issues.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n }\r\n\r\n // Large data URI\r\n if (dataUriBytes > DATA_URI_WARN_BYTES) {\r\n const kb = Math.round(dataUriBytes / 1024);\r\n imgIssues.push(\"large-data-uri\");\r\n issues.push({\r\n rule: \"large-data-uri\",\r\n severity: \"warning\",\r\n message: `Data URI is ${kb}KB — consider hosting the image externally to reduce email size.`,\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // Missing alt\r\n if (alt === null) {\r\n imgIssues.push(\"missing-alt\");\r\n issues.push({\r\n rule: \"missing-alt\",\r\n severity: \"warning\",\r\n message: \"Image missing alt attribute — hurts deliverability and accessibility.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // WebP format\r\n if (src.toLowerCase().endsWith(\".webp\") || src.includes(\"image/webp\")) {\r\n imgIssues.push(\"webp-format\");\r\n issues.push({\r\n rule: \"webp-format\",\r\n severity: \"info\",\r\n message: \"WebP format detected — not supported by all email clients. Consider PNG or JPEG.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // SVG format\r\n if (src.toLowerCase().endsWith(\".svg\") || src.includes(\"image/svg\")) {\r\n imgIssues.push(\"svg-format\");\r\n issues.push({\r\n rule: \"svg-format\",\r\n severity: \"info\",\r\n message: \"SVG format detected — not supported by most email clients. Use PNG instead.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n // Missing display:block\r\n if (!style.includes(\"display:block\") && !style.includes(\"display: block\")) {\r\n imgIssues.push(\"missing-display-block\");\r\n issues.push({\r\n rule: \"missing-display-block\",\r\n severity: \"info\",\r\n message: \"Image without display:block — may cause unwanted gaps in Outlook.\",\r\n src: truncateSrc(src),\r\n });\r\n }\r\n\r\n images.push({\r\n src: truncateSrc(src),\r\n alt, width, height,\r\n isTrackingPixel: false,\r\n dataUriBytes,\r\n issues: imgIssues,\r\n });\r\n });\r\n\r\n // Aggregate checks\r\n const nonTrackingImages = images.filter((i) => !i.isTrackingPixel);\r\n\r\n if (nonTrackingImages.length > HIGH_IMAGE_COUNT) {\r\n issues.push({\r\n rule: \"high-image-count\",\r\n severity: \"info\",\r\n message: `Email contains ${nonTrackingImages.length} images — heavy emails may be clipped or load slowly.`,\r\n });\r\n }\r\n\r\n const trackingPixels = images.filter((i) => i.isTrackingPixel);\r\n if (trackingPixels.length > 0) {\r\n issues.push({\r\n rule: \"tracking-pixel\",\r\n severity: \"info\",\r\n message: `${trackingPixels.length} tracking pixel${trackingPixels.length > 1 ? \"s\" : \"\"} detected.`,\r\n });\r\n }\r\n\r\n if (totalDataUriBytes > TOTAL_DATA_URI_WARN_BYTES) {\r\n const kb = Math.round(totalDataUriBytes / 1024);\r\n issues.push({\r\n rule: \"total-data-uri-size\",\r\n severity: \"warning\",\r\n message: `Total data URI size is ${kb}KB — consider hosting images externally to reduce email size.`,\r\n });\r\n }\r\n\r\n return { total: images.length, totalDataUriBytes, issues, images };\r\n}\r\n","import { analyzeEmail, generateCompatibilityScore } from \"./analyze\";\r\nimport { analyzeSpam } from \"./spam-scorer\";\r\nimport { validateLinks } from \"./link-validator\";\r\nimport { checkAccessibility } from \"./accessibility-checker\";\r\nimport { analyzeImages } from \"./image-analyzer\";\r\nimport { MAX_HTML_SIZE } from \"./constants\";\r\nimport type {\r\n CSSWarning,\r\n Framework,\r\n SpamAnalysisOptions,\r\n SpamReport,\r\n LinkReport,\r\n AccessibilityReport,\r\n ImageReport,\r\n} from \"./types\";\r\n\r\nexport interface AuditOptions {\r\n framework?: Framework;\r\n /** Options for spam analysis */\r\n spam?: SpamAnalysisOptions;\r\n /** Skip specific checks */\r\n skip?: Array<\"spam\" | \"links\" | \"accessibility\" | \"images\" | \"compatibility\">;\r\n}\r\n\r\nexport interface AuditReport {\r\n compatibility: {\r\n warnings: CSSWarning[];\r\n scores: Record<string, { score: number; errors: number; warnings: number; info: number }>;\r\n };\r\n spam: SpamReport;\r\n links: LinkReport;\r\n accessibility: AccessibilityReport;\r\n images: ImageReport;\r\n}\r\n\r\nconst EMPTY_SPAM: SpamReport = { score: 100, level: \"low\", issues: [] };\r\nconst EMPTY_LINKS: LinkReport = {\r\n totalLinks: 0,\r\n issues: [],\r\n breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 },\r\n};\r\nconst EMPTY_ACCESSIBILITY: AccessibilityReport = { score: 100, issues: [] };\r\nconst EMPTY_IMAGES: ImageReport = { total: 0, totalDataUriBytes: 0, issues: [], images: [] };\r\n\r\n/**\r\n * Run all email analysis checks in a single call.\r\n *\r\n * Returns a unified report with compatibility warnings + scores,\r\n * spam analysis, link validation, accessibility audit, and image analysis.\r\n * Use the `skip` option to omit checks you don't need.\r\n */\r\nexport function auditEmail(html: string, options?: AuditOptions): AuditReport {\r\n if (!html || !html.trim()) {\r\n return {\r\n compatibility: { warnings: [], scores: {} },\r\n spam: EMPTY_SPAM,\r\n links: EMPTY_LINKS,\r\n accessibility: EMPTY_ACCESSIBILITY,\r\n images: EMPTY_IMAGES,\r\n };\r\n }\r\n if (html.length > MAX_HTML_SIZE) {\r\n throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);\r\n }\r\n\r\n const framework = options?.framework;\r\n const skip = new Set(options?.skip ?? []);\r\n\r\n const warnings = skip.has(\"compatibility\") ? [] : analyzeEmail(html, framework);\r\n const scores = skip.has(\"compatibility\") ? {} : generateCompatibilityScore(warnings);\r\n const spam = skip.has(\"spam\") ? EMPTY_SPAM : analyzeSpam(html, options?.spam);\r\n const links = skip.has(\"links\") ? EMPTY_LINKS : validateLinks(html);\r\n const accessibility = skip.has(\"accessibility\") ? EMPTY_ACCESSIBILITY : checkAccessibility(html);\r\n const images = skip.has(\"images\") ? EMPTY_IMAGES : analyzeImages(html);\r\n\r\n return { compatibility: { warnings, scores }, spam, links, accessibility, images };\r\n}\r\n"],"mappings":"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","names":["_","cheerio","csstree","_a","value","cheerio","csstree","cheerio","cheerio","cheerio","cheerio"]}