{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "cdc-replication-stream",
  "title": "Cdc Replication Stream",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/cdc-replication-stream/cdc-replication-stream.component.ts",
      "content": "import { ChangeDetectionStrategy, Component, DestroyRef, Input, OnInit, computed, inject, signal } from '@angular/core'\nimport {\n  Activity,\n  ArrowRight,\n  ArrowRightLeft,\n  Check,\n  ChevronDown,\n  ChevronRight,\n  Copy,\n  Database,\n  Download,\n  Filter,\n  Layers,\n  LucideAngularModule,\n  Pause,\n  Play,\n  RefreshCw,\n  Search,\n  Server,\n  ShieldCheck,\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 { UiInputComponent } from '@/ui/input/input.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport type CdcOperation = 'INSERT' | 'UPDATE' | 'DELETE'\nexport type CdcOperationFilter = 'all' | CdcOperation\n\nexport interface CdcTableMetric {\n  name: string\n  schema: string\n  primaryKey: string\n  syncedRows: number\n  replicationDelayMs: number\n  status: 'synchronized' | 'syncing' | 'paused'\n  operations: {\n    insertPercent: number\n    updatePercent: number\n    deletePercent: number\n  }\n  mode: string\n}\n\nexport interface CdcStreamEvent {\n  id: string\n  timestamp: string\n  table: string\n  operation: CdcOperation\n  primaryKey: string\n  binlogPosition: string\n  lsn: string\n  txId: string\n  summary: string\n  modifiedFields?: string[]\n  before?: Record<string, unknown>\n  after?: Record<string, unknown>\n}\n\nconst defaultTables: CdcTableMetric[] = [\n  {\n    name: 'users',\n    schema: 'public',\n    primaryKey: 'id [UUID]',\n    syncedRows: 1842910,\n    replicationDelayMs: 12,\n    status: 'synchronized',\n    operations: {\n      insertPercent: 65,\n      updatePercent: 30,\n      deletePercent: 5,\n    },\n    mode: 'Continuous WAL',\n  },\n  {\n    name: 'orders',\n    schema: 'public',\n    primaryKey: 'order_id [BIGINT]',\n    syncedRows: 2145820,\n    replicationDelayMs: 16,\n    status: 'synchronized',\n    operations: {\n      insertPercent: 78,\n      updatePercent: 20,\n      deletePercent: 2,\n    },\n    mode: 'Continuous WAL',\n  },\n  {\n    name: 'transactions',\n    schema: 'public',\n    primaryKey: 'tx_hash [VARCHAR]',\n    syncedRows: 624300,\n    replicationDelayMs: 18,\n    status: 'synchronized',\n    operations: {\n      insertPercent: 92,\n      updatePercent: 7,\n      deletePercent: 1,\n    },\n    mode: 'Continuous WAL',\n  },\n  {\n    name: 'subscriptions',\n    schema: 'public',\n    primaryKey: 'sub_id [UUID]',\n    syncedRows: 208440,\n    replicationDelayMs: 14,\n    status: 'synchronized',\n    operations: {\n      insertPercent: 45,\n      updatePercent: 50,\n      deletePercent: 5,\n    },\n    mode: 'Continuous WAL',\n  },\n]\n\nconst defaultEvents: CdcStreamEvent[] = [\n  {\n    id: 'evt-cdc-104',\n    timestamp: '2026-08-21T14:32:05.812Z',\n    table: 'public.subscriptions',\n    operation: 'UPDATE',\n    primaryKey: 'sub_99a8b1c4',\n    binlogPosition: 'mysql-bin.000412 : 8492014',\n    lsn: '0/16B38E0',\n    txId: 'tx_883019',\n    summary: 'status: \"trialing\" → \"active\", plan: \"starter_monthly\" → \"enterprise_annual\", seats: 5 → 25',\n    modifiedFields: ['status', 'plan', 'seats', 'monthly_amount', 'trial_ends_at', 'updated_at'],\n    before: {\n      id: 'sub_99a8b1c4',\n      user_id: 'usr_44018',\n      plan: 'starter_monthly',\n      status: 'trialing',\n      seats: 5,\n      monthly_amount: 49.0,\n      auto_renew: true,\n      trial_ends_at: '2026-08-21T23:59:59Z',\n      updated_at: '2026-08-14T10:00:00Z',\n    },\n    after: {\n      id: 'sub_99a8b1c4',\n      user_id: 'usr_44018',\n      plan: 'enterprise_annual',\n      status: 'active',\n      seats: 25,\n      monthly_amount: 490.0,\n      auto_renew: true,\n      trial_ends_at: null,\n      updated_at: '2026-08-21T14:32:05Z',\n    },\n  },\n  {\n    id: 'evt-cdc-103',\n    timestamp: '2026-08-21T14:32:04.195Z',\n    table: 'public.orders',\n    operation: 'INSERT',\n    primaryKey: 'ord_8819203',\n    binlogPosition: 'mysql-bin.000412 : 8491820',\n    lsn: '0/16B3698',\n    txId: 'tx_883018',\n    summary: 'New checkout order created for $1,280.00 (Customer: usr_99120, 3 line items)',\n    modifiedFields: [],\n    after: {\n      order_id: 'ord_8819203',\n      customer_id: 'usr_99120',\n      currency: 'USD',\n      total_amount: 1280.0,\n      tax_amount: 102.4,\n      status: 'paid',\n      payment_gateway: 'stripe_card',\n      shipping_country: 'US',\n      created_at: '2026-08-21T14:32:04Z',\n    },\n  },\n  {\n    id: 'evt-cdc-102',\n    timestamp: '2026-08-21T14:31:59.604Z',\n    table: 'public.transactions',\n    operation: 'UPDATE',\n    primaryKey: 'tx_0x99b1f41',\n    binlogPosition: 'mysql-bin.000412 : 8491412',\n    lsn: '0/16B3440',\n    txId: 'tx_883017',\n    summary: 'status: \"processing\" → \"settled\", settled_at timestamp set',\n    modifiedFields: ['status', 'settled_at'],\n    before: {\n      tx_hash: 'tx_0x99b1f41',\n      order_id: 'ord_8819198',\n      amount: 349.5,\n      status: 'processing',\n      settled_at: null,\n      fee_cents: 1045,\n    },\n    after: {\n      tx_hash: 'tx_0x99b1f41',\n      order_id: 'ord_8819198',\n      amount: 349.5,\n      status: 'settled',\n      settled_at: '2026-08-21T14:31:59Z',\n      fee_cents: 1045,\n    },\n  },\n  {\n    id: 'evt-cdc-101',\n    timestamp: '2026-08-21T14:31:52.410Z',\n    table: 'public.users',\n    operation: 'INSERT',\n    primaryKey: 'usr_99304',\n    binlogPosition: 'mysql-bin.000412 : 8490980',\n    lsn: '0/16B31F0',\n    txId: 'tx_883016',\n    summary: 'New user registration: elena.rostova@cloudscale.io (Org: org_7720)',\n    modifiedFields: [],\n    after: {\n      id: 'usr_99304',\n      email: 'elena.rostova@cloudscale.io',\n      full_name: 'Elena Rostova',\n      org_id: 'org_7720',\n      role: 'data_engineer',\n      email_verified: true,\n      mfa_enabled: true,\n      created_at: '2026-08-21T14:31:52Z',\n    },\n  },\n  {\n    id: 'evt-cdc-100',\n    timestamp: '2026-08-21T14:31:40.118Z',\n    table: 'public.subscriptions',\n    operation: 'DELETE',\n    primaryKey: 'sub_7718021',\n    binlogPosition: 'mysql-bin.000412 : 8490410',\n    lsn: '0/16B2FA0',\n    txId: 'tx_883015',\n    summary: 'Hard purge of cancelled expired trial subscription sub_7718021',\n    modifiedFields: [],\n    before: {\n      id: 'sub_7718021',\n      user_id: 'usr_22910',\n      plan: 'developer_free',\n      status: 'cancelled',\n      seats: 1,\n      deleted_at: '2026-08-21T14:31:40Z',\n    },\n  },\n]\n\nconst currentBinlog = 'mysql-bin.000412 : 8492014'\n\nexport function matchesSearch(event: CdcStreamEvent, query: string): boolean {\n  if (!query.trim()) return true\n  const q = query.trim()\n\n  if (q.startsWith('/') && q.length > 1) {\n    const lastSlash = q.lastIndexOf('/')\n    if (lastSlash > 0) {\n      const pattern = q.slice(1, lastSlash)\n      const flags = q.slice(lastSlash + 1)\n      try {\n        const re = new RegExp(pattern, flags)\n        return (\n          re.test(event.table) ||\n          re.test(event.operation) ||\n          re.test(event.primaryKey) ||\n          re.test(event.binlogPosition) ||\n          re.test(event.lsn) ||\n          re.test(event.txId) ||\n          re.test(event.summary) ||\n          re.test(JSON.stringify(event.before ?? {})) ||\n          re.test(JSON.stringify(event.after ?? {}))\n        )\n      } catch {\n        // Fall back to plain search on invalid regex\n      }\n    }\n  }\n\n  const lower = q.toLowerCase()\n  return (\n    event.table.toLowerCase().includes(lower) ||\n    event.operation.toLowerCase().includes(lower) ||\n    event.primaryKey.toLowerCase().includes(lower) ||\n    event.binlogPosition.toLowerCase().includes(lower) ||\n    event.lsn.toLowerCase().includes(lower) ||\n    event.txId.toLowerCase().includes(lower) ||\n    event.summary.toLowerCase().includes(lower) ||\n    JSON.stringify(event.before ?? {})\n      .toLowerCase()\n      .includes(lower) ||\n    JSON.stringify(event.after ?? {})\n      .toLowerCase()\n      .includes(lower)\n  )\n}\n\nfunction formatTimestamp(iso: string): string {\n  try {\n    const d = new Date(iso)\n    return d.toISOString().replace('T', ' ').replace('Z', ' UTC')\n  } catch {\n    return iso\n  }\n}\n\nfunction formatNumber(num: number): string {\n  return new Intl.NumberFormat('en-US').format(num)\n}\n\nconst pillBase =\n  'focus-visible:ring-ring inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-cdc-replication-stream, [ui-cdc-replication-stream]',\n  standalone: true,\n  // React renders the root <div> itself: keep this wrapper out of the layout.\n  host: { class: 'contents' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n    UiTableComponent,\n    UiTableHeaderComponent,\n    UiTableBodyComponent,\n    UiTableRowComponent,\n    UiTableHeadComponent,\n    UiTableCellComponent,\n  ],\n  template: `\n    <div data-slot=\"cdc-replication-stream\" [class]=\"rootClass\">\n      <!-- Header: Sync Pipeline Overview & Global Controls -->\n      <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <div class=\"flex items-start gap-3 sm:items-center\">\n          <div class=\"bg-card flex size-10 shrink-0 items-center justify-center rounded-lg border shadow-xs\">\n            <lucide-icon [img]=\"ArrowRightLeft\" class=\"text-primary size-5\" aria-hidden=\"true\" />\n          </div>\n          <div class=\"space-y-1\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <h2 class=\"text-foreground text-2xl font-bold tracking-tight\">CDC Replication Stream</h2>\n\n              <!-- Real-Time Status Badge -->\n              @if (isSnapshotting()) {\n                <div\n                  class=\"border-info/30 bg-info/10 text-info inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium\"\n                >\n                  <lucide-icon [img]=\"RefreshCw\" class=\"size-3 animate-spin\" aria-hidden=\"true\" />\n                  <span>Snapshot Ingest Active</span>\n                </div>\n              } @else if (!isPaused()) {\n                <div\n                  class=\"border-success/30 bg-success/10 text-success inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium\"\n                >\n                  <span class=\"relative flex size-2 shrink-0\">\n                    <span class=\"bg-success absolute inline-flex h-full w-full rounded-full opacity-75\"></span>\n                    <span class=\"bg-success relative inline-flex size-2 rounded-full\"></span>\n                  </span>\n                  <span>Replication Streaming · 18ms Latency</span>\n                </div>\n              } @else {\n                <div\n                  class=\"border-warning/30 bg-warning/10 text-warning inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium\"\n                >\n                  <span class=\"bg-warning relative inline-flex size-2 rounded-full\"></span>\n                  <span>Replication Paused</span>\n                </div>\n              }\n            </div>\n\n            <!-- Pipeline Architecture Breadcrumb -->\n            <div class=\"flex flex-wrap items-center gap-1.5 text-xs\">\n              <span class=\"text-foreground font-semibold\">Postgres Source (OLTP)</span>\n              <lucide-icon [img]=\"ArrowRight\" class=\"text-muted-foreground size-3.5\" aria-hidden=\"true\" />\n              <span class=\"text-primary font-semibold\">Snowflake DW (OLAP)</span>\n              <span class=\"text-muted-foreground hidden font-mono sm:inline\">\n                Debezium 2.7 · Apache Kafka · pgoutput\n              </span>\n            </div>\n          </div>\n        </div>\n\n        <!-- Header Action Controls & Binlog Pointer -->\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <!-- Binlog Position Indicator Chip -->\n          <button\n            type=\"button\"\n            aria-label=\"Copy current binlog position\"\n            class=\"bg-muted/50 hover:bg-muted focus-visible:ring-ring flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n            (click)=\"copyBinlogPosition()\"\n          >\n            <lucide-icon [img]=\"Terminal\" class=\"text-muted-foreground size-3.5\" aria-hidden=\"true\" />\n            <span class=\"text-muted-foreground\">Binlog:</span>\n            <span class=\"text-foreground font-mono font-medium\">{{ currentBinlog }}</span>\n            @if (copiedBinlog()) {\n              <lucide-icon [img]=\"Check\" class=\"text-success size-3\" aria-hidden=\"true\" />\n            } @else {\n              <lucide-icon [img]=\"Copy\" class=\"text-muted-foreground size-3 opacity-70\" aria-hidden=\"true\" />\n            }\n          </button>\n\n          <button\n            ui-button\n            variant=\"outline\"\n            size=\"sm\"\n            [disabled]=\"isSnapshotting()\"\n            class=\"gap-1.5\"\n            (click)=\"triggerSnapshot()\"\n          >\n            <lucide-icon [img]=\"RefreshCw\" [class]=\"snapshotIconClass()\" aria-hidden=\"true\" />\n            <span>{{ isSnapshotting() ? 'Syncing Snapshot...' : 'Force Sync Snapshot' }}</span>\n          </button>\n\n          <button\n            ui-button\n            variant=\"outline\"\n            size=\"sm\"\n            [class]=\"isPaused() ? 'border-warning/40 text-warning' : ''\"\n            (click)=\"togglePause()\"\n          >\n            @if (isPaused()) {\n              <lucide-icon [img]=\"Play\" class=\"size-3.5 fill-current\" aria-hidden=\"true\" />\n            } @else {\n              <lucide-icon [img]=\"Pause\" class=\"size-3.5\" aria-hidden=\"true\" />\n            }\n            <span>{{ isPaused() ? 'Resume Stream' : 'Pause Stream' }}</span>\n          </button>\n\n          <button\n            ui-button\n            aria-label=\"Download attachment\"\n            variant=\"default\"\n            size=\"sm\"\n            class=\"gap-1.5\"\n            (click)=\"exportJson()\"\n          >\n            @if (exportedNotice()) {\n              <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" aria-hidden=\"true\" />\n            } @else {\n              <lucide-icon [img]=\"Download\" class=\"size-3.5\" aria-hidden=\"true\" />\n            }\n            <span>{{ exportedNotice() ? 'Exported' : 'Export JSON' }}</span>\n          </button>\n        </div>\n      </div>\n\n      <!-- Snapshot Notification Banner -->\n      @if (snapshotNotice()) {\n        <div\n          class=\"text-foreground border-info/30 bg-info/10 flex items-center justify-between rounded-lg border p-3 text-xs shadow-xs\"\n        >\n          <div class=\"flex items-center gap-2.5\">\n            <lucide-icon [img]=\"RefreshCw\" class=\"text-info size-4 shrink-0\" aria-hidden=\"true\" />\n            <div>\n              <span class=\"text-info font-semibold\">Snapshot sync initiated:</span>\n              <span>\n                Executing non-blocking logical schema snapshot across 4 tables at WAL offset\n                <code class=\"font-mono font-medium\">mysql-bin.000412 : 8492014</code>.\n              </span>\n            </div>\n          </div>\n          <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">LSN: 0/16B38E0</span>\n        </div>\n      }\n\n      <!-- 4 CDC Telemetry Metric Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        <!-- 1. Replication Lag -->\n        <ui-card class=\"border-border shadow-xs\">\n          <ui-card-content class=\"p-4\">\n            <div class=\"flex items-center justify-between\">\n              <span class=\"text-muted-foreground text-xs font-medium\">Replication Lag</span>\n              <div\n                class=\"bg-primary/10 text-primary border-primary/20 flex size-8 items-center justify-center rounded-md border\"\n              >\n                <lucide-icon [img]=\"Zap\" class=\"size-4\" aria-hidden=\"true\" />\n              </div>\n            </div>\n            <div class=\"mt-3 space-y-1.5\">\n              <div class=\"flex items-baseline gap-2\">\n                <span class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">18ms</span>\n                <span class=\"text-muted-foreground text-xs\">p99: 24ms</span>\n              </div>\n              <div class=\"flex items-center gap-2\">\n                <span ui-badge variant=\"success\" class=\"font-mono text-xs\">Sub-second real-time</span>\n              </div>\n              <div class=\"pt-1\">\n                <div class=\"text-muted-foreground flex justify-between text-xs\">\n                  <span>SLA Headroom</span>\n                  <span class=\"font-mono\">96.4%</span>\n                </div>\n                <ui-progress [value]=\"3.6\" class=\"mt-1 h-1.5\" />\n              </div>\n            </div>\n          </ui-card-content>\n        </ui-card>\n\n        <!-- 2. Throughput -->\n        <ui-card class=\"border-border shadow-xs\">\n          <ui-card-content class=\"p-4\">\n            <div class=\"flex items-center justify-between\">\n              <span class=\"text-muted-foreground text-xs font-medium\">Throughput</span>\n              <div\n                class=\"border-info/20 bg-info/10 text-info flex size-8 items-center justify-center rounded-md border\"\n              >\n                <lucide-icon [img]=\"Activity\" class=\"size-4\" aria-hidden=\"true\" />\n              </div>\n            </div>\n            <div class=\"mt-3 space-y-1.5\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">1,420</span>\n                <span class=\"text-muted-foreground text-xs\">events/sec</span>\n              </div>\n              <div class=\"flex items-center gap-2\">\n                <span ui-badge variant=\"info\" class=\"font-mono text-xs\">380 KB/s bandwidth</span>\n              </div>\n              <div class=\"text-muted-foreground flex items-center justify-between pt-1 text-xs\">\n                <span>Hourly Pace</span>\n                <span class=\"text-foreground font-mono font-medium\">5.11M evts/hr</span>\n              </div>\n            </div>\n          </ui-card-content>\n        </ui-card>\n\n        <!-- 3. Total Synced Today -->\n        <ui-card class=\"border-border shadow-xs\">\n          <ui-card-content class=\"p-4\">\n            <div class=\"flex items-center justify-between\">\n              <span class=\"text-muted-foreground text-xs font-medium\">Total Synced Today</span>\n              <div\n                class=\"border-success/20 bg-success/10 text-success flex size-8 items-center justify-center rounded-md border\"\n              >\n                <lucide-icon [img]=\"Database\" class=\"size-4\" aria-hidden=\"true\" />\n              </div>\n            </div>\n            <div class=\"mt-3 space-y-1.5\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">4.82M</span>\n                <span class=\"text-muted-foreground text-xs\">Row Changes</span>\n              </div>\n              <div class=\"flex items-center gap-2\">\n                <span ui-badge variant=\"outline\" class=\"border-success/30 text-success font-mono text-xs\">\n                  +14.2% vs yesterday\n                </span>\n              </div>\n              <div class=\"text-muted-foreground flex items-center justify-between pt-1 text-xs\">\n                <span>WAL Integrity</span>\n                <span class=\"text-success font-mono font-medium\">100% Consistent</span>\n              </div>\n            </div>\n          </ui-card-content>\n        </ui-card>\n\n        <!-- 4. Error / Dead Letter Queue -->\n        <ui-card class=\"border-border shadow-xs\">\n          <ui-card-content class=\"p-4\">\n            <div class=\"flex items-center justify-between\">\n              <span class=\"text-muted-foreground text-xs font-medium\">Error / DLQ</span>\n              <div\n                class=\"border-success/20 bg-success/10 text-success flex size-8 items-center justify-center rounded-md border\"\n              >\n                <lucide-icon [img]=\"ShieldCheck\" class=\"size-4\" aria-hidden=\"true\" />\n              </div>\n            </div>\n            <div class=\"mt-3 space-y-1.5\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">0</span>\n                <span class=\"text-muted-foreground text-xs\">Failed Events</span>\n              </div>\n              <div class=\"flex items-center gap-2\">\n                <span ui-badge variant=\"success\" class=\"font-mono text-xs\">DLQ Empty · Healthy</span>\n              </div>\n              <div class=\"text-muted-foreground flex items-center justify-between pt-1 text-xs\">\n                <span>Schema Drift</span>\n                <span class=\"text-foreground font-mono font-medium\">0 Violations</span>\n              </div>\n            </div>\n          </ui-card-content>\n        </ui-card>\n      </div>\n\n      <!-- Active Synced Tables Table -->\n      <ui-card class=\"border-border shadow-xs\">\n        <ui-card-header class=\"pb-3\">\n          <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n            <div>\n              <ui-card-title class=\"text-base font-semibold\"\n                >Active Synced Tables ({{ tables().length }})</ui-card-title\n              >\n              <ui-card-description class=\"text-xs\">\n                Per-table WAL change capture distribution, row synchronization counts, and per-table replication latency\n              </ui-card-description>\n            </div>\n            <div class=\"text-muted-foreground flex items-center gap-2 text-xs\">\n              <span class=\"flex items-center gap-1\">\n                <span class=\"bg-success size-2 rounded-full\"></span>\n                INSERT\n              </span>\n              <span class=\"flex items-center gap-1\">\n                <span class=\"bg-info size-2 rounded-full\"></span>\n                UPDATE\n              </span>\n              <span class=\"flex items-center gap-1\">\n                <span class=\"bg-destructive size-2 rounded-full\"></span>\n                DELETE\n              </span>\n            </div>\n          </div>\n        </ui-card-header>\n        <ui-card-content class=\"p-0\">\n          <div class=\"overflow-x-auto\">\n            <div ui-table density=\"cozy\">\n              <thead ui-table-header>\n                <tr ui-table-row>\n                  <th ui-table-head class=\"text-xs font-semibold\">Table Name &amp; Schema</th>\n                  <th ui-table-head class=\"text-xs font-semibold\">Change Operations Breakdown</th>\n                  <th ui-table-head class=\"text-right text-xs font-semibold\">Synced Rows Count</th>\n                  <th ui-table-head class=\"text-right text-xs font-semibold\">Replication Delay</th>\n                  <th ui-table-head class=\"text-center text-xs font-semibold\">Status</th>\n                </tr>\n              </thead>\n              <tbody ui-table-body>\n                @for (table of tables(); track table.name) {\n                  <tr ui-table-row class=\"group\">\n                    <!-- Table Name & Schema -->\n                    <td ui-table-cell class=\"py-3\">\n                      <div class=\"space-y-0.5\">\n                        <div class=\"flex items-center gap-2\">\n                          <span class=\"text-foreground font-mono text-xs font-semibold\"\n                            >{{ table.schema }}.{{ table.name }}</span\n                          >\n                          <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">{{ table.primaryKey }}</span>\n                        </div>\n                        <p class=\"text-muted-foreground text-xs font-normal\">Mode: {{ table.mode }}</p>\n                      </div>\n                    </td>\n\n                    <!-- Operations Multi-Segment Stacked Progress Bar & Percentages -->\n                    <td ui-table-cell class=\"py-3\">\n                      <div class=\"w-full max-w-xs space-y-1.5\">\n                        <!-- Stacked Progress Bar -->\n                        <div class=\"bg-muted flex h-2 w-full overflow-hidden rounded-full border\">\n                          <div\n                            class=\"bg-success transition-colors duration-300\"\n                            [style.width.%]=\"table.operations.insertPercent\"\n                            [attr.title]=\"'INSERT: ' + table.operations.insertPercent + '%'\"\n                          ></div>\n                          <div\n                            class=\"bg-info transition-colors duration-300\"\n                            [style.width.%]=\"table.operations.updatePercent\"\n                            [attr.title]=\"'UPDATE: ' + table.operations.updatePercent + '%'\"\n                          ></div>\n                          <div\n                            class=\"bg-destructive transition-colors duration-300\"\n                            [style.width.%]=\"table.operations.deletePercent\"\n                            [attr.title]=\"'DELETE: ' + table.operations.deletePercent + '%'\"\n                          ></div>\n                        </div>\n\n                        <!-- Breakdown Text -->\n                        <div class=\"flex items-center gap-2 font-mono text-xs\">\n                          <span class=\"text-success font-medium\">INS {{ table.operations.insertPercent }}%</span>\n                          <span class=\"text-info font-medium\">UPD {{ table.operations.updatePercent }}%</span>\n                          <span class=\"text-destructive font-medium\">DEL {{ table.operations.deletePercent }}%</span>\n                        </div>\n                      </div>\n                    </td>\n\n                    <!-- Synced Rows Count -->\n                    <td ui-table-cell class=\"py-3 text-right\">\n                      <span class=\"text-foreground font-mono text-xs font-semibold tabular-nums\">{{\n                        formatNumber(table.syncedRows)\n                      }}</span>\n                      <p class=\"text-muted-foreground text-xs\">rows committed</p>\n                    </td>\n\n                    <!-- Replication Delay -->\n                    <td ui-table-cell class=\"py-3 text-right\">\n                      <span class=\"text-success font-mono text-xs font-semibold tabular-nums\"\n                        >{{ table.replicationDelayMs }}ms</span\n                      >\n                      <p class=\"text-muted-foreground text-xs\">sub-second</p>\n                    </td>\n\n                    <!-- Status Badge -->\n                    <td ui-table-cell class=\"py-3 text-center\">\n                      <span ui-badge variant=\"success\" class=\"gap-1 font-mono text-xs\">\n                        <span class=\"bg-success size-1.5 animate-pulse rounded-full\"></span>\n                        Synchronized\n                      </span>\n                    </td>\n                  </tr>\n                }\n              </tbody>\n            </div>\n          </div>\n        </ui-card-content>\n      </ui-card>\n\n      <!-- Live CDC Event Stream Ticker Card -->\n      <ui-card class=\"border-border shadow-xs\">\n        <!-- Terminal Header Bar -->\n        <div class=\"bg-muted/40 flex flex-col gap-3 border-b p-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"flex items-center gap-2\">\n            <div class=\"flex items-center gap-1.5\">\n              <span class=\"bg-destructive/80 size-2.5 rounded-full\"></span>\n              <span class=\"bg-warning/80 size-2.5 rounded-full\"></span>\n              <span class=\"bg-success/80 size-2.5 rounded-full\"></span>\n            </div>\n            <div>\n              <span class=\"text-foreground text-xs font-semibold\">Live CDC Event Stream Ticker</span>\n              <span class=\"text-muted-foreground hidden font-mono text-xs sm:inline\">\n                · wal_level=logical · slot: debezium_cdc_01\n              </span>\n            </div>\n          </div>\n\n          <div class=\"flex items-center gap-2\">\n            <span class=\"text-muted-foreground hidden font-mono text-xs sm:inline\">\n              Matches: {{ filteredEvents().length }} / {{ events().length }}\n            </span>\n            <button\n              ui-button\n              variant=\"ghost\"\n              size=\"xs\"\n              class=\"h-7 gap-1 px-2 text-xs\"\n              [disabled]=\"filteredEvents().length === 0\"\n              (click)=\"toggleAllExpand()\"\n            >\n              @if (allExpanded()) {\n                <lucide-icon [img]=\"ChevronDown\" class=\"size-3.5\" aria-hidden=\"true\" />\n              } @else {\n                <lucide-icon [img]=\"ChevronRight\" class=\"size-3.5\" aria-hidden=\"true\" />\n              }\n              <span>{{ allExpanded() ? 'Collapse All' : 'Expand All' }}</span>\n            </button>\n          </div>\n        </div>\n\n        <!-- Stream Filter Toolbar -->\n        <div class=\"border-b p-3\">\n          <div class=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n            <!-- Search Input -->\n            <div class=\"relative flex-1\">\n              <!-- [class] (not class=) styles only the svg: the absolute offsets must not apply twice. -->\n              <lucide-icon\n                [img]=\"Search\"\n                [class]=\"'text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2'\"\n                aria-hidden=\"true\"\n              />\n              <ui-input\n                [value]=\"searchQuery()\"\n                (valueChange)=\"searchQuery.set($event)\"\n                placeholder=\"Search table, operation, primary key, column, or /regex/...\"\n                class=\"pl-9 text-xs\"\n              />\n            </div>\n\n            <!-- Operation Filter Buttons -->\n            <div class=\"flex flex-wrap items-center gap-1.5\">\n              <span class=\"text-muted-foreground mr-1 flex items-center gap-1 text-xs font-medium\">\n                <lucide-icon [img]=\"Filter\" class=\"size-3\" aria-hidden=\"true\" />\n                Op:\n              </span>\n\n              @for (op of operationFilters; track op) {\n                <button type=\"button\" [class]=\"pillClass(op)\" (click)=\"selectedOperation.set(op)\">\n                  @if (op !== 'all') {\n                    <span [class]=\"pillDotClass(op)\"></span>\n                  }\n                  {{ op === 'all' ? 'All' : op }}\n                  <span [class]=\"pillCountClass(op)\">{{ operationCount(op) }}</span>\n                </button>\n              }\n            </div>\n          </div>\n        </div>\n\n        <!-- Event Rows Feed -->\n        <ui-card-content class=\"p-0\">\n          @if (filteredEvents().length === 0) {\n            <!-- Empty State -->\n            <div class=\"flex flex-col items-center justify-center gap-3 px-4 py-16 text-center\">\n              <div class=\"bg-muted flex size-12 items-center justify-center rounded-full\">\n                <lucide-icon [img]=\"Database\" class=\"text-muted-foreground size-6 opacity-60\" aria-hidden=\"true\" />\n              </div>\n              <div class=\"space-y-1\">\n                <p class=\"text-foreground text-sm font-semibold\">No CDC events match current criteria</p>\n                <p class=\"text-muted-foreground text-xs\">\n                  {{\n                    events().length === 0\n                      ? 'The replication event stream buffer was cleared.'\n                      : 'Try adjusting your operation filter or search query.'\n                  }}\n                </p>\n              </div>\n              <button ui-button variant=\"outline\" size=\"sm\" class=\"mt-2 text-xs\" (click)=\"resetStream()\">\n                <lucide-icon [img]=\"RefreshCw\" class=\"size-3.5\" aria-hidden=\"true\" />\n                Restore CDC stream data\n              </button>\n            </div>\n          } @else {\n            <ul class=\"divide-border/60 divide-y\">\n              @for (event of filteredEvents(); track event.id) {\n                <li [class]=\"rowClass(event.id)\">\n                  <!-- Primary Row Trigger -->\n                  <div\n                    role=\"button\"\n                    tabindex=\"0\"\n                    [attr.aria-expanded]=\"isExpanded(event.id)\"\n                    class=\"focus-visible:ring-ring flex cursor-pointer flex-col gap-3 p-3.5 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset sm:flex-row sm:items-center sm:justify-between\"\n                    (click)=\"toggleExpand(event.id)\"\n                    (keydown)=\"onRowKeydown($event, event.id)\"\n                  >\n                    <!-- Left Rail: Chevron + Timestamp + Operation Badge + Table + Summary -->\n                    <div class=\"flex min-w-0 items-start gap-3 sm:items-center\">\n                      <button\n                        type=\"button\"\n                        aria-label=\"Toggle before and after payload diff\"\n                        [class]=\"chevronClass(event.id)\"\n                      >\n                        <lucide-icon [img]=\"ChevronRight\" class=\"size-4\" aria-hidden=\"true\" />\n                      </button>\n\n                      <div class=\"space-y-1 sm:space-y-0.5\">\n                        <div class=\"flex flex-wrap items-center gap-2\">\n                          <span class=\"text-muted-foreground font-mono text-xs whitespace-nowrap\">{{\n                            formatTimestamp(event.timestamp)\n                          }}</span>\n\n                          @if (event.operation === 'INSERT') {\n                            <span ui-badge class=\"bg-success/10 text-success border-transparent font-mono text-xs\"\n                              >INSERT</span\n                            >\n                          }\n                          @if (event.operation === 'UPDATE') {\n                            <span ui-badge class=\"bg-info/10 text-info border-transparent font-mono text-xs\"\n                              >UPDATE</span\n                            >\n                          }\n                          @if (event.operation === 'DELETE') {\n                            <span\n                              ui-badge\n                              class=\"bg-destructive/10 text-destructive border-transparent font-mono text-xs\"\n                              >DELETE</span\n                            >\n                          }\n\n                          <span class=\"text-foreground font-mono text-xs font-semibold\">{{ event.table }}</span>\n\n                          <span class=\"bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs\">{{\n                            event.primaryKey\n                          }}</span>\n                        </div>\n\n                        <p class=\"text-muted-foreground text-xs font-normal\">{{ event.summary }}</p>\n                      </div>\n                    </div>\n\n                    <!-- Right Rail: Binlog Offset & Tx ID -->\n                    <div class=\"flex shrink-0 flex-wrap items-center gap-3 sm:justify-end\">\n                      <div class=\"bg-muted/60 flex items-center gap-1.5 rounded-md border px-2 py-1 font-mono text-xs\">\n                        <span class=\"text-muted-foreground\">Tx:</span>\n                        <span class=\"text-foreground font-medium\">{{ event.txId }}</span>\n                      </div>\n\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 font-mono text-xs\">\n                        <span>{{ event.binlogPosition }}</span>\n                        <span class=\"opacity-50\">·</span>\n                        <span class=\"text-foreground/80 font-medium\">{{ event.lsn }}</span>\n                      </div>\n                    </div>\n                  </div>\n\n                  <!-- Expanded Payload Diff View -->\n                  @if (isExpanded(event.id)) {\n                    <div class=\"bg-muted/15 border-t px-4 py-3.5\">\n                      <div class=\"space-y-3\">\n                        <!-- Modified Fields Tag Bar -->\n                        @if (event.modifiedFields && event.modifiedFields.length > 0) {\n                          <div class=\"bg-card flex flex-wrap items-center gap-1.5 rounded-md border p-2.5 text-xs\">\n                            <span class=\"text-muted-foreground font-medium\">Modified Columns:</span>\n                            @for (field of event.modifiedFields; track field) {\n                              <span ui-badge variant=\"outline\" class=\"text-info font-mono text-xs\">{{ field }}</span>\n                            }\n                          </div>\n                        }\n\n                        <!-- Side-by-Side or Stacked JSON Diff -->\n                        <div class=\"grid grid-cols-1 gap-3 lg:grid-cols-2\">\n                          <!-- Before Payload (For UPDATE and DELETE) -->\n                          @if (event.before; as before) {\n                            <div\n                              class=\"overflow-hidden rounded-md border border-zinc-800 bg-zinc-950 font-mono text-xs text-zinc-100 shadow-inner\"\n                            >\n                              <div\n                                class=\"flex items-center justify-between border-b border-zinc-800 bg-zinc-900/90 px-3 py-1.5\"\n                              >\n                                <div class=\"flex items-center gap-2\">\n                                  <span class=\"bg-destructive size-2 rounded-full\"></span>\n                                  <span class=\"text-destructive text-xs font-semibold\">before_image.json</span>\n                                  <span class=\"text-muted-foreground text-xs font-normal\">(Previous State)</span>\n                                </div>\n                                <button\n                                  ui-button\n                                  variant=\"ghost\"\n                                  size=\"xs\"\n                                  class=\"h-6 gap-1 px-2 text-xs text-zinc-300 hover:bg-zinc-800 hover:text-zinc-100\"\n                                  (click)=\"copyPayload(event.id + '-before', before)\"\n                                >\n                                  @if (copiedEventId() === event.id + '-before') {\n                                    <lucide-icon [img]=\"Check\" class=\"text-success size-3\" aria-hidden=\"true\" />\n                                  } @else {\n                                    <lucide-icon [img]=\"Copy\" class=\"size-3\" aria-hidden=\"true\" />\n                                  }\n                                  <span>{{ copiedEventId() === event.id + '-before' ? 'Copied' : 'Copy' }}</span>\n                                </button>\n                              </div>\n\n                              <pre\n                                class=\"max-h-60 overflow-x-auto overflow-y-auto p-3 text-xs leading-relaxed select-text\"\n                              ><code class=\"text-destructive/90\">{{ json(before) }}</code></pre>\n                            </div>\n                          }\n\n                          <!-- After Payload (For INSERT and UPDATE) -->\n                          @if (event.after; as after) {\n                            <div [class]=\"afterPanelClass(event)\">\n                              <div\n                                class=\"flex items-center justify-between border-b border-zinc-800 bg-zinc-900/90 px-3 py-1.5\"\n                              >\n                                <div class=\"flex items-center gap-2\">\n                                  <span class=\"bg-success size-2 rounded-full\"></span>\n                                  <span class=\"text-success text-xs font-semibold\">after_image.json</span>\n                                  <span class=\"text-muted-foreground text-xs font-normal\"\n                                    >(Replicated Target State)</span\n                                  >\n                                </div>\n                                <button\n                                  ui-button\n                                  variant=\"ghost\"\n                                  size=\"xs\"\n                                  class=\"h-6 gap-1 px-2 text-xs text-zinc-300 hover:bg-zinc-800 hover:text-zinc-100\"\n                                  (click)=\"copyPayload(event.id + '-after', after)\"\n                                >\n                                  @if (copiedEventId() === event.id + '-after') {\n                                    <lucide-icon [img]=\"Check\" class=\"text-success size-3\" aria-hidden=\"true\" />\n                                  } @else {\n                                    <lucide-icon [img]=\"Copy\" class=\"size-3\" aria-hidden=\"true\" />\n                                  }\n                                  <span>{{ copiedEventId() === event.id + '-after' ? 'Copied' : 'Copy' }}</span>\n                                </button>\n                              </div>\n\n                              <pre\n                                class=\"max-h-60 overflow-x-auto overflow-y-auto p-3 text-xs leading-relaxed select-text\"\n                              ><code class=\"text-success\">{{ json(after) }}</code></pre>\n                            </div>\n                          }\n                        </div>\n                      </div>\n                    </div>\n                  }\n                </li>\n              }\n            </ul>\n          }\n        </ui-card-content>\n\n        <!-- Bottom Engine & Ingest Protocol Telemetry Bar -->\n        <div\n          class=\"bg-muted/40 flex flex-col gap-2.5 border-t px-4 py-3 text-xs sm:flex-row sm:items-center sm:justify-between\"\n        >\n          <div class=\"text-muted-foreground flex flex-wrap items-center gap-4\">\n            <div class=\"text-foreground flex items-center gap-1.5 font-medium\">\n              <lucide-icon [img]=\"Activity\" class=\"text-success size-3.5\" aria-hidden=\"true\" />\n              <span>CDC Pipeline:</span>\n              <span class=\"font-mono\">{{ isPaused() ? '0 evt/s (Paused)' : '1,420 events/sec' }}</span>\n            </div>\n\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n\n            <div class=\"flex items-center gap-1.5\">\n              <lucide-icon [img]=\"Layers\" class=\"size-3.5\" aria-hidden=\"true\" />\n              <span>Sink Topic:</span>\n              <span class=\"text-foreground font-mono\">snowflake.cdc.raw_events</span>\n            </div>\n\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n\n            <div class=\"flex items-center gap-1.5\">\n              <lucide-icon [img]=\"Server\" class=\"text-info size-3.5\" aria-hidden=\"true\" />\n              <span>Format:</span>\n              <span class=\"text-foreground font-mono\">JSON + Schema Registry (v2)</span>\n            </div>\n          </div>\n\n          <div class=\"text-muted-foreground flex items-center gap-2 font-mono\">\n            <span class=\"bg-success size-1.5 rounded-full\"></span>\n            <span>Postgres WAL2JSON · TLS 1.3 · Heartbeat: 1,000ms OK</span>\n          </div>\n        </div>\n      </ui-card>\n    </div>\n  `,\n})\nexport class UiCdcReplicationStreamComponent implements OnInit {\n  @Input() initialEvents?: CdcStreamEvent[]\n  @Input() initialTables?: CdcTableMetric[]\n  @Input() initialPaused = false\n  @Input() initialOperationFilter: CdcOperationFilter = 'all'\n  @Input() initialSearch = ''\n  @Input('class') className?: string\n\n  protected readonly ArrowRightLeft = ArrowRightLeft\n  protected readonly ArrowRight = ArrowRight\n  protected readonly RefreshCw = RefreshCw\n  protected readonly Terminal = Terminal\n  protected readonly Check = Check\n  protected readonly Copy = Copy\n  protected readonly Play = Play\n  protected readonly Pause = Pause\n  protected readonly Download = Download\n  protected readonly Zap = Zap\n  protected readonly Activity = Activity\n  protected readonly Database = Database\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly ChevronDown = ChevronDown\n  protected readonly ChevronRight = ChevronRight\n  protected readonly Search = Search\n  protected readonly Filter = Filter\n  protected readonly Layers = Layers\n  protected readonly Server = Server\n\n  protected readonly currentBinlog = currentBinlog\n  protected readonly operationFilters: CdcOperationFilter[] = ['all', 'INSERT', 'UPDATE', 'DELETE']\n  protected readonly formatNumber = formatNumber\n  protected readonly formatTimestamp = formatTimestamp\n\n  readonly tables = signal<CdcTableMetric[]>([...defaultTables])\n  readonly events = signal<CdcStreamEvent[]>([...defaultEvents])\n  readonly isPaused = signal(false)\n  readonly isSnapshotting = signal(false)\n  readonly snapshotNotice = signal(false)\n  readonly selectedOperation = signal<CdcOperationFilter>('all')\n  readonly searchQuery = signal('')\n  readonly expandedIds = signal<Record<string, boolean>>({ 'evt-cdc-104': true })\n  readonly copiedEventId = signal<string | null>(null)\n  readonly copiedBinlog = signal(false)\n  readonly exportedNotice = signal(false)\n\n  readonly filteredEvents = computed(() => {\n    const op = this.selectedOperation()\n    const q = this.searchQuery()\n    return this.events().filter((event) => (op === 'all' || event.operation === op) && matchesSearch(event, q))\n  })\n\n  readonly operationCounts = computed(() => {\n    const events = this.events()\n    return {\n      all: events.length,\n      INSERT: events.filter((e) => e.operation === 'INSERT').length,\n      UPDATE: events.filter((e) => e.operation === 'UPDATE').length,\n      DELETE: events.filter((e) => e.operation === 'DELETE').length,\n    }\n  })\n\n  readonly allExpanded = computed(() => {\n    const filtered = this.filteredEvents()\n    const expanded = this.expandedIds()\n    return filtered.length > 0 && filtered.every((e) => expanded[e.id])\n  })\n\n  private readonly timers = new Set<ReturnType<typeof setTimeout>>()\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => this.timers.forEach((t) => clearTimeout(t)))\n  }\n\n  ngOnInit(): void {\n    if (this.initialTables) this.tables.set([...this.initialTables])\n    if (this.initialEvents) this.events.set([...this.initialEvents])\n    this.isPaused.set(this.initialPaused)\n    this.selectedOperation.set(this.initialOperationFilter)\n    this.searchQuery.set(this.initialSearch)\n  }\n\n  protected get rootClass(): string {\n    return cn('w-full space-y-4', this.className)\n  }\n\n  private later(fn: () => void, ms: number): void {\n    const t = setTimeout(() => {\n      this.timers.delete(t)\n      fn()\n    }, ms)\n    this.timers.add(t)\n  }\n\n  isExpanded(id: string): boolean {\n    return !!this.expandedIds()[id]\n  }\n\n  operationCount(op: CdcOperationFilter): number {\n    return this.operationCounts()[op]\n  }\n\n  togglePause(): void {\n    this.isPaused.update((prev) => !prev)\n  }\n\n  toggleExpand(id: string): void {\n    this.expandedIds.update((prev) => ({ ...prev, [id]: !prev[id] }))\n  }\n\n  toggleAllExpand(): void {\n    if (this.allExpanded()) {\n      this.expandedIds.set({})\n    } else {\n      const next: Record<string, boolean> = {}\n      this.filteredEvents().forEach((e) => {\n        next[e.id] = true\n      })\n      this.expandedIds.set(next)\n    }\n  }\n\n  onRowKeydown(e: KeyboardEvent, id: string): void {\n    if (e.key === 'Enter' || e.key === ' ') {\n      e.preventDefault()\n      this.toggleExpand(id)\n    }\n  }\n\n  triggerSnapshot(): void {\n    if (this.isSnapshotting()) return\n    this.isSnapshotting.set(true)\n    this.snapshotNotice.set(true)\n    this.later(() => this.isSnapshotting.set(false), 2200)\n    this.later(() => this.snapshotNotice.set(false), 6000)\n  }\n\n  clearStream(): void {\n    this.events.set([])\n    this.expandedIds.set({})\n  }\n\n  resetStream(): void {\n    this.events.set(this.initialEvents ? [...this.initialEvents] : [...defaultEvents])\n    this.searchQuery.set('')\n    this.selectedOperation.set('all')\n    this.expandedIds.set({ 'evt-cdc-104': true })\n  }\n\n  copyBinlogPosition(): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      navigator.clipboard.writeText(currentBinlog).catch(() => {})\n      this.copiedBinlog.set(true)\n      this.later(() => this.copiedBinlog.set(false), 2000)\n    }\n  }\n\n  copyPayload(id: string, payload: Record<string, unknown>): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      navigator.clipboard.writeText(JSON.stringify(payload, null, 2)).catch(() => {})\n      this.copiedEventId.set(id)\n      this.later(() => this.copiedEventId.update((current) => (current === id ? null : current)), 2000)\n    }\n  }\n\n  exportJson(): void {\n    const dataStr =\n      'data:text/json;charset=utf-8,' +\n      encodeURIComponent(\n        JSON.stringify(\n          {\n            pipeline: 'Postgres Source (OLTP) -> Snowflake DW (OLAP)',\n            binlog_position: currentBinlog,\n            timestamp: new Date().toISOString(),\n            tables: this.tables(),\n            events: this.filteredEvents(),\n          },\n          null,\n          2,\n        ),\n      )\n    if (typeof document !== 'undefined') {\n      const downloadAnchor = document.createElement('a')\n      downloadAnchor.setAttribute('href', dataStr)\n      downloadAnchor.setAttribute(\n        'download',\n        `cdc-replication-stream-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`,\n      )\n      document.body.appendChild(downloadAnchor)\n      downloadAnchor.click()\n      downloadAnchor.remove()\n      this.exportedNotice.set(true)\n      this.later(() => this.exportedNotice.set(false), 2000)\n    }\n  }\n\n  json(value: Record<string, unknown>): string {\n    return JSON.stringify(value, null, 2)\n  }\n\n  snapshotIconClass(): string {\n    return cn('size-3.5', this.isSnapshotting() && 'text-primary animate-spin')\n  }\n\n  pillClass(op: CdcOperationFilter): string {\n    const active = this.selectedOperation() === op\n    if (op === 'all') {\n      return cn(\n        pillBase,\n        active\n          ? 'border-primary bg-primary text-primary-foreground shadow-xs'\n          : 'border-border bg-card text-muted-foreground hover:bg-accent hover:text-foreground',\n      )\n    }\n    if (op === 'INSERT') {\n      return cn(\n        pillBase,\n        active\n          ? 'border-success bg-success text-white shadow-xs'\n          : 'border-border bg-card text-muted-foreground hover:border-success/40 hover:text-success dark:hover:text-success',\n      )\n    }\n    if (op === 'UPDATE') {\n      return cn(\n        pillBase,\n        active\n          ? 'border-info bg-info text-white shadow-xs'\n          : 'border-border bg-card text-muted-foreground hover:border-info/40 hover:text-info dark:hover:text-info',\n      )\n    }\n    return cn(\n      pillBase,\n      active\n        ? 'border-destructive bg-destructive text-white shadow-xs'\n        : 'border-border bg-card text-muted-foreground hover:border-destructive/40 hover:text-destructive dark:hover:text-destructive',\n    )\n  }\n\n  pillDotClass(op: CdcOperationFilter): string {\n    if (op === 'INSERT') return 'bg-success size-1.5 rounded-full'\n    if (op === 'UPDATE') return 'bg-info size-1.5 rounded-full'\n    return 'bg-destructive size-1.5 rounded-full'\n  }\n\n  pillCountClass(op: CdcOperationFilter): string {\n    const active = this.selectedOperation() === op\n    if (op === 'all') {\n      return cn(\n        'py-0.2 rounded-full px-1.5 text-xs',\n        active ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-muted text-muted-foreground',\n      )\n    }\n    const idle =\n      op === 'INSERT'\n        ? 'bg-success/10 text-success'\n        : op === 'UPDATE'\n          ? 'bg-info/10 text-info'\n          : 'bg-destructive/10 text-destructive'\n    return cn('py-0.2 rounded-full px-1.5 text-xs', active ? 'bg-white/20 text-white' : idle)\n  }\n\n  rowClass(id: string): string {\n    return cn('group transition-colors', this.isExpanded(id) ? 'bg-muted/30' : 'hover:bg-muted/20')\n  }\n\n  chevronClass(id: string): string {\n    return cn(\n      'text-muted-foreground group-hover:text-foreground mt-0.5 shrink-0 transition-transform sm:mt-0',\n      this.isExpanded(id) && 'text-foreground rotate-90',\n    )\n  }\n\n  afterPanelClass(event: CdcStreamEvent): string {\n    return cn(\n      'overflow-hidden rounded-md border border-zinc-800 bg-zinc-950 font-mono text-xs text-zinc-100 shadow-inner',\n      !event.before && 'lg:col-span-2',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/cdc-replication-stream/cdc-replication-stream.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/cdc-replication-stream/index.ts",
      "content": "export {\n  UiCdcReplicationStreamComponent,\n  matchesSearch,\n  type CdcOperation,\n  type CdcOperationFilter,\n  type CdcTableMetric,\n  type CdcStreamEvent,\n} from './cdc-replication-stream.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/cdc-replication-stream/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/input.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Debezium and Fivetran style Change Data Capture (CDC) replication monitor and binlog sync pipeline with live telemetry metrics, table operation breakdowns, and real-time before/after row diff streaming.",
  "categories": [
    "devops",
    "app"
  ]
}