{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "code-snippet-playground",
  "title": "Code Snippet Playground",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/code-snippet-playground/code-snippet-playground.component.ts",
      "content": "// Edit this file to change the files shown in the editor, the test log or the copy.\n// Everything is mocked: \"Run Code\" (or Cmd/Ctrl+Enter) replays the Vitest run after 480ms.\nimport { Component, DestroyRef, Input, computed, inject, signal, ChangeDetectionStrategy } from '@angular/core'\nimport {\n  Braces,\n  Check,\n  CheckCircle2,\n  Code2,\n  Copy,\n  Eye,\n  FileCode,\n  Layers,\n  Loader2,\n  LucideAngularModule,\n  Play,\n  RotateCcw,\n  Terminal,\n  Zap,\n  type LucideIconData,\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  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 CodeSnippetPlaygroundFile {\n  id: string\n  name: string\n  path: string\n  language: string\n  size: string\n  content: string\n}\n\nexport interface CodeSnippetPlaygroundTestLog {\n  id: string\n  name: string\n  duration: string\n  status: 'pass' | 'fail'\n}\n\nconst files: CodeSnippetPlaygroundFile[] = [\n  {\n    id: 'button-tsx',\n    name: 'Button.tsx',\n    path: 'packages/registry-react/components/button/Button.tsx',\n    language: 'typescript',\n    size: '1.1 KB',\n    content: `import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cn } from '@/lib/utils'\nimport { buttonVariants, type ButtonVariants } from './button.variants'\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    ButtonVariants {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, type = 'button', ...props }, ref) => {\n    const Comp = asChild ? Slot : 'button'\n    return (\n      <Comp\n        data-slot=\"button\"\n        data-variant={variant ?? undefined}\n        data-size={size ?? undefined}\n        className={cn(buttonVariants({ variant, size }), className)}\n        {...(asChild ? {} : { type })}\n        ref={ref}\n        {...props}\n      />\n    )\n  },\n)\nButton.displayName = 'Button'\n\nexport { Button }`,\n  },\n  {\n    id: 'button-variants',\n    name: 'button.variants.ts',\n    path: 'packages/registry-react/components/button/button.variants.ts',\n    language: 'typescript',\n    size: '1.4 KB',\n    content: `import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const buttonVariants = cva(\n  'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\"size-\"])]:size-4 shrink-0',\n  {\n    variants: {\n      variant: {\n        default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',\n        destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90',\n        outline: 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',\n        secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',\n        ghost: 'hover:bg-accent hover:text-accent-foreground',\n        link: 'text-primary underline-offset-4 hover:underline',\n      },\n      size: {\n        default: 'h-9 px-4 py-2',\n        sm: 'h-8 rounded-md px-3 text-xs',\n        lg: 'h-10 rounded-md px-6',\n        icon: 'size-9',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>`,\n  },\n  {\n    id: 'index-ts',\n    name: 'index.ts',\n    path: 'packages/registry-react/components/button/index.ts',\n    language: 'typescript',\n    size: '220 B',\n    content: `export { Button, type ButtonProps } from './Button'\nexport { buttonVariants, type ButtonVariants } from './button.variants'`,\n  },\n  {\n    id: 'button-test',\n    name: 'button.test.tsx',\n    path: 'packages/registry-react/components/button/__tests__/button.spec.tsx',\n    language: 'typescript',\n    size: '1.7 KB',\n    content: `import { describe, it, expect, vi } from 'vitest'\nimport { render, screen, fireEvent } from '@testing-library/react'\nimport { Button } from '../Button'\n\ndescribe('Button Primitive', () => {\n  it('renders default button with children text', () => {\n    render(<Button>Submit</Button>)\n    const btn = screen.getByRole('button', { name: /submit/i })\n    expect(btn).toBeInTheDocument()\n    expect(btn).toHaveAttribute('data-slot', 'button')\n  })\n\n  it('applies variant classes correctly', () => {\n    render(<Button variant=\"destructive\">Delete</Button>)\n    const btn = screen.getByRole('button', { name: /delete/i })\n    expect(btn).toHaveAttribute('data-variant', 'destructive')\n    expect(btn.className).toContain('bg-destructive')\n  })\n\n  it('applies size styles correctly', () => {\n    render(<Button size=\"sm\">Small</Button>)\n    const btn = screen.getByRole('button', { name: /small/i })\n    expect(btn).toHaveAttribute('data-size', 'sm')\n    expect(btn.className).toContain('h-8')\n  })\n\n  it('handles click events when active', () => {\n    const handleClick = vi.fn()\n    render(<Button onClick={handleClick}>Trigger</Button>)\n    fireEvent.click(screen.getByRole('button', { name: /trigger/i }))\n    expect(handleClick).toHaveBeenCalledTimes(1)\n  })\n\n  it('respects disabled state attribute', () => {\n    render(<Button disabled>Disabled</Button>)\n    expect(screen.getByRole('button', { name: /disabled/i })).toBeDisabled()\n  })\n\n  it('forwards custom class names with cn()', () => {\n    render(<Button className=\"custom-btn\">Custom</Button>)\n    const btn = screen.getByRole('button', { name: /custom/i })\n    expect(btn.className).toContain('custom-btn')\n  })\n})`,\n  },\n]\n\nconst testLogs: CodeSnippetPlaygroundTestLog[] = [\n  { id: '1', name: 'renders default button with children text', duration: '4ms', status: 'pass' },\n  { id: '2', name: 'applies variant classes correctly', duration: '7ms', status: 'pass' },\n  { id: '3', name: 'applies size styles correctly', duration: '5ms', status: 'pass' },\n  { id: '4', name: 'handles click events when active', duration: '8ms', status: 'pass' },\n  { id: '5', name: 'respects disabled state attribute', duration: '6ms', status: 'pass' },\n  { id: '6', name: 'forwards custom class names with cn()', duration: '12ms', status: 'pass' },\n]\n\nexport type CodeSnippetPlaygroundButtonVariant = 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link'\nexport type CodeSnippetPlaygroundButtonSize = 'default' | 'sm' | 'lg' | 'icon'\nfunction highlightSyntaxLine(line: string): string {\n  if (!line.trim()) return '&nbsp;'\n\n  const escaped = line.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n\n  // Single line comments\n  if (/^\\s*\\/\\//.test(escaped) || /^\\s*\\/\\*/.test(escaped) || /^\\s*\\*/.test(escaped) || /^\\s*&lt;!--/.test(escaped)) {\n    return `<span class=\"text-zinc-500 italic\">${escaped}</span>`\n  }\n\n  // Every emitted <span> is parked behind a letter-only placeholder so later\n  // passes cannot match inside the markup they already produced.\n  const parked: string[] = []\n  const park = (html: string) => {\n    const key = String(parked.length)\n      .split('')\n      .map((d) => String.fromCharCode(97 + Number(d)))\n      .join('')\n    parked.push(html)\n    return `\\u0000${key}\\u0000`\n  }\n\n  let out = escaped\n\n  // Strings\n  out = out.replace(/([\"'`])(?:(?=(\\\\?))\\2.)*?\\1/g, (m) => park(`<span class=\"text-success font-normal\">${m}</span>`))\n\n  // Trailing comments\n  out = out.replace(/(\\/\\/.*$)/, (m) => park(`<span class=\"text-zinc-500 italic\">${m}</span>`))\n\n  // Keywords\n  out = out.replace(\n    /\\b(import|export|from|const|let|var|function|return|interface|type|default|as|typeof|withDefaults|defineProps|defineEmits|describe|it|expect|test|async|await|extends|new|true|false|null|undefined)\\b/g,\n    (m) => park(`<span class=\"text-chart-1 font-semibold\">${m}</span>`),\n  )\n\n  // Types\n  out = out.replace(\n    /\\b(string|boolean|number|void|HTMLAttributes|VariantProps|ButtonVariants|ButtonProps|Props|Variant|Size|HTMLButtonElement|Record)\\b/g,\n    (m) => park(`<span class=\"text-warning font-medium\">${m}</span>`),\n  )\n\n  // Functions / methods\n  out = out.replace(\n    /\\b(cn|buttonVariants|cva|mount|render|screen|getByRole|fireEvent|vi|trigger|classes|attributes|emitted|toBe|toContain|toHaveProperty|toBeDefined|toBeInTheDocument|toHaveAttribute|toHaveBeenCalledTimes|toHaveBeenCalled|forwardRef|displayName|ref|computed|onMounted|onUnmounted|fn)\\b/g,\n    (m) => park(`<span class=\"text-info\">${m}</span>`),\n  )\n\n  // Vue/TSX tags\n  out = out.replace(\n    /(&lt;\\/?(?:template|script|Primitive|Button|Slot|Comp|slot|div|span|button)\\b(?:\\s|\\/|&gt;)?)/g,\n    (m) => park(`<span class=\"text-info font-medium\">${m}</span>`),\n  )\n\n  // Attributes / directives\n  out = out.replace(\n    /(\\b(?:data-slot|data-variant|data-size|className|variant|size|asChild|as|type|class|lang|setup|ref|onClick|disabled)\\b|:[a-zA-Z0-9_-]+)/g,\n    (m) => park(`<span class=\"text-chart-2\">${m}</span>`),\n  )\n\n  // Numbers\n  out = out.replace(/\\b(\\d+)\\b/g, (m) => park(`<span class=\"text-warning\">${m}</span>`))\n\n  // Restore every parked span\n  return out.replace(/\\u0000([a-j]+)\\u0000/g, (_, key: string) => {\n    const idx = Number(\n      key\n        .split('')\n        .map((c: string) => String(c.charCodeAt(0) - 97))\n        .join(''),\n    )\n    return parked[idx] ?? ''\n  })\n}\n\nconst variants: CodeSnippetPlaygroundButtonVariant[] = [\n  'default',\n  'secondary',\n  'destructive',\n  'outline',\n  'ghost',\n  'link',\n]\nconst sizes: CodeSnippetPlaygroundButtonSize[] = ['sm', 'default', 'lg', 'icon']\n\n/**\n * Angular port of the React CodeSnippetPlayground block: a two-column studio with file\n * tabs and a syntax-coloured editor on the left, and a Live Preview / Terminal tab pair on\n * the right. Signals for state; Cmd/Ctrl+Enter runs the (mock) suite from anywhere.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-code-snippet-playground, [ui-code-snippet-playground]',\n  standalone: true,\n  // React renders the Card itself: keep this wrapper out of the layout.\n  host: { class: 'contents', '(window:keydown)': 'onWindowKeydown($event)' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiSeparatorComponent,\n    UiTabsComponent,\n    UiTabsContentComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n  ],\n  template: `\n    <div ui-card data-slot=\"code-snippet-playground\" [class]=\"rootClass\">\n      <!-- Top Toolbar -->\n      <div\n        ui-card-header\n        class=\"border-border bg-muted/20 flex flex-col gap-3 border-b p-4 sm:flex-row sm:items-center sm:justify-between\"\n      >\n        <div class=\"flex items-center gap-3\">\n          <div class=\"bg-primary/10 text-primary flex size-9 items-center justify-center rounded-lg\">\n            <lucide-icon [img]=\"Code2\" class=\"size-4.5\" />\n          </div>\n          <div>\n            <div class=\"flex items-center gap-2\">\n              <h3 ui-card-title class=\"text-base font-semibold tracking-tight\">Button.tsx · Radix UI Primitive</h3>\n              <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\">React 19</span>\n              <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\">TypeScript 5.6</span>\n            </div>\n            <p ui-card-description class=\"text-muted-foreground text-xs\">\n              Multi-file component studio with live preview, syntax engine, and Vitest suite\n            </p>\n          </div>\n        </div>\n\n        <!-- Action Buttons -->\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <div\n            class=\"border-border/80 bg-background/80 flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs\"\n          >\n            <span\n              [class]=\"\n                cn('size-2 rounded-full transition-colors', isRunning() ? 'bg-warning animate-pulse' : 'bg-success')\n              \"\n            ></span>\n            <span class=\"text-muted-foreground font-mono\">{{\n              isRunning() ? 'Running suite...' : '6 tests passing'\n            }}</span>\n          </div>\n\n          <button\n            ui-button\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs shadow-none\"\n            title=\"Reset Playground\"\n            (click)=\"handleResetPlayground()\"\n          >\n            <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n            <span class=\"hidden sm:inline\">Reset</span>\n          </button>\n\n          <button\n            ui-button\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs shadow-none\"\n            title=\"Copy active file contents\"\n            (click)=\"handleCopyFile()\"\n          >\n            @if (isCopied()) {\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>{{ isCopied() ? 'Copied!' : 'Copy File' }}</span>\n          </button>\n\n          <button\n            ui-button\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs font-medium\"\n            [disabled]=\"isRunning()\"\n            (click)=\"handleRunCode()\"\n          >\n            @if (isRunning()) {\n              <lucide-icon [img]=\"Loader2\" class=\"size-3.5 animate-spin\" />\n            } @else {\n              <lucide-icon [img]=\"Play\" class=\"size-3.5 fill-current\" />\n            }\n            <span>Run Code</span>\n            <kbd\n              class=\"border-primary-foreground/30 bg-primary-foreground/10 hidden rounded border px-1 font-mono text-xs sm:inline\"\n            >\n              ⌘↵\n            </kbd>\n          </button>\n        </div>\n      </div>\n\n      <!-- Main 2-Column Studio Grid -->\n      <div class=\"divide-border grid grid-cols-1 divide-y lg:grid-cols-12 lg:divide-x lg:divide-y-0\">\n        <!-- Left Column: File Tabs & Dark Code Editor (7/12 cols) -->\n        <section class=\"flex min-h-[560px] flex-col bg-zinc-950 text-zinc-100 lg:col-span-7\">\n          <!-- File Navigation Tabs -->\n          <div class=\"flex items-center justify-between border-b border-zinc-800/80 bg-zinc-900/70 px-2\">\n            <div class=\"flex scrollbar-none items-center gap-1 overflow-x-auto py-1.5\">\n              @for (file of files; track file.id) {\n                <button\n                  type=\"button\"\n                  [class]=\"\n                    cn(\n                      'group flex cursor-pointer items-center gap-2 rounded-md px-3 py-1.5 font-mono text-xs transition-colors',\n                      activeFileId() === file.id\n                        ? 'border border-zinc-800 bg-zinc-950 text-zinc-100 shadow-xs'\n                        : 'text-zinc-400 hover:bg-zinc-800/50 hover:text-zinc-200'\n                    )\n                  \"\n                  (click)=\"activeFileId.set(file.id)\"\n                >\n                  <lucide-icon\n                    [img]=\"fileIcon(file)\"\n                    [class]=\"\n                      cn('size-3.5 transition-colors', activeFileId() === file.id ? fileTone(file) : 'text-zinc-500')\n                    \"\n                  />\n                  <span>{{ file.name }}</span>\n                </button>\n              }\n            </div>\n\n            <span class=\"hidden font-mono text-xs text-zinc-500 xl:inline\">{{ activeFile().size }}</span>\n          </div>\n\n          <!-- Breadcrumb / Path Info -->\n          <div\n            class=\"flex items-center justify-between border-b border-zinc-800/60 bg-zinc-950/80 px-4 py-1.5 font-mono text-xs text-zinc-400\"\n          >\n            <div class=\"flex items-center gap-1.5 overflow-hidden text-ellipsis whitespace-nowrap\">\n              <span class=\"text-zinc-600\">src /</span>\n              <span>{{ activeFile().path }}</span>\n            </div>\n            <span class=\"text-zinc-500\">{{ activeFileLines().length }} lines</span>\n          </div>\n\n          <!-- Code Editor Body -->\n          <div\n            class=\"relative flex flex-1 overflow-x-auto bg-zinc-950 py-3 font-mono text-xs leading-relaxed select-text\"\n          >\n            <!-- Gutter Line Numbers -->\n            <div class=\"flex flex-col border-r border-zinc-800/60 px-3 text-right text-zinc-600 select-none\">\n              @for (line of activeFileLines(); track $index) {\n                <span\n                  [class]=\"\n                    cn(\n                      'h-5 leading-5 transition-colors',\n                      hoveredLine() === $index + 1 ? 'font-semibold text-zinc-300' : ''\n                    )\n                  \"\n                  >{{ $index + 1 }}</span\n                >\n              }\n            </div>\n\n            <!-- Code Lines with Syntax Coloring -->\n            <div class=\"flex-1 px-4 whitespace-pre\">\n              @for (html of highlightedLines(); track $index) {\n                <div\n                  [class]=\"\n                    cn(\n                      'group flex h-5 items-center rounded-xs px-1 leading-5 transition-colors',\n                      hoveredLine() === $index + 1 ? 'bg-zinc-800/40' : ''\n                    )\n                  \"\n                  (mouseenter)=\"hoveredLine.set($index + 1)\"\n                  (mouseleave)=\"hoveredLine.set(null)\"\n                >\n                  <span [innerHTML]=\"html\"></span>\n                </div>\n              }\n            </div>\n          </div>\n\n          <!-- Editor Status Bar -->\n          <div\n            class=\"flex items-center justify-between border-t border-zinc-800/60 bg-zinc-900/90 px-3 py-1 font-mono text-xs text-zinc-400\"\n          >\n            <div class=\"flex items-center gap-3\">\n              <span>UTF-8</span>\n              <span>2 Spaces</span>\n              <span class=\"text-zinc-500\">React TSX</span>\n            </div>\n            <div class=\"flex items-center gap-3\">\n              <span>Ln {{ hoveredLine() ?? 1 }}, Col 1</span>\n              <span class=\"text-success\">Prettier ✓</span>\n            </div>\n          </div>\n        </section>\n\n        <!-- Right Column: Live Sandbox & Terminal Console (5/12 cols) -->\n        <section class=\"bg-background flex min-h-[560px] flex-col lg:col-span-5\">\n          <ui-tabs [value]=\"activeRightTab()\" (valueChange)=\"activeRightTab.set($event)\" class=\"flex h-full flex-col\">\n            <!-- Right Tab Header -->\n            <div class=\"border-border bg-muted/30 border-b px-3 py-2\">\n              <ui-tabs-list class=\"grid w-full grid-cols-2\">\n                <button ui-tabs-trigger value=\"preview\" class=\"gap-1.5 text-xs\">\n                  <lucide-icon [img]=\"Eye\" class=\"size-3.5\" />\n                  <span>Live Preview</span>\n                </button>\n                <button ui-tabs-trigger value=\"console\" class=\"gap-1.5 text-xs\">\n                  <lucide-icon [img]=\"Terminal\" class=\"size-3.5\" />\n                  <span>Terminal</span>\n                  <span ui-badge variant=\"secondary\" class=\"text-success ml-1 px-1 py-0 font-mono text-xs\">PASS</span>\n                </button>\n              </ui-tabs-list>\n            </div>\n\n            <!-- TAB 1: Live Component Sandbox -->\n            <ui-tabs-content value=\"preview\" class=\"m-0 flex flex-1 flex-col gap-4 p-4\">\n              <!-- Variant & Style Controls -->\n              <div class=\"border-border bg-card space-y-3 rounded-lg border p-3 shadow-xs\">\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-foreground text-xs font-semibold\">Variant</span>\n                  <span class=\"text-muted-foreground font-mono text-xs\">{{ selectedVariant() }}</span>\n                </div>\n                <div class=\"flex flex-wrap gap-1.5\">\n                  @for (v of variants; track v) {\n                    <button\n                      type=\"button\"\n                      [class]=\"\n                        cn(\n                          'min-h-6 cursor-pointer rounded-md px-2.5 py-1 font-mono text-xs capitalize transition-colors',\n                          selectedVariant() === v\n                            ? 'bg-primary text-primary-foreground font-medium shadow-xs'\n                            : 'bg-muted/60 text-muted-foreground hover:bg-muted hover:text-foreground'\n                        )\n                      \"\n                      (click)=\"selectedVariant.set(v)\"\n                    >\n                      {{ v }}\n                    </button>\n                  }\n                </div>\n\n                <ui-separator class=\"my-2\" />\n\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-foreground text-xs font-semibold\">Size &amp; Options</span>\n                </div>\n                <div class=\"flex flex-wrap items-center gap-1.5\">\n                  @for (s of sizes; track s) {\n                    <button\n                      type=\"button\"\n                      [class]=\"\n                        cn(\n                          'min-h-6 cursor-pointer rounded-md px-2 py-0.5 font-mono text-xs uppercase transition-colors',\n                          selectedSize() === s\n                            ? 'bg-secondary text-secondary-foreground font-semibold'\n                            : 'bg-muted/40 text-muted-foreground hover:bg-muted'\n                        )\n                      \"\n                      (click)=\"selectedSize.set(s)\"\n                    >\n                      {{ s }}\n                    </button>\n                  }\n\n                  <div class=\"ml-auto flex items-center gap-2\">\n                    <label class=\"text-muted-foreground flex cursor-pointer items-center gap-1.5 text-xs select-none\">\n                      <input\n                        type=\"checkbox\"\n                        [checked]=\"isDisabled()\"\n                        (change)=\"isDisabled.set(checked($event))\"\n                        class=\"accent-primary\"\n                      />\n                      <span>Disabled</span>\n                    </label>\n                    <label class=\"text-muted-foreground flex cursor-pointer items-center gap-1.5 text-xs select-none\">\n                      <input\n                        type=\"checkbox\"\n                        [checked]=\"isLoading()\"\n                        (change)=\"isLoading.set(checked($event))\"\n                        class=\"accent-primary\"\n                      />\n                      <span>Loading</span>\n                    </label>\n                    <label class=\"text-muted-foreground flex cursor-pointer items-center gap-1.5 text-xs select-none\">\n                      <input\n                        type=\"checkbox\"\n                        [checked]=\"withIcon()\"\n                        (change)=\"withIcon.set(checked($event))\"\n                        class=\"accent-primary\"\n                      />\n                      <span>Icon</span>\n                    </label>\n                  </div>\n                </div>\n              </div>\n\n              <!-- Render Stage Canvas -->\n              <div\n                class=\"border-border bg-muted/20 relative flex min-h-[160px] flex-1 flex-col items-center justify-center rounded-lg border border-dashed p-6\"\n              >\n                <div class=\"flex flex-col items-center gap-3\">\n                  <button\n                    ui-button\n                    [variant]=\"selectedVariant()\"\n                    [size]=\"selectedSize()\"\n                    [disabled]=\"isDisabled() || isLoading()\"\n                    class=\"transition-[color,background-color,border-color,box-shadow,opacity,transform,scale,translate,rotate] active:scale-95\"\n                    (click)=\"handleButtonClick()\"\n                  >\n                    @if (isLoading()) {\n                      <lucide-icon [img]=\"Loader2\" class=\"size-4 animate-spin\" />\n                    } @else if (withIcon() || selectedSize() === 'icon') {\n                      <lucide-icon [img]=\"Zap\" class=\"size-4\" />\n                    }\n                    @if (selectedSize() !== 'icon') {\n                      <span>Interactive Button</span>\n                    }\n                  </button>\n\n                  <p class=\"text-muted-foreground font-mono text-xs\">\n                    Clicks: <span class=\"text-foreground font-semibold\">{{ clickCount() }}</span>\n                  </p>\n                </div>\n\n                <!-- Last Interaction Status -->\n                <div\n                  class=\"bg-background/80 text-muted-foreground absolute right-2 bottom-2 left-2 flex items-center justify-between rounded-md px-2 py-1 text-xs backdrop-blur-xs\"\n                >\n                  <span class=\"truncate font-mono\">{{ lastInteraction() }}</span>\n                  <span class=\"text-success shrink-0 font-medium\">Rendered OK</span>\n                </div>\n              </div>\n\n              <!-- Dynamic Snippet Output -->\n              <div class=\"space-y-1.5\">\n                <div class=\"flex items-center justify-between text-xs\">\n                  <span class=\"text-foreground font-medium\">Usage Code</span>\n                  <button\n                    type=\"button\"\n                    class=\"text-muted-foreground hover:text-foreground flex min-h-6 cursor-pointer items-center gap-1\"\n                    (click)=\"handleCopySnippet()\"\n                  >\n                    @if (isSnippetCopied()) {\n                      <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                    } @else {\n                      <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                    }\n                    <span>{{ isSnippetCopied() ? 'Copied' : 'Copy' }}</span>\n                  </button>\n                </div>\n                <pre\n                  class=\"border-border bg-muted/40 text-foreground overflow-x-auto rounded-md border p-2.5 font-mono text-xs leading-relaxed\"\n                ><code>{{ generatedSnippet() }}</code></pre>\n              </div>\n            </ui-tabs-content>\n\n            <!-- TAB 2: Terminal Console Execution Output -->\n            <ui-tabs-content\n              value=\"console\"\n              class=\"m-0 flex flex-1 flex-col bg-zinc-950 p-4 font-mono text-xs text-zinc-300\"\n            >\n              <!-- Console Toolbar -->\n              <div class=\"mb-3 flex items-center justify-between border-b border-zinc-800 pb-2 text-zinc-400\">\n                <div class=\"flex items-center gap-2\">\n                  <span class=\"font-semibold text-zinc-200\">Vitest Test Runner</span>\n                  <span ui-badge variant=\"outline\" class=\"border-zinc-700 bg-zinc-900 font-mono text-xs text-zinc-300\"\n                    >v2.1.8</span\n                  >\n                </div>\n                <div class=\"flex items-center gap-3\">\n                  <span class=\"text-zinc-500\">Run #{{ executionCount() }} ({{ lastRunTime() }})</span>\n                  <button\n                    type=\"button\"\n                    class=\"cursor-pointer text-zinc-400 hover:text-zinc-100\"\n                    title=\"Re-run tests\"\n                    (click)=\"handleRunCode()\"\n                  >\n                    <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n                  </button>\n                </div>\n              </div>\n\n              <!-- Terminal Execution Logs -->\n              <div class=\"flex-1 space-y-2 overflow-y-auto pr-1\">\n                <div class=\"text-zinc-500\">\n                  $ vitest run packages/registry-react/components/button/__tests__/button.spec.tsx\n                </div>\n\n                <div class=\"text-success flex items-center gap-2 font-semibold\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"size-3.5 shrink-0\" />\n                  <span>PASS packages/registry-react/components/button/__tests__/button.spec.tsx (6 tests)</span>\n                  <span class=\"font-normal text-zinc-500\">42ms</span>\n                </div>\n\n                <div class=\"ml-4 space-y-1.5 border-l border-zinc-800 pl-3\">\n                  @for (log of testLogs; track log.id) {\n                    <div class=\"flex items-center justify-between text-zinc-300\">\n                      <div class=\"flex items-center gap-2\">\n                        <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                        <span>{{ log.name }}</span>\n                      </div>\n                      <span class=\"text-zinc-500\">{{ log.duration }}</span>\n                    </div>\n                  }\n                </div>\n\n                <!-- Summary Card -->\n                <div class=\"mt-4 space-y-1 rounded-md border border-zinc-800/80 bg-zinc-900/60 p-2.5 text-zinc-400\">\n                  <div class=\"flex justify-between\">\n                    <span>Test Files</span>\n                    <span class=\"text-success font-medium\">1 passed (1)</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span>Tests</span>\n                    <span class=\"text-success font-medium\">6 passed (6)</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span>Start at</span>\n                    <span class=\"text-zinc-300\">{{ lastRunTime() }}</span>\n                  </div>\n                  <div class=\"flex justify-between\">\n                    <span>Duration</span>\n                    <span class=\"text-zinc-300\">42ms (transform 12ms, setup 0ms, collect 8ms, tests 42ms)</span>\n                  </div>\n                </div>\n              </div>\n\n              <!-- Bottom Console Status Banner -->\n              <div\n                class=\"border-success/30 bg-success/10 text-success mt-3 flex items-center justify-between rounded-md border px-3 py-2\"\n              >\n                <div class=\"flex items-center gap-2 font-semibold\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"size-4\" />\n                  <span>PASS · 6 passed, 0 failed in 42ms</span>\n                </div>\n                <span class=\"text-success/80 text-xs\">Exit code: 0</span>\n              </div>\n            </ui-tabs-content>\n          </ui-tabs>\n        </section>\n      </div>\n    </div>\n  `,\n})\nexport class UiCodeSnippetPlaygroundComponent {\n  /** Extra classes for the root Card (React `className`). */\n  @Input('class') className?: string\n\n  protected readonly cn = cn\n  protected readonly files = files\n  protected readonly testLogs = testLogs\n  protected readonly variants = variants\n  protected readonly sizes = sizes\n  protected readonly Code2 = Code2\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Copy = Copy\n  protected readonly Eye = Eye\n  protected readonly Loader2 = Loader2\n  protected readonly Play = Play\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Terminal = Terminal\n  protected readonly Zap = Zap\n\n  readonly activeFileId = signal('button-tsx')\n  readonly activeRightTab = signal('preview')\n  readonly hoveredLine = signal<number | null>(null)\n  readonly isCopied = signal(false)\n  readonly isSnippetCopied = signal(false)\n  readonly isRunning = signal(false)\n  readonly lastRunTime = signal('14:32:08')\n  readonly executionCount = signal(1)\n\n  // Interactive Workbench State\n  readonly selectedVariant = signal<CodeSnippetPlaygroundButtonVariant>('default')\n  readonly selectedSize = signal<CodeSnippetPlaygroundButtonSize>('default')\n  readonly isDisabled = signal(false)\n  readonly isLoading = signal(false)\n  readonly withIcon = signal(true)\n  readonly clickCount = signal(0)\n  readonly lastInteraction = signal('Ready for test interactions')\n\n  readonly activeFile = computed(() => files.find((f) => f.id === this.activeFileId()) ?? files[0]!)\n  readonly activeFileLines = computed(() => this.activeFile().content.split('\\n'))\n  readonly highlightedLines = computed(() => this.activeFileLines().map(highlightSyntaxLine))\n\n  readonly generatedSnippet = computed(() => {\n    const variant = this.selectedVariant()\n    const size = this.selectedSize()\n    const v = variant !== 'default' ? ` variant=\"${variant}\"` : ''\n    const s = size !== 'default' ? ` size=\"${size}\"` : ''\n    const d = this.isDisabled() ? ' disabled' : ''\n\n    if (size === 'icon') {\n      return `<Button${v}${s}${d}>\\n  <Zap className=\"size-4\" />\\n</Button>`\n    }\n\n    if (this.isLoading()) {\n      return `<Button${v}${s}${d}>\\n  <Loader2 className=\"size-4 animate-spin\" />\\n  Please wait\\n</Button>`\n    }\n\n    if (this.withIcon()) {\n      return `<Button${v}${s}${d}>\\n  <Zap className=\"size-4\" />\\n  Interactive Button\\n</Button>`\n    }\n\n    return `<Button${v}${s}${d}>Interactive Button</Button>`\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  get rootClass(): string {\n    return cn('border-border bg-card w-full overflow-hidden shadow-xs', this.className)\n  }\n\n  fileIcon(file: CodeSnippetPlaygroundFile): LucideIconData {\n    if (file.name.endsWith('.tsx')) return FileCode\n    if (file.name.endsWith('.variants.ts')) return Braces\n    if (file.name === 'index.ts') return Layers\n    return CheckCircle2\n  }\n\n  fileTone(file: CodeSnippetPlaygroundFile): string {\n    if (file.name.endsWith('.tsx')) return 'text-info'\n    if (file.name.endsWith('.variants.ts')) return 'text-chart-1'\n    if (file.name === 'index.ts') return 'text-warning'\n    return 'text-success'\n  }\n\n  checked(event: Event): boolean {\n    return (event.target as HTMLInputElement).checked\n  }\n\n  handleRunCode(): void {\n    if (this.isRunning()) return\n    this.isRunning.set(true)\n    this.activeRightTab.set('console')\n\n    this.later(() => {\n      this.isRunning.set(false)\n      this.executionCount.update((prev) => prev + 1)\n      this.lastRunTime.set(new Date().toTimeString().split(' ')[0]!)\n    }, 480)\n  }\n\n  handleCopyFile(): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(this.activeFile().content)\n      this.isCopied.set(true)\n      this.later(() => this.isCopied.set(false), 2000)\n    }\n  }\n\n  handleCopySnippet(): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(this.generatedSnippet())\n      this.isSnippetCopied.set(true)\n      this.later(() => this.isSnippetCopied.set(false), 2000)\n    }\n  }\n\n  handleResetPlayground(): void {\n    this.activeFileId.set('button-tsx')\n    this.selectedVariant.set('default')\n    this.selectedSize.set('default')\n    this.isDisabled.set(false)\n    this.isLoading.set(false)\n    this.withIcon.set(true)\n    this.clickCount.set(0)\n    this.lastInteraction.set('Playground reset to initial state')\n  }\n\n  handleButtonClick(): void {\n    if (this.isDisabled() || this.isLoading()) return\n    const next = this.clickCount() + 1\n    this.clickCount.set(next)\n    this.lastInteraction.set(`Dispatched onClick event #${next} at ${new Date().toLocaleTimeString()}`)\n  }\n\n  onWindowKeydown(e: KeyboardEvent): void {\n    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n      e.preventDefault()\n      this.handleRunCode()\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/code-snippet-playground/code-snippet-playground.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/code-snippet-playground/index.ts",
      "content": "export {\n  UiCodeSnippetPlaygroundComponent,\n  type CodeSnippetPlaygroundFile,\n  type CodeSnippetPlaygroundTestLog,\n  type CodeSnippetPlaygroundButtonVariant,\n  type CodeSnippetPlaygroundButtonSize,\n} from './code-snippet-playground.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/code-snippet-playground/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/separator.json",
    "https://uipkge.dev/r/angular/tabs.json"
  ],
  "description": "Multi-file interactive code runner, editor tabs, and terminal console output for components with live render sandbox, syntax highlighting, and test execution.",
  "categories": [
    "devops",
    "app",
    "developer",
    "education"
  ]
}