{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "email-template-previewer",
  "title": "Email Template Previewer",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/email-template-previewer/email-template-previewer.component.ts",
      "content": "// Resend and Klaviyo style marketing email designer preview with viewport switcher, subject\n// line tester, deliverability & spam score analyzer, and live test dispatch modal.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  ArrowRight,\n  Check,\n  CheckCircle2,\n  Code2,\n  Copy,\n  FileCode,\n  Flame,\n  Inbox,\n  Layers,\n  Lock,\n  LucideAngularModule,\n  Mail,\n  Monitor,\n  Palette,\n  Send,\n  ShieldCheck,\n  Smartphone,\n  Terminal,\n  Zap,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiDialogComponent,\n  UiDialogContentComponent,\n  UiDialogDescriptionComponent,\n  UiDialogFooterComponent,\n  UiDialogHeaderComponent,\n  UiDialogTitleComponent,\n} from '@/ui/dialog/dialog.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport type PreviewerViewport = 'desktop' | 'mobile' | 'raw'\nexport type PreviewerClient = 'all' | 'gmail' | 'apple' | 'outlook'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-email-template-previewer, [ui-email-template-previewer]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiDialogComponent,\n    UiDialogContentComponent,\n    UiDialogDescriptionComponent,\n    UiDialogFooterComponent,\n    UiDialogHeaderComponent,\n    UiDialogTitleComponent,\n    UiInputComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"email-template-previewer\" [class]=\"rootClass\">\n      <!-- Top Toolbar Card -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-header class=\"pb-4\">\n          <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div class=\"space-y-1\">\n              <div class=\"flex items-center gap-2\">\n                <h3 ui-card-title class=\"text-lg font-semibold tracking-tight\">Marketing Email Designer</h3>\n                <span ui-badge variant=\"outline\" class=\"text-muted-foreground gap-1 border-dashed\">\n                  <lucide-icon [img]=\"Send\" class=\"text-primary size-3\" />\n                  Resend Engine\n                </span>\n              </div>\n              <p ui-card-description class=\"text-xs\">\n                Live viewport rendering, deliverability telemetry, and RFC-compliant HTML generator.\n              </p>\n            </div>\n\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <div\n                class=\"bg-muted/80 border-border inline-flex max-w-full items-center overflow-x-auto rounded-lg border p-1 shadow-xs\"\n              >\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"viewport() === 'desktop' ? 'default' : 'ghost'\"\n                  class=\"gap-1.5 text-xs font-medium\"\n                  (click)=\"viewport.set('desktop')\"\n                >\n                  <lucide-icon [img]=\"Monitor\" class=\"size-3.5\" />\n                  <span>Desktop 600px</span>\n                </button>\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"viewport() === 'mobile' ? 'default' : 'ghost'\"\n                  class=\"gap-1.5 text-xs font-medium\"\n                  (click)=\"viewport.set('mobile')\"\n                >\n                  <lucide-icon [img]=\"Smartphone\" class=\"size-3.5\" />\n                  <span>Mobile 375px</span>\n                </button>\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"viewport() === 'raw' ? 'default' : 'ghost'\"\n                  class=\"gap-1.5 text-xs font-medium\"\n                  (click)=\"viewport.set('raw')\"\n                >\n                  <lucide-icon [img]=\"Code2\" class=\"size-3.5\" />\n                  <span>Raw HTML</span>\n                </button>\n              </div>\n\n              <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5 text-xs\" (click)=\"isTestModalOpen.set(true)\">\n                <lucide-icon [img]=\"Send\" class=\"text-primary size-3.5\" />\n                <span>Send Test Email</span>\n              </button>\n\n              <button ui-button variant=\"default\" size=\"sm\" class=\"gap-1.5 text-xs\" (click)=\"copyHtmlToClipboard()\">\n                @if (isCopied()) {\n                  <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                } @else {\n                  <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n                }\n                <span>{{ isCopied() ? 'Copied HTML!' : 'Export HTML' }}</span>\n              </button>\n            </div>\n          </div>\n        </div>\n\n        <div ui-separator></div>\n\n        <div ui-card-content class=\"pt-4\">\n          <div class=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between text-xs\">\n                <label class=\"text-foreground font-medium\">Subject Line</label>\n                <div class=\"flex items-center gap-1.5\">\n                  <span ui-badge [variant]=\"subjectStatus().variant\" class=\"h-5 px-1.5 text-xs font-normal\">\n                    {{ subjectCharCount() }}/60 chars\n                  </span>\n                  <span ui-badge variant=\"outline\" class=\"text-muted-foreground h-5 gap-1 px-1.5 text-xs font-normal\">\n                    <lucide-icon [img]=\"Flame\" class=\"text-warning size-3\" />\n                    <span>{{ emojiCount() }} emoji</span>\n                  </span>\n                </div>\n              </div>\n              <ui-input\n                [value]=\"subject()\"\n                (valueChange)=\"subject.set($event)\"\n                class=\"text-xs\"\n                placeholder=\"Enter campaign subject line...\"\n              />\n            </div>\n\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between text-xs\">\n                <label class=\"text-foreground font-medium\">Preview Text / Preheader</label>\n                <span ui-badge variant=\"secondary\" class=\"text-muted-foreground h-5 px-1.5 text-xs font-normal\">\n                  {{ preheaderCharCount() }}/90 chars\n                </span>\n              </div>\n              <ui-input\n                [value]=\"previewText()\"\n                (valueChange)=\"previewText.set($event)\"\n                class=\"text-xs\"\n                placeholder=\"Secondary snippet shown in mail list...\"\n              />\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- 2-Column Preview Workspace -->\n      <div class=\"grid grid-cols-1 items-start gap-6 lg:grid-cols-12\">\n        <div class=\"flex flex-col items-center lg:col-span-8\">\n          <div\n            class=\"border-border bg-muted/30 relative flex min-h-[720px] w-full flex-col items-center justify-start overflow-hidden rounded-xl border p-4 sm:p-6\"\n          >\n            @if (viewport() === 'raw') {\n              <div class=\"w-full space-y-3\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2\">\n                    <lucide-icon [img]=\"FileCode\" class=\"text-muted-foreground size-4\" />\n                    <span class=\"text-foreground font-mono text-xs font-semibold\">template.compiled.html</span>\n                    <span ui-badge variant=\"secondary\" class=\"h-4.5 text-xs\">14.2 KB</span>\n                  </div>\n                  <button ui-button variant=\"outline\" size=\"xs\" class=\"gap-1 text-xs\" (click)=\"copyHtmlToClipboard()\">\n                    @if (isCopied()) {\n                      <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                    } @else {\n                      <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                    }\n                    <span>{{ isCopied() ? 'Copied' : 'Copy Source' }}</span>\n                  </button>\n                </div>\n                <pre\n                  class=\"border-border bg-muted/70 text-foreground max-h-[640px] overflow-auto rounded-lg border p-4 font-mono text-xs leading-relaxed\"\n                ><code>{{ rawHtmlOutput() }}</code></pre>\n              </div>\n            } @else {\n              <div [class]=\"previewCanvasClass\">\n                <div class=\"border-border bg-card overflow-hidden rounded-xl border shadow-sm\">\n                  <div class=\"border-border bg-muted/50 flex items-center justify-between border-b px-4 py-2.5\">\n                    <div class=\"flex items-center gap-1.5\">\n                      <div class=\"bg-destructive/80 size-2.5 rounded-full\"></div>\n                      <div class=\"bg-warning/80 size-2.5 rounded-full\"></div>\n                      <div class=\"bg-success/80 size-2.5 rounded-full\"></div>\n                    </div>\n                    <div class=\"text-muted-foreground flex items-center gap-1.5 font-mono text-xs\">\n                      <lucide-icon [img]=\"Inbox\" class=\"size-3\" />\n                      <span>UIPKGE Inbox Preview · {{ viewport() === 'desktop' ? '600px' : '375px' }}</span>\n                    </div>\n                    <span\n                      ui-badge\n                      variant=\"outline\"\n                      class=\"border-border text-muted-foreground h-4.5 gap-1 px-1.5 text-xs font-normal\"\n                    >\n                      <lucide-icon [img]=\"Lock\" class=\"text-success size-2.5\" />\n                      TLS 1.3\n                    </span>\n                  </div>\n\n                  <div class=\"border-border bg-card/60 space-y-2 border-b p-4 text-xs\">\n                    <div class=\"flex items-center justify-between\">\n                      <div class=\"flex items-center gap-2\">\n                        <div\n                          class=\"bg-primary text-primary-foreground flex size-7 items-center justify-center rounded-full font-bold\"\n                        >\n                          U\n                        </div>\n                        <div>\n                          <div class=\"flex items-center gap-1.5\">\n                            <span class=\"text-foreground font-semibold\">{{ senderName }}</span>\n                            <span class=\"text-muted-foreground\">&lt;{{ senderEmail }}&gt;</span>\n                          </div>\n                          <div class=\"text-muted-foreground text-xs\">To: {{ recipientEmail }}</div>\n                        </div>\n                      </div>\n                      <span class=\"text-muted-foreground text-xs\">Aug 21, 2026, 10:00 AM</span>\n                    </div>\n\n                    <div class=\"pt-1\">\n                      <div class=\"text-foreground text-sm font-semibold tracking-tight\">{{ subject() }}</div>\n                      <div class=\"text-muted-foreground line-clamp-1 text-xs\">{{ previewText() }}</div>\n                    </div>\n                  </div>\n\n                  <div class=\"bg-card space-y-6 p-6 sm:p-8\">\n                    <div class=\"border-border flex items-center justify-between border-b pb-4\">\n                      <div class=\"flex items-center gap-2\">\n                        <div\n                          class=\"bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md\"\n                        >\n                          <lucide-icon [img]=\"Zap\" class=\"size-3.5 fill-current\" />\n                        </div>\n                        <span class=\"text-foreground text-sm font-bold tracking-tight\">UIPKGE</span>\n                      </div>\n                      <span ui-badge variant=\"secondary\" class=\"text-xs font-medium\">Changelog #24</span>\n                    </div>\n\n                    <div class=\"space-y-3\">\n                      <span\n                        ui-badge\n                        variant=\"outline\"\n                        class=\"text-primary border-primary/30 text-xs font-semibold tracking-wider uppercase\"\n                      >\n                        Major Release\n                      </span>\n                      <h1 class=\"text-foreground text-2xl leading-tight font-bold tracking-tight sm:text-3xl\">\n                        Ship polished UIs in minutes\n                      </h1>\n                      <p class=\"text-muted-foreground text-sm leading-relaxed\">\n                        We completely rebuilt the registry engine. Zero npm dependencies, 100% code ownership, and full\n                        native support for Vue 3 and React with modern OKLCH tokens.\n                      </p>\n                    </div>\n\n                    <div class=\"border-border bg-muted/40 overflow-hidden rounded-lg border\">\n                      <div\n                        class=\"border-border bg-muted/60 flex items-center justify-between gap-2 border-b px-3.5 py-2\"\n                      >\n                        <div class=\"text-muted-foreground flex min-w-0 items-center gap-1.5 font-mono text-xs\">\n                          <lucide-icon [img]=\"Terminal\" class=\"text-primary size-3 shrink-0\" />\n                          <span class=\"truncate\">components/ui/button.tsx</span>\n                        </div>\n                        <span class=\"text-success shrink-0 font-mono text-xs font-medium\">TypeScript</span>\n                      </div>\n                      <div class=\"space-y-3 p-4\">\n                        <div\n                          class=\"border-border bg-background text-muted-foreground rounded-md border p-2.5 font-mono text-xs\"\n                        >\n                          <span class=\"text-primary font-semibold\">$</span> npx shadcn add @uipkge/button\n                        </div>\n\n                        <div\n                          class=\"bg-card border-border flex flex-wrap items-center justify-center gap-2 rounded-md border p-3\"\n                        >\n                          <button ui-button size=\"xs\" variant=\"default\">Primary</button>\n                          <button ui-button size=\"xs\" variant=\"secondary\">Secondary</button>\n                          <button ui-button size=\"xs\" variant=\"outline\">Outline</button>\n                          <button ui-button size=\"xs\" variant=\"destructive\">Delete</button>\n                        </div>\n                      </div>\n                    </div>\n\n                    <div class=\"flex flex-col items-center justify-center space-y-2 pt-2 text-center\">\n                      <button ui-button size=\"lg\" class=\"w-full px-8 font-semibold shadow-xs sm:w-auto\">\n                        <span>Explore Components</span>\n                        <lucide-icon [img]=\"ArrowRight\" class=\"size-4\" />\n                      </button>\n                      <p class=\"text-muted-foreground text-xs\">\n                        Free & open source · MIT License · 300+ Primitives & Blocks\n                      </p>\n                    </div>\n\n                    <div ui-separator></div>\n\n                    <div class=\"space-y-3.5\">\n                      <div class=\"flex items-start gap-3\">\n                        <div\n                          class=\"bg-primary/10 text-primary mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full\"\n                        >\n                          <lucide-icon [img]=\"Zap\" class=\"size-3.5\" />\n                        </div>\n                        <div class=\"space-y-0.5\">\n                          <h4 class=\"text-foreground text-xs font-semibold\">Zero Dependency Footprint</h4>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            Every component lives directly in your codebase. No semver conflicts or registry lock-in.\n                          </p>\n                        </div>\n                      </div>\n\n                      <div class=\"flex items-start gap-3\">\n                        <div\n                          class=\"bg-primary/10 text-primary mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full\"\n                        >\n                          <lucide-icon [img]=\"Palette\" class=\"size-3.5\" />\n                        </div>\n                        <div class=\"space-y-0.5\">\n                          <h4 class=\"text-foreground text-xs font-semibold\">OKLCH Dark Mode Tokens</h4>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            Calibrated light & dark surfaces with verified WCAG AA contrast and fluid spring physics.\n                          </p>\n                        </div>\n                      </div>\n\n                      <div class=\"flex items-start gap-3\">\n                        <div\n                          class=\"bg-primary/10 text-primary mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full\"\n                        >\n                          <lucide-icon [img]=\"Layers\" class=\"size-3.5\" />\n                        </div>\n                        <div class=\"space-y-0.5\">\n                          <h4 class=\"text-foreground text-xs font-semibold\">Dual-Framework Registry</h4>\n                          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                            First-class TypeScript support for both Vue 3 (Reka UI) and React (Radix UI).\n                          </p>\n                        </div>\n                      </div>\n                    </div>\n\n                    <div ui-separator></div>\n\n                    <div class=\"text-muted-foreground space-y-3 pt-2 text-center text-xs\">\n                      <div class=\"flex flex-wrap items-center justify-center gap-3 font-medium\">\n                        <span class=\"hover:text-foreground cursor-pointer transition-colors\">Documentation</span>\n                        <span>·</span>\n                        <span class=\"hover:text-foreground cursor-pointer transition-colors\">GitHub</span>\n                        <span>·</span>\n                        <span class=\"hover:text-foreground cursor-pointer transition-colors\">Discord</span>\n                        <span>·</span>\n                        <span class=\"hover:text-foreground cursor-pointer transition-colors\">Preferences</span>\n                      </div>\n                      <p class=\"text-xs\">UIPKGE Inc. · 548 Market St, Suite 29314, San Francisco, CA 94104</p>\n                      <p class=\"text-xs\">\n                        You received this email because you subscribed to UIPKGE updates.\n                        <span class=\"hover:text-foreground cursor-pointer underline\">Unsubscribe anytime</span>.\n                      </p>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            }\n          </div>\n        </div>\n\n        <!-- Right Column: Quality & Deliverability Inspector -->\n        <div class=\"space-y-4 lg:col-span-4\">\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center justify-between\">\n                <h3 ui-card-title class=\"text-sm font-semibold tracking-tight\">Spam Score Analyzer</h3>\n                <lucide-icon [img]=\"ShieldCheck\" class=\"text-success size-4\" />\n              </div>\n              <p ui-card-description class=\"text-xs\">Predictive inbox delivery rating based on content heuristics.</p>\n            </div>\n            <div ui-card-content class=\"space-y-3.5\">\n              <div class=\"border-success/20 bg-success/10 rounded-lg border p-3\">\n                <div class=\"flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5\">\n                  <span class=\"text-success text-2xl font-bold tracking-tight\">0.4</span>\n                  <span class=\"text-muted-foreground text-xs font-medium\">/ 10.0 scale</span>\n                </div>\n                <div class=\"text-success mt-1.5 flex items-center gap-1.5 text-xs font-medium\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"size-3.5\" />\n                  <span>Excellent inbox probability</span>\n                </div>\n                <div class=\"bg-muted mt-2.5 h-1.5 w-full overflow-hidden rounded-full\">\n                  <div class=\"bg-success h-full w-[96%] rounded-full\"></div>\n                </div>\n              </div>\n\n              <div class=\"space-y-2 text-xs\">\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                    Spam trigger words\n                  </span>\n                  <span class=\"text-foreground font-medium\">0 detected</span>\n                </div>\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                    Text-to-image ratio\n                  </span>\n                  <span class=\"text-foreground font-medium\">92% text (Safe)</span>\n                </div>\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                    RFC 8058 Unsubscribe\n                  </span>\n                  <span class=\"text-foreground font-medium\">Included</span>\n                </div>\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                    Valid HTTPS links\n                  </span>\n                  <span class=\"text-foreground font-medium\">6 links checked</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <h3 ui-card-title class=\"text-sm font-semibold tracking-tight\">Domain Authentication</h3>\n              <p ui-card-description class=\"text-xs\">Security headers verified against DNS records.</p>\n            </div>\n            <div ui-card-content class=\"space-y-2.5\">\n              <div class=\"flex items-center justify-between text-xs\">\n                <span class=\"text-muted-foreground\">SPF Record</span>\n                <span ui-badge variant=\"success\" class=\"h-5 px-2 text-xs\">Pass</span>\n              </div>\n              <div class=\"flex items-center justify-between text-xs\">\n                <span class=\"text-muted-foreground\">DKIM Signature</span>\n                <span ui-badge variant=\"success\" class=\"h-5 px-2 text-xs\">Pass (2048-bit)</span>\n              </div>\n              <div class=\"flex items-center justify-between text-xs\">\n                <span class=\"text-muted-foreground\">DMARC Policy</span>\n                <span ui-badge variant=\"success\" class=\"h-5 px-2 text-xs\">Pass (p=reject)</span>\n              </div>\n              <div class=\"flex items-center justify-between text-xs\">\n                <span class=\"text-muted-foreground\">TLS Handshake</span>\n                <span ui-badge variant=\"outline\" class=\"text-foreground h-5 px-2 text-xs\">TLS 1.3 Active</span>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <h3 ui-card-title class=\"text-sm font-semibold tracking-tight\">Contrast & Dark Mode</h3>\n              <p ui-card-description class=\"text-xs\">Client dark mode and WCAG contrast conformance.</p>\n            </div>\n            <div ui-card-content class=\"space-y-2.5 text-xs\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Dark Mode Contrast</span>\n                <span ui-badge variant=\"success\" class=\"h-5 px-2 text-xs\">100% WCAG AA</span>\n              </div>\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Text Contrast Ratio</span>\n                <span class=\"text-foreground font-semibold\">14.8:1 (AAA)</span>\n              </div>\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Plaintext Fallback</span>\n                <span class=\"text-foreground font-medium\">Auto-generated</span>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <h3 ui-card-title class=\"text-sm font-semibold tracking-tight\">Email Telemetry</h3>\n              <p ui-card-description class=\"text-xs\">Reading metrics and payload optimization.</p>\n            </div>\n            <div ui-card-content class=\"space-y-2.5 text-xs\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Estimated Reading Time</span>\n                <span class=\"text-foreground font-medium\">45 sec · 185 words</span>\n              </div>\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Payload Size</span>\n                <span class=\"text-foreground font-medium\">14.2 KB (102 KB safe)</span>\n              </div>\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground\">Inline Assets</span>\n                <span class=\"text-foreground font-medium\">1 Vector SVG</span>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Send Test Dialog Modal -->\n      <ui-dialog [open]=\"isTestModalOpen()\" (openChange)=\"isTestModalOpen.set($event)\">\n        <ui-dialog-content class=\"sm:max-w-md\">\n          <div ui-dialog-header>\n            <h2 ui-dialog-title class=\"flex items-center gap-2 text-base\">\n              <lucide-icon [img]=\"Mail\" class=\"text-primary size-4\" />\n              Dispatch Test Preview\n            </h2>\n            <p ui-dialog-description class=\"text-xs\">\n              Send a live test message to your inbox to inspect rendering across email clients.\n            </p>\n          </div>\n\n          <div class=\"space-y-4 py-2\">\n            <div class=\"space-y-1.5\">\n              <label class=\"text-muted-foreground text-xs font-medium\">Recipient Address</label>\n              <ui-input\n                [value]=\"testTargetEmail()\"\n                (valueChange)=\"testTargetEmail.set($event)\"\n                type=\"email\"\n                placeholder=\"you@company.com\"\n                class=\"text-xs\"\n                [disabled]=\"isSendingTest()\"\n              />\n            </div>\n\n            <div class=\"space-y-1.5\">\n              <label class=\"text-muted-foreground text-xs font-medium\">Target Preview Profile</label>\n              <div class=\"grid grid-cols-2 gap-2\">\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"selectedClient() === 'all' ? 'default' : 'outline'\"\n                  class=\"justify-start text-xs\"\n                  (click)=\"selectedClient.set('all')\"\n                >\n                  🌐 All Clients\n                </button>\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"selectedClient() === 'gmail' ? 'default' : 'outline'\"\n                  class=\"justify-start text-xs\"\n                  (click)=\"selectedClient.set('gmail')\"\n                >\n                  ✉️ Gmail (Web)\n                </button>\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"selectedClient() === 'apple' ? 'default' : 'outline'\"\n                  class=\"justify-start text-xs\"\n                  (click)=\"selectedClient.set('apple')\"\n                >\n                  🍏 Apple Mail (iOS)\n                </button>\n                <button\n                  type=\"button\"\n                  ui-button\n                  size=\"xs\"\n                  [variant]=\"selectedClient() === 'outlook' ? 'default' : 'outline'\"\n                  class=\"justify-start text-xs\"\n                  (click)=\"selectedClient.set('outlook')\"\n                >\n                  💼 Outlook 365\n                </button>\n              </div>\n            </div>\n\n            @if (testSentSuccess()) {\n              <div\n                class=\"border-success/30 bg-success/10 text-success flex items-center gap-2 rounded-md border p-2.5 text-xs font-medium\"\n              >\n                <lucide-icon [img]=\"CheckCircle2\" class=\"size-4 shrink-0\" />\n                <span>Test email dispatched to {{ testTargetEmail() }}!</span>\n              </div>\n            }\n          </div>\n\n          <div ui-dialog-footer class=\"gap-2 sm:gap-0\">\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"sm\"\n              [disabled]=\"isSendingTest()\"\n              (click)=\"isTestModalOpen.set(false)\"\n            >\n              Cancel\n            </button>\n            <button ui-button size=\"sm\" class=\"gap-1.5\" [disabled]=\"isSendingTest()\" (click)=\"handleSendTest()\">\n              <lucide-icon [img]=\"Send\" class=\"size-3.5\" />\n              <span>{{ isSendingTest() ? 'Sending...' : 'Send Test' }}</span>\n            </button>\n          </div>\n        </ui-dialog-content>\n      </ui-dialog>\n    </div>\n  `,\n})\nexport class UiEmailTemplatePreviewerComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Code2 = Code2\n  protected readonly Copy = Copy\n  protected readonly FileCode = FileCode\n  protected readonly Flame = Flame\n  protected readonly Inbox = Inbox\n  protected readonly Layers = Layers\n  protected readonly Lock = Lock\n  protected readonly Mail = Mail\n  protected readonly Monitor = Monitor\n  protected readonly Palette = Palette\n  protected readonly Send = Send\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Smartphone = Smartphone\n  protected readonly Terminal = Terminal\n  protected readonly Zap = Zap\n\n  protected readonly senderName = 'UIPKGE Team'\n  protected readonly senderEmail = 'updates@uipkge.dev'\n  protected readonly recipientEmail = 'subscriber@example.com'\n\n  @Input('class') className?: string\n\n  readonly viewport = signal<PreviewerViewport>('desktop')\n  readonly subject = signal('🚀 Introducing UIPKGE 2.0: The unbundled UI registry')\n  readonly previewText = signal(\n    'Explore 300+ production components with zero npm dependencies. Native Vue 3 & React code.',\n  )\n\n  readonly isTestModalOpen = signal(false)\n  readonly testTargetEmail = signal('alex.developer@company.com')\n  readonly selectedClient = signal<PreviewerClient>('all')\n  readonly isSendingTest = signal(false)\n  readonly testSentSuccess = signal(false)\n  readonly isCopied = signal(false)\n\n  private timers: ReturnType<typeof setTimeout>[] = []\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => this.timers.forEach((t) => clearTimeout(t)))\n  }\n\n  readonly subjectCharCount = computed(() => this.subject().length)\n  readonly preheaderCharCount = computed(() => this.previewText().length)\n\n  readonly emojiCount = computed(() => {\n    const matches = this.subject().match(/\\p{Extended_Pictographic}/gu)\n    return matches ? matches.length : 0\n  })\n\n  readonly subjectStatus = computed(() => {\n    const count = this.subjectCharCount()\n    if (count >= 30 && count <= 60) return { label: 'Optimal length', variant: 'success' as const }\n    if (count < 30) return { label: 'Short subject', variant: 'info' as const }\n    return { label: 'Long subject (may truncate)', variant: 'warning' as const }\n  })\n\n  readonly rawHtmlOutput = computed(() => {\n    const subject = this.subject().replace(/\"/g, '&quot;')\n    const preview = this.previewText().replace(/\"/g, '&quot;')\n    return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <title>${subject}</title>\n  <!--[if mso]>\n  <noscript>\n    <xml>\n      <o:OfficeDocumentSettings>\n        <o:PixelsPerInch>96</o:PixelsPerInch>\n      </o:OfficeDocumentSettings>\n    </xml>\n  </noscript>\n  <![endif]-->\n  <style>\n    body { margin:0; padding:0; background-color:#09090b; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif; color:#fafafa; }\n    table { border-collapse:collapse; }\n    .email-container { max-width:600px; margin:0 auto; background-color:#18181b; border:1px solid #27272a; border-radius:12px; overflow:hidden; }\n    .cta-btn { display:inline-block; background-color:#ffffff; color:#09090b; font-weight:600; padding:12px 28px; border-radius:8px; text-decoration:none; }\n  </style>\n</head>\n<body style=\"margin:0; padding:0; background-color:#09090b; color:#fafafa;\">\n  <!-- Preheader text snippet -->\n  <div style=\"display:none; max-height:0px; overflow:hidden; font-size:1px; line-height:1px; color:#fff; opacity:0;\">\n    ${preview}\n  </div>\n\n  <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"background-color:#09090b;\">\n    <tr>\n      <td align=\"center\" style=\"padding:40px 16px;\">\n        <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"max-width:600px; background-color:#18181b; border:1px solid #27272a; border-radius:12px; overflow:hidden;\">\n          <!-- Header Logo Banner -->\n          <tr>\n            <td style=\"padding:24px 32px; border-bottom:1px solid #27272a;\">\n              <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n                <tr>\n                  <td align=\"left\" style=\"font-weight:700; font-size:18px; color:#ffffff; letter-spacing:-0.02em;\">\n                    ⚡ UIPKGE\n                  </td>\n                  <td align=\"right\" style=\"font-size:12px; color:#a1a1aa;\">\n                    Changelog #24\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n\n          <!-- Hero Section -->\n          <tr>\n            <td style=\"padding:36px 32px 20px 32px;\">\n              <span style=\"display:inline-block; padding:4px 10px; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; background-color:rgba(255,255,255,0.08); color:#a1a1aa; border-radius:9999px; margin-bottom:16px;\">\n                Major Release\n              </span>\n              <h1 style=\"margin:0 0 16px 0; font-size:26px; font-weight:700; line-height:1.25; color:#ffffff; letter-spacing:-0.03em;\">\n                Ship polished UIs in minutes\n              </h1>\n              <p style=\"margin:0 0 24px 0; font-size:15px; line-height:1.6; color:#a1a1aa;\">\n                We completely rebuilt the registry engine. Zero npm dependencies, 100% code ownership, and full native support for Vue 3 and React with modern OKLCH tokens.\n              </p>\n            </td>\n          </tr>\n\n          <!-- Code Snippet Card -->\n          <tr>\n            <td style=\"padding:0 32px 24px 32px;\">\n              <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"background-color:#09090b; border:1px solid #27272a; border-radius:8px;\">\n                <tr>\n                  <td style=\"padding:10px 16px; border-bottom:1px solid #27272a; font-family:monospace; font-size:11px; color:#71717a;\">\n                    Terminal &bull; Quick Install\n                  </td>\n                </tr>\n                <tr>\n                  <td style=\"padding:14px 16px; font-family:monospace; font-size:13px; color:#38bdf8;\">\n                    npx shadcn-vue add @uipkge/button\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n\n          <!-- Primary CTA Button -->\n          <tr>\n            <td align=\"center\" style=\"padding:12px 32px 32px 32px;\">\n              <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n                <tr>\n                  <td align=\"center\" style=\"border-radius:8px; background-color:#ffffff;\">\n                    <a href=\"https://uipkge.dev\" target=\"_blank\" style=\"font-size:14px; font-weight:600; color:#09090b; text-decoration:none; padding:12px 32px; display:inline-block; border-radius:8px;\">\n                      Explore Components &rarr;\n                    </a>\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n\n          <!-- Highlights List -->\n          <tr>\n            <td style=\"padding:24px 32px; border-top:1px solid #27272a; background-color:rgba(255,255,255,0.02);\">\n              <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n                <tr>\n                  <td style=\"padding:8px 0;\">\n                    <strong style=\"color:#ffffff; font-size:14px;\">⚡ Zero Dependency Footprint:</strong>\n                    <span style=\"color:#a1a1aa; font-size:14px;\"> Components live directly in your code.</span>\n                  </td>\n                </tr>\n                <tr>\n                  <td style=\"padding:8px 0;\">\n                    <strong style=\"color:#ffffff; font-size:14px;\">🎨 OKLCH Dark Mode Tokens:</strong>\n                    <span style=\"color:#a1a1aa; font-size:14px;\"> Fluid WCAG AA contrast calibrated for all themes.</span>\n                  </td>\n                </tr>\n                <tr>\n                  <td style=\"padding:8px 0;\">\n                    <strong style=\"color:#ffffff; font-size:14px;\">🧩 Dual-Framework Registry:</strong>\n                    <span style=\"color:#a1a1aa; font-size:14px;\"> Full TypeScript parity for Vue 3 and React.</span>\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n\n          <!-- Footer -->\n          <tr>\n            <td style=\"padding:28px 32px; border-top:1px solid #27272a; text-align:center;\">\n              <p style=\"margin:0 0 10px 0; font-size:12px; color:#71717a;\">\n                UIPKGE Inc. &bull; 548 Market St, Suite 29314, San Francisco, CA 94104\n              </p>\n              <p style=\"margin:0; font-size:12px; color:#71717a;\">\n                <a href=\"https://uipkge.dev\" style=\"color:#a1a1aa; text-decoration:underline;\">Docs</a> &bull;\n                <a href=\"https://github.com/uipkge\" style=\"color:#a1a1aa; text-decoration:underline;\">GitHub</a> &bull;\n                <a href=\"#unsubscribe\" style=\"color:#a1a1aa; text-decoration:underline;\">Unsubscribe</a>\n              </p>\n            </td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n  </table>\n</body>\n</html>`\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  get previewCanvasClass(): string {\n    return cn(\n      'w-full transition-[max-width] duration-300 ease-in-out',\n      this.viewport() === 'desktop' ? 'max-w-[600px]' : 'max-w-[375px]',\n    )\n  }\n\n  async copyHtmlToClipboard(): Promise<void> {\n    try {\n      await navigator.clipboard.writeText(this.rawHtmlOutput())\n      this.isCopied.set(true)\n      this.timers.push(\n        setTimeout(() => {\n          this.isCopied.set(false)\n        }, 2000),\n      )\n    } catch {\n      // Clipboard unavailable (jsdom): leave the button state untouched.\n    }\n  }\n\n  handleSendTest(): void {\n    if (!this.testTargetEmail()) return\n    this.isSendingTest.set(true)\n    this.testSentSuccess.set(false)\n    this.timers.push(\n      setTimeout(() => {\n        this.isSendingTest.set(false)\n        this.testSentSuccess.set(true)\n        this.timers.push(\n          setTimeout(() => {\n            this.testSentSuccess.set(false)\n            this.isTestModalOpen.set(false)\n          }, 2000),\n        )\n      }, 700),\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/email-template-previewer/email-template-previewer.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/email-template-previewer/index.ts",
      "content": "export {\n  UiEmailTemplatePreviewerComponent,\n  type PreviewerClient,\n  type PreviewerViewport,\n} from './email-template-previewer.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/email-template-previewer/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dialog.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Resend and Klaviyo style marketing email designer preview with viewport switcher, subject line tester, deliverability & spam score analyzer, and live test dispatch modal.",
  "categories": [
    "marketing",
    "preview",
    "app"
  ]
}