{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/tabs",
  "title": "Tabs",
  "description": "Tab bar with left/right arrow and tab-key navigation",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/interaction.json",
    "https://termcn.dev/r/ink/terminal-style.json",
    "https://termcn.dev/r/ink/types.json",
    "https://termcn.dev/r/ink/use-theme.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/tabs.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 { useTheme } from \"@/hooks/use-theme\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\nimport { resolveBorderStyle } from \"@/lib/terminal-style\";\nimport type { BorderStyle } from \"@/components/ui/types\";\n\nexport interface Tab {\n  key: string;\n  label: string;\n  content: ReactNode;\n  disabled?: boolean;\n}\n\nexport interface TabsProps {\n  tabs: Tab[];\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (key: string) => void;\n  defaultTab?: string;\n  activeTab?: string;\n  onTabChange?: (key: string) => void;\n  borderColor?: string;\n  borderStyle?: BorderStyle;\n  separator?: string;\n  tabBarPaddingX?: number;\n  paddingX?: number;\n  paddingY?: number;\n  id?: string;\n  autoFocus?: boolean;\n  isActive?: boolean;\n  disabled?: boolean;\n  \"aria-label\"?: string;\n}\n\nexport const Tabs = ({\n  tabs,\n  value,\n  defaultValue,\n  onValueChange,\n  defaultTab,\n  activeTab: controlledTab,\n  onTabChange,\n  borderColor,\n  borderStyle = \"single\",\n  separator,\n  tabBarPaddingX = 2,\n  paddingX = 1,\n  paddingY = 0,\n  id,\n  autoFocus = false,\n  isActive = true,\n  disabled = false,\n  \"aria-label\": ariaLabel,\n}: TabsProps) => {\n  const unicode = useUnicode();\n  const theme = useTheme();\n  const [internalTab, setInternalTab] = useState(\n    defaultValue ?? defaultTab ?? tabs.find((tab) => !tab.disabled)?.key ?? \"\"\n  );\n  const activeKey = value ?? controlledTab ?? internalTab;\n  const activeIndex = tabs.findIndex((t) => t.key === activeKey);\n\n  const resolvedBorderColor = borderColor ?? theme.colors.border;\n  const resolvedSeparator = separator ?? (unicode ? \" │ \" : \" | \");\n\n  const switchTab = (nextKey: string | undefined) => {\n    if (!nextKey || nextKey === activeKey) {\n      return;\n    }\n    const nextTab = tabs.find((tab) => tab.key === nextKey);\n    if (nextTab?.disabled) {\n      return;\n    }\n    const changeHandler = onValueChange ?? onTabChange;\n    if (changeHandler) {\n      changeHandler(nextKey);\n    } else {\n      setInternalTab(nextKey);\n    }\n  };\n\n  const { isFocused } = useInteraction(\n    (input, key) => {\n      if (key.leftArrow || key.rightArrow) {\n        const direction = key.leftArrow ? -1 : 1;\n        let nextIndex = activeIndex;\n        for (const _tab of tabs) {\n          nextIndex = (nextIndex + direction + tabs.length) % tabs.length;\n          if (!tabs[nextIndex]?.disabled) {\n            switchTab(tabs[nextIndex]?.key);\n            break;\n          }\n        }\n      } else if (key.home) {\n        switchTab(tabs.find((tab) => !tab.disabled)?.key);\n      } else if (key.end) {\n        switchTab(tabs.findLast((tab) => !tab.disabled)?.key);\n      }\n    },\n    { autoFocus, disabled, id, isActive }\n  );\n\n  const activeTab = tabs.find((t) => t.key === activeKey);\n\n  return (\n    <Box flexDirection=\"column\">\n      <Box\n        aria-role=\"tablist\"\n        aria-state={{ disabled }}\n        paddingX={tabBarPaddingX}\n        gap={0}\n      >\n        <Text aria-label={ariaLabel ?? \"Tabs\"}>{\"\"}</Text>\n        {tabs.map((tab, idx) => {\n          const isActive = tab.key === activeKey;\n          return (\n            <Box\n              key={tab.key}\n              aria-label={tab.label}\n              aria-role=\"tab\"\n              aria-state={{ disabled: tab.disabled, selected: isActive }}\n            >\n              <Text\n                aria-hidden\n                color={\n                  isActive ? theme.colors.primary : theme.colors.mutedForeground\n                }\n                bold={isActive}\n                underline={isActive}\n                inverse={isActive}\n              >\n                {tab.label}\n              </Text>\n              {idx < tabs.length - 1 && (\n                <Text aria-hidden color={resolvedBorderColor}>\n                  {resolvedSeparator}\n                </Text>\n              )}\n            </Box>\n          );\n        })}\n      </Box>\n      <Box\n        borderStyle={resolveBorderStyle(borderStyle, unicode)}\n        borderColor={resolvedBorderColor}\n        paddingX={paddingX}\n        paddingY={paddingY}\n      >\n        {activeTab?.content}\n      </Box>\n    </Box>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}