{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "code-snippet-playground",
  "title": "Code Snippet Playground",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/code-snippet-playground/CodeSnippetPlayground.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport {\n  Braces,\n  Check,\n  CheckCircle2,\n  Code2,\n  Copy,\n  Eye,\n  FileCode,\n  Layers,\n  Loader2,\n  Play,\n  RotateCcw,\n  Terminal,\n  Zap,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Separator } from '@/components/ui/separator'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'\n\ninterface CodeFile {\n  id: string\n  name: string\n  path: string\n  language: string\n  size: string\n  content: string\n}\n\ninterface TestLog {\n  id: string\n  name: string\n  duration: string\n  status: 'pass' | 'fail'\n}\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n\n// --- Multi-file Definitions ---\nconst files: CodeFile[] = [\n  {\n    id: 'button-vue',\n    name: 'Button.vue',\n    path: 'packages/registry-vue/components/button/Button.vue',\n    language: 'vue',\n    size: '1.2 KB',\n    content: `<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { buttonVariants } from './button.variants'\n\ntype Variant = 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'\ntype Size = 'default' | 'sm' | 'lg' | 'xs' | 'icon'\n\ninterface Props {\n  as?: string\n  asChild?: boolean\n  variant?: Variant\n  size?: Size\n  type?: 'button' | 'submit' | 'reset'\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  as: 'button',\n  type: 'button',\n})\n<\\/script>\n\n<template>\n  <Primitive\n    data-slot=\"button\"\n    :data-variant=\"variant\"\n    :data-size=\"size\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :type=\"as === 'button' && !asChild ? type : undefined\"\n    :class=\"cn(buttonVariants({ variant, size }), props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>`,\n  },\n  {\n    id: 'button-variants',\n    name: 'button.variants.ts',\n    path: 'packages/registry-vue/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-vue/components/button/index.ts',\n    language: 'typescript',\n    size: '240 B',\n    content: `export { default as Button } from './Button.vue'\nexport { buttonVariants, type ButtonVariants } from './button.variants'`,\n  },\n  {\n    id: 'button-test',\n    name: 'button.test.ts',\n    path: 'packages/registry-vue/components/button/__tests__/button.spec.ts',\n    language: 'typescript',\n    size: '1.6 KB',\n    content: `import { describe, it, expect } from 'vitest'\nimport { mount } from '@vue/test-utils'\nimport Button from '../Button.vue'\n\ndescribe('Button Primitive', () => {\n  it('renders default button with slot text', () => {\n    const wrapper = mount(Button, { slots: { default: 'Submit' } })\n    expect(wrapper.text()).toBe('Submit')\n    expect(wrapper.attributes('data-slot')).toBe('button')\n  })\n\n  it('applies variant classes correctly', () => {\n    const wrapper = mount(Button, { props: { variant: 'destructive' } })\n    expect(wrapper.attributes('data-variant')).toBe('destructive')\n    expect(wrapper.classes()).toContain('bg-destructive')\n  })\n\n  it('applies size styles correctly', () => {\n    const wrapper = mount(Button, { props: { size: 'sm' } })\n    expect(wrapper.attributes('data-size')).toBe('sm')\n    expect(wrapper.classes()).toContain('h-8')\n  })\n\n  it('handles click events when active', async () => {\n    const wrapper = mount(Button)\n    await wrapper.trigger('click')\n    expect(wrapper.emitted('click')).toBeTruthy()\n  })\n\n  it('respects disabled state attribute', () => {\n    const wrapper = mount(Button, { attrs: { disabled: true } })\n    expect(wrapper.attributes('disabled')).toBeDefined()\n  })\n\n  it('forwards custom class names with cn()', () => {\n    const wrapper = mount(Button, { props: { class: 'custom-btn' } })\n    expect(wrapper.classes()).toContain('custom-btn')\n  })\n})`,\n  },\n]\n\nconst testLogs: TestLog[] = [\n  { id: '1', name: 'renders default button with slot 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\n// --- State ---\nconst activeFileId = ref('button-vue')\nconst activeRightTab = ref<'preview' | 'console'>('preview')\nconst hoveredLine = ref<number | null>(null)\nconst isCopied = ref(false)\nconst isSnippetCopied = ref(false)\nconst isRunning = ref(false)\nconst lastRunTime = ref('14:32:08')\nconst executionCount = ref(1)\n\n// Interactive Preview Sandbox State\ntype ButtonVariant = 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link'\ntype ButtonSize = 'default' | 'sm' | 'lg' | 'icon'\n\nconst selectedVariant = ref<ButtonVariant>('default')\nconst selectedSize = ref<ButtonSize>('default')\nconst isDisabled = ref(false)\nconst isLoading = ref(false)\nconst withIcon = ref(true)\nconst clickCount = ref(0)\nconst lastInteraction = ref('Ready for test interactions')\n\n// --- Computed ---\nconst activeFile = computed(() => {\n  return files.find((f) => f.id === activeFileId.value) || files[0]\n})\n\nconst activeFileLines = computed(() => {\n  return activeFile.value.content.split('\\n')\n})\n\nconst generatedSnippet = computed(() => {\n  const v = selectedVariant.value !== 'default' ? ` variant=\"${selectedVariant.value}\"` : ''\n  const s = selectedSize.value !== 'default' ? ` size=\"${selectedSize.value}\"` : ''\n  const d = isDisabled.value ? ' disabled' : ''\n\n  if (selectedSize.value === 'icon') {\n    return `<Button${v}${s}${d}>\\n  <Zap class=\"size-4\" />\\n</Button>`\n  }\n\n  if (isLoading.value) {\n    return `<Button${v}${s}${d}>\\n  <Loader2 class=\"size-4 animate-spin\" />\\n  Please wait\\n</Button>`\n  }\n\n  if (withIcon.value) {\n    return `<Button${v}${s}${d}>\\n  <Zap class=\"size-4\" />\\n  Interactive Button\\n</Button>`\n  }\n\n  return `<Button${v}${s}${d}>Interactive Button</Button>`\n})\n\n// --- Syntax Highlighting Engine ---\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\n// --- Action Handlers ---\nfunction handleRunCode() {\n  if (isRunning.value) return\n  isRunning.value = true\n  activeRightTab.value = 'console'\n\n  setTimeout(() => {\n    isRunning.value = false\n    executionCount.value++\n    const now = new Date()\n    lastRunTime.value = now.toTimeString().split(' ')[0]\n  }, 480)\n}\n\nfunction handleCopyFile() {\n  if (typeof navigator !== 'undefined' && navigator.clipboard) {\n    navigator.clipboard.writeText(activeFile.value.content)\n    isCopied.value = true\n    setTimeout(() => {\n      isCopied.value = false\n    }, 2000)\n  }\n}\n\nfunction handleCopySnippet() {\n  if (typeof navigator !== 'undefined' && navigator.clipboard) {\n    navigator.clipboard.writeText(generatedSnippet.value)\n    isSnippetCopied.value = true\n    setTimeout(() => {\n      isSnippetCopied.value = false\n    }, 2000)\n  }\n}\n\nfunction handleResetPlayground() {\n  activeFileId.value = 'button-vue'\n  selectedVariant.value = 'default'\n  selectedSize.value = 'default'\n  isDisabled.value = false\n  isLoading.value = false\n  withIcon.value = true\n  clickCount.value = 0\n  lastInteraction.value = 'Playground reset to initial state'\n}\n\nfunction handleButtonClick() {\n  if (isDisabled.value || isLoading.value) return\n  clickCount.value++\n  lastInteraction.value = `Dispatched onClick event #${clickCount.value} at ${new Date().toLocaleTimeString()}`\n}\n\nfunction handleKeyDown(e: KeyboardEvent) {\n  if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n    e.preventDefault()\n    handleRunCode()\n  }\n}\n\nonMounted(() => {\n  if (typeof window !== 'undefined') {\n    window.addEventListener('keydown', handleKeyDown)\n  }\n})\n\nonUnmounted(() => {\n  if (typeof window !== 'undefined') {\n    window.removeEventListener('keydown', handleKeyDown)\n  }\n})\n</script>\n\n<template>\n  <Card\n    :class=\"cn('border-border bg-card w-full overflow-hidden shadow-xs', props.class)\"\n    data-slot=\"code-snippet-playground\"\n  >\n    <!-- Top Toolbar -->\n    <CardHeader\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          <Code2 class=\"size-4.5\" />\n        </div>\n        <div>\n          <div class=\"flex items-center gap-2\">\n            <CardTitle class=\"text-base font-semibold tracking-tight\">Button.vue · Reka UI Primitive</CardTitle>\n            <Badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\"> Vue 3.5 </Badge>\n            <Badge variant=\"secondary\" class=\"font-mono text-xs\"> TypeScript 5.6 </Badge>\n          </div>\n          <CardDescription class=\"text-muted-foreground text-xs\">\n            Multi-file component studio with live preview, syntax engine, and Vitest suite\n          </CardDescription>\n        </div>\n      </div>\n\n      <!-- Action Buttons -->\n      <div class=\"flex flex-wrap items-center gap-2\">\n        <div class=\"border-border/80 bg-background/80 flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs\">\n          <span\n            :class=\"cn('size-2 rounded-full transition-colors', isRunning ? 'bg-warning animate-pulse' : 'bg-success')\"\n          />\n          <span class=\"text-muted-foreground font-mono\">\n            {{ isRunning ? 'Running suite...' : '6 tests passing' }}\n          </span>\n        </div>\n\n        <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          <RotateCcw class=\"size-3.5\" />\n          <span class=\"hidden sm:inline\">Reset</span>\n        </Button>\n\n        <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          <Check v-if=\"isCopied\" class=\"text-success size-3.5\" />\n          <Copy v-else class=\"size-3.5\" />\n          <span>{{ isCopied ? 'Copied!' : 'Copy File' }}</span>\n        </Button>\n\n        <Button size=\"sm\" class=\"h-8 gap-1.5 text-xs font-medium\" :disabled=\"isRunning\" @click=\"handleRunCode\">\n          <Loader2 v-if=\"isRunning\" class=\"size-3.5 animate-spin\" />\n          <Play v-else class=\"size-3.5 fill-current\" />\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    </CardHeader>\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 (60-65% width = 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            <button\n              v-for=\"file in files\"\n              :key=\"file.id\"\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 = file.id\"\n            >\n              <FileCode\n                v-if=\"file.name.endsWith('.vue')\"\n                :class=\"cn('size-3.5 transition-colors', activeFileId === file.id ? 'text-success' : 'text-zinc-500')\"\n              />\n              <Braces\n                v-else-if=\"file.name.endsWith('.variants.ts')\"\n                :class=\"cn('size-3.5 transition-colors', activeFileId === file.id ? 'text-chart-1' : 'text-zinc-500')\"\n              />\n              <Layers\n                v-else-if=\"file.name === 'index.ts'\"\n                :class=\"cn('size-3.5 transition-colors', activeFileId === file.id ? 'text-warning' : 'text-zinc-500')\"\n              />\n              <CheckCircle2\n                v-else\n                :class=\"cn('size-3.5 transition-colors', activeFileId === file.id ? 'text-info' : 'text-zinc-500')\"\n              />\n              <span>{{ file.name }}</span>\n            </button>\n          </div>\n\n          <span class=\"hidden font-mono text-xs text-zinc-500 xl:inline\">\n            {{ activeFile.size }}\n          </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            <span\n              v-for=\"(_, index) in activeFileLines\"\n              :key=\"index\"\n              :class=\"\n                cn('h-5 leading-5 transition-colors', hoveredLine === index + 1 ? 'font-semibold text-zinc-300' : '')\n              \"\n            >\n              {{ index + 1 }}\n            </span>\n          </div>\n\n          <!-- Code Lines with Syntax Coloring -->\n          <div class=\"flex-1 px-4 whitespace-pre\">\n            <div\n              v-for=\"(line, index) in activeFileLines\"\n              :key=\"index\"\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 = index + 1\"\n              @mouseleave=\"hoveredLine = null\"\n            >\n              <span v-html=\"highlightSyntaxLine(line)\" />\n            </div>\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\">Vue SFC / TS</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 (35-40% width = 5/12 cols) -->\n      <section class=\"bg-background flex min-h-[560px] flex-col lg:col-span-5\">\n        <Tabs v-model=\"activeRightTab\" 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            <TabsList class=\"grid w-full grid-cols-2\">\n              <TabsTrigger value=\"preview\" class=\"gap-1.5 text-xs\">\n                <Eye class=\"size-3.5\" />\n                <span>Live Preview</span>\n              </TabsTrigger>\n              <TabsTrigger value=\"console\" class=\"gap-1.5 text-xs\">\n                <Terminal class=\"size-3.5\" />\n                <span>Terminal</span>\n                <Badge variant=\"secondary\" class=\"text-success ml-1 px-1 py-0 font-mono text-xs\"> PASS </Badge>\n              </TabsTrigger>\n            </TabsList>\n          </div>\n\n          <!-- TAB 1: Live Component Sandbox -->\n          <TabsContent 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                <button\n                  v-for=\"v in ['default', 'secondary', 'destructive', 'outline', 'ghost', 'link']\"\n                  :key=\"v\"\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 = v\"\n                >\n                  {{ v }}\n                </button>\n              </div>\n\n              <Separator class=\"my-2\" />\n\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-foreground text-xs font-semibold\">Size & Options</span>\n              </div>\n              <div class=\"flex flex-wrap items-center gap-1.5\">\n                <button\n                  v-for=\"s in ['sm', 'default', 'lg', 'icon'] as ButtonSize[]\"\n                  :key=\"s\"\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 = s\"\n                >\n                  {{ s }}\n                </button>\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 v-model=\"isDisabled\" type=\"checkbox\" class=\"accent-primary\" />\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 v-model=\"isLoading\" type=\"checkbox\" class=\"accent-primary\" />\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 v-model=\"withIcon\" type=\"checkbox\" class=\"accent-primary\" />\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                  :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                  <Loader2 v-if=\"isLoading\" class=\"size-4 animate-spin\" />\n                  <Zap v-else-if=\"withIcon || selectedSize === 'icon'\" class=\"size-4\" />\n                  <span v-if=\"selectedSize !== 'icon'\">Interactive Button</span>\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                  <Check v-if=\"isSnippetCopied\" class=\"text-success size-3\" />\n                  <Copy v-else class=\"size-3\" />\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          </TabsContent>\n\n          <!-- TAB 2: Terminal Console Execution Output -->\n          <TabsContent value=\"console\" class=\"m-0 flex flex-1 flex-col bg-zinc-950 p-4 font-mono text-xs text-zinc-300\">\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                <Badge variant=\"outline\" class=\"border-zinc-700 bg-zinc-900 font-mono text-xs text-zinc-300\">\n                  v2.1.8\n                </Badge>\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                  <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-vue/components/button/__tests__/button.spec.ts\n              </div>\n\n              <div class=\"text-success flex items-center gap-2 font-semibold\">\n                <CheckCircle2 class=\"size-3.5 shrink-0\" />\n                <span>PASS packages/registry-vue/components/button/__tests__/button.spec.ts (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                <div v-for=\"log in testLogs\" :key=\"log.id\" class=\"flex items-center justify-between text-zinc-300\">\n                  <div class=\"flex items-center gap-2\">\n                    <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              </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                <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          </TabsContent>\n        </Tabs>\n      </section>\n    </div>\n  </Card>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/CodeSnippetPlayground.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/separator.json",
    "https://uipkge.dev/r/vue/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"
  ]
}