{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "smart-inbox-triage",
  "title": "Smart Inbox Triage",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/smart-inbox-triage/smart-inbox-triage.component.ts",
      "content": "import {\n  Component,\n  DestroyRef,\n  ElementRef,\n  Input,\n  OnInit,\n  ViewChild,\n  computed,\n  inject,\n  signal,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport {\n  Archive,\n  ArrowDown,\n  ArrowUp,\n  AtSign,\n  Check,\n  CheckCircle2,\n  Clock,\n  Command as CommandIcon,\n  FileText,\n  Forward,\n  LucideAngularModule,\n  Mail,\n  Paperclip,\n  Reply,\n  ReplyAll,\n  Search,\n  Send,\n  Smile,\n  Star,\n  Undo2,\n  X,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport type InboxCategory = 'vip' | 'team' | 'newsletters' | 'archived'\nexport type InboxReplyMode = 'reply' | 'reply-all' | 'forward'\n\nexport interface EmailMessage {\n  id: string\n  sender: {\n    name: string\n    email: string\n    initials: string\n  }\n  recipient: string\n  subject: string\n  snippet: string\n  body: string\n  timestamp: string\n  fullDate: string\n  category: 'vip' | 'team' | 'newsletters'\n  categoryLabel: string\n  badgeVariant?: 'default' | 'secondary' | 'outline' | 'info' | 'success' | 'warning'\n  unread: boolean\n  starred: boolean\n  archived: boolean\n  snoozed?: boolean\n  attachment?: {\n    name: string\n    size: string\n    type: string\n  }\n}\n\nexport interface InboxCategoryTab {\n  id: InboxCategory\n  label: string\n  count: number\n}\n\nexport interface InboxCommand {\n  key: string\n  label: string\n  action: () => void\n}\n\nconst defaultMessages: EmailMessage[] = [\n  {\n    id: 'msg-1',\n    sender: {\n      name: 'Marcus Vance',\n      email: 'marcus.vance@uipkge.internal',\n      initials: 'MV',\n    },\n    recipient: 'you@uipkge.dev, core-eng@uipkge.dev',\n    subject: 'Architecture Review: Real-time Sync & CRDT Conflict Resolution',\n    snippet: 'I’ve updated the design doc with event-sourcing and CRDT tradeoffs for the multi-region sync engine...',\n    body: `Hey team,\n\nI’ve updated the architecture RFC for the multi-region state synchronization engine. Based on yesterday's benchmark results, we are proceeding with hybrid state-based CRDTs for deterministic document convergence.\n\nKey RFC Highlights:\n• P99 sync latency reduced from 140ms to 24ms across edge worker replicas\n• Deterministic Last-Write-Wins (LWW) resolver for metadata mutations\n• Offline reconciliation buffer with automated exponential retry backoff\n• Zero dependency footprint — pure TypeScript state machines\n\nPlease review the attached technical specification and drop your comments before Thursday's design council.\n\nBest,\nMarcus Vance\nPrincipal Systems Architect`,\n    timestamp: '14:28',\n    fullDate: 'Today at 14:28 PM',\n    category: 'vip',\n    categoryLabel: 'VIP Review',\n    badgeVariant: 'default',\n    unread: true,\n    starred: true,\n    archived: false,\n    attachment: {\n      name: 'sync-engine-rfc-v2.4.pdf',\n      size: '2.4 MB',\n      type: 'PDF',\n    },\n  },\n  {\n    id: 'msg-2',\n    sender: {\n      name: 'Sarah Connor',\n      email: 'sarah.connor@uipkge.internal',\n      initials: 'SC',\n    },\n    recipient: 'you@uipkge.dev, exec@uipkge.dev',\n    subject: 'Q2 OKR Sign-off & Headcount Allocation',\n    snippet: 'Final draft from the leadership offsite. Flagged two key decisions before Wednesday lock...',\n    body: `Hi team,\n\nI’ve compiled the finalized Q2 OKR draft following our executive offsite. There are two strategic items requiring your explicit sign-off before we lock the plan Wednesday EOD:\n\n1. Engineering Headcount: 4 vs. 6 senior backend roles\n2. Multi-cloud deployment schedule: Target June instead of August\n\nPlease review and confirm your alignment so we can present to the board.\n\nThanks,\nSarah Connor\nVP of Engineering`,\n    timestamp: '15:40',\n    fullDate: 'Today at 15:40 PM',\n    category: 'vip',\n    categoryLabel: 'VIP Review',\n    badgeVariant: 'default',\n    unread: true,\n    starred: false,\n    archived: false,\n  },\n  {\n    id: 'msg-3',\n    sender: {\n      name: 'Stripe Merchant Services',\n      email: 'payouts@stripe.com',\n      initials: 'ST',\n    },\n    recipient: 'billing@uipkge.dev',\n    subject: 'Payout Confirmed: $18,420.50 USD',\n    snippet:\n      'Your daily payout for May 18 has been processed and initiated to your linked corporate checking account...',\n    body: `Hello UIPKGE Team,\n\nYour automatic daily payout has been initiated and is on its way to your designated bank account ending in •••• 8842.\n\nTransfer Summary:\n• Gross volume (last 24h): $19,250.00\n• Stripe processing fees: -$829.50\n• Net settled transfer: $18,420.50 USD\n• Estimated arrival: Thursday, May 22, 2026\n\nNo further action is required. You can download your official tax statement and transaction logs directly in the Stripe Merchant Portal.`,\n    timestamp: '12:15',\n    fullDate: 'Today at 12:15 PM',\n    category: 'vip',\n    categoryLabel: 'Finance',\n    badgeVariant: 'success',\n    unread: true,\n    starred: false,\n    archived: false,\n  },\n  {\n    id: 'msg-4',\n    sender: {\n      name: 'GitHub Notifications',\n      email: 'notifications@github.com',\n      initials: 'GH',\n    },\n    recipient: 'you@uipkge.dev',\n    subject: '[PR #412] Merged: feat(tokens): OKLCH color scale & dynamic theme engine',\n    snippet: 'Merged by @uday into main with 4 approvals and all CI checks passing in 42s...',\n    body: `Pull Request #412 has been successfully merged into \\`main\\` by @uday.\n\nBranch: feat/oklch-tokens → main\nApprovals: 4 / 4 (Marcus Vance, Elena Rostova, Devon Patel, Sarah Connor)\nCI Workflow: Build, Typecheck, and Parity Verification passed in 42s.\n\nKey Deliverables:\n• Implemented OKLCH lightness/chroma token scale with WCAG AAA contrast\n• Tailwind v4 @theme inline integration with zero CSS variable bloat\n• 100% test coverage across both Vue and React registry packages\n\nAutomated staging preview is now active at https://staging.uipkge.dev.`,\n    timestamp: '10:45',\n    fullDate: 'Today at 10:45 AM',\n    category: 'team',\n    categoryLabel: 'GitHub',\n    badgeVariant: 'info',\n    unread: false,\n    starred: true,\n    archived: false,\n  },\n  {\n    id: 'msg-5',\n    sender: {\n      name: 'Elena Rostova',\n      email: 'elena.rostova@uipkge.internal',\n      initials: 'ER',\n    },\n    recipient: 'you@uipkge.dev, design-system@uipkge.dev',\n    subject: 'Design Tokens OKLCH Migration Spec & Guidelines',\n    snippet: 'Here is the finalized Figma token mapping and color ramp guide for the new high-contrast theme...',\n    body: `Hi team,\n\nFollowing up on PR #412, I’ve published the companion Figma variables library and developer handoff sheet.\n\nWhat’s included in this release:\n1. Semantic tokens: added \\`--surface-elevated\\` and \\`--sidebar-accent\\`\n2. Focus ring contrast ratios audited across all 14 colorways\n3. Mobile breakpoint typography scale verified with 12px min floor\n4. Tactile micro-transitions mapped to \\`--ease-spring\\` (140ms duration)\n\nPlease review the design system documentation and let me know if any component needs tailored adjustments.\n\nWarmly,\nElena Rostova\nLead Product Designer`,\n    timestamp: '09:12',\n    fullDate: 'Today at 09:12 AM',\n    category: 'team',\n    categoryLabel: 'Design',\n    badgeVariant: 'secondary',\n    unread: false,\n    starred: false,\n    archived: false,\n  },\n  {\n    id: 'msg-6',\n    sender: {\n      name: 'Substack / The Unbundled Engineer',\n      email: 'digest@unbundled.dev',\n      initials: 'SE',\n    },\n    recipient: 'you@uipkge.dev',\n    subject: 'The Unbundled Engineer: Issue #84 — The Rise of Component Registries',\n    snippet:\n      'Why unbundled component architecture is replacing bloated npm packages across modern frontend engineering...',\n    body: `Welcome to Issue #84 of The Unbundled Engineer.\n\nThis week: Why the component registry model is winning.\n\n\"When you install a traditional monolithic npm UI library, you inherit its dependency tree, its CSS opinions, and its release bottlenecks. When you add registry source code directly into your own repository, you gain zero-dependency ownership, effortless code customization, and total UI autonomy.\"\n\nIn this edition:\n• Benchmarking bundle sizes: Registry components vs. Monolithic packages\n• Tactile micro-interactions and keyboard-first inbox triage UX\n• The future of dual-framework design systems (Vue 3.5 & React 19)\n\nRead the full deep-dive on our website.`,\n    timestamp: 'Yesterday',\n    fullDate: 'Yesterday at 17:30 PM',\n    category: 'newsletters',\n    categoryLabel: 'Newsletter',\n    badgeVariant: 'warning',\n    unread: false,\n    starred: false,\n    archived: false,\n  },\n]\n\nconst KBD = 'bg-card border-border/80 text-foreground rounded border px-1.5 py-0.5 font-mono text-xs'\nconst ACTION_KBD = 'bg-muted text-muted-foreground border-border/80 ml-0.5 rounded border px-1 font-mono text-xs'\n\nfunction matchesQuery(m: EmailMessage, q: string): boolean {\n  return (\n    m.subject.toLowerCase().includes(q) ||\n    m.sender.name.toLowerCase().includes(q) ||\n    m.sender.email.toLowerCase().includes(q) ||\n    m.snippet.toLowerCase().includes(q) ||\n    m.body.toLowerCase().includes(q)\n  )\n}\n\nfunction inCategory(m: EmailMessage, cat: InboxCategory): boolean {\n  if (cat === 'archived') return m.archived\n  return !m.archived && m.category === cat\n}\n\n/**\n * Angular port of the UIPKGE SmartInboxTriage block (React `SmartInboxTriage`): a keyboard-first\n * inbox with split category tabs, a message list, a reader with triage actions (archive, snooze,\n * star, undo), a quick-reply composer and a ⌘K command palette. Shortcuts listen on `window`\n * like React: E archive, H snooze, R reply, S star, J/K or arrows navigate, / search, 1-4\n * categories, ⌘Z undo, ⌘↵ send, Esc closes / blurs / clears the search. `messages` seeds the\n * inbox once (a deep copy, like React's initial state); state stays inside the component.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-smart-inbox-triage, [ui-smart-inbox-triage]',\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\"', '(window:keydown)': 'onKeydown($event)' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"smart-inbox-triage\" [class]=\"rootClass\">\n      <!-- TOP COMMAND BAR -->\n      <header class=\"bg-card/95 border-border/80 border-b px-4 py-3 backdrop-blur-xs\">\n        <div class=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n          <!-- Left: Inbox Status & Goal Badge -->\n          <div class=\"flex flex-wrap items-center gap-2.5\">\n            <div\n              class=\"bg-primary/10 border-primary/20 text-foreground inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-semibold tracking-tight\"\n            >\n              <span class=\"bg-primary size-2 animate-pulse rounded-full\"></span>\n              <span>Inbox Zero Goal</span>\n              <span class=\"text-primary font-mono tabular-nums\">{{ remainingLabel() }}</span>\n            </div>\n\n            <div\n              class=\"border-border/70 bg-muted/40 text-muted-foreground hidden items-center gap-1.5 rounded-full border px-3 py-1 text-xs xl:inline-flex\"\n            >\n              <span>Shortcuts:</span>\n              <kbd [class]=\"kbd\">E</kbd>\n              <span>Archive</span>\n              <kbd [class]=\"kbd\">H</kbd>\n              <span>Snooze</span>\n              <kbd [class]=\"kbd\">R</kbd>\n              <span>Reply</span>\n              <kbd [class]=\"kbd\">J/K</kbd>\n              <span>Navigate</span>\n            </div>\n          </div>\n\n          <!-- Right: Search Bar & Command Key Trigger -->\n          <div class=\"flex items-center gap-2\">\n            <div class=\"relative w-full sm:w-72\">\n              <!-- [class] (not class=) keeps the absolute offsets on the svg only, not doubled on the host. -->\n              <lucide-icon\n                [img]=\"Search\"\n                [class]=\"'text-muted-foreground absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2'\"\n              />\n              <input\n                #searchInput\n                [value]=\"search()\"\n                (input)=\"search.set(inputValue($event))\"\n                type=\"text\"\n                placeholder=\"Search messages...\"\n                class=\"border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-8 w-full rounded-md border pr-8 pl-8 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none\"\n              />\n              <kbd\n                class=\"bg-muted text-muted-foreground border-border/70 absolute top-1/2 right-2 -translate-y-1/2 rounded border px-1 font-mono text-xs\"\n                >/</kbd\n              >\n            </div>\n\n            <button\n              ui-button\n              aria-label=\"Close command palette\"\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"h-8 shrink-0 gap-1.5 px-2.5 text-xs font-medium\"\n              (click)=\"commandPaletteOpen.set(true)\"\n            >\n              <lucide-icon [img]=\"CommandIcon\" class=\"size-3.5\" />\n              <span class=\"hidden sm:inline\">Commands</span>\n              <kbd class=\"bg-muted text-muted-foreground border-border/60 ml-0.5 rounded border px-1 font-mono text-xs\"\n                >⌘K</kbd\n              >\n            </button>\n          </div>\n        </div>\n\n        <!-- Split Category Tabs Bar -->\n        <div class=\"border-border/40 mt-3 flex items-center justify-between border-t pt-2.5\">\n          <nav class=\"flex flex-wrap items-center gap-1.5\" aria-label=\"Inbox Categories\">\n            @for (cat of categoriesList(); track cat.id) {\n              <button type=\"button\" [class]=\"categoryClass(cat.id)\" (click)=\"selectCategory(cat.id)\">\n                <span>{{ cat.label }}</span>\n                <span [class]=\"categoryCountClass(cat.id)\">{{ cat.count }}</span>\n              </button>\n            }\n          </nav>\n\n          <div class=\"hidden items-center gap-1 sm:flex\">\n            <button\n              ui-button\n              variant=\"ghost\"\n              size=\"sm\"\n              class=\"text-muted-foreground hover:text-foreground h-7 px-2 text-xs\"\n              (click)=\"markAllRead()\"\n            >\n              <lucide-icon [img]=\"Check\" [class]=\"'mr-1 size-3'\" />\n              Mark read\n            </button>\n          </div>\n        </div>\n      </header>\n\n      <!-- 2-COLUMN SPLIT PANE -->\n      <div class=\"grid h-[620px] grid-cols-1 md:grid-cols-[384px_1fr]\">\n        <!-- LEFT: EMAIL LIST (w-96) -->\n        <aside class=\"bg-card border-border flex min-w-0 flex-col border-r\">\n          <!-- List Header -->\n          <div class=\"bg-muted/20 border-border/80 flex h-11 shrink-0 items-center justify-between border-b px-3.5\">\n            <div class=\"flex items-center gap-2\">\n              <span class=\"text-muted-foreground text-xs font-semibold tracking-tight uppercase\">{{\n                activeCategoryLabel()\n              }}</span>\n              <span ui-badge variant=\"outline\" class=\"h-4 px-1.5 font-mono text-xs tabular-nums\">{{\n                visibleMails().length\n              }}</span>\n            </div>\n            <span class=\"text-muted-foreground/80 font-mono text-xs\">[J/K] navigate</span>\n          </div>\n\n          <!-- Scrollable Messages Feed -->\n          <div class=\"list-scroll divide-border/40 flex-1 divide-y overflow-y-auto\">\n            @for (mail of visibleMails(); track mail.id) {\n              <div\n                role=\"button\"\n                tabindex=\"0\"\n                [class]=\"rowClass(mail)\"\n                (click)=\"selectMail(mail.id)\"\n                (keydown)=\"onRowKeydown($event, mail.id)\"\n              >\n                <!-- Active indicator left bar -->\n                @if (mail.id === activeMail()?.id) {\n                  <span class=\"bg-primary absolute top-2 bottom-2 left-0 w-[3px] rounded-r-full\"></span>\n                }\n\n                <!-- Row 1: Sender avatar, name, time, star -->\n                <div class=\"flex items-center justify-between gap-2\">\n                  <div class=\"flex min-w-0 items-center gap-2.5\">\n                    <ui-avatar class=\"size-6 shrink-0 text-xs\">\n                      <ui-avatar-fallback class=\"bg-muted text-xs font-semibold\">{{\n                        mail.sender.initials\n                      }}</ui-avatar-fallback>\n                    </ui-avatar>\n                    <p [class]=\"senderClass(mail)\">{{ mail.sender.name }}</p>\n                  </div>\n\n                  <div class=\"flex shrink-0 items-center gap-1.5\">\n                    <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">{{ mail.timestamp }}</span>\n                    <button\n                      type=\"button\"\n                      [class]=\"rowStarClass(mail)\"\n                      [title]=\"mail.starred ? 'Unstar message' : 'Star message'\"\n                      (click)=\"toggleStar(mail.id, $event)\"\n                    >\n                      <lucide-icon [img]=\"Star\" [class]=\"mail.starred ? 'fill-warning size-3.5' : 'size-3.5'\" />\n                    </button>\n                  </div>\n                </div>\n\n                <!-- Row 2: Subject line with unread bullet -->\n                <div class=\"mt-0.5 flex items-center gap-1.5\">\n                  @if (mail.unread) {\n                    <span class=\"bg-primary size-1.5 shrink-0 rounded-full\"></span>\n                  }\n                  <p [class]=\"subjectClass(mail)\">{{ mail.subject }}</p>\n                </div>\n\n                <!-- Row 3: 1-line snippet -->\n                <p class=\"text-muted-foreground line-clamp-1 text-xs leading-relaxed\">{{ mail.snippet }}</p>\n\n                <!-- Row 4: Category badge and attachment -->\n                <div class=\"mt-1 flex items-center justify-between gap-2\">\n                  <span\n                    ui-badge\n                    [variant]=\"mail.badgeVariant ?? 'secondary'\"\n                    class=\"h-4.5 px-1.5 text-xs font-medium\"\n                    >{{ mail.categoryLabel }}</span\n                  >\n\n                  @if (mail.attachment; as attachment) {\n                    <div class=\"text-muted-foreground flex items-center gap-1 text-xs\">\n                      <lucide-icon [img]=\"Paperclip\" class=\"size-3\" />\n                      <span>{{ attachment.type }}</span>\n                    </div>\n                  }\n                </div>\n              </div>\n            }\n\n            <!-- Empty State -->\n            @if (visibleMails().length === 0) {\n              <div class=\"flex flex-col items-center justify-center px-6 py-20 text-center\">\n                <div\n                  class=\"bg-muted/80 text-muted-foreground mb-3 flex size-12 items-center justify-center rounded-full\"\n                >\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"text-primary size-6\" />\n                </div>\n                <p class=\"text-foreground text-sm font-semibold\">Inbox Zero Achieved</p>\n                <p class=\"text-muted-foreground mt-1 max-w-[220px] text-xs\">\n                  No emails in {{ activeCategoryLabel() }}. You are completely caught up!\n                </p>\n                @if (activeCategory() !== 'archived') {\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"sm\"\n                    class=\"mt-4 h-7 text-xs\"\n                    (click)=\"selectCategory('archived')\"\n                  >\n                    View Archived\n                  </button>\n                }\n              </div>\n            }\n          </div>\n        </aside>\n\n        <!-- RIGHT: SELECTED EMAIL READER & QUICK ACTION -->\n        <main class=\"bg-background flex min-w-0 flex-col overflow-hidden\">\n          @if (activeMail(); as active) {\n            <!-- Top Action Bar -->\n            <div\n              class=\"bg-card/80 border-border flex h-11 shrink-0 items-center justify-between gap-2 overflow-x-auto border-b px-4 backdrop-blur-xs\"\n            >\n              <div class=\"flex items-center gap-1\">\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2.5 text-xs font-medium shadow-xs\"\n                  (click)=\"archiveMail(active.id)\"\n                >\n                  <lucide-icon [img]=\"Archive\" class=\"size-3.5\" />\n                  <span>Archive</span>\n                  <kbd [class]=\"actionKbd\">E</kbd>\n                </button>\n\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2.5 text-xs font-medium shadow-xs\"\n                  (click)=\"snoozeMail(active.id)\"\n                >\n                  <lucide-icon [img]=\"Clock\" class=\"size-3.5\" />\n                  <span>Snooze</span>\n                  <kbd [class]=\"actionKbd\">H</kbd>\n                </button>\n\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2.5 text-xs font-medium shadow-xs\"\n                  (click)=\"focusReply()\"\n                >\n                  <lucide-icon [img]=\"Reply\" class=\"size-3.5\" />\n                  <span>Reply</span>\n                  <kbd [class]=\"actionKbd\">R</kbd>\n                </button>\n\n                <button\n                  ui-button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-7\"\n                  [title]=\"active.starred ? 'Unstar' : 'Star'\"\n                  (click)=\"toggleStar(active.id, $event)\"\n                >\n                  <lucide-icon\n                    [img]=\"Star\"\n                    [class]=\"active.starred ? 'fill-warning text-warning size-4' : 'text-muted-foreground size-4'\"\n                  />\n                </button>\n              </div>\n\n              <!-- Reader Navigation buttons -->\n              <div class=\"flex items-center gap-1\">\n                <button\n                  ui-button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"text-muted-foreground size-7\"\n                  title=\"Previous (K)\"\n                  (click)=\"navigatePrev()\"\n                  aria-label=\"Action\"\n                >\n                  <lucide-icon [img]=\"ArrowUp\" class=\"size-3.5\" />\n                </button>\n                <button\n                  ui-button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"text-muted-foreground size-7\"\n                  title=\"Next (J)\"\n                  (click)=\"navigateNext()\"\n                  aria-label=\"Action\"\n                >\n                  <lucide-icon [img]=\"ArrowDown\" class=\"size-3.5\" />\n                </button>\n              </div>\n            </div>\n\n            <!-- Toast Feedback Notification Banner -->\n            @if (toastMessage()) {\n              <div\n                class=\"bg-primary text-primary-foreground flex items-center justify-between px-4 py-2 text-xs font-medium shadow-xs transition-colors\"\n              >\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"size-3.5\" />\n                  <span>{{ toastMessage() }}</span>\n                </div>\n                @if (hasUndo()) {\n                  <button\n                    type=\"button\"\n                    class=\"bg-primary-foreground/20 hover:bg-primary-foreground/30 inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs font-semibold transition-colors\"\n                    (click)=\"undoLastAction()\"\n                  >\n                    <lucide-icon [img]=\"Undo2\" class=\"size-3\" />\n                    <span>Undo (⌘Z)</span>\n                  </button>\n                }\n              </div>\n            }\n\n            <!-- Reader Scroll Area -->\n            <div class=\"reader-scroll flex-1 overflow-y-auto px-6 py-5\">\n              <!-- Subject Title -->\n              <div class=\"flex items-start justify-between gap-4\">\n                <h1 class=\"text-foreground text-lg font-bold tracking-tight sm:text-xl lg:text-2xl\">\n                  {{ active.subject }}\n                </h1>\n                <span ui-badge [variant]=\"active.badgeVariant ?? 'default'\" class=\"shrink-0 text-xs\">{{\n                  active.categoryLabel\n                }}</span>\n              </div>\n\n              <!-- Sender & Recipient Information Card -->\n              <div class=\"border-border/80 bg-muted/20 mt-4 flex items-start gap-3 rounded-lg border p-3.5\">\n                <ui-avatar class=\"size-9 shrink-0\">\n                  <ui-avatar-fallback class=\"bg-primary/10 text-primary text-xs font-bold\">{{\n                    active.sender.initials\n                  }}</ui-avatar-fallback>\n                </ui-avatar>\n\n                <div class=\"min-w-0 flex-1\">\n                  <div class=\"flex flex-wrap items-baseline justify-between gap-x-2\">\n                    <p class=\"text-foreground min-w-0 truncate text-sm font-semibold\">\n                      {{ active.sender.name }}\n                      <span class=\"text-muted-foreground text-xs font-normal\">&lt;{{ active.sender.email }}&gt;</span>\n                    </p>\n                    <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">{{ active.fullDate }}</span>\n                  </div>\n                  <p class=\"text-muted-foreground mt-0.5 text-xs\">\n                    to <span class=\"text-foreground/80 font-medium\">{{ active.recipient }}</span>\n                  </p>\n                </div>\n              </div>\n\n              <ui-separator class=\"my-5\" />\n\n              <!-- Email Body Content (one line: whitespace-pre-wrap would render template indentation) -->\n              <article class=\"text-foreground/90 space-y-4 text-sm leading-relaxed whitespace-pre-wrap\">\n                {{ active.body }}\n              </article>\n\n              <!-- Attachment Tile -->\n              @if (active.attachment; as attachment) {\n                <div class=\"mt-6\">\n                  <p class=\"text-muted-foreground mb-2 text-xs font-semibold tracking-wider uppercase\">Attachment</p>\n                  <div\n                    class=\"border-border/80 bg-card hover:bg-muted/30 flex max-w-sm items-center justify-between gap-3 rounded-lg border p-3 shadow-xs\"\n                  >\n                    <div class=\"flex items-center gap-3\">\n                      <div class=\"bg-info/10 text-info flex size-9 items-center justify-center rounded-md\">\n                        <lucide-icon [img]=\"FileText\" class=\"size-4\" />\n                      </div>\n                      <div>\n                        <p class=\"text-foreground text-xs font-semibold\">{{ attachment.name }}</p>\n                        <p class=\"text-muted-foreground font-mono text-xs\">\n                          {{ attachment.size }} · {{ attachment.type }}\n                        </p>\n                      </div>\n                    </div>\n                    <button\n                      ui-button\n                      aria-label=\"Download attachment\"\n                      variant=\"outline\"\n                      size=\"sm\"\n                      class=\"h-7 px-2 text-xs\"\n                    >\n                      Download\n                    </button>\n                  </div>\n                </div>\n              }\n            </div>\n\n            <!-- Quick Reply Composer -->\n            <div class=\"border-border bg-card border-t p-3.5\">\n              <div class=\"mb-2 flex items-center justify-between\">\n                <!-- Mode pills -->\n                <div class=\"flex items-center gap-1\">\n                  <button type=\"button\" [class]=\"modeClass('reply')\" (click)=\"replyMode.set('reply')\">\n                    <lucide-icon [img]=\"Reply\" class=\"size-3\" />\n                    Reply\n                  </button>\n                  <button type=\"button\" [class]=\"modeClass('reply-all')\" (click)=\"replyMode.set('reply-all')\">\n                    <lucide-icon [img]=\"ReplyAll\" class=\"size-3\" />\n                    Reply all\n                  </button>\n                  <button type=\"button\" [class]=\"modeClass('forward')\" (click)=\"replyMode.set('forward')\">\n                    <lucide-icon [img]=\"Forward\" class=\"size-3\" />\n                    Forward\n                  </button>\n                </div>\n\n                <span class=\"text-muted-foreground hidden min-w-0 truncate font-mono text-xs lg:inline\"\n                  >To: {{ active.sender.email }}</span\n                >\n              </div>\n\n              <!-- Composer Input Box -->\n              <div\n                class=\"border-input bg-background focus-within:border-ring focus-within:ring-ring/50 relative flex flex-col rounded-lg border shadow-xs focus-within:ring-2\"\n              >\n                <textarea\n                  #replyTextarea\n                  [value]=\"replyDraft()\"\n                  (input)=\"replyDraft.set(inputValue($event))\"\n                  rows=\"2\"\n                  [placeholder]=\"'Write a reply to ' + active.sender.name + '... (⌘↵ to send)'\"\n                  class=\"placeholder:text-muted-foreground w-full resize-none bg-transparent px-3 py-2 text-xs leading-relaxed outline-none\"\n                ></textarea>\n\n                <div class=\"border-border/40 flex items-center justify-between border-t px-2 py-1.5\">\n                  <div class=\"flex items-center gap-0.5\">\n                    <button\n                      ui-button\n                      aria-label=\"Attach file\"\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      class=\"text-muted-foreground hover:text-foreground size-7\"\n                    >\n                      <lucide-icon [img]=\"Paperclip\" class=\"size-3.5\" />\n                    </button>\n                    <button\n                      ui-button\n                      aria-label=\"Add emoji\"\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      class=\"text-muted-foreground hover:text-foreground size-7\"\n                    >\n                      <lucide-icon [img]=\"Smile\" class=\"size-3.5\" />\n                    </button>\n                    <button\n                      ui-button\n                      aria-label=\"Mention someone\"\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      class=\"text-muted-foreground hover:text-foreground size-7\"\n                    >\n                      <lucide-icon [img]=\"AtSign\" class=\"size-3.5\" />\n                    </button>\n                  </div>\n\n                  <div class=\"flex items-center gap-2\">\n                    <kbd\n                      class=\"bg-muted text-muted-foreground border-border/70 hidden rounded border px-1.5 py-0.5 font-mono text-xs sm:inline-flex\"\n                      >⌘↵</kbd\n                    >\n                    <button\n                      ui-button\n                      size=\"sm\"\n                      class=\"h-7 gap-1 px-3 text-xs font-semibold shadow-xs\"\n                      [disabled]=\"!replyDraft().trim()\"\n                      (click)=\"sendReply()\"\n                    >\n                      <span>Send</span>\n                      <lucide-icon [img]=\"Send\" class=\"size-3\" />\n                    </button>\n                  </div>\n                </div>\n              </div>\n            </div>\n          } @else {\n            <!-- No Email Selected Placeholder -->\n            <div class=\"flex flex-1 flex-col items-center justify-center p-8 text-center\">\n              <div class=\"bg-muted/60 text-muted-foreground mb-3 flex size-14 items-center justify-center rounded-full\">\n                <lucide-icon [img]=\"Mail\" class=\"size-7\" />\n              </div>\n              <p class=\"text-foreground text-sm font-semibold\">No message selected</p>\n              <p class=\"text-muted-foreground mt-1 max-w-sm text-xs\">\n                Select an email from the list or use J/K to navigate through your triage queue.\n              </p>\n            </div>\n          }\n        </main>\n      </div>\n\n      <!-- COMMAND PALETTE MODAL (⌘K) -->\n      @if (commandPaletteOpen()) {\n        <div\n          class=\"bg-background/80 fixed inset-0 z-50 flex items-center justify-center p-4 backdrop-blur-xs\"\n          (click)=\"onOverlayClick($event)\"\n        >\n          <div\n            class=\"bg-card text-card-foreground border-border animate-in fade-in-0 zoom-in-95 w-full max-w-lg overflow-hidden rounded-xl border shadow-xl\"\n          >\n            <div class=\"border-border flex items-center border-b px-3 py-2.5\">\n              <lucide-icon [img]=\"CommandIcon\" [class]=\"'text-muted-foreground mr-2 size-4'\" />\n              <input\n                #commandInput\n                [value]=\"commandFilter()\"\n                (input)=\"commandFilter.set(inputValue($event))\"\n                type=\"text\"\n                placeholder=\"Type a command or shortcut...\"\n                class=\"placeholder:text-muted-foreground w-full bg-transparent text-xs outline-none\"\n              />\n              <button\n                type=\"button\"\n                class=\"text-muted-foreground hover:text-foreground ml-2 rounded p-1\"\n                aria-label=\"Close command palette\"\n                (click)=\"commandPaletteOpen.set(false)\"\n              >\n                <lucide-icon [img]=\"X\" class=\"size-4\" />\n              </button>\n            </div>\n\n            <div class=\"max-h-72 overflow-y-auto p-2\">\n              <p class=\"text-muted-foreground px-2 py-1 text-xs font-semibold tracking-wider uppercase\">\n                High Velocity Actions\n              </p>\n              @for (item of filteredCommandPaletteItems(); track item.key) {\n                <button\n                  type=\"button\"\n                  class=\"hover:bg-muted/70 flex w-full items-center justify-between rounded-md px-2.5 py-1.5 text-left text-xs transition-colors\"\n                  (click)=\"executeCommand(item)\"\n                >\n                  <span>{{ item.label }}</span>\n                  <kbd\n                    class=\"bg-muted text-muted-foreground border-border/80 rounded border px-1.5 py-0.5 font-mono text-xs\"\n                    >{{ item.key }}</kbd\n                  >\n                </button>\n              }\n            </div>\n\n            <div\n              class=\"bg-muted/40 border-border text-muted-foreground flex items-center justify-between border-t px-3 py-2 text-xs\"\n            >\n              <span>Use <strong>Esc</strong> to close</span>\n              <span>Superhuman Mode Active</span>\n            </div>\n          </div>\n        </div>\n      }\n    </div>\n  `,\n})\nexport class UiSmartInboxTriageComponent implements OnInit {\n  @Input() initialCategory: InboxCategory = 'vip'\n  @Input() initialSearch = ''\n  @Input() initialSelectedId = 'msg-1'\n  /** Seeds the inbox once (deep-copied, like React's initial state). Defaults to the sample inbox. */\n  @Input() messages?: EmailMessage[]\n  @Input('class') className?: string\n\n  @ViewChild('searchInput', { static: true }) searchInput?: ElementRef<HTMLInputElement>\n  @ViewChild('replyTextarea') replyTextarea?: ElementRef<HTMLTextAreaElement>\n  /** React `autoFocus` on the palette input: focus it when it mounts. */\n  @ViewChild('commandInput') set commandInput(ref: ElementRef<HTMLInputElement> | undefined) {\n    ref?.nativeElement.focus()\n  }\n\n  readonly mails = signal<EmailMessage[]>(defaultMessages)\n  readonly activeCategory = signal<InboxCategory>('vip')\n  readonly search = signal('')\n  readonly activeId = signal('msg-1')\n  readonly replyMode = signal<InboxReplyMode>('reply')\n  readonly replyDraft = signal('')\n  readonly toastMessage = signal('')\n  readonly hasUndo = signal(false)\n  readonly lastArchivedId = signal<string | null>(null)\n  readonly commandPaletteOpen = signal(false)\n  readonly commandFilter = signal('')\n\n  private toastTimer?: ReturnType<typeof setTimeout>\n\n  protected readonly kbd = KBD\n  protected readonly actionKbd = ACTION_KBD\n\n  protected readonly Archive = Archive\n  protected readonly ArrowDown = ArrowDown\n  protected readonly ArrowUp = ArrowUp\n  protected readonly AtSign = AtSign\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Clock = Clock\n  protected readonly CommandIcon = CommandIcon\n  protected readonly FileText = FileText\n  protected readonly Forward = Forward\n  protected readonly Mail = Mail\n  protected readonly Paperclip = Paperclip\n  protected readonly Reply = Reply\n  protected readonly ReplyAll = ReplyAll\n  protected readonly Search = Search\n  protected readonly Send = Send\n  protected readonly Smile = Smile\n  protected readonly Star = Star\n  protected readonly Undo2 = Undo2\n  protected readonly X = X\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => clearTimeout(this.toastTimer))\n  }\n\n  ngOnInit(): void {\n    // React seeds its useState once from these props; inputs land after construction.\n    this.mails.set(this.messages ? JSON.parse(JSON.stringify(this.messages)) : defaultMessages)\n    this.activeCategory.set(this.initialCategory)\n    this.search.set(this.initialSearch)\n    this.activeId.set(this.initialSelectedId)\n  }\n\n  readonly remainingCount = computed(() => this.mails().filter((m) => !m.archived).length)\n  readonly remainingLabel = computed(() =>\n    this.remainingCount() === 0 ? 'Zero Achieved 🎉' : `${this.remainingCount()} Messages Remaining`,\n  )\n\n  readonly categoriesList = computed<InboxCategoryTab[]>(() => {\n    const mails = this.mails()\n    const count = (cat: InboxCategory) => mails.filter((m) => inCategory(m, cat)).length\n    return [\n      { id: 'vip', label: 'Important & VIP', count: count('vip') },\n      { id: 'team', label: 'Team & GitHub', count: count('team') },\n      { id: 'newsletters', label: 'Newsletters', count: count('newsletters') },\n      { id: 'archived', label: 'Done / Archived', count: count('archived') },\n    ]\n  })\n\n  readonly activeCategoryLabel = computed(\n    () => this.categoriesList().find((c) => c.id === this.activeCategory())?.label ?? '',\n  )\n\n  readonly visibleMails = computed(() => this.filterFor(this.activeCategory()))\n\n  readonly activeMail = computed<EmailMessage | null>(() => {\n    const id = this.activeId()\n    const visible = this.visibleMails()\n    if (!id) return visible[0] ?? null\n    return this.mails().find((m) => m.id === id) ?? visible[0] ?? null\n  })\n\n  readonly commandPaletteItems: InboxCommand[] = [\n    { key: 'E', label: 'Archive active email', action: () => this.archiveMail() },\n    { key: 'H', label: 'Snooze until tomorrow', action: () => this.snoozeMail() },\n    { key: 'R', label: 'Reply to sender', action: () => this.focusReply() },\n    { key: 'S', label: 'Toggle star / priority', action: () => this.toggleStar() },\n    { key: '1', label: 'Switch to Important & VIP', action: () => this.selectCategory('vip') },\n    { key: '2', label: 'Switch to Team & GitHub', action: () => this.selectCategory('team') },\n    { key: '3', label: 'Switch to Newsletters', action: () => this.selectCategory('newsletters') },\n    { key: '4', label: 'Switch to Done / Archived', action: () => this.selectCategory('archived') },\n    { key: 'Shift+A', label: 'Mark all as read in current category', action: () => this.markAllRead() },\n    { key: 'Shift+E', label: 'Archive all in category', action: () => this.archiveAllInCategory() },\n  ]\n\n  readonly filteredCommandPaletteItems = computed(() => {\n    const q = this.commandFilter().trim().toLowerCase()\n    if (!q) return this.commandPaletteItems\n    return this.commandPaletteItems.filter((i) => i.label.toLowerCase().includes(q) || i.key.toLowerCase().includes(q))\n  })\n\n  get rootClass(): string {\n    return cn(\n      'bg-card text-card-foreground border-border relative flex flex-col overflow-hidden rounded-xl border shadow-xs',\n      this.className,\n    )\n  }\n\n  /** Messages of `cat`, narrowed by the current search (React's visibleMails / selectCategory filter). */\n  private filterFor(cat: InboxCategory): EmailMessage[] {\n    const q = this.search().trim().toLowerCase()\n    const list = this.mails().filter((m) => inCategory(m, cat))\n    return q ? list.filter((m) => matchesQuery(m, q)) : list\n  }\n\n  private patch(id: string, change: Partial<EmailMessage>): void {\n    this.mails.update((prev) => prev.map((m) => (m.id === id ? { ...m, ...change } : m)))\n  }\n\n  showToast(msg: string, allowUndo = false): void {\n    this.toastMessage.set(msg)\n    this.hasUndo.set(allowUndo)\n    clearTimeout(this.toastTimer)\n    this.toastTimer = setTimeout(() => {\n      this.toastMessage.set('')\n      this.hasUndo.set(false)\n    }, 4000)\n  }\n\n  selectMail(id: string): void {\n    this.activeId.set(id)\n    this.patch(id, { unread: false })\n  }\n\n  selectCategory(cat: InboxCategory): void {\n    const first = this.filterFor(cat)[0]\n    this.activeCategory.set(cat)\n    if (first) this.selectMail(first.id)\n    else this.activeId.set('')\n  }\n\n  archiveMail(id?: string): void {\n    this.triage(id, false)\n  }\n\n  snoozeMail(id?: string): void {\n    this.triage(id, true)\n  }\n\n  /** Archive (or snooze) one message, keep it undoable and select the next visible one. */\n  private triage(id: string | undefined, snoozed: boolean): void {\n    const targetId = id ?? this.activeMail()?.id\n    if (!targetId) return\n    const item = this.mails().find((m) => m.id === targetId)\n    if (!item) return\n\n    const remainingVisible = this.visibleMails().filter((m) => m.id !== targetId)\n    this.patch(targetId, { archived: true, snoozed })\n    this.lastArchivedId.set(targetId)\n    this.showToast(snoozed ? 'Snoozed until tomorrow 08:00 AM' : `Archived: \"${item.subject}\"`, true)\n\n    const next = remainingVisible[0]\n    if (next) this.selectMail(next.id)\n    else this.activeId.set('')\n  }\n\n  undoLastAction(): void {\n    const lastId = this.lastArchivedId()\n    if (!lastId) return\n    const item = this.mails().find((m) => m.id === lastId)\n    if (item) {\n      this.patch(lastId, { archived: false, snoozed: false })\n      this.activeCategory.set(item.category)\n      this.activeId.set(item.id)\n      this.showToast(`Restored: \"${item.subject}\"`)\n    }\n    this.lastArchivedId.set(null)\n    this.hasUndo.set(false)\n  }\n\n  toggleStar(id?: string, e?: Event): void {\n    e?.stopPropagation()\n    const targetId = id ?? this.activeMail()?.id\n    if (!targetId) return\n    this.mails.update((prev) => prev.map((m) => (m.id === targetId ? { ...m, starred: !m.starred } : m)))\n  }\n\n  navigateNext(): void {\n    const visible = this.visibleMails()\n    if (visible.length === 0) return\n    const currentIndex = visible.findIndex((m) => m.id === this.activeId())\n    const next = currentIndex === -1 || currentIndex >= visible.length - 1 ? visible[0] : visible[currentIndex + 1]\n    if (next) this.selectMail(next.id)\n  }\n\n  navigatePrev(): void {\n    const visible = this.visibleMails()\n    if (visible.length === 0) return\n    const currentIndex = visible.findIndex((m) => m.id === this.activeId())\n    const prev = currentIndex <= 0 ? visible[visible.length - 1] : visible[currentIndex - 1]\n    if (prev) this.selectMail(prev.id)\n  }\n\n  sendReply(): void {\n    const content = this.replyDraft().trim()\n    const mail = this.activeMail()\n    if (!content || !mail) return\n    this.replyDraft.set('')\n    this.showToast(`Reply sent to ${mail.sender.name}`)\n  }\n\n  markAllRead(): void {\n    const cat = this.activeCategory()\n    this.mails.update((prev) => prev.map((m) => (inCategory(m, cat) ? { ...m, unread: false } : m)))\n    this.showToast('Marked all as read')\n  }\n\n  archiveAllInCategory(): void {\n    const cat = this.activeCategory()\n    if (cat === 'archived') return\n    this.mails.update((prev) => prev.map((m) => (!m.archived && m.category === cat ? { ...m, archived: true } : m)))\n    this.showToast('All messages in category archived')\n    this.activeId.set('')\n  }\n\n  focusReply(): void {\n    this.replyTextarea?.nativeElement.focus()\n  }\n\n  executeCommand(item: InboxCommand): void {\n    item.action()\n    this.commandPaletteOpen.set(false)\n    this.commandFilter.set('')\n  }\n\n  onOverlayClick(e: MouseEvent): void {\n    if (e.target === e.currentTarget) this.commandPaletteOpen.set(false)\n  }\n\n  onRowKeydown(e: KeyboardEvent, id: string): void {\n    if (e.key === 'Enter' || e.key === ' ') {\n      e.preventDefault()\n      this.selectMail(id)\n    }\n  }\n\n  /** Global keyboard triage (window keydown), same map and guards as React. */\n  onKeydown(e: KeyboardEvent): void {\n    const key = (e.key ?? '').toLowerCase()\n    const activeEl = document.activeElement\n    const isInputActive =\n      activeEl instanceof HTMLInputElement ||\n      activeEl instanceof HTMLTextAreaElement ||\n      activeEl?.getAttribute('contenteditable') === 'true'\n\n    if ((e.metaKey || e.ctrlKey) && key === 'k') {\n      e.preventDefault()\n      this.commandPaletteOpen.update((open) => !open)\n      return\n    }\n\n    if ((e.metaKey || e.ctrlKey) && key === 'z') {\n      e.preventDefault()\n      this.undoLastAction()\n      return\n    }\n\n    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n      if (this.replyDraft().trim()) {\n        e.preventDefault()\n        this.sendReply()\n        return\n      }\n    }\n\n    if (e.key === 'Escape') {\n      if (this.commandPaletteOpen()) {\n        e.preventDefault()\n        this.commandPaletteOpen.set(false)\n        return\n      }\n      if (isInputActive) {\n        ;(activeEl as HTMLElement).blur()\n        return\n      }\n      if (this.search()) {\n        this.search.set('')\n        return\n      }\n    }\n\n    if (isInputActive) return\n\n    switch (key) {\n      case 'j':\n      case 'arrowdown':\n        e.preventDefault()\n        this.navigateNext()\n        break\n      case 'k':\n      case 'arrowup':\n        e.preventDefault()\n        this.navigatePrev()\n        break\n      case 'e':\n        e.preventDefault()\n        this.archiveMail()\n        break\n      case 'h':\n        e.preventDefault()\n        this.snoozeMail()\n        break\n      case 'r':\n        e.preventDefault()\n        this.focusReply()\n        break\n      case 's':\n        e.preventDefault()\n        this.toggleStar()\n        break\n      case '/':\n        e.preventDefault()\n        this.searchInput?.nativeElement.focus()\n        this.searchInput?.nativeElement.select()\n        break\n      case '1':\n        e.preventDefault()\n        this.selectCategory('vip')\n        break\n      case '2':\n        e.preventDefault()\n        this.selectCategory('team')\n        break\n      case '3':\n        e.preventDefault()\n        this.selectCategory('newsletters')\n        break\n      case '4':\n        e.preventDefault()\n        this.selectCategory('archived')\n        break\n    }\n  }\n\n  protected inputValue(e: Event): string {\n    return (e.target as HTMLInputElement | HTMLTextAreaElement).value\n  }\n\n  protected categoryClass(id: InboxCategory): string {\n    return cn(\n      'inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-xs font-medium transition-colors duration-150',\n      this.activeCategory() === id\n        ? 'bg-primary text-primary-foreground shadow-xs'\n        : 'text-muted-foreground hover:bg-muted/70 hover:text-foreground',\n    )\n  }\n\n  protected categoryCountClass(id: InboxCategory): string {\n    return cn(\n      'inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 font-mono text-xs font-semibold tabular-nums',\n      this.activeCategory() === id ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-muted text-foreground',\n    )\n  }\n\n  protected rowClass(mail: EmailMessage): string {\n    return cn(\n      'group relative flex cursor-pointer flex-col gap-1 px-4 py-3 text-left transition-colors duration-150',\n      mail.id === this.activeMail()?.id ? 'bg-accent/70 text-accent-foreground' : 'hover:bg-muted/40 text-foreground',\n    )\n  }\n\n  protected senderClass(mail: EmailMessage): string {\n    return cn('truncate text-xs', mail.unread ? 'text-foreground font-semibold' : 'text-foreground/80 font-medium')\n  }\n\n  protected subjectClass(mail: EmailMessage): string {\n    return cn(\n      'truncate text-xs tracking-tight',\n      mail.unread ? 'text-foreground font-semibold' : 'text-foreground/90 font-medium',\n    )\n  }\n\n  protected rowStarClass(mail: EmailMessage): string {\n    return cn(\n      'hover:text-warning inline-flex size-6 items-center justify-center rounded transition-colors',\n      mail.starred ? 'text-warning' : 'text-muted-foreground/40 hover:text-muted-foreground',\n    )\n  }\n\n  protected modeClass(mode: InboxReplyMode): string {\n    return cn(\n      'inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n      this.replyMode() === mode ? 'bg-primary/10 text-primary font-semibold' : 'text-muted-foreground hover:bg-muted',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/smart-inbox-triage/smart-inbox-triage.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/smart-inbox-triage/index.ts",
      "content": "export {\n  UiSmartInboxTriageComponent,\n  type EmailMessage,\n  type InboxCategory,\n  type InboxCategoryTab,\n  type InboxCommand,\n  type InboxReplyMode,\n} from './smart-inbox-triage.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/smart-inbox-triage/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Superhuman-style high-velocity email inbox triage command center with keyboard shortcuts, split category tabs (VIP, Team & GitHub, Newsletters, Archived), dual-pane split reader, fast triage actions (Archive, Snooze, Star, Quick Reply), and command palette.",
  "categories": [
    "communication",
    "productivity",
    "layout"
  ]
}