{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "kafka-stream-monitor",
  "title": "Kafka Stream Monitor",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/kafka-stream-monitor/kafka-stream-monitor.component.ts",
      "content": "// Apache Kafka topic stream telemetry dashboard: partition offset monitor, leader broker\n// distribution, consumer group lag tracker, and real-time message payload inspector with\n// deserialized JSON viewer. Port of the React block 1:1 (sample data, copy, filters).\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  Activity,\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  ChevronRight,\n  Copy,\n  Filter,\n  Gauge,\n  HardDrive,\n  Layers,\n  LucideAngularModule,\n  Pause,\n  Play,\n  PlusCircle,\n  Radio,\n  RotateCcw,\n  Search,\n  ShieldCheck,\n  Terminal,\n  Trash2,\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 interface KafkaStreamMessage {\n  id: string\n  offset: number\n  partition: number\n  key: string\n  timestamp: string\n  eventType: string\n  sizeBytes: number\n  schemaId: number\n  traceId: string\n  payload: Record<string, unknown>\n}\n\nexport interface PartitionTelemetryRow {\n  partitionId: number\n  leaderBroker: string\n  rack: string\n  logEndOffset: number\n  consumerOffset: number\n  lag: number\n  messageRate: string\n  isrCount: number\n  replicasCount: number\n  status: 'healthy' | 'warning'\n}\n\nconst defaultPartitions: PartitionTelemetryRow[] = [\n  {\n    partitionId: 0,\n    leaderBroker: 'Broker #102',\n    rack: 'us-east-1a',\n    logEndOffset: 1482910,\n    consumerOffset: 1482910,\n    lag: 0,\n    messageRate: '1.2k msg/s',\n    isrCount: 3,\n    replicasCount: 3,\n    status: 'healthy',\n  },\n  {\n    partitionId: 1,\n    leaderBroker: 'Broker #103',\n    rack: 'us-east-1b',\n    logEndOffset: 1482884,\n    consumerOffset: 1482884,\n    lag: 0,\n    messageRate: '1.1k msg/s',\n    isrCount: 3,\n    replicasCount: 3,\n    status: 'healthy',\n  },\n  {\n    partitionId: 2,\n    leaderBroker: 'Broker #101',\n    rack: 'us-east-1c',\n    logEndOffset: 1482905,\n    consumerOffset: 1482905,\n    lag: 0,\n    messageRate: '1.4k msg/s',\n    isrCount: 3,\n    replicasCount: 3,\n    status: 'healthy',\n  },\n  {\n    partitionId: 3,\n    leaderBroker: 'Broker #104',\n    rack: 'us-east-1a',\n    logEndOffset: 1482872,\n    consumerOffset: 1482872,\n    lag: 0,\n    messageRate: '1.2k msg/s',\n    isrCount: 3,\n    replicasCount: 3,\n    status: 'healthy',\n  },\n]\n\nconst defaultMessages: KafkaStreamMessage[] = [\n  {\n    id: 'msg-1482910',\n    offset: 1482910,\n    partition: 0,\n    key: 'cust_99201a',\n    timestamp: '2026-08-21 14:32:05.812 UTC',\n    eventType: 'checkout.order_completed',\n    sizeBytes: 1420,\n    schemaId: 104,\n    traceId: '00-4bf92f3577b34da6a3ce929d0e0e4736',\n    payload: {\n      order_id: 'ord_9920184a',\n      customer_id: 'cust_99201a',\n      checkout_session: 'cs_live_992019482',\n      currency: 'USD',\n      total_amount: 349.5,\n      item_count: 3,\n      items: [\n        { sku: 'SKU-PRO-4K', name: 'UltraHD Pro Monitor 32-inch', qty: 1, price: 299.0 },\n        { sku: 'SKU-USB-C-2M', name: 'Braided Thunderbolt 4 Cable 2m', qty: 2, price: 25.25 },\n      ],\n      payment_status: 'authorized',\n      fraud_risk_score: 0.02,\n      shipping_destination: 'US-CA-94105',\n      produced_at_epoch_ms: 1787322725812,\n    },\n  },\n  {\n    id: 'msg-1482909',\n    offset: 1482909,\n    partition: 2,\n    key: 'cust_88301b',\n    timestamp: '2026-08-21 14:31:58.240 UTC',\n    eventType: 'checkout.payment_authorized',\n    sizeBytes: 890,\n    schemaId: 104,\n    traceId: '00-7c201a9981f34ec189aa1029481920aa',\n    payload: {\n      transaction_id: 'txn_8830192b',\n      customer_id: 'cust_88301b',\n      payment_provider: 'stripe_us_direct',\n      amount: 129.0,\n      currency: 'USD',\n      card_brand: 'visa',\n      last4: '4242',\n      avs_check: 'matched',\n      cvv_check: 'matched',\n      '3ds_authenticated': true,\n      latency_ms: 142,\n    },\n  },\n  {\n    id: 'msg-1482908',\n    offset: 1482908,\n    partition: 1,\n    key: 'cust_44109c',\n    timestamp: '2026-08-21 14:31:44.102 UTC',\n    eventType: 'checkout.cart_updated',\n    sizeBytes: 1150,\n    schemaId: 104,\n    traceId: '00-11209e8832a44bb0902188492019ab23',\n    payload: {\n      cart_id: 'cart_4410912c',\n      customer_id: 'cust_44109c',\n      action: 'item_added',\n      sku: 'SKU-MECH-KEYBOARD',\n      name: 'Custom Wireless Mechanical Keyboard',\n      quantity: 1,\n      unit_price: 189.0,\n      applied_promo_code: 'DEVCON2026',\n      discount_amount: 18.9,\n      cart_subtotal: 170.1,\n    },\n  },\n  {\n    id: 'msg-1482907',\n    offset: 1482907,\n    partition: 3,\n    key: 'cust_11094d',\n    timestamp: '2026-08-21 14:31:22.955 UTC',\n    eventType: 'checkout.shipping_calculated',\n    sizeBytes: 760,\n    schemaId: 104,\n    traceId: '00-66a9104882194cc2810992384719bb81',\n    payload: {\n      quote_id: 'shp_1109482d',\n      customer_id: 'cust_11094d',\n      carrier: 'FedEx Express',\n      service_tier: 'Priority Overnight',\n      estimated_delivery: '2026-08-22T10:30:00Z',\n      rate_usd: 24.5,\n      origin_postal: '94105',\n      destination_postal: '10001',\n      weight_kg: 1.85,\n    },\n  },\n  {\n    id: 'msg-1482906',\n    offset: 1482906,\n    partition: 0,\n    key: 'cust_77210e',\n    timestamp: '2026-08-21 14:31:05.419 UTC',\n    eventType: 'checkout.address_validated',\n    sizeBytes: 640,\n    schemaId: 104,\n    traceId: '00-559012bb99304ff091920394881029cc',\n    payload: {\n      validation_id: 'val_7721098e',\n      customer_id: 'cust_77210e',\n      country: 'US',\n      state: 'CA',\n      city: 'San Francisco',\n      postal_code: '94107',\n      is_residential: true,\n      standardized: true,\n      dpv_match: 'Y',\n      service: 'smartystreets_v2',\n    },\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-kafka-stream-monitor, [ui-kafka-stream-monitor]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n    UiTableComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"kafka-stream-monitor\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <div class=\"space-y-1.5\">\n          <div class=\"flex flex-wrap items-center gap-2.5\">\n            <div\n              class=\"bg-muted text-muted-foreground border-border flex size-9 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n              aria-hidden=\"true\"\n            >\n              <lucide-icon [img]=\"Radio\" class=\"text-primary size-4.5\" />\n            </div>\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <span class=\"text-muted-foreground font-mono text-xs font-medium\">production-kafka-us-east-1</span>\n              <span class=\"text-muted-foreground text-xs\">/</span>\n              <h1 class=\"text-foreground font-mono text-xl font-bold tracking-tight break-all sm:text-2xl\">\n                events.customer.checkout_v2\n              </h1>\n            </div>\n            <span ui-badge variant=\"success\" class=\"gap-1.5 text-xs font-medium\">\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>Healthy · Zero Consumer Lag</span>\n            </span>\n          </div>\n          <p class=\"text-muted-foreground text-xs\">\n            12 Partitions · Replication Factor: 3 · min.insync.replicas: 2 · Cleanup Policy: delete,compact\n          </p>\n        </div>\n\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5 text-xs\" (click)=\"resetStream()\">\n            <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n            <span>Reset</span>\n          </button>\n          <button ui-button variant=\"default\" size=\"sm\" class=\"gap-1.5 text-xs\" (click)=\"produceTestMessage()\">\n            <lucide-icon [img]=\"PlusCircle\" class=\"size-3.5\" />\n            <span>Produce Test Message</span>\n          </button>\n        </div>\n      </div>\n\n      <!-- Active Produce Alert Banner -->\n      @if (produceNotice()) {\n        <div\n          class=\"border-border bg-card flex items-center justify-between rounded-lg border p-3 text-xs shadow-xs\"\n          role=\"status\"\n        >\n          <div class=\"flex items-center gap-2\">\n            <lucide-icon [img]=\"CheckCircle2\" class=\"text-primary size-4 shrink-0\" />\n            <span class=\"text-foreground font-medium\">{{ produceNotice() }}</span>\n          </div>\n          <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">Offset Ack: OK (2ms)</span>\n        </div>\n      }\n\n      <!-- 4 Kafka Telemetry Metric Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n          <div ui-card-header class=\"pb-2\">\n            <div class=\"flex items-center justify-between gap-2\">\n              <div class=\"flex min-w-0 items-center gap-2\">\n                <div\n                  class=\"bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n                  aria-hidden=\"true\"\n                >\n                  <lucide-icon [img]=\"Activity\" class=\"size-4\" />\n                </div>\n                <h3 ui-card-title class=\"truncate text-xs font-medium\">Message Inflow Rate</h3>\n              </div>\n              <span ui-badge variant=\"outline\" class=\"shrink-0 text-xs font-normal tabular-nums\">+5.8% 1h</span>\n            </div>\n          </div>\n          <div ui-card-content class=\"space-y-3\">\n            <div>\n              <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">14,250 msgs/sec</div>\n              <p class=\"text-muted-foreground text-xs tabular-nums\">2.4 MB/s throughput</p>\n            </div>\n            <div class=\"space-y-1.5\">\n              <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n                <span>Ingress Bandwidth</span>\n                <span class=\"text-foreground font-medium tabular-nums\">48% of cap</span>\n              </div>\n              <ui-progress [value]=\"48\" class=\"h-1.5\" />\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n          <div ui-card-header class=\"pb-2\">\n            <div class=\"flex items-center justify-between gap-2\">\n              <div class=\"flex min-w-0 items-center gap-2\">\n                <div\n                  class=\"bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n                  aria-hidden=\"true\"\n                >\n                  <lucide-icon [img]=\"Gauge\" class=\"size-4\" />\n                </div>\n                <h3 ui-card-title class=\"truncate text-xs font-medium\">Consumer Group Lag</h3>\n              </div>\n              <span ui-badge variant=\"success\" class=\"shrink-0 text-xs\">Real-time</span>\n            </div>\n          </div>\n          <div ui-card-content class=\"space-y-3\">\n            <div>\n              <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">0 messages lag</div>\n              <p class=\"text-muted-foreground text-xs\">across 4 consumer groups</p>\n            </div>\n            <div class=\"space-y-1.5\">\n              <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n                <span>Processing Drift</span>\n                <span class=\"text-foreground font-medium tabular-nums\">0 ms lag</span>\n              </div>\n              <ui-progress [value]=\"100\" class=\"h-1.5\" />\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n          <div ui-card-header class=\"pb-2\">\n            <div class=\"flex items-center justify-between gap-2\">\n              <div class=\"flex min-w-0 items-center gap-2\">\n                <div\n                  class=\"bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n                  aria-hidden=\"true\"\n                >\n                  <lucide-icon [img]=\"HardDrive\" class=\"size-4\" />\n                </div>\n                <h3 ui-card-title class=\"truncate text-xs font-medium\">Total Retention</h3>\n              </div>\n              <span ui-badge variant=\"outline\" class=\"shrink-0 text-xs font-normal\">7 Days</span>\n            </div>\n          </div>\n          <div ui-card-content class=\"space-y-3\">\n            <div>\n              <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">480 GB total footprint</div>\n              <p class=\"text-muted-foreground text-xs tabular-nums\">40 GB avg / partition</p>\n            </div>\n            <div class=\"space-y-1.5\">\n              <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n                <span>Disk Footprint</span>\n                <span class=\"text-foreground font-medium tabular-nums\">480 GB / 1.0 TB</span>\n              </div>\n              <ui-progress [value]=\"48\" class=\"h-1.5\" />\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n          <div ui-card-header class=\"pb-2\">\n            <div class=\"flex items-center justify-between gap-2\">\n              <div class=\"flex min-w-0 items-center gap-2\">\n                <div\n                  class=\"bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n                  aria-hidden=\"true\"\n                >\n                  <lucide-icon [img]=\"ShieldCheck\" class=\"size-4\" />\n                </div>\n                <h3 ui-card-title class=\"truncate text-xs font-medium\">Under-Replicated</h3>\n              </div>\n              <span ui-badge variant=\"success\" class=\"shrink-0 text-xs\">In-Sync</span>\n            </div>\n          </div>\n          <div ui-card-content class=\"space-y-3\">\n            <div>\n              <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">0 / 12 Partitions</div>\n              <p class=\"text-muted-foreground text-xs\">All 36 replicas fully in-sync (ISR)</p>\n            </div>\n            <div class=\"space-y-1.5\">\n              <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n                <span>Replication Health</span>\n                <span class=\"text-foreground font-medium tabular-nums\">100% ISR</span>\n              </div>\n              <ui-progress [value]=\"100\" class=\"h-1.5\" />\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Partitions & Offset Distribution Table -->\n      <div ui-card class=\"shadow-xs\">\n        <div ui-card-header>\n          <div class=\"flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between\">\n            <div>\n              <h3 ui-card-title class=\"text-base\">Partitions & Offset Distribution</h3>\n              <p ui-card-description class=\"text-xs\">\n                Per-partition log high-water marks, consumer group commitments, and leader broker assignments.\n              </p>\n            </div>\n            <span ui-badge variant=\"outline\" class=\"w-fit font-mono text-xs font-normal tabular-nums\">\n              Showing 4 of 12 Partitions\n            </span>\n          </div>\n        </div>\n        <div ui-card-content class=\"p-0 sm:p-6 sm:pt-0\">\n          <div class=\"overflow-x-auto\">\n            <table ui-table>\n              <thead ui-table-header>\n                <tr ui-table-row>\n                  <th ui-table-head class=\"text-xs\">Partition ID</th>\n                  <th ui-table-head class=\"text-xs\">Leader Broker</th>\n                  <th ui-table-head class=\"text-right text-xs\">Log End Offset</th>\n                  <th ui-table-head class=\"text-right text-xs\">Consumer Offset</th>\n                  <th ui-table-head class=\"text-right text-xs\">Lag</th>\n                  <th ui-table-head class=\"text-right text-xs\">Message Rate</th>\n                  <th ui-table-head class=\"text-right text-xs\">Replicas (ISR)</th>\n                </tr>\n              </thead>\n              <tbody ui-table-body>\n                @for (partition of partitions(); track partition.partitionId) {\n                  <tr ui-table-row>\n                    <td ui-table-cell class=\"text-xs font-medium\">\n                      <div class=\"flex items-center gap-2\">\n                        <span class=\"bg-success size-2 rounded-full\" aria-hidden=\"true\"></span>\n                        <span class=\"font-mono\">Partition {{ partition.partitionId }}</span>\n                      </div>\n                    </td>\n                    <td ui-table-cell class=\"text-xs\">\n                      <div class=\"flex items-center gap-1.5\">\n                        <span class=\"text-foreground font-medium\">{{ partition.leaderBroker }}</span>\n                        <span class=\"text-muted-foreground font-mono text-xs\">({{ partition.rack }})</span>\n                      </div>\n                    </td>\n                    <td ui-table-cell class=\"text-foreground text-right font-mono text-xs font-medium tabular-nums\">\n                      {{ partition.logEndOffset.toLocaleString() }}\n                    </td>\n                    <td ui-table-cell class=\"text-muted-foreground text-right font-mono text-xs tabular-nums\">\n                      {{ partition.consumerOffset.toLocaleString() }}\n                    </td>\n                    <td ui-table-cell class=\"text-right\">\n                      <span ui-badge variant=\"success\" class=\"font-mono text-xs tabular-nums\">{{ partition.lag }}</span>\n                    </td>\n                    <td ui-table-cell class=\"text-foreground text-right font-mono text-xs font-medium tabular-nums\">\n                      {{ partition.messageRate }}\n                    </td>\n                    <td ui-table-cell class=\"text-right\">\n                      <span ui-badge variant=\"outline\" class=\"font-mono text-xs tabular-nums\">\n                        {{ partition.isrCount }} / {{ partition.replicasCount }} In-Sync\n                      </span>\n                    </td>\n                  </tr>\n                }\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n\n      <!-- Live Message Stream Inspector -->\n      <div ui-card class=\"shadow-xs\">\n        <div class=\"bg-muted/40 flex flex-col gap-3 border-b p-3.5 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"flex items-center gap-2.5\">\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 class=\"flex items-center gap-2\">\n              <span class=\"text-foreground text-xs font-semibold\">Live Message Stream Inspector</span>\n              @if (!isPaused()) {\n                <div\n                  class=\"border-border bg-card inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium\"\n                >\n                  <span class=\"relative flex size-1.5 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-1.5 rounded-full\"></span>\n                  </span>\n                  <span class=\"text-muted-foreground\">Streaming</span>\n                </div>\n              } @else {\n                <div\n                  class=\"border-border bg-card inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium\"\n                >\n                  <span class=\"bg-warning relative inline-flex size-1.5 rounded-full\"></span>\n                  <span class=\"text-muted-foreground\">Paused</span>\n                </div>\n              }\n            </div>\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"xs\"\n              class=\"h-7 gap-1 text-xs\"\n              [disabled]=\"filteredMessages().length === 0\"\n              (click)=\"toggleAllExpand()\"\n            >\n              @if (allExpanded()) {\n                <lucide-icon [img]=\"ChevronDown\" class=\"size-3\" />\n              } @else {\n                <lucide-icon [img]=\"ChevronRight\" class=\"size-3\" />\n              }\n              <span>{{ allExpanded() ? 'Collapse All' : 'Expand All' }}</span>\n            </button>\n            <button ui-button variant=\"outline\" size=\"xs\" class=\"h-7 gap-1 text-xs\" (click)=\"togglePause()\">\n              @if (isPaused()) {\n                <lucide-icon [img]=\"Play\" class=\"size-3\" />\n              } @else {\n                <lucide-icon [img]=\"Pause\" class=\"size-3\" />\n              }\n              <span>{{ isPaused() ? 'Resume' : 'Pause' }}</span>\n            </button>\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"xs\"\n              class=\"h-7 gap-1 text-xs\"\n              [disabled]=\"messages().length === 0\"\n              (click)=\"clearStream()\"\n            >\n              <lucide-icon [img]=\"Trash2\" class=\"size-3\" />\n              <span>Clear</span>\n            </button>\n          </div>\n        </div>\n\n        <div class=\"bg-card/50 flex flex-col gap-3 border-b p-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"relative flex-1\">\n            <lucide-icon\n              [img]=\"Search\"\n              class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2\"\n            />\n            <ui-input\n              [value]=\"searchQuery()\"\n              (valueChange)=\"searchQuery.set($event)\"\n              placeholder=\"Filter message key, offset, event type, or JSON payload...\"\n              class=\"h-8 pl-8 text-xs\"\n            />\n          </div>\n          <div class=\"flex flex-wrap items-center gap-1\">\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\" />\n              Partition:\n            </span>\n            <button type=\"button\" [class]=\"partitionPillClass('all')\" (click)=\"selectedPartition.set('all')\">\n              All\n            </button>\n            @for (p of partitionIds; track p) {\n              <button type=\"button\" [class]=\"partitionPillClass(p)\" (click)=\"selectedPartition.set(p)\">\n                P-{{ p }}\n              </button>\n            }\n          </div>\n        </div>\n\n        <div ui-card-content class=\"p-0\">\n          @if (filteredMessages().length === 0) {\n            <div class=\"flex flex-col items-center justify-center gap-2.5 px-4 py-12 text-center\">\n              <div class=\"bg-muted flex size-10 items-center justify-center rounded-full\">\n                <lucide-icon [img]=\"Radio\" class=\"text-muted-foreground size-5 opacity-60\" />\n              </div>\n              <div class=\"space-y-1\">\n                <p class=\"text-foreground text-sm font-semibold\">No messages found</p>\n                <p class=\"text-muted-foreground text-xs\">\n                  {{\n                    messages().length === 0\n                      ? 'Message buffer was cleared. Reset to restore sample messages or produce a test event.'\n                      : 'Try clearing the search query or changing partition filter.'\n                  }}\n                </p>\n              </div>\n              <button ui-button variant=\"outline\" size=\"sm\" class=\"mt-1 gap-1.5 text-xs\" (click)=\"resetStream()\">\n                <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n                <span>Reset Stream</span>\n              </button>\n            </div>\n          } @else {\n            <ul class=\"divide-border/60 divide-y\">\n              @for (msg of filteredMessages(); track msg.id) {\n                <li [class]=\"rowClass(msg.offset)\">\n                  <div\n                    role=\"button\"\n                    tabindex=\"0\"\n                    [attr.aria-expanded]=\"!!expandedOffsets()[msg.offset]\"\n                    class=\"focus-visible:ring-ring flex cursor-pointer flex-col gap-2.5 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(msg.offset)\"\n                    (keydown.enter)=\"toggleExpandKey($event, msg.offset)\"\n                    (keydown.space)=\"toggleExpandKey($event, msg.offset)\"\n                  >\n                    <div class=\"flex min-w-0 items-start gap-3 sm:items-center\">\n                      <span\n                        class=\"text-muted-foreground mt-0.5 shrink-0 transition-transform sm:mt-0\"\n                        [class.rotate-90]=\"expandedOffsets()[msg.offset]\"\n                      >\n                        <lucide-icon [img]=\"ChevronRight\" class=\"size-4\" />\n                      </span>\n                      <div class=\"space-y-1\">\n                        <div class=\"flex flex-wrap items-center gap-2\">\n                          <span class=\"text-foreground font-mono text-xs font-bold tabular-nums\">\n                            #{{ msg.offset.toLocaleString() }}\n                          </span>\n                          <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">P-{{ msg.partition }}</span>\n                          <span ui-badge variant=\"secondary\" class=\"font-mono text-xs font-normal\">\n                            key: {{ msg.key }}\n                          </span>\n                          <span class=\"text-foreground font-mono text-xs font-semibold\">{{ msg.eventType }}</span>\n                        </div>\n                        <p class=\"text-muted-foreground font-mono text-xs tabular-nums\">{{ msg.timestamp }}</p>\n                      </div>\n                    </div>\n                    <div class=\"flex shrink-0 items-center gap-2.5 sm:justify-end\">\n                      <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                        {{ formatBytes(msg.sizeBytes) }}\n                      </span>\n                      <span ui-badge variant=\"outline\" class=\"hidden font-mono text-xs font-normal sm:inline-flex\">\n                        schema_id: {{ msg.schemaId }}\n                      </span>\n                    </div>\n                  </div>\n\n                  @if (expandedOffsets()[msg.offset]) {\n                    <div class=\"bg-muted/15 border-t px-4 py-3.5\">\n                      <div class=\"space-y-3\">\n                        <div\n                          class=\"bg-card flex flex-col gap-2 rounded-md border p-2.5 text-xs sm:flex-row sm:items-center sm:justify-between\"\n                        >\n                          <div class=\"flex min-w-0 items-center gap-2\">\n                            <span class=\"text-muted-foreground shrink-0 font-medium\">Trace ID:</span>\n                            <span class=\"text-foreground truncate font-mono\">{{ msg.traceId }}</span>\n                          </div>\n                          <div class=\"flex items-center gap-2\">\n                            <span class=\"text-muted-foreground\">Format:</span>\n                            <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">json+avro</span>\n                          </div>\n                        </div>\n                        <div\n                          class=\"overflow-hidden rounded-md border 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                              <lucide-icon [img]=\"Terminal\" class=\"text-muted-foreground size-3.5\" />\n                              <span class=\"text-muted-foreground text-xs\">deserialized_payload.json</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(msg.id, msg.payload); $event.stopPropagation()\"\n                            >\n                              @if (copiedId() === msg.id) {\n                                <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                              } @else {\n                                <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                              }\n                              <span>{{ copiedId() === msg.id ? 'Copied' : 'Copy JSON' }}</span>\n                            </button>\n                          </div>\n                          <pre\n                            class=\"max-h-64 overflow-x-auto overflow-y-auto p-3 text-xs leading-relaxed select-text\"\n                          ><code class=\"text-success\">{{ stringify(msg.payload) }}</code></pre>\n                        </div>\n                      </div>\n                    </div>\n                  }\n                </li>\n              }\n            </ul>\n          }\n        </div>\n\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\" />\n              <span>Rate:</span>\n              <span class=\"font-mono\">{{ isPaused() ? '0 msg/s (Paused)' : '14,250 msgs/sec' }}</span>\n            </div>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <div class=\"flex items-center gap-1.5\">\n              <lucide-icon [img]=\"Layers\" class=\"size-3.5\" />\n              <span>Buffer:</span>\n              <span class=\"text-foreground font-mono\"\n                >{{ filteredMessages().length }} / {{ messages().length }} cached</span\n              >\n            </div>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <div class=\"flex items-center gap-1.5\">\n              <lucide-icon [img]=\"Zap\" class=\"text-warning size-3.5\" />\n              <span>Avg Latency:</span>\n              <span class=\"text-foreground font-mono\">1.8ms</span>\n            </div>\n          </div>\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>SASL_SSL · LZ4 Compression · Schema Registry OK</span>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiKafkaStreamMonitorComponent {\n  protected readonly Activity = Activity\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly ChevronDown = ChevronDown\n  protected readonly ChevronRight = ChevronRight\n  protected readonly Copy = Copy\n  protected readonly Filter = Filter\n  protected readonly Gauge = Gauge\n  protected readonly HardDrive = HardDrive\n  protected readonly Layers = Layers\n  protected readonly Pause = Pause\n  protected readonly Play = Play\n  protected readonly PlusCircle = PlusCircle\n  protected readonly Radio = Radio\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Search = Search\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Terminal = Terminal\n  protected readonly Trash2 = Trash2\n  protected readonly Zap = Zap\n\n  protected readonly partitionIds = [0, 1, 2, 3]\n\n  @Input() set initialMessages(v: KafkaStreamMessage[] | undefined) {\n    if (v) this.messages.set([...v])\n  }\n  @Input() set initialPartition(v: number | 'all') {\n    this.selectedPartition.set(v ?? 'all')\n  }\n  @Input() set initialSearch(v: string) {\n    this.searchQuery.set(v ?? '')\n  }\n  @Input('class') className?: string\n\n  readonly partitions = signal<PartitionTelemetryRow[]>([...defaultPartitions])\n  readonly messages = signal<KafkaStreamMessage[]>([...defaultMessages])\n  readonly isPaused = signal(false)\n  readonly selectedPartition = signal<number | 'all'>('all')\n  readonly searchQuery = signal('')\n  readonly expandedOffsets = signal<Record<number, boolean>>({ 1482910: true })\n  readonly copiedId = signal<string | null>(null)\n  readonly produceNotice = signal<string | null>(null)\n  private nextOffset = 1482911\n  private copyTimer: ReturnType<typeof setTimeout> | undefined\n  private noticeTimer: ReturnType<typeof setTimeout> | undefined\n\n  readonly filteredMessages = computed(() => {\n    const selected = this.selectedPartition()\n    const q = this.searchQuery().trim().toLowerCase()\n    return this.messages().filter((msg) => {\n      if (selected !== 'all' && msg.partition !== selected) return false\n      if (!q) return true\n      return (\n        msg.key.toLowerCase().includes(q) ||\n        msg.eventType.toLowerCase().includes(q) ||\n        msg.offset.toString().includes(q) ||\n        msg.partition.toString().includes(q) ||\n        JSON.stringify(msg.payload).toLowerCase().includes(q)\n      )\n    })\n  })\n\n  readonly allExpanded = computed(() => {\n    const list = this.filteredMessages()\n    return list.length > 0 && list.every((m) => this.expandedOffsets()[m.offset])\n  })\n\n  get rootClass(): string {\n    return cn('mx-auto w-full max-w-6xl space-y-6', this.className)\n  }\n\n  partitionPillClass(p: number | 'all'): string {\n    const active = this.selectedPartition() === p\n    return cn(\n      'focus-visible:ring-ring inline-flex h-7 items-center rounded-md border px-2.5 font-mono text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none',\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\n  rowClass(offset: number): string {\n    return cn('group transition-colors', this.expandedOffsets()[offset] ? 'bg-muted/30' : 'hover:bg-muted/20')\n  }\n\n  stringify(payload: Record<string, unknown>): string {\n    return JSON.stringify(payload, null, 2)\n  }\n\n  formatBytes(bytes: number): string {\n    if (bytes < 1024) return `${bytes} B`\n    return `${(bytes / 1024).toFixed(1)} KB`\n  }\n\n  togglePause(): void {\n    this.isPaused.update((v) => !v)\n  }\n\n  toggleExpand(offset: number): void {\n    this.expandedOffsets.update((prev) => ({ ...prev, [offset]: !prev[offset] }))\n  }\n\n  toggleExpandKey(e: KeyboardEvent, offset: number): void {\n    e.preventDefault()\n    this.toggleExpand(offset)\n  }\n\n  toggleAllExpand(): void {\n    if (this.allExpanded()) {\n      this.expandedOffsets.set({})\n    } else {\n      const next: Record<number, boolean> = {}\n      for (const m of this.filteredMessages()) next[m.offset] = true\n      this.expandedOffsets.set(next)\n    }\n  }\n\n  clearStream(): void {\n    this.messages.set([])\n    this.expandedOffsets.set({})\n  }\n\n  resetStream(): void {\n    this.messages.set([...defaultMessages])\n    this.searchQuery.set('')\n    this.selectedPartition.set('all')\n    this.expandedOffsets.set({ 1482910: true })\n  }\n\n  copyPayload(id: string, payload: Record<string, unknown>): void {\n    try {\n      void navigator.clipboard?.writeText(JSON.stringify(payload, null, 2))\n      this.copiedId.set(id)\n      clearTimeout(this.copyTimer)\n      this.copyTimer = setTimeout(() => {\n        if (this.copiedId() === id) this.copiedId.set(null)\n      }, 2000)\n    } catch {\n      // Clipboard fallback\n    }\n  }\n\n  produceTestMessage(): void {\n    const currentOffset = this.nextOffset++\n    const targetPartition = currentOffset % 4\n    const rand = Math.random().toString(36).substring(2, 8)\n    const newMessage: KafkaStreamMessage = {\n      id: `msg-${currentOffset}`,\n      offset: currentOffset,\n      partition: targetPartition,\n      key: `cust_${rand}`,\n      timestamp: new Date().toISOString().replace('T', ' ').slice(0, 23) + ' UTC',\n      eventType: 'checkout.order_placed',\n      sizeBytes: 1280,\n      schemaId: 104,\n      traceId: `00-${rand}${rand}${rand}${rand}`.slice(0, 35),\n      payload: {\n        order_id: `ord_${rand}`,\n        customer_id: `cust_${rand}`,\n        currency: 'USD',\n        total_amount: 129.0,\n        item_count: 1,\n        items: [{ sku: 'SKU-SYNTH-DEVICE', name: 'Developer Workstation Hub', qty: 1, price: 129.0 }],\n        payment_status: 'authorized',\n        producer: 'console-stream-tester-ui',\n        test_message: true,\n      },\n    }\n    this.messages.update((prev) => [newMessage, ...prev])\n    this.expandedOffsets.update((prev) => ({ ...prev, [currentOffset]: true }))\n    this.produceNotice.set(`Produced message #${currentOffset.toLocaleString()} to Partition ${targetPartition}`)\n    clearTimeout(this.noticeTimer)\n    this.noticeTimer = setTimeout(() => this.produceNotice.set(null), 3500)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/kafka-stream-monitor/kafka-stream-monitor.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/kafka-stream-monitor/index.ts",
      "content": "export {\n  UiKafkaStreamMonitorComponent,\n  type KafkaStreamMessage,\n  type PartitionTelemetryRow,\n} from './kafka-stream-monitor.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/kafka-stream-monitor/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": "Apache Kafka & Redpanda topic stream telemetry dashboard: partition offset monitor, leader broker distribution, consumer group lag tracker, and real-time message payload inspector with deserialized JSON viewer.",
  "categories": [
    "devops",
    "dashboard",
    "data",
    "app"
  ]
}