{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/tabbed-content",
  "title": "Tabbed Content",
  "description": "Tab bar wired to content panels",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/interaction.json",
    "https://termcn.dev/r/ink/terminal-style.json",
    "https://termcn.dev/r/ink/use-theme.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/tabbed-content.tsx",
      "content": "import { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useInteraction } from \"@/hooks/use-interaction\";\nimport type { InteractionProps } from \"@/hooks/use-interaction\";\nimport { useTheme } from \"@/hooks/use-theme\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\nimport { resolveBorderStyle } from \"@/lib/terminal-style\";\n\nexport interface TabbedContentTab {\n  id: string;\n  label: string;\n  content: ReactNode;\n  disabled?: boolean;\n}\n\nexport interface TabbedContentProps extends InteractionProps {\n  tabs: TabbedContentTab[];\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (id: string) => void;\n  defaultTab?: string;\n  activeTab?: string;\n  onChange?: (id: string) => void;\n  tabBarStyle?: \"underline\" | \"box\" | \"minimal\";\n  \"aria-label\"?: string;\n}\n\nexport const TabbedContent = ({\n  tabs,\n  value,\n  defaultValue,\n  defaultTab,\n  activeTab: controlledTab,\n  onValueChange,\n  onChange,\n  tabBarStyle = \"underline\",\n  id,\n  autoFocus,\n  isActive,\n  disabled,\n  \"aria-label\": ariaLabel = \"Tabbed content\",\n}: TabbedContentProps) => {\n  const unicode = useUnicode();\n  const theme = useTheme();\n  const [internalTab, setInternalTab] = useState(\n    defaultValue ?? defaultTab ?? tabs.find((tab) => !tab.disabled)?.id ?? \"\"\n  );\n  const activeId = value ?? controlledTab ?? internalTab;\n  const activeIndex = tabs.findIndex((t) => t.id === activeId);\n\n  const switchTab = (id: string) => {\n    if (value === undefined && controlledTab === undefined) {\n      setInternalTab(id);\n    }\n    onValueChange?.(id);\n    onChange?.(id);\n  };\n\n  const { isFocused } = useInteraction(\n    (_input, key) => {\n      if (key.leftArrow) {\n        let prev = activeIndex - 1;\n        while (prev >= 0 && tabs[prev]?.disabled) {\n          prev -= 1;\n        }\n        if (prev >= 0) {\n          switchTab(tabs[prev].id);\n        }\n      } else if (key.rightArrow) {\n        let next = activeIndex + 1;\n        while (next < tabs.length && tabs[next]?.disabled) {\n          next += 1;\n        }\n        if (next < tabs.length) {\n          switchTab(tabs[next].id);\n        }\n      } else if (key.home) {\n        const first = tabs.find((tab) => !tab.disabled);\n        if (first) {\n          switchTab(first.id);\n        }\n      } else if (key.end) {\n        const last = tabs.findLast((tab) => !tab.disabled);\n        if (last) {\n          switchTab(last.id);\n        }\n      }\n    },\n    { autoFocus, disabled, id, isActive }\n  );\n\n  const activeTab = tabs.find((t) => t.id === activeId);\n\n  const renderTabBar = () => {\n    if (tabBarStyle === \"box\") {\n      return (\n        <Box\n          aria-role=\"tablist\"\n          borderStyle={resolveBorderStyle(\"single\", unicode)}\n          borderColor={theme.colors.border}\n        >\n          {tabs.map((tab, idx) => {\n            const isActive = tab.id === activeId;\n            let textColor: string;\n            if (tab.disabled) {\n              textColor = theme.colors.mutedForeground;\n            } else if (isActive) {\n              textColor = theme.colors.primary;\n            } else {\n              textColor = theme.colors.foreground;\n            }\n            return (\n              <Box\n                key={tab.id}\n                paddingX={1}\n                aria-role=\"tab\"\n                aria-label={tab.label}\n                aria-state={{\n                  disabled: tab.disabled || undefined,\n                  selected: isActive,\n                }}\n              >\n                <Text color={textColor} bold={isActive} dimColor={tab.disabled}>\n                  {isActive ? \"[>\" : \" \"}\n                  {tab.label}\n                  {isActive ? \"<]\" : \" \"}\n                </Text>\n                {idx < tabs.length - 1 && (\n                  <Text color={theme.colors.border}>\n                    {unicode ? \" │ \" : \" | \"}\n                  </Text>\n                )}\n              </Box>\n            );\n          })}\n        </Box>\n      );\n    }\n\n    if (tabBarStyle === \"minimal\") {\n      return (\n        <Box gap={2} aria-role=\"tablist\">\n          {tabs.map((tab) => {\n            const isActive = tab.id === activeId;\n            let textColor: string;\n            if (tab.disabled) {\n              textColor = theme.colors.mutedForeground;\n            } else if (isActive) {\n              textColor = theme.colors.primary;\n            } else {\n              textColor = theme.colors.mutedForeground;\n            }\n            return (\n              <Box\n                key={tab.id}\n                aria-role=\"tab\"\n                aria-label={tab.label}\n                aria-state={{\n                  disabled: tab.disabled || undefined,\n                  selected: isActive,\n                }}\n              >\n                <Text color={textColor} bold={isActive} dimColor={tab.disabled}>\n                  {isActive ? `[${tab.label}]` : tab.label}\n                </Text>\n              </Box>\n            );\n          })}\n        </Box>\n      );\n    }\n\n    return (\n      <Box aria-role=\"tablist\">\n        {tabs.map((tab, idx) => {\n          const isActive = tab.id === activeId;\n          let textColor: string;\n          if (tab.disabled) {\n            textColor = theme.colors.mutedForeground;\n          } else if (isActive) {\n            textColor = theme.colors.primary;\n          } else {\n            textColor = theme.colors.foreground;\n          }\n          return (\n            <Box\n              key={tab.id}\n              aria-role=\"tab\"\n              aria-label={tab.label}\n              aria-state={{\n                disabled: tab.disabled || undefined,\n                selected: isActive,\n              }}\n            >\n              <Text\n                color={textColor}\n                bold={isActive}\n                underline={isActive}\n                dimColor={tab.disabled}\n              >\n                {isActive ? `[${tab.label}]` : tab.label}\n              </Text>\n              {idx < tabs.length - 1 && (\n                <Text color={theme.colors.border}>\n                  {unicode ? \" │ \" : \" | \"}\n                </Text>\n              )}\n            </Box>\n          );\n        })}\n      </Box>\n    );\n  };\n\n  return (\n    <Box\n      flexDirection=\"column\"\n      aria-state={{ disabled: disabled || undefined }}\n    >\n      <Text aria-label={ariaLabel}>{\"\"}</Text>\n      {renderTabBar()}\n      <Box\n        borderStyle={resolveBorderStyle(\"single\", unicode)}\n        borderColor={theme.colors.border}\n        paddingX={1}\n        marginTop={0}\n      >\n        {activeTab?.content}\n      </Box>\n      <Text aria-hidden color={theme.colors.mutedForeground} dimColor>\n        {unicode ? \"←→ to switch tabs\" : \"left/right to switch tabs\"}\n      </Text>\n    </Box>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}