{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "ai-code-review-assistant",
  "title": "Ai Code Review Assistant",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/ai-code-review-assistant/ai-code-review-assistant.component.ts",
      "content": "// Edit this file to change the sample diff, AI annotations, chat thread and copy.\n// Patches toggle on click; \"Apply All AI Patches\" applies both after a short delay.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  Bot,\n  Check,\n  CheckCheck,\n  CircleCheck,\n  CodeXml,\n  Copy,\n  Cpu,\n  FileCodeCorner,\n  GitBranch,\n  GitCommitHorizontal,\n  GitPullRequest,\n  Layers,\n  LucideAngularModule,\n  MessageSquare,\n  RefreshCw,\n  RotateCcw,\n  Send,\n  ShieldCheck,\n  Sparkles,\n  TrendingDown,\n  TrendingUp,\n  X,\n  Zap,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent } 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 { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport {\n  UiTabsComponent,\n  UiTabsContentComponent,\n  UiTabsListComponent,\n  UiTabsTriggerComponent,\n} from '@/ui/tabs/tabs.component'\n\nexport interface DiffLine {\n  id: string\n  oldNum?: number\n  newNum?: number\n  type: 'neutral' | 'deletion' | 'addition' | 'hunk'\n  text: string\n  annotationId?: string\n}\n\nexport interface ChatMessage {\n  id: string\n  sender: 'ai' | 'user'\n  author: string\n  timestamp: string\n  text: string\n  codeSnippet?: string\n}\n\nconst initialChatMessages: ChatMessage[] = [\n  {\n    id: 'msg-1',\n    sender: 'ai',\n    author: 'Glia AI Code Reviewer',\n    timestamp: '2 min ago',\n    text: \"I completed the AST review for PR #412. Migrating to OKLCH perceptual contrast is a massive win. I've pinpointed 2 optimization opportunities to maximize token efficiency and eliminate 20 DOM variables.\",\n  },\n  {\n    id: 'msg-2',\n    sender: 'user',\n    author: 'Elena Rostova',\n    timestamp: '1 min ago',\n    text: 'Why is `color-mix(in oklab, ... 10%, transparent)` better than declaring separate opacity custom properties?',\n  },\n  {\n    id: 'msg-3',\n    sender: 'ai',\n    author: 'Glia AI Code Reviewer',\n    timestamp: 'Just now',\n    text: 'Direct OKLab color-mix calculates the alpha composite on the GPU shader pipeline without registering 20 CSS custom properties in the browser style recalculation tree. This cuts ~1.8 KB from DOM memory while keeping luminance completely linear.',\n    codeSnippet: 'color-mix(in oklab, var(--primary) 10%, transparent)',\n  },\n]\n\nfunction buildDiffLines(applied: ReadonlySet<string>): DiffLine[] {\n  return [\n    { id: 'hunk-1', type: 'hunk', text: '@@ -32,18 +32,24 @@ packages/shared/styles/tailwind.css' },\n    { id: 'line-32', oldNum: 32, newNum: 32, type: 'neutral', text: '@import \"tailwindcss\";' },\n    { id: 'line-33', oldNum: 33, newNum: 33, type: 'neutral', text: '@import \"tw-animate-css\";' },\n    { id: 'line-34', oldNum: 34, newNum: 34, type: 'neutral', text: '' },\n    { id: 'line-35', oldNum: 35, newNum: 35, type: 'neutral', text: '@theme inline {' },\n    { id: 'line-36', oldNum: 36, newNum: 36, type: 'neutral', text: '  --font-sans: \"Inter\", system-ui, sans-serif;' },\n    { id: 'line-37', oldNum: 37, newNum: 37, type: 'neutral', text: '  --font-mono: \"DM Mono\", monospace;' },\n    {\n      id: 'line-38',\n      oldNum: 38,\n      type: 'deletion',\n      text: '-  --primary-10: color-mix(in srgb, var(--primary) 10%, #ffffff);',\n    },\n    {\n      id: 'line-39',\n      oldNum: 39,\n      type: 'deletion',\n      text: '-  --primary-20: color-mix(in srgb, var(--primary) 20%, #ffffff);',\n    },\n    { id: 'line-40', oldNum: 40, type: 'deletion', text: '-  --primary-hover: hsl(var(--primary-hsl) / 0.85);' },\n    {\n      id: 'line-41',\n      newNum: 38,\n      type: 'addition',\n      text: '+  --primary-hover: oklch(from var(--primary) calc(l * 0.92) c h);',\n    },\n    {\n      id: 'line-42',\n      newNum: 39,\n      type: 'addition',\n      text: applied.has('patch-1')\n        ? '+  --primary-accent: color-mix(in oklab, var(--primary) 10%, transparent); /* [Glia AI Patch Applied] */'\n        : '+  --primary-accent: color-mix(in oklab, var(--primary) 15%, transparent);',\n      annotationId: 'patch-1',\n    },\n    { id: 'line-43', oldNum: 41, newNum: 40, type: 'neutral', text: '  --color-background: var(--background);' },\n    { id: 'line-44', oldNum: 42, newNum: 41, type: 'neutral', text: '  --color-foreground: var(--foreground);' },\n    { id: 'line-45', oldNum: 43, type: 'deletion', text: '-  --muted-border: rgb(229 231 235 / 0.6);' },\n    { id: 'line-46', newNum: 42, type: 'addition', text: '+  --muted-border: oklch(0.92 0.005 240 / 0.8);' },\n    { id: 'line-47', oldNum: 44, newNum: 43, type: 'neutral', text: '  --color-border: var(--border);' },\n    { id: 'line-48', oldNum: 45, newNum: 44, type: 'neutral', text: '  --color-input: var(--input);' },\n    { id: 'line-49', oldNum: 46, type: 'deletion', text: '-  --ring-offset-width: 2px;' },\n    { id: 'line-50', newNum: 45, type: 'addition', text: '+  --color-ring: oklch(from var(--primary) l c h / 0.5);' },\n    {\n      id: 'line-51',\n      newNum: 46,\n      type: 'addition',\n      text: applied.has('patch-2')\n        ? '+  --ring-focus-shadow: 0 0 0 2px var(--color-ring); /* [Glia AI Patch Applied] */'\n        : '+  --ring-focus-shadow: 0 0 0 2px var(--color-ring);',\n      annotationId: 'patch-2',\n    },\n    { id: 'line-52', oldNum: 47, newNum: 47, type: 'neutral', text: '  --color-card: var(--card);' },\n    {\n      id: 'line-53',\n      oldNum: 48,\n      newNum: 48,\n      type: 'neutral',\n      text: '  --color-card-foreground: var(--card-foreground);',\n    },\n    { id: 'line-54', oldNum: 49, newNum: 49, type: 'neutral', text: '}' },\n  ]\n}\n\n/**\n * AiCodeReviewAssistant -- the React block, 1:1: a PR header with review status, four\n * health cards, an annotated unified diff (with 1-click AI patches) in tabs, and an\n * AI chat thread that opens from the header or the annotations.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-ai-code-review-assistant, [ui-ai-code-review-assistant]',\n  standalone: true,\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarImageComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCardDescriptionComponent,\n    UiCardContentComponent,\n    UiSeparatorComponent,\n    UiTabsComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n    UiTabsContentComponent,\n  ],\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'ai-code-review-assistant',\n    '[class]': 'hostClass',\n  },\n  template: `\n    <!-- Header: Pull Request Info & AI Review Status -->\n    <div ui-card class=\"border-border bg-card shadow-xs\">\n      <div ui-card-header class=\"pb-4\">\n        <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n          <div class=\"space-y-2\">\n            <!-- PR Badge & Title -->\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <span ui-badge wrap variant=\"outline\" class=\"gap-1.5 font-mono text-xs\">\n                <lucide-icon [img]=\"GitPullRequest\" class=\"text-primary size-3.5\" />\n                <span>PR #{{ prNumber }}</span>\n              </span>\n              <h2 class=\"text-foreground text-base font-bold tracking-tight sm:text-lg\">{{ prTitle }}</h2>\n            </div>\n\n            <!-- Meta: Author, Branches, Commit -->\n            <div class=\"text-muted-foreground flex flex-wrap items-center gap-2 text-xs sm:text-sm\">\n              <div class=\"flex items-center gap-1.5\">\n                <span ui-avatar class=\"size-5 border\">\n                  <ui-avatar-image [src]=\"authorAvatar\" [alt]=\"authorName\" />\n                  <span ui-avatar-fallback class=\"text-xs\">ER</span>\n                </span>\n                <span class=\"text-foreground font-medium\">{{ authorName }}</span>\n                <span class=\"text-muted-foreground\">(&#64;{{ authorHandle }})</span>\n              </div>\n\n              <ui-separator orientation=\"vertical\" class=\"h-3.5\" />\n\n              <div class=\"flex items-center gap-1.5 font-mono text-xs\">\n                <lucide-icon [img]=\"GitBranch\" class=\"text-primary size-3.5\" />\n                <span class=\"text-foreground font-semibold\">{{ targetBranch }}</span>\n                <span class=\"text-muted-foreground\">←</span>\n                <span class=\"text-foreground font-semibold\">{{ sourceBranch }}</span>\n              </div>\n\n              <ui-separator orientation=\"vertical\" class=\"h-3.5\" />\n\n              <div class=\"flex items-center gap-1 font-mono text-xs\">\n                <lucide-icon [img]=\"GitCommitHorizontal\" class=\"text-muted-foreground size-3.5\" />\n                <span>8f2a49b</span>\n                <span>·</span>\n                <span class=\"text-success font-medium\">+142</span>\n                <span class=\"text-destructive font-medium\">-68</span>\n              </div>\n            </div>\n          </div>\n\n          <!-- Actions & Review Status -->\n          <div class=\"flex flex-wrap items-center gap-2.5\">\n            @if (!allPatchesApplied()) {\n              <span\n                ui-badge\n                wrap\n                variant=\"success\"\n                class=\"border-success/20 bg-success/10 text-success gap-1.5 px-3 py-1 text-xs font-semibold\"\n              >\n                <lucide-icon [img]=\"Sparkles\" class=\"size-3.5 shrink-0\" />\n                <span>Review Completed · 2 Suggestions · 0 Security Flaws</span>\n              </span>\n            } @else {\n              <span\n                ui-badge\n                wrap\n                variant=\"success\"\n                class=\"border-success/30 bg-success/15 text-success gap-1.5 px-3 py-1 text-xs font-semibold\"\n              >\n                <lucide-icon [img]=\"CheckCheck\" class=\"size-3.5 shrink-0\" />\n                <span>All Patches Applied · Ready to Merge</span>\n              </span>\n            }\n\n            @if (!allPatchesApplied()) {\n              <button\n                ui-button\n                size=\"sm\"\n                variant=\"default\"\n                class=\"gap-1.5 text-xs font-medium shadow-xs\"\n                [disabled]=\"isApplyingAll()\"\n                (click)=\"applyAllPatches()\"\n              >\n                @if (isApplyingAll()) {\n                  <lucide-icon [img]=\"RefreshCw\" class=\"size-3.5 animate-spin\" />\n                } @else {\n                  <lucide-icon [img]=\"Sparkles\" class=\"size-3.5\" />\n                }\n                <span>{{ isApplyingAll() ? 'Applying Patches...' : 'Apply All AI Patches' }}</span>\n              </button>\n            } @else {\n              <button\n                ui-button\n                size=\"sm\"\n                variant=\"outline\"\n                class=\"gap-1.5 text-xs font-medium shadow-xs\"\n                (click)=\"resetPatches()\"\n              >\n                <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n                <span>Reset State</span>\n              </button>\n            }\n\n            <button\n              ui-button\n              size=\"sm\"\n              variant=\"outline\"\n              class=\"gap-1.5 text-xs font-medium shadow-xs\"\n              (click)=\"openChatWithTopic()\"\n            >\n              <lucide-icon [img]=\"MessageSquare\" class=\"size-3.5\" />\n              <span>AI Chat (3)</span>\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- 4 Code Review Health Cards -->\n    <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n      <!-- Card 1: Quality Score -->\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Code Quality Score</span>\n            <div class=\"bg-success/10 text-success flex size-8 items-center justify-center rounded-md\">\n              <lucide-icon [img]=\"CircleCheck\" class=\"size-4\" />\n            </div>\n          </div>\n          <div class=\"mt-3\">\n            <div class=\"text-foreground text-2xl font-bold tracking-tight\">{{ qualityScore() }} / 100</div>\n            <p class=\"text-muted-foreground mt-1 text-xs\">\n              Clean Architecture · <span class=\"text-success font-medium\">+14 pts</span> vs baseline\n            </p>\n          </div>\n          <div class=\"text-success mt-3 flex items-center gap-1.5 text-xs font-medium\">\n            <lucide-icon [img]=\"Check\" class=\"size-3.5\" />\n            <span>0 Anti-patterns · Clean CVA tokens</span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 2: Security & Vulnerability Check -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Security &amp; SAST Check</span>\n            <div class=\"bg-success/10 text-success flex size-8 items-center justify-center rounded-md\">\n              <lucide-icon [img]=\"ShieldCheck\" class=\"size-4\" />\n            </div>\n          </div>\n          <div class=\"mt-3\">\n            <div class=\"text-foreground text-2xl font-bold tracking-tight\">0 Vulnerabilities</div>\n            <p class=\"text-muted-foreground mt-1 text-xs\">\n              0 Vulnerabilities Detected · <span class=\"text-success font-medium\">Pass</span>\n            </p>\n          </div>\n          <div class=\"text-success mt-3 flex items-center gap-1.5 text-xs font-medium\">\n            <lucide-icon [img]=\"Check\" class=\"size-3.5\" />\n            <span>OWASP Top 10 validated · Zero leaks</span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 3: Performance & Bundle Impact -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Performance &amp; Bundle Impact</span>\n            <div class=\"bg-warning/10 text-warning flex size-8 items-center justify-center rounded-md\">\n              <lucide-icon [img]=\"Zap\" class=\"size-4\" />\n            </div>\n          </div>\n          <div class=\"mt-3\">\n            <div class=\"text-foreground text-2xl font-bold tracking-tight\">-4.2 KB (-18%)</div>\n            <p class=\"text-muted-foreground mt-1 text-xs\">Bundle Size Reduction · 14.8 KB → 10.6 KB</p>\n          </div>\n          <div class=\"text-warning mt-3 flex items-center gap-1.5 text-xs font-medium\">\n            <lucide-icon [img]=\"TrendingDown\" class=\"size-3.5\" />\n            <span>20 CSS variables eliminated from DOM</span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 4: Test Coverage Delta -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Test Coverage Delta</span>\n            <div class=\"bg-info/10 text-info flex size-8 items-center justify-center rounded-md\">\n              <lucide-icon [img]=\"Layers\" class=\"size-4\" />\n            </div>\n          </div>\n          <div class=\"mt-3\">\n            <div class=\"text-foreground text-2xl font-bold tracking-tight\">+2.4% Coverage</div>\n            <p class=\"text-muted-foreground mt-1 text-xs\">48 new tests · 94.8% total coverage</p>\n          </div>\n          <div class=\"text-success mt-3 flex items-center gap-1.5 text-xs font-medium\">\n            <lucide-icon [img]=\"TrendingUp\" class=\"size-3.5\" />\n            <span>100% token regression suites pass</span>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Annotated Code Diff Reviewer Card -->\n    <div ui-card class=\"border-border bg-card shadow-xs\">\n      <div ui-card-header class=\"border-border border-b pb-4\">\n        <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"space-y-1\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <lucide-icon [img]=\"FileCodeCorner\" class=\"text-primary size-4 shrink-0\" />\n              <h3 ui-card-title class=\"text-foreground font-mono text-sm font-semibold sm:text-base\">\n                packages/shared/styles/tailwind.css\n              </h3>\n              <span ui-badge wrap variant=\"outline\" class=\"font-mono text-xs\">CSS / Tailwind v4</span>\n              <span ui-badge wrap variant=\"secondary\" class=\"font-mono text-xs\">+24 -12 lines</span>\n            </div>\n            <p ui-card-description class=\"text-muted-foreground text-xs\">\n              AST parser inspected 162 declarations · 2 optimization nodes annotated\n            </p>\n          </div>\n\n          <div class=\"flex items-center gap-2\">\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"h-8 gap-1.5 text-xs font-medium shadow-xs\"\n              (click)=\"copyRawDiff()\"\n            >\n              @if (copiedDiff()) {\n                <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n              } @else {\n                <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n              }\n              <span>{{ copiedDiff() ? 'Copied Raw Diff' : 'Copy Raw Diff' }}</span>\n            </button>\n          </div>\n        </div>\n      </div>\n\n      <div ui-card-content class=\"p-4 sm:p-6\">\n        <div ui-tabs [value]=\"activeTab()\" (valueChange)=\"activeTab.set($event)\" class=\"w-full space-y-4\">\n          <div ui-tabs-list class=\"grid w-full grid-cols-3 sm:inline-flex sm:w-auto\">\n            <button ui-tabs-trigger value=\"diff\" class=\"gap-1.5 text-xs\">\n              <lucide-icon [img]=\"CodeXml\" class=\"size-3.5\" />\n              <span>Unified Code Diff</span>\n            </button>\n            <button ui-tabs-trigger value=\"rules\" class=\"gap-1.5 text-xs\">\n              <lucide-icon [img]=\"ShieldCheck\" class=\"size-3.5\" />\n              <span>AST Rule Checks (4)</span>\n            </button>\n            <button ui-tabs-trigger value=\"impact\" class=\"gap-1.5 text-xs\">\n              <lucide-icon [img]=\"Cpu\" class=\"size-3.5\" />\n              <span>Token Matrix &amp; Gamut</span>\n            </button>\n          </div>\n\n          <!-- Tab 1: Visual Unified Code Diff with Inline AI Annotations -->\n          <div ui-tabs-content value=\"diff\" class=\"space-y-4 focus-visible:outline-none\">\n            <div\n              class=\"border-border bg-muted/40 flex items-center justify-between rounded-t-md border-x border-t px-3 py-2 text-xs\"\n            >\n              <div class=\"text-muted-foreground flex items-center gap-2\">\n                <span class=\"text-foreground font-medium\">Unified Diff View</span>\n                <span>·</span>\n                <span class=\"text-success font-medium\">+24 additions</span>\n                <span>·</span>\n                <span class=\"text-destructive font-medium\">-12 deletions</span>\n              </div>\n              <span class=\"text-muted-foreground font-mono\">AST Analyzed</span>\n            </div>\n\n            <div\n              class=\"border-border bg-muted/20 overflow-x-auto rounded-b-md border font-mono text-xs leading-relaxed\"\n            >\n              <div class=\"max-w-[620px] min-w-full py-2\">\n                @for (line of diffLines(); track line.id) {\n                  @if (line.type === 'hunk') {\n                    <!-- Hunk Line -->\n                    <div\n                      class=\"bg-muted/60 text-muted-foreground px-3 py-1 font-mono text-xs font-semibold select-none\"\n                    >\n                      {{ line.text }}\n                    </div>\n                  } @else {\n                    <!-- Diff Code Row -->\n                    <div [class]=\"rowClass(line)\">\n                      <span class=\"text-muted-foreground/50 w-8 shrink-0 pr-2 text-right select-none\">{{\n                        line.oldNum ?? ''\n                      }}</span>\n                      <span class=\"text-muted-foreground/50 w-8 shrink-0 pr-3 text-right select-none\">{{\n                        line.newNum ?? ''\n                      }}</span>\n                      <span [class]=\"prefixClass(line)\">{{ prefix(line) }}</span>\n                      <span class=\"pl-1 whitespace-pre\">{{ line.text }}</span>\n                    </div>\n                  }\n\n                  <!-- Inline AI Review Annotation Card #1 (Pinned under line 42) -->\n                  @if (line.annotationId === 'patch-1') {\n                    <div\n                      class=\"border-warning/40 bg-warning/[0.04] border-warning/30 dark:bg-warning/10] mx-4 my-3 rounded-lg border p-4 text-xs shadow-xs\"\n                    >\n                      <div class=\"flex flex-col gap-3 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                            <div class=\"bg-warning/20 text-warning flex size-6 items-center justify-center rounded-md\">\n                              <lucide-icon [img]=\"Bot\" class=\"size-3.5\" />\n                            </div>\n                            <span class=\"text-foreground font-semibold\"> Glia AI Code Review · AST Suggestion </span>\n                            <span ui-badge wrap variant=\"warning\" class=\"gap-1 font-semibold\">\n                              <lucide-icon [img]=\"Sparkles\" class=\"size-3\" />\n                              <span>Performance Optimization · High Impact</span>\n                            </span>\n                            <span class=\"text-muted-foreground text-xs\">Confidence 99.4% · AST Node #42</span>\n                          </div>\n\n                          <!-- Explanation -->\n                          <p class=\"text-foreground font-sans text-xs leading-relaxed font-medium\">\n                            Using\n                            <code class=\"bg-muted text-foreground rounded px-1.5 py-0.5 font-mono\"\n                              >color-mix(in oklab, var(--primary) 10%, transparent)</code\n                            >\n                            achieves identical contrast without injecting 20 additional CSS variables into the global\n                            DOM.\n                          </p>\n\n                          <!-- Code Preview Replacement -->\n                          <div class=\"border-border/60 bg-muted/50 rounded-md border p-2.5 font-mono text-xs\">\n                            <div class=\"text-muted-foreground mb-1 text-xs select-none\">\n                              Proposed AST Patch Replacement (Line 39):\n                            </div>\n                            <div class=\"text-success font-semibold\">\n                              + --primary-accent: color-mix(in oklab, var(--primary) 10%, transparent);\n                            </div>\n                          </div>\n                        </div>\n\n                        <!-- Action Buttons -->\n                        <div class=\"flex shrink-0 flex-wrap items-center gap-2 self-start sm:self-auto\">\n                          <button\n                            ui-button\n                            size=\"xs\"\n                            variant=\"outline\"\n                            class=\"h-7 gap-1 text-xs\"\n                            (click)=\"copyPatchCode('patch-1', 'color-mix(in oklab, var(--primary) 10%, transparent)')\"\n                          >\n                            @if (copiedPatchId() === 'patch-1') {\n                              <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                            } @else {\n                              <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                            }\n                            <span>{{ copiedPatchId() === 'patch-1' ? 'Copied' : 'Copy Patch' }}</span>\n                          </button>\n\n                          <button\n                            ui-button\n                            size=\"xs\"\n                            variant=\"outline\"\n                            class=\"h-7 gap-1 text-xs\"\n                            (click)=\"\n                              openChatWithTopic(\n                                'Explain why OKLab color-mix avoids DOM stylesheet recalculation overhead'\n                              )\n                            \"\n                          >\n                            <lucide-icon [img]=\"MessageSquare\" class=\"size-3\" />\n                            <span>Explain in Chat</span>\n                          </button>\n\n                          <button\n                            ui-button\n                            size=\"xs\"\n                            [variant]=\"isApplied('patch-1') ? 'secondary' : 'default'\"\n                            [class]=\"applyButtonClass('patch-1')\"\n                            (click)=\"togglePatch('patch-1')\"\n                          >\n                            @if (isApplied('patch-1')) {\n                              <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                            } @else {\n                              <lucide-icon [img]=\"Zap\" class=\"size-3\" />\n                            }\n                            <span>{{ isApplied('patch-1') ? 'Patch Applied ✓' : 'Apply Suggestion (1-Click)' }}</span>\n                          </button>\n                        </div>\n                      </div>\n                    </div>\n                  }\n\n                  <!-- Inline AI Review Annotation Card #2 (Pinned under line 51) -->\n                  @if (line.annotationId === 'patch-2') {\n                    <div\n                      class=\"border-success/40 bg-success/[0.04] border-success/30 dark:bg-success/10] mx-4 my-3 rounded-lg border p-4 text-xs shadow-xs\"\n                    >\n                      <div class=\"flex flex-col gap-3 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                            <div class=\"bg-success/20 text-success flex size-6 items-center justify-center rounded-md\">\n                              <lucide-icon [img]=\"ShieldCheck\" class=\"size-3.5\" />\n                            </div>\n                            <span class=\"text-foreground font-semibold\"> Glia AI Code Review · Gamut Engine </span>\n                            <span ui-badge wrap variant=\"success\" class=\"gap-1 font-semibold\">\n                              <lucide-icon [img]=\"CircleCheck\" class=\"size-3\" />\n                              <span>Accessibility &amp; Gamut · WCAG AAA Verified</span>\n                            </span>\n                            <span class=\"text-muted-foreground text-xs\">Confidence 98.7% · AST Node #51</span>\n                          </div>\n\n                          <!-- Explanation -->\n                          <p class=\"text-foreground font-sans text-xs leading-relaxed font-medium\">\n                            OKLCH relative gamut mapping guarantees 7:1 contrast ratio across both light and dark themes\n                            with zero perceptual hue shift on wide-gamut displays.\n                          </p>\n\n                          <!-- Code Preview Replacement -->\n                          <div class=\"border-border/60 bg-muted/50 rounded-md border p-2.5 font-mono text-xs\">\n                            <div class=\"text-muted-foreground mb-1 text-xs select-none\">\n                              Verified Relative Color Syntax:\n                            </div>\n                            <div class=\"text-success font-semibold\">\n                              + --ring-focus-shadow: 0 0 0 2px var(--color-ring);\n                            </div>\n                          </div>\n                        </div>\n\n                        <!-- Action Buttons -->\n                        <div class=\"flex shrink-0 flex-wrap items-center gap-2 self-start sm:self-auto\">\n                          <button\n                            ui-button\n                            size=\"xs\"\n                            variant=\"outline\"\n                            class=\"h-7 gap-1 text-xs\"\n                            (click)=\"\n                              openChatWithTopic(\n                                'How does OKLCH guarantee WCAG AAA contrast without shift on P3 monitors?'\n                              )\n                            \"\n                          >\n                            <lucide-icon [img]=\"MessageSquare\" class=\"size-3\" />\n                            <span>Explain in Chat</span>\n                          </button>\n\n                          <button\n                            ui-button\n                            size=\"xs\"\n                            [variant]=\"isApplied('patch-2') ? 'secondary' : 'default'\"\n                            [class]=\"applyButtonClass('patch-2')\"\n                            (click)=\"togglePatch('patch-2')\"\n                          >\n                            @if (isApplied('patch-2')) {\n                              <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                            } @else {\n                              <lucide-icon [img]=\"Zap\" class=\"size-3\" />\n                            }\n                            <span>{{ isApplied('patch-2') ? 'Patch Applied ✓' : 'Apply Suggestion (1-Click)' }}</span>\n                          </button>\n                        </div>\n                      </div>\n                    </div>\n                  }\n                }\n              </div>\n            </div>\n          </div>\n\n          <!-- Tab 2: AST Rule Verification Suite -->\n          <div ui-tabs-content value=\"rules\" class=\"space-y-4 focus-visible:outline-none\">\n            <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n              <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-4\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2 text-sm font-semibold\">\n                    <lucide-icon [img]=\"ShieldCheck\" class=\"text-success size-4\" />\n                    <span>Rule 1: Zero Global DOM Pollution</span>\n                  </div>\n                  <span ui-badge wrap variant=\"success\" class=\"text-xs\">Pass · Verified</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                  Eliminates 20 static opacity custom properties. All transparency channels are evaluated dynamically in\n                  OKLab color space.\n                </p>\n              </div>\n\n              <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-4\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2 text-sm font-semibold\">\n                    <lucide-icon [img]=\"Zap\" class=\"text-success size-4\" />\n                    <span>Rule 2: Perceptual Uniformity (ΔE &lt; 0.8)</span>\n                  </div>\n                  <span ui-badge wrap variant=\"success\" class=\"text-xs\">Pass · Verified</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                  OKLCH lightness (L) scale provides mathematical linearity. Step changes in lightness feel uniform to\n                  human perception across all hues.\n                </p>\n              </div>\n\n              <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-4\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2 text-sm font-semibold\">\n                    <lucide-icon [img]=\"Cpu\" class=\"text-success size-4\" />\n                    <span>Rule 3: PostCSS Lightning Fallbacks</span>\n                  </div>\n                  <span ui-badge wrap variant=\"success\" class=\"text-xs\">Pass · Verified</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                  Tailwind v4 engine generates automated sRGB fallback values for legacy web clients while streaming raw\n                  OKLCH to modern browsers.\n                </p>\n              </div>\n\n              <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-4\">\n                <div class=\"flex items-center justify-between\">\n                  <div class=\"flex items-center gap-2 text-sm font-semibold\">\n                    <lucide-icon [img]=\"CircleCheck\" class=\"text-success size-4\" />\n                    <span>Rule 4: WCAG 2.2 AAA Contrast Ratios</span>\n                  </div>\n                  <span ui-badge wrap variant=\"success\" class=\"text-xs\">Pass · 7.1:1 Ratio</span>\n                </div>\n                <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                  Background to foreground contrast exceeds the 7:1 ratio threshold required for government and\n                  enterprise accessible software.\n                </p>\n              </div>\n            </div>\n          </div>\n\n          <!-- Tab 3: Token Matrix & Gamut Breakdown -->\n          <div ui-tabs-content value=\"impact\" class=\"space-y-4 focus-visible:outline-none\">\n            <div class=\"border-border overflow-x-auto rounded-lg border\">\n              <div class=\"bg-muted/40 border-border max-w-[480px] min-w-full border-b px-4 py-3 text-xs font-semibold\">\n                Token Architecture Comparison: Legacy vs. OKLCH Engine\n              </div>\n              <div class=\"divide-border max-w-[480px] min-w-full divide-y text-xs\">\n                <div class=\"grid grid-cols-3 p-3 font-medium\">\n                  <span class=\"text-muted-foreground\">Evaluation Metric</span>\n                  <span class=\"text-muted-foreground\">Legacy HSL / sRGB</span>\n                  <span class=\"text-success font-bold\">OKLCH Perceptual Engine</span>\n                </div>\n                <div class=\"grid grid-cols-3 p-3\">\n                  <span class=\"font-medium\">Total Token CSS Bundle</span>\n                  <span class=\"text-muted-foreground font-mono\">14.8 KB</span>\n                  <span class=\"text-success font-mono font-bold\">10.6 KB (-28.4%)</span>\n                </div>\n                <div class=\"grid grid-cols-3 p-3\">\n                  <span class=\"font-medium\">DOM Custom Properties</span>\n                  <span class=\"text-muted-foreground font-mono\">64 variables</span>\n                  <span class=\"text-success font-mono font-bold\">44 variables (-31.2%)</span>\n                </div>\n                <div class=\"grid grid-cols-3 p-3\">\n                  <span class=\"font-medium\">Display P3 Wide Gamut</span>\n                  <span class=\"text-destructive\">Clipping on 14 colors</span>\n                  <span class=\"text-success font-bold\">100% P3 Coverage</span>\n                </div>\n                <div class=\"grid grid-cols-3 p-3\">\n                  <span class=\"font-medium\">Perceptual Uniformity</span>\n                  <span class=\"text-muted-foreground\">Non-linear (HSL shift)</span>\n                  <span class=\"text-success font-bold\">Mathematically Uniform (LCH)</span>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Interactive AI Review Assistant Chat Panel -->\n    @if (isChatOpen()) {\n      <div ui-card class=\"border-primary/30 bg-card shadow-md\">\n        <div ui-card-header class=\"border-border border-b pb-3\">\n          <div class=\"flex items-center justify-between\">\n            <div class=\"flex items-center gap-2.5\">\n              <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n                <lucide-icon [img]=\"Bot\" class=\"size-4\" />\n              </div>\n              <div>\n                <h3 ui-card-title class=\"text-sm font-semibold\">\n                  Glia AI Assistant · PR #{{ prNumber }} Review Thread\n                </h3>\n                <p ui-card-description class=\"text-xs\">\n                  Contextual code review dialogue with active AST parser &amp; contrast engine\n                </p>\n              </div>\n            </div>\n            <button\n              ui-button\n              variant=\"ghost\"\n              size=\"xs\"\n              class=\"size-7 p-0\"\n              aria-label=\"Close chat\"\n              (click)=\"isChatOpen.set(false)\"\n            >\n              <lucide-icon [img]=\"X\" class=\"size-4\" />\n            </button>\n          </div>\n        </div>\n\n        <div ui-card-content class=\"space-y-4 p-4\">\n          <!-- Message History -->\n          <div class=\"max-h-[320px] space-y-3 overflow-y-auto pr-1\">\n            @for (msg of chatMessages(); track msg.id) {\n              <div [class]=\"messageClass(msg)\">\n                <div class=\"flex items-center justify-between font-semibold\">\n                  <span class=\"flex items-center gap-1.5\">\n                    @if (msg.sender === 'ai') {\n                      <lucide-icon [img]=\"Bot\" class=\"text-primary size-3.5\" />\n                    } @else {\n                      <span ui-avatar class=\"size-4 border\">\n                        <ui-avatar-image [src]=\"authorAvatar\" />\n                        <span ui-avatar-fallback class=\"text-xs\">ER</span>\n                      </span>\n                    }\n                    <span>{{ msg.author }}</span>\n                  </span>\n                  <span class=\"text-muted-foreground text-xs font-normal\">{{ msg.timestamp }}</span>\n                </div>\n                <p class=\"mt-1 leading-relaxed\">{{ msg.text }}</p>\n                @if (msg.codeSnippet) {\n                  <div\n                    class=\"border-border/80 bg-muted/80 text-foreground mt-2 rounded p-2 font-mono text-xs select-all\"\n                  >\n                    {{ msg.codeSnippet }}\n                  </div>\n                }\n              </div>\n            }\n          </div>\n\n          <!-- Preset Prompt Chips -->\n          <div class=\"flex flex-wrap items-center gap-1.5 pt-1\">\n            <span class=\"text-muted-foreground text-xs\">Quick ask:</span>\n            <button\n              type=\"button\"\n              class=\"border-border bg-muted/40 hover:bg-muted text-foreground cursor-pointer rounded-full border px-2.5 py-0.5 text-xs transition-colors\"\n              (click)=\"openChatWithTopic('Why is OKLab preferred over sRGB color space?')\"\n            >\n              Why OKLab over sRGB?\n            </button>\n            <button\n              type=\"button\"\n              class=\"border-border bg-muted/40 hover:bg-muted text-foreground cursor-pointer rounded-full border px-2.5 py-0.5 text-xs transition-colors\"\n              (click)=\"openChatWithTopic('How does this affect browser performance?')\"\n            >\n              Browser performance impact?\n            </button>\n          </div>\n\n          <!-- Chat Input Field -->\n          <form class=\"flex items-center gap-2 pt-1\" (submit)=\"handleSendMessage($event)\">\n            <input\n              [value]=\"chatInput()\"\n              (input)=\"onChatInput($event)\"\n              type=\"text\"\n              placeholder=\"Ask Glia AI about this code review or AST transformation...\"\n              class=\"border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring flex-1 rounded-md border px-3 py-2 text-xs focus-visible:ring-2 focus-visible:outline-none\"\n            />\n            <button ui-button type=\"submit\" size=\"sm\" class=\"gap-1.5 text-xs\">\n              <lucide-icon [img]=\"Send\" class=\"size-3.5\" />\n              <span>Send</span>\n            </button>\n          </form>\n        </div>\n      </div>\n    }\n  `,\n})\nexport class UiAiCodeReviewAssistantComponent {\n  @Input() prNumber = 412\n  @Input() prTitle = 'refactor(tokens): migrate to OKLCH perceptual contrast engine'\n  @Input() authorName = 'Elena Rostova'\n  @Input() authorHandle = 'elena-rostova'\n  @Input() authorRole = 'Staff Design Systems Engineer'\n  @Input() authorAvatar = 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=96&h=96&fit=crop&crop=face'\n  @Input() sourceBranch = 'feature/oklch-engine'\n  @Input() targetBranch = 'main'\n  @Input('class') className?: string\n\n  readonly activeTab = signal('diff')\n  readonly isApplyingAll = signal(false)\n  readonly appliedPatches = signal<ReadonlySet<string>>(new Set())\n  readonly isChatOpen = signal(false)\n  readonly chatInput = signal('')\n  readonly copiedDiff = signal(false)\n  readonly copiedPatchId = signal<string | null>(null)\n  readonly chatMessages = signal<ChatMessage[]>(initialChatMessages)\n\n  readonly diffLines = computed(() => buildDiffLines(this.appliedPatches()))\n  readonly allPatchesApplied = computed(() => this.appliedPatches().size === 2)\n  readonly qualityScore = computed(() => (this.allPatchesApplied() ? 99 : 96))\n\n  protected readonly Bot = Bot\n  protected readonly Check = Check\n  protected readonly CheckCheck = CheckCheck\n  protected readonly CircleCheck = CircleCheck\n  protected readonly CodeXml = CodeXml\n  protected readonly Copy = Copy\n  protected readonly Cpu = Cpu\n  protected readonly FileCodeCorner = FileCodeCorner\n  protected readonly GitBranch = GitBranch\n  protected readonly GitCommitHorizontal = GitCommitHorizontal\n  protected readonly GitPullRequest = GitPullRequest\n  protected readonly Layers = Layers\n  protected readonly MessageSquare = MessageSquare\n  protected readonly RefreshCw = RefreshCw\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Send = Send\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Sparkles = Sparkles\n  protected readonly TrendingDown = TrendingDown\n  protected readonly TrendingUp = TrendingUp\n  protected readonly X = X\n  protected readonly Zap = Zap\n\n  private readonly timeouts = new Set<ReturnType<typeof setTimeout>>()\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => this.timeouts.forEach((t) => clearTimeout(t)))\n  }\n\n  get hostClass(): string {\n    return cn('block w-full space-y-6', this.className)\n  }\n\n  isApplied(patchId: string): boolean {\n    return this.appliedPatches().has(patchId)\n  }\n\n  rowClass(line: DiffLine): string {\n    return cn(\n      'flex items-center px-3 py-0.5 transition-colors',\n      line.type === 'addition' && 'border-success bg-success/10 text-success border-l-2 font-medium',\n      line.type === 'deletion' &&\n        'border-destructive bg-destructive/10 text-foreground dark:text-foreground border-l-2 font-medium line-through opacity-85',\n      line.type === 'neutral' && 'text-muted-foreground border-l-2 border-transparent',\n    )\n  }\n\n  prefixClass(line: DiffLine): string {\n    return cn(\n      'w-4 shrink-0 text-center font-bold select-none',\n      line.type === 'addition' && 'text-success',\n      line.type === 'deletion' && 'text-destructive',\n      line.type === 'neutral' && 'text-transparent',\n    )\n  }\n\n  prefix(line: DiffLine): string {\n    return line.type === 'addition' ? '+' : line.type === 'deletion' ? '-' : ' '\n  }\n\n  applyButtonClass(patchId: string): string {\n    return cn(\n      'h-7 gap-1 text-xs font-medium shadow-xs',\n      this.isApplied(patchId) && 'border-success/30 text-success font-semibold',\n    )\n  }\n\n  messageClass(msg: ChatMessage): string {\n    return cn(\n      'flex flex-col gap-1 rounded-lg p-3 text-xs',\n      msg.sender === 'ai'\n        ? 'border-border/60 bg-muted/40 mr-8 border'\n        : 'bg-primary/10 text-foreground border-primary/20 ml-8 border',\n    )\n  }\n\n  togglePatch(patchId: string): void {\n    this.appliedPatches.update((prev) => {\n      const next = new Set(prev)\n      if (next.has(patchId)) next.delete(patchId)\n      else next.add(patchId)\n      return next\n    })\n  }\n\n  applyAllPatches(): void {\n    if (this.isApplyingAll()) return\n    this.isApplyingAll.set(true)\n    this.later(() => {\n      this.appliedPatches.set(new Set(['patch-1', 'patch-2']))\n      this.isApplyingAll.set(false)\n    }, 600)\n  }\n\n  resetPatches(): void {\n    this.appliedPatches.set(new Set())\n  }\n\n  openChatWithTopic(topicText?: string): void {\n    this.isChatOpen.set(true)\n    if (!topicText) return\n    const userMsg: ChatMessage = {\n      id: `user-${Date.now()}`,\n      sender: 'user',\n      author: 'Elena Rostova',\n      timestamp: 'Just now',\n      text: topicText,\n    }\n    this.chatMessages.update((prev) => [...prev, userMsg])\n    this.later(() => {\n      const aiMsg: ChatMessage = {\n        id: `ai-${Date.now()}`,\n        sender: 'ai',\n        author: 'Glia AI Code Reviewer',\n        timestamp: 'Just now',\n        text: `Regarding \"${topicText}\": Our AST lint engine verified that OKLCH relative color syntax produces 7:1 contrast on dark surfaces with zero gamut clipping on Apple Display P3 panels.`,\n      }\n      this.chatMessages.update((prev) => [...prev, aiMsg])\n    }, 450)\n  }\n\n  onChatInput(e: Event): void {\n    this.chatInput.set((e.target as HTMLInputElement).value)\n  }\n\n  handleSendMessage(e: Event): void {\n    e.preventDefault()\n    const text = this.chatInput().trim()\n    if (!text) return\n    const userMsg: ChatMessage = {\n      id: `msg-${Date.now()}`,\n      sender: 'user',\n      author: 'Elena Rostova',\n      timestamp: 'Just now',\n      text,\n    }\n    this.chatMessages.update((prev) => [...prev, userMsg])\n    this.chatInput.set('')\n    this.later(() => {\n      const aiMsg: ChatMessage = {\n        id: `ai-${Date.now()}`,\n        sender: 'ai',\n        author: 'Glia AI Code Reviewer',\n        timestamp: 'Just now',\n        text: 'Validated against UIPKGE craft standards. The suggested AST refactor is fully backward-compatible with PostCSS fallback polyfills.',\n      }\n      this.chatMessages.update((prev) => [...prev, aiMsg])\n    }, 500)\n  }\n\n  copyRawDiff(): void {\n    const rawText = this.diffLines()\n      .map((l) => `${l.text}`)\n      .join('\\n')\n    void navigator.clipboard?.writeText(rawText)\n    this.copiedDiff.set(true)\n    this.later(() => this.copiedDiff.set(false), 2000)\n  }\n\n  copyPatchCode(id: string, code: string): void {\n    void navigator.clipboard?.writeText(code)\n    this.copiedPatchId.set(id)\n    this.later(() => this.copiedPatchId.set(null), 2000)\n  }\n\n  private later(fn: () => void, ms: number): void {\n    const t = setTimeout(() => {\n      this.timeouts.delete(t)\n      fn()\n    }, ms)\n    this.timeouts.add(t)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/ai-code-review-assistant/ai-code-review-assistant.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/ai-code-review-assistant/index.ts",
      "content": "export { UiAiCodeReviewAssistantComponent, type DiffLine, type ChatMessage } from './ai-code-review-assistant.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/ai-code-review-assistant/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/separator.json",
    "https://uipkge.dev/r/angular/tabs.json"
  ],
  "description": "GitHub PR automated AI code review assistant with AST syntax diff annotations, performance suggestions, and 1-click patch application.",
  "categories": [
    "devops",
    "app",
    "ai"
  ]
}