{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "deployment-pipeline",
  "title": "Deployment Pipeline",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/deployment-pipeline/deployment-pipeline.component.ts",
      "content": "import { ChangeDetectionStrategy, Component, DestroyRef, Input, OnInit, computed, inject, signal } from '@angular/core'\nimport {\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  ChevronUp,\n  Clock,\n  Copy,\n  ExternalLink,\n  GitBranch,\n  GitCommit,\n  Layers,\n  Loader2,\n  LucideAngularModule,\n  MoreHorizontal,\n  Play,\n  RotateCcw,\n  RotateCw,\n  Terminal,\n  Undo2,\n  XCircle,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuLabelComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.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'\nimport { UiTabsComponent, UiTabsListComponent, UiTabsTriggerComponent } from '@/ui/tabs/tabs.component'\n\nexport type DeploymentEnvironment = 'all' | 'production' | 'preview' | 'staging'\nexport type DeploymentStatus = 'ready' | 'building' | 'failed' | 'rolled-back'\nexport type DeploymentTrigger = 'push' | 'manual' | 'rollback'\n\nexport interface DeploymentRecord {\n  id: string\n  commitMessage: string\n  commitHash: string\n  branch: string\n  environment: 'production' | 'preview' | 'staging'\n  status: DeploymentStatus\n  duration: string\n  deployer: {\n    name: string\n    initials: string\n  }\n  trigger: DeploymentTrigger\n  deployedAt: string\n  url: string\n}\n\nexport interface PipelineStep {\n  id: string\n  name: string\n  duration: string\n  description: string\n  status: 'completed' | 'in-progress' | 'pending'\n}\n\nexport type TerminalLogType = 'info' | 'cmd' | 'stdout' | 'success'\n\nexport interface TerminalLogLine {\n  time: string\n  type: TerminalLogType\n  prefix: string\n  text: string\n}\n\nconst branches = ['main', 'staging', 'preview/safari-tabs', 'preview/e2e-tests']\n\nconst pipelineSteps: PipelineStep[] = [\n  { id: 'step-1', name: 'Queued', duration: '0.2s', description: 'Worker allocated', status: 'completed' },\n  { id: 'step-2', name: 'Build & Bundle', duration: '42s', description: 'Vite & TS compilation', status: 'completed' },\n  {\n    id: 'step-3',\n    name: 'Static Generation',\n    duration: '18s',\n    description: '42 SSG pages rendered',\n    status: 'completed',\n  },\n  { id: 'step-4', name: 'Edge Propagation', duration: '3s', description: 'Synced to 310 PoPs', status: 'completed' },\n  { id: 'step-5', name: 'Ready', duration: 'Instant', description: 'Verified & live', status: 'completed' },\n]\n\nconst terminalLogs: TerminalLogLine[] = [\n  {\n    time: '00:00:00.120',\n    type: 'info',\n    prefix: '❯ [worker]',\n    text: 'Initializing deployment worker in region iad-1 (runner-edge-iad-4)...',\n  },\n  {\n    time: '00:00:00.340',\n    type: 'info',\n    prefix: '❯ [git]',\n    text: 'Cloning repository uipkge/core-registry at commit a3f89b1 (branch: main)...',\n  },\n  {\n    time: '00:00:01.050',\n    type: 'info',\n    prefix: '❯ [cache]',\n    text: 'Restoring pnpm store cache (v9.15.0)... (1,482 packages restored, 98.4% cache hit)',\n  },\n  { time: '00:00:02.410', type: 'cmd', prefix: '$', text: 'pnpm run build:registry && pnpm run build' },\n  { time: '00:00:04.180', type: 'stdout', prefix: '  [vite]', text: 'Transformed 482 modules in 312ms' },\n  {\n    time: '00:00:15.920',\n    type: 'stdout',\n    prefix: '  [astro]',\n    text: 'Building static entrypoints for production (Vue 3.5 + React 19 islands)...',\n  },\n  { time: '00:00:32.400', type: 'stdout', prefix: '  [astro]', text: '✓ 42 static pages rendered in 16.48s' },\n  {\n    time: '00:00:44.210',\n    type: 'stdout',\n    prefix: '  [registry]',\n    text: 'Generated 120 manifests into public/r/vue/*.json and public/r/react/*.json',\n  },\n  {\n    time: '00:00:58.800',\n    type: 'info',\n    prefix: '❯ [edge]',\n    text: 'Optimizing edge assets with Brotli compression (saved 4.8 MB)...',\n  },\n  {\n    time: '00:01:01.320',\n    type: 'info',\n    prefix: '❯ [edge]',\n    text: 'Propagating edge routes to Cloudflare global network across 310 PoPs...',\n  },\n  {\n    time: '00:01:03.200',\n    type: 'success',\n    prefix: '✓ [ready]',\n    text: 'Deployment successfully verified and healthy at https://uipkge.dev',\n  },\n  {\n    time: '00:01:03.210',\n    type: 'success',\n    prefix: '✓ [exit]',\n    text: 'Process completed with exit code 0 (success in 1m 03.2s)',\n  },\n]\n\nconst deployments: DeploymentRecord[] = [\n  {\n    id: 'd-1042',\n    commitMessage: 'feat(blocks): add ecommerce suite (#412)',\n    commitHash: 'a3f89b1',\n    branch: 'main',\n    environment: 'production',\n    status: 'ready',\n    duration: '1m 03s',\n    deployer: { name: 'Jordan Diaz', initials: 'JD' },\n    trigger: 'push',\n    deployedAt: '12m ago',\n    url: 'https://uipkge.dev',\n  },\n  {\n    id: 'd-1041',\n    commitMessage: 'fix(tabs): resolve indicator jitter in safari (#411)',\n    commitHash: 'f79b20e',\n    branch: 'preview/safari-tabs',\n    environment: 'preview',\n    status: 'ready',\n    duration: '48s',\n    deployer: { name: 'Amara Osei', initials: 'AO' },\n    trigger: 'push',\n    deployedAt: '2h ago',\n    url: 'https://uipkge-pr-411.uipkge.dev',\n  },\n  {\n    id: 'd-1040',\n    commitMessage: 'refactor(avatar): extract avatar-group primitive (#410)',\n    commitHash: '91bc8d3',\n    branch: 'staging',\n    environment: 'staging',\n    status: 'ready',\n    duration: '54s',\n    deployer: { name: 'Jonas Weber', initials: 'JW' },\n    trigger: 'manual',\n    deployedAt: '5h ago',\n    url: 'https://staging.uipkge.dev',\n  },\n  {\n    id: 'd-1039',\n    commitMessage: 'test(e2e): add playwright visual regression suite (#409)',\n    commitHash: '62e08a4',\n    branch: 'preview/e2e-tests',\n    environment: 'preview',\n    status: 'building',\n    duration: '1m 12s',\n    deployer: { name: 'Priya Nair', initials: 'PN' },\n    trigger: 'push',\n    deployedAt: 'Just now',\n    url: 'https://uipkge-pr-409.uipkge.dev',\n  },\n  {\n    id: 'd-1038',\n    commitMessage: 'perf(theme): eliminate css custom property recalculation (#408)',\n    commitHash: '3d12fa9',\n    branch: 'staging',\n    environment: 'staging',\n    status: 'failed',\n    duration: '24s',\n    deployer: { name: 'Marcus Lee', initials: 'ML' },\n    trigger: 'push',\n    deployedAt: 'Yesterday',\n    url: 'https://staging-failed.uipkge.dev',\n  },\n  {\n    id: 'd-1037',\n    commitMessage: 'chore(deps): update reka-ui to v1.1.0 (#407)',\n    commitHash: '180eac2',\n    branch: 'main',\n    environment: 'production',\n    status: 'rolled-back',\n    duration: '1m 20s',\n    deployer: { name: 'Jordan Diaz', initials: 'JD' },\n    trigger: 'rollback',\n    deployedAt: '2 days ago',\n    url: 'https://uipkge-v2-rollback.uipkge.dev',\n  },\n]\n\n/**\n * CI/CD deployments dashboard: environment tabs, latest-deployment hero with the\n * execution stepper and a collapsible build-log terminal, and a filterable history\n * table with per-row actions. Edit the data consts above to wire it to real data.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-deployment-pipeline, [ui-deployment-pipeline]',\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    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuLabelComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiSeparatorComponent,\n    UiTableComponent,\n    UiTableHeaderComponent,\n    UiTableBodyComponent,\n    UiTableRowComponent,\n    UiTableHeadComponent,\n    UiTableCellComponent,\n    UiTabsComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n  ],\n  template: `\n    <div data-slot=\"deployment-pipeline\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"flex items-center gap-3\">\n          <div class=\"bg-card flex size-10 items-center justify-center rounded-lg border shadow-xs\">\n            <lucide-icon [img]=\"Layers\" class=\"text-primary size-5\" aria-hidden=\"true\" />\n          </div>\n          <div>\n            <h2 class=\"text-2xl font-bold tracking-tight\">Deployments</h2>\n            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n              <span class=\"text-foreground font-mono font-medium\">uipkge</span>\n              <span>/</span>\n              <span class=\"font-mono\">core-registry</span>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <ui-dropdown-menu>\n            <button ui-button ui-dropdown-menu-trigger variant=\"outline\" size=\"sm\" class=\"font-mono text-xs\">\n              <lucide-icon [img]=\"GitBranch\" class=\"text-muted-foreground size-3.5\" aria-hidden=\"true\" />\n              <span>{{ selectedBranch() }}</span>\n              <lucide-icon [img]=\"ChevronDown\" class=\"text-muted-foreground size-3.5 opacity-60\" aria-hidden=\"true\" />\n            </button>\n            <ui-dropdown-menu-content align=\"end\" class=\"w-48\">\n              <ui-dropdown-menu-label>Select branch</ui-dropdown-menu-label>\n              <ui-dropdown-menu-separator />\n              @for (branch of branches; track branch) {\n                <ui-dropdown-menu-item\n                  [class]=\"selectedBranch() === branch ? 'bg-accent font-semibold' : ''\"\n                  (click)=\"selectedBranch.set(branch)\"\n                >\n                  <lucide-icon [img]=\"GitBranch\" class=\"mr-2 size-3.5\" aria-hidden=\"true\" />\n                  <span class=\"font-mono text-xs\">{{ branch }}</span>\n                </ui-dropdown-menu-item>\n              }\n            </ui-dropdown-menu-content>\n          </ui-dropdown-menu>\n\n          <button ui-button size=\"sm\" [disabled]=\"isDeploying()\" (click)=\"triggerDeployment()\">\n            @if (isDeploying()) {\n              <lucide-icon [img]=\"Loader2\" class=\"size-3.5 animate-spin\" aria-hidden=\"true\" />\n            } @else {\n              <lucide-icon [img]=\"Play\" class=\"size-3.5 fill-current\" aria-hidden=\"true\" />\n            }\n            {{ isDeploying() ? 'Triggering...' : 'Trigger Deployment' }}\n          </button>\n        </div>\n      </div>\n\n      <!-- Environment Tabs Navigation -->\n      <ui-tabs [value]=\"selectedEnv()\" (valueChange)=\"setEnv($event)\" class=\"w-full\">\n        <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n          <ui-tabs-list variant=\"segmented\" class=\"w-full sm:w-auto\">\n            <ui-tabs-trigger value=\"all\">All</ui-tabs-trigger>\n            <ui-tabs-trigger value=\"production\">Production</ui-tabs-trigger>\n            <ui-tabs-trigger value=\"preview\">Preview</ui-tabs-trigger>\n            <ui-tabs-trigger value=\"staging\">Staging</ui-tabs-trigger>\n          </ui-tabs-list>\n          <span class=\"text-muted-foreground text-xs tabular-nums\">\n            Showing {{ filteredDeployments().length }} of {{ deployments.length }} deployments\n          </span>\n        </div>\n      </ui-tabs>\n\n      <!-- Latest Deployment Hero Card -->\n      <ui-card class=\"border-border shadow-xs\">\n        <ui-card-header class=\"space-y-4 pb-4\">\n          <div class=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <ui-card-title class=\"text-base font-semibold\">Latest Deployment</ui-card-title>\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                  Live · Production\n                </div>\n              </div>\n              <ui-card-description>\n                <a\n                  href=\"https://uipkge.dev\"\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  class=\"text-foreground hover:text-primary inline-flex min-h-6 items-center gap-1 py-0.5 font-mono text-xs font-medium underline underline-offset-4 transition-colors\"\n                >\n                  https://uipkge.dev\n                  <lucide-icon [img]=\"ExternalLink\" class=\"text-muted-foreground size-3\" aria-hidden=\"true\" />\n                </a>\n              </ui-card-description>\n            </div>\n\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <button ui-button variant=\"outline\" size=\"sm\" (click)=\"showLogs.set(!showLogs())\">\n                <lucide-icon [img]=\"Terminal\" class=\"size-3.5\" aria-hidden=\"true\" />\n                <span>{{ showLogs() ? 'Hide Build Logs' : 'View Build Logs' }}</span>\n                @if (showLogs()) {\n                  <lucide-icon [img]=\"ChevronUp\" class=\"text-muted-foreground size-3.5\" aria-hidden=\"true\" />\n                } @else {\n                  <lucide-icon [img]=\"ChevronDown\" class=\"text-muted-foreground size-3.5\" aria-hidden=\"true\" />\n                }\n              </button>\n              <button ui-button variant=\"outline\" size=\"sm\">\n                <lucide-icon [img]=\"RotateCw\" class=\"size-3.5\" aria-hidden=\"true\" />\n                <span>Redeploy</span>\n              </button>\n              <button ui-button variant=\"outline\" size=\"sm\">\n                <lucide-icon [img]=\"Undo2\" class=\"size-3.5\" aria-hidden=\"true\" />\n                <span>Instant Rollback</span>\n              </button>\n            </div>\n          </div>\n\n          <!-- Commit Details Bar -->\n          <div class=\"bg-muted/40 flex flex-wrap items-center gap-x-4 gap-y-2 rounded-lg border px-3.5 py-2.5 text-xs\">\n            <div class=\"flex items-center gap-2\">\n              <ui-avatar class=\"size-5\">\n                <ui-avatar-fallback class=\"text-xs font-medium\">JD</ui-avatar-fallback>\n              </ui-avatar>\n              <span class=\"text-foreground font-medium\">Jordan Diaz</span>\n            </div>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <span class=\"text-foreground font-medium\">feat(blocks): add ecommerce suite (#412)</span>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <div class=\"text-muted-foreground flex items-center gap-1 font-mono\">\n              <lucide-icon [img]=\"GitCommit\" class=\"size-3.5\" aria-hidden=\"true\" />\n              <span>a3f89b1</span>\n            </div>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <div class=\"text-muted-foreground flex items-center gap-1 font-mono\">\n              <lucide-icon [img]=\"GitBranch\" class=\"size-3.5\" aria-hidden=\"true\" />\n              <span>main</span>\n            </div>\n            <ui-separator orientation=\"vertical\" class=\"hidden h-3.5 sm:block\" />\n            <div class=\"text-muted-foreground flex items-center gap-1\">\n              <lucide-icon [img]=\"Clock\" class=\"size-3.5\" aria-hidden=\"true\" />\n              <span>Deployed 12m ago</span>\n            </div>\n          </div>\n        </ui-card-header>\n\n        <ui-card-content class=\"space-y-4 pt-0\">\n          <!-- Execution Stepper -->\n          <div class=\"space-y-2.5\">\n            <div class=\"flex items-center justify-between\">\n              <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                Pipeline Execution\n              </span>\n              <span class=\"text-muted-foreground font-mono text-xs\">5 of 5 steps completed · 1m 03s</span>\n            </div>\n\n            <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-5\">\n              @for (step of pipelineSteps; track step.id; let index = $index) {\n                <div\n                  class=\"bg-card/60 hover:bg-muted/40 flex flex-col justify-between rounded-lg border p-3 shadow-2xs transition-colors\"\n                >\n                  <div class=\"flex items-center justify-between\">\n                    <span class=\"bg-success/15 text-success flex size-5 items-center justify-center rounded-full\">\n                      <lucide-icon [img]=\"Check\" class=\"size-3 stroke-[3]\" aria-hidden=\"true\" />\n                    </span>\n                    <span class=\"text-muted-foreground font-mono text-xs\">{{ step.duration }}</span>\n                  </div>\n                  <div class=\"mt-2\">\n                    <p class=\"text-foreground text-xs font-semibold\">{{ index + 1 }}. {{ step.name }}</p>\n                    <p class=\"text-muted-foreground truncate text-xs\">{{ step.description }}</p>\n                  </div>\n                </div>\n              }\n            </div>\n          </div>\n\n          <!-- Collapsible Dark Terminal / Build Log Viewer -->\n          @if (showLogs()) {\n            <div class=\"overflow-hidden rounded-lg border border-zinc-800 bg-zinc-950 font-mono text-xs shadow-inner\">\n              <div class=\"flex items-center justify-between border-b border-zinc-800 bg-zinc-900/90 px-3.5 py-2\">\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                  <span class=\"text-muted-foreground text-xs\"> build-stdout — node v22.12.0 — runner-edge-iad-4 </span>\n                </div>\n                <div class=\"flex items-center gap-2\">\n                  <span class=\"text-muted-foreground hidden text-xs sm:inline\">Total: 1m 03.2s</span>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"xs\"\n                    class=\"h-6 gap-1 px-2 text-zinc-300 hover:bg-zinc-800 hover:text-zinc-100\"\n                    (click)=\"copyLogs()\"\n                  >\n                    @if (copiedLogs()) {\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>{{ copiedLogs() ? 'Copied' : 'Copy logs' }}</span>\n                  </button>\n                </div>\n              </div>\n\n              <div class=\"max-h-72 space-y-1 overflow-y-auto p-4 select-text\">\n                @for (line of terminalLogs; track $index) {\n                  <div class=\"flex items-start gap-2 leading-relaxed\">\n                    <span class=\"text-muted-foreground select-none\">[{{ line.time }}]</span>\n                    <span [class]=\"prefixClass(line.type)\">{{ line.prefix }}</span>\n                    <span [class]=\"textClass(line.type)\">{{ line.text }}</span>\n                  </div>\n                }\n              </div>\n\n              <div\n                class=\"text-muted-foreground flex items-center justify-between border-t border-zinc-800 bg-zinc-900/60 px-3.5 py-1.5 text-xs\"\n              >\n                <div class=\"flex items-center gap-2\">\n                  <span\n                    class=\"bg-success/10 text-success inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-medium\"\n                  >\n                    <lucide-icon [img]=\"Check\" class=\"size-3\" aria-hidden=\"true\" /> Exit code: 0\n                  </span>\n                  <span>Clean build</span>\n                </div>\n                <span class=\"text-muted-foreground\">Memory: 412 MB / 2048 MB</span>\n              </div>\n            </div>\n          }\n        </ui-card-content>\n      </ui-card>\n\n      <!-- Deployment History Table -->\n      <ui-card class=\"border-border shadow-xs\">\n        <ui-card-header class=\"pb-3\">\n          <ui-card-title class=\"text-base font-semibold\">Deployment History</ui-card-title>\n          <ui-card-description class=\"text-xs\">\n            Past deployments across production, preview, and staging environments.\n          </ui-card-description>\n        </ui-card-header>\n        <ui-card-content class=\"p-0\">\n          <div class=\"overflow-x-auto\">\n            <div ui-table>\n              <thead ui-table-header>\n                <tr ui-table-row>\n                  <th ui-table-head class=\"w-10\">\n                    <span class=\"sr-only\">Status</span>\n                  </th>\n                  <th ui-table-head>Commit &amp; Message</th>\n                  <th ui-table-head>Environment</th>\n                  <th ui-table-head>Duration</th>\n                  <th ui-table-head>Deployer</th>\n                  <th ui-table-head>Trigger</th>\n                  <th ui-table-head class=\"text-right\">Deployed</th>\n                  <th ui-table-head class=\"w-10\">\n                    <span class=\"sr-only\">Actions</span>\n                  </th>\n                </tr>\n              </thead>\n              <tbody ui-table-body>\n                @for (deployment of filteredDeployments(); track deployment.id) {\n                  <tr ui-table-row class=\"hover:bg-muted/50\">\n                    <td ui-table-cell class=\"py-3\">\n                      @switch (deployment.status) {\n                        @case ('ready') {\n                          <span title=\"Ready\">\n                            <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-4\" aria-hidden=\"true\" />\n                          </span>\n                        }\n                        @case ('building') {\n                          <span title=\"Building\">\n                            <lucide-icon [img]=\"Loader2\" class=\"text-info size-4 animate-spin\" aria-hidden=\"true\" />\n                          </span>\n                        }\n                        @case ('failed') {\n                          <span title=\"Failed\">\n                            <lucide-icon [img]=\"XCircle\" class=\"text-destructive size-4\" aria-hidden=\"true\" />\n                          </span>\n                        }\n                        @case ('rolled-back') {\n                          <span title=\"Rolled back\">\n                            <lucide-icon [img]=\"Undo2\" class=\"text-warning size-4\" aria-hidden=\"true\" />\n                          </span>\n                        }\n                      }\n                    </td>\n                    <td ui-table-cell>\n                      <div class=\"min-w-[220px]\">\n                        <p class=\"text-foreground max-w-sm truncate text-sm font-medium\">\n                          {{ deployment.commitMessage }}\n                        </p>\n                        <div class=\"text-muted-foreground mt-0.5 flex items-center gap-2 font-mono text-xs\">\n                          <span class=\"flex items-center gap-1\">\n                            <lucide-icon [img]=\"GitBranch\" class=\"size-3\" aria-hidden=\"true\" />\n                            {{ deployment.branch }}\n                          </span>\n                          <span>·</span>\n                          <span class=\"flex items-center gap-1\">\n                            <lucide-icon [img]=\"GitCommit\" class=\"size-3\" aria-hidden=\"true\" />\n                            {{ deployment.commitHash }}\n                          </span>\n                        </div>\n                      </div>\n                    </td>\n                    <td ui-table-cell>\n                      <span ui-badge [variant]=\"envBadgeVariant(deployment)\" class=\"text-xs capitalize\">\n                        {{ deployment.environment }}\n                      </span>\n                    </td>\n                    <td ui-table-cell class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                      {{ deployment.duration }}\n                    </td>\n                    <td ui-table-cell>\n                      <div class=\"flex items-center gap-2\">\n                        <ui-avatar class=\"size-6\">\n                          <ui-avatar-fallback class=\"text-xs font-medium\">\n                            {{ deployment.deployer.initials }}\n                          </ui-avatar-fallback>\n                        </ui-avatar>\n                        <span class=\"text-foreground text-xs\">{{ deployment.deployer.name }}</span>\n                      </div>\n                    </td>\n                    <td ui-table-cell>\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                        @switch (deployment.trigger) {\n                          @case ('push') {\n                            <lucide-icon [img]=\"GitCommit\" class=\"size-3.5\" aria-hidden=\"true\" />\n                          }\n                          @case ('manual') {\n                            <lucide-icon [img]=\"Play\" class=\"size-3.5\" aria-hidden=\"true\" />\n                          }\n                          @case ('rollback') {\n                            <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" aria-hidden=\"true\" />\n                          }\n                        }\n                        <span class=\"capitalize\">{{ deployment.trigger }}</span>\n                      </div>\n                    </td>\n                    <td ui-table-cell class=\"text-muted-foreground text-right text-xs whitespace-nowrap tabular-nums\">\n                      {{ deployment.deployedAt }}\n                    </td>\n                    <td ui-table-cell>\n                      <ui-dropdown-menu>\n                        <button\n                          ui-button\n                          ui-dropdown-menu-trigger\n                          variant=\"ghost\"\n                          size=\"icon-sm\"\n                          class=\"text-muted-foreground\"\n                        >\n                          <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" aria-hidden=\"true\" />\n                          <span class=\"sr-only\">Open actions for {{ deployment.id }}</span>\n                        </button>\n                        <ui-dropdown-menu-content align=\"end\">\n                          <ui-dropdown-menu-label>Actions</ui-dropdown-menu-label>\n                          <ui-dropdown-menu-separator />\n                          <ui-dropdown-menu-item>\n                            <lucide-icon [img]=\"Undo2\" class=\"mr-2 size-4\" aria-hidden=\"true\" />\n                            Rollback to this deploy\n                          </ui-dropdown-menu-item>\n                          <ui-dropdown-menu-item (click)=\"copyUrl(deployment.url, deployment.id)\">\n                            @if (copiedUrlId() === deployment.id) {\n                              <lucide-icon [img]=\"Check\" class=\"text-success mr-2 size-4\" aria-hidden=\"true\" />\n                            } @else {\n                              <lucide-icon [img]=\"Copy\" class=\"mr-2 size-4\" aria-hidden=\"true\" />\n                            }\n                            {{ copiedUrlId() === deployment.id ? 'Copied URL' : 'Copy deployment URL' }}\n                          </ui-dropdown-menu-item>\n                          <ui-dropdown-menu-item (click)=\"showLogs.set(true)\">\n                            <lucide-icon [img]=\"Terminal\" class=\"mr-2 size-4\" aria-hidden=\"true\" />\n                            Inspect build logs\n                          </ui-dropdown-menu-item>\n                          <a\n                            ui-dropdown-menu-item\n                            [href]=\"deployment.url\"\n                            target=\"_blank\"\n                            rel=\"noopener noreferrer\"\n                            class=\"flex items-center\"\n                          >\n                            <lucide-icon [img]=\"ExternalLink\" class=\"mr-2 size-4\" aria-hidden=\"true\" />\n                            View live preview\n                          </a>\n                        </ui-dropdown-menu-content>\n                      </ui-dropdown-menu>\n                    </td>\n                  </tr>\n                }\n                @if (filteredDeployments().length === 0) {\n                  <tr ui-table-row>\n                    <td ui-table-cell colspan=\"8\" class=\"text-muted-foreground h-24 text-center text-sm\">\n                      No deployments match the selected environment filter.\n                    </td>\n                  </tr>\n                }\n              </tbody>\n            </div>\n          </div>\n        </ui-card-content>\n      </ui-card>\n    </div>\n  `,\n})\nexport class UiDeploymentPipelineComponent implements OnInit {\n  @Input() initialEnv: DeploymentEnvironment = 'all'\n  @Input() initialLogsOpen = true\n  @Input('class') className?: string\n\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly ChevronDown = ChevronDown\n  protected readonly ChevronUp = ChevronUp\n  protected readonly Clock = Clock\n  protected readonly Copy = Copy\n  protected readonly ExternalLink = ExternalLink\n  protected readonly GitBranch = GitBranch\n  protected readonly GitCommit = GitCommit\n  protected readonly Layers = Layers\n  protected readonly Loader2 = Loader2\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly Play = Play\n  protected readonly RotateCcw = RotateCcw\n  protected readonly RotateCw = RotateCw\n  protected readonly Terminal = Terminal\n  protected readonly Undo2 = Undo2\n  protected readonly XCircle = XCircle\n\n  protected readonly branches = branches\n  protected readonly pipelineSteps = pipelineSteps\n  protected readonly terminalLogs = terminalLogs\n  protected readonly deployments = deployments\n\n  readonly selectedEnv = signal<DeploymentEnvironment>('all')\n  readonly selectedBranch = signal('main')\n  readonly showLogs = signal(true)\n  readonly copiedLogs = signal(false)\n  readonly copiedUrlId = signal<string | null>(null)\n  readonly isDeploying = signal(false)\n\n  readonly filteredDeployments = computed(() => {\n    const env = this.selectedEnv()\n    return env === 'all' ? deployments : deployments.filter((d) => d.environment === env)\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    this.selectedEnv.set(this.initialEnv)\n    this.showLogs.set(this.initialLogsOpen)\n  }\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  setEnv(value: string): void {\n    this.selectedEnv.set(value as DeploymentEnvironment)\n  }\n\n  envBadgeVariant(deployment: DeploymentRecord): 'default' | 'secondary' | 'outline' {\n    return deployment.environment === 'production'\n      ? 'default'\n      : deployment.environment === 'preview'\n        ? 'secondary'\n        : 'outline'\n  }\n\n  prefixClass(type: TerminalLogType): string {\n    return cn(\n      'font-medium select-none',\n      type === 'success' && 'text-success',\n      type === 'info' && 'text-info',\n      type === 'cmd' && 'text-warning',\n      type === 'stdout' && 'text-muted-foreground',\n    )\n  }\n\n  textClass(type: TerminalLogType): string {\n    return cn(\n      type === 'success' && 'text-success font-medium',\n      type === 'cmd' && 'font-semibold text-zinc-100',\n      type === 'info' && 'text-zinc-300',\n      type === 'stdout' && 'text-zinc-300',\n    )\n  }\n\n  triggerDeployment(): void {\n    this.isDeploying.set(true)\n    this.later(() => this.isDeploying.set(false), 1200)\n  }\n\n  copyLogs(): void {\n    const text = terminalLogs.map((l) => `[${l.time}] ${l.prefix} ${l.text}`).join('\\n')\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(text)\n      this.copiedLogs.set(true)\n      this.later(() => this.copiedLogs.set(false), 2000)\n    }\n  }\n\n  copyUrl(url: string, id: string): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(url)\n      this.copiedUrlId.set(id)\n      this.later(() => this.copiedUrlId.set(null), 2000)\n    }\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",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/deployment-pipeline/deployment-pipeline.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/deployment-pipeline/index.ts",
      "content": "export {\n  UiDeploymentPipelineComponent,\n  type DeploymentEnvironment,\n  type DeploymentStatus,\n  type DeploymentTrigger,\n  type DeploymentRecord,\n  type PipelineStep,\n  type TerminalLogType,\n  type TerminalLogLine,\n} from './deployment-pipeline.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/deployment-pipeline/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dropdown-menu.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/table.json",
    "https://uipkge.dev/r/angular/tabs.json"
  ],
  "description": "CI/CD deployment history, active environment status, execution stepper, collapsible build log terminal with stdout timestamps, and filterable deployments table.",
  "categories": [
    "devops",
    "app"
  ]
}