{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "snippet-tabs",
  "title": "Snippet Tabs",
  "description": "Tabbed code samples in several languages with syntax highlighting, a copy button and a remembered language choice.",
  "dependencies": [
    "@radix-ui/react-tabs"
  ],
  "registryDependencies": [
    "@tealmac/copy-button",
    "@tealmac/highlight"
  ],
  "files": [
    {
      "path": "registry/tealmac/snippet-tabs.tsx",
      "content": "// Adapted from Ballmac UI (https://github.com/vamsiy78/ballmac-ui), MIT License, Copyright (c) 2026 Ballmac.\n\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CopyButton } from \"@/registry/tealmac/copy-button\"\nimport {\n  highlightLines,\n  languageFromName,\n  tokenClass,\n  type HighlightLanguage,\n} from \"@/registry/tealmac/highlight\"\n\ntype Snippet = {\n  /** Tab text, such as \"cURL\" or \"Python\". Also picks the highlighter unless `language` is set. */\n  label: string\n  /** The code. `{{name}}` placeholders are replaced from the `variables` prop. */\n  code: string\n  /** Highlighter to use. Defaults to a guess from `label`. */\n  language?: HighlightLanguage\n}\n\nconst memory = new Map<string, string>()\nconst listeners = new Map<string, Set<() => void>>()\n\nfunction readStored(key: string): string | null {\n  if (memory.has(key)) return memory.get(key) ?? null\n  try {\n    return window.localStorage.getItem(key)\n  } catch {\n    return null\n  }\n}\n\nfunction writeStored(key: string, value: string) {\n  memory.set(key, value)\n  try {\n    window.localStorage.setItem(key, value)\n  } catch {\n    // Storage is blocked; the in-memory value still syncs this page.\n  }\n  listeners.get(key)?.forEach((notify) => notify())\n}\n\nfunction subscribeStored(key: string, notify: () => void) {\n  let set = listeners.get(key)\n  if (!set) listeners.set(key, (set = new Set()))\n  set.add(notify)\n  const onStorage = (event: StorageEvent) => {\n    if (event.key !== key) return\n    if (event.newValue === null) memory.delete(key)\n    else memory.set(key, event.newValue)\n    notify()\n  }\n  window.addEventListener(\"storage\", onStorage)\n  return () => {\n    set.delete(notify)\n    window.removeEventListener(\"storage\", onStorage)\n  }\n}\n\nfunction useStoredLabel(key: string | undefined) {\n  const subscribe = React.useCallback(\n    (notify: () => void) => (key ? subscribeStored(key, notify) : () => {}),\n    [key]\n  )\n  return React.useSyncExternalStore(\n    subscribe,\n    () => (key ? readStored(key) : null),\n    // Server and hydration never see storage, so markup always matches.\n    () => null\n  )\n}\n\nfunction fill(code: string, variables?: Record<string, string>) {\n  if (!variables) return code\n  return code.replace(\n    /\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g,\n    (match, name: string) => variables[name] ?? match\n  )\n}\n\ntype SnippetTabsProps = Omit<\n  React.ComponentProps<\"div\">,\n  \"defaultValue\" | \"dir\" | \"title\"\n> & {\n  /** One tab per language. */\n  snippets: Snippet[]\n  /** Heading on the left of the tab bar, such as \"Create a customer\". */\n  title?: string\n  /** Label of the tab open at first. */\n  defaultValue?: string\n  /** Controlled open tab (a label). */\n  value?: string\n  /** Called with the label of the newly selected tab. */\n  onValueChange?: (label: string) => void\n  /** Remember the reader's language in localStorage under this key. Every instance sharing the key switches together. */\n  storageKey?: string\n  /** Values for `{{name}}` placeholders in the code, for example the viewer's API key. */\n  variables?: Record<string, string>\n  /** Show line numbers. */\n  lineNumbers?: boolean\n  /** Classes for the scrolling code area, for example a max height. */\n  bodyClassName?: string\n}\n\nfunction SnippetTabs({\n  snippets,\n  title,\n  defaultValue,\n  value: valueProp,\n  onValueChange,\n  storageKey,\n  variables,\n  lineNumbers = false,\n  bodyClassName,\n  className,\n  ...props\n}: SnippetTabsProps) {\n  const labels = snippets.map((s) => s.label)\n  const stored = useStoredLabel(storageKey)\n  const [internal, setInternal] = React.useState(\n    defaultValue ?? labels[0] ?? \"\"\n  )\n  const preferred = stored && labels.includes(stored) ? stored : null\n  const value = valueProp ?? preferred ?? internal\n  const titleId = React.useId()\n\n  function select(next: string) {\n    setInternal(next)\n    if (storageKey) writeStored(storageKey, next)\n    onValueChange?.(next)\n  }\n\n  return (\n    <TabsPrimitive.Root\n      data-slot=\"snippet-tabs\"\n      value={value}\n      onValueChange={select}\n      className={cn(\n        \"bg-card text-card-foreground w-full overflow-hidden rounded-xl border\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"bg-muted/40 flex min-h-11 items-center gap-2 border-b ps-1.5 pe-1.5\">\n        {title && (\n          <span\n            id={titleId}\n            className=\"text-foreground hidden shrink-0 ps-2 text-[13px] font-medium sm:block\"\n          >\n            {title}\n          </span>\n        )}\n        <TabsPrimitive.List\n          aria-label={title ?? \"Language\"}\n          className=\"flex min-w-0 flex-1 [scrollbar-width:none] items-center gap-0.5 overflow-x-auto py-1 sm:justify-end [&::-webkit-scrollbar]:hidden\"\n        >\n          {snippets.map((s) => (\n            <TabsPrimitive.Trigger\n              key={s.label}\n              value={s.label}\n              className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 data-[state=active]:bg-background data-[state=active]:text-foreground relative h-7 shrink-0 rounded-md px-2.5 text-[13px] font-medium transition-colors duration-150 outline-none focus-visible:ring-[3px] data-[state=active]:shadow-xs motion-reduce:transition-none\"\n            >\n              {s.label}\n            </TabsPrimitive.Trigger>\n          ))}\n        </TabsPrimitive.List>\n        <CopyButton\n          size=\"sm\"\n          ariaLabel=\"Copy snippet\"\n          getValue={() =>\n            fill(snippets.find((s) => s.label === value)?.code ?? \"\", variables)\n          }\n        />\n      </div>\n      {snippets.map((s) => {\n        const text = fill(s.code, variables)\n        const lines = highlightLines(\n          text,\n          s.language ?? languageFromName(s.label)\n        )\n        return (\n          <TabsPrimitive.Content\n            key={s.label}\n            value={s.label}\n            tabIndex={-1}\n            className=\"outline-none data-[state=inactive]:hidden\"\n          >\n            <pre\n              dir=\"ltr\"\n              role=\"region\"\n              aria-label={`${s.label} code`}\n              // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- scrollable region; focusable so keyboard users can scroll it\n              tabIndex={0}\n              className={cn(\n                \"text-foreground focus-visible:ring-ring/50 overflow-auto p-4 font-mono text-[13px] leading-6 outline-none focus-visible:ring-[3px] focus-visible:ring-inset\",\n                bodyClassName\n              )}\n            >\n              <code className=\"grid\">\n                {lines.map((line, i) => (\n                  <span key={i} className=\"flex min-w-max\">\n                    {lineNumbers && (\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"text-muted-foreground me-4 w-5 shrink-0 text-end tabular-nums select-none\"\n                      >\n                        {i + 1}\n                      </span>\n                    )}\n                    <span className=\"whitespace-pre\">\n                      {line.length === 0\n                        ? \" \"\n                        : line.map((t, j) => (\n                            <span key={j} className={tokenClass[t.type]}>\n                              {t.text}\n                            </span>\n                          ))}\n                    </span>\n                  </span>\n                ))}\n              </code>\n            </pre>\n          </TabsPrimitive.Content>\n        )\n      })}\n    </TabsPrimitive.Root>\n  )\n}\n\nexport { SnippetTabs, type SnippetTabsProps, type Snippet }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}