{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/multi-progress",
  "title": "Multi Progress",
  "description": "Parallel progress display for package managers, build pipelines, and deploy tools",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/terminal-text.json",
    "https://termcn.dev/r/ink/use-theme.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/multi-progress.tsx",
      "content": "import { Box, Text } from \"ink\";\n\nimport { useTheme } from \"@/hooks/use-theme\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\nimport {\n  padToTerminalWidth,\n  truncateToTerminalWidth,\n} from \"@/lib/terminal-text\";\n\nexport type MultiProgressStatus = \"pending\" | \"running\" | \"done\" | \"error\";\n\nexport interface MultiProgressItem {\n  id: string;\n  label: string;\n  value: number;\n  total: number;\n  status?: MultiProgressStatus;\n  statusText?: string;\n}\n\nexport interface MultiProgressProps {\n  items: MultiProgressItem[];\n  barWidth?: number;\n  labelWidth?: number;\n  compact?: boolean;\n  showPercent?: boolean;\n  \"aria-label\"?: string;\n}\n\nconst truncate = (s: string, n: number, ellipsis: string): string =>\n  padToTerminalWidth(truncateToTerminalWidth(s, n, ellipsis), n);\n\nexport const MultiProgress = ({\n  items,\n  barWidth = 20,\n  labelWidth = 20,\n  compact = false,\n  showPercent = true,\n  \"aria-label\": ariaLabel = \"Progress tasks\",\n}: MultiProgressProps) => {\n  const theme = useTheme();\n  const unicode = useUnicode();\n\n  const statusColor = (status: MultiProgressStatus | undefined): string => {\n    switch (status) {\n      case \"done\": {\n        return theme.colors.success;\n      }\n      case \"error\": {\n        return theme.colors.error;\n      }\n      case \"pending\": {\n        return theme.colors.mutedForeground;\n      }\n      default: {\n        return theme.colors.primary;\n      }\n    }\n  };\n\n  const renderBar = (item: MultiProgressItem): string => {\n    const pct = item.total > 0 ? Math.min(1, item.value / item.total) : 0;\n    const filled = Math.round(pct * barWidth);\n    const empty = barWidth - filled;\n    const fill = unicode ? \"█\" : \"#\";\n    const emptyFill = unicode ? \"░\" : \".\";\n    return `${fill.repeat(filled)}${emptyFill.repeat(empty)}`;\n  };\n\n  return (\n    <Box flexDirection=\"column\" aria-role=\"list\">\n      <Text aria-label={`${ariaLabel}. ${items.length} tasks.`}>{\"\"}</Text>\n      {items.map((item) => {\n        const pct =\n          item.total > 0\n            ? Math.min(100, Math.round((item.value / item.total) * 100))\n            : 0;\n        const color = statusColor(item.status);\n        const bar = renderBar(item);\n        const label = truncate(item.label, labelWidth, unicode ? \"…\" : \"...\");\n\n        if (compact) {\n          return (\n            <Box\n              key={item.id}\n              flexDirection=\"row\"\n              gap={1}\n              aria-role=\"progressbar\"\n              aria-label={`${item.label}: ${pct}%. ${item.statusText ?? item.status ?? \"running\"}.`}\n              aria-state={{\n                busy: item.status === \"running\" || item.status === undefined,\n              }}\n            >\n              <Text color={color}>{label}</Text>\n              <Text color={theme.colors.mutedForeground}>[</Text>\n              <Text color={color}>{bar}</Text>\n              <Text color={theme.colors.mutedForeground}>]</Text>\n              {showPercent && (\n                <Text color={theme.colors.mutedForeground}>\n                  {String(pct).padStart(3)}%\n                </Text>\n              )}\n              {item.statusText && (\n                <Text color={theme.colors.mutedForeground} dimColor>\n                  {item.statusText}\n                </Text>\n              )}\n            </Box>\n          );\n        }\n\n        return (\n          <Box\n            key={item.id}\n            flexDirection=\"column\"\n            marginBottom={0}\n            aria-role=\"progressbar\"\n            aria-label={`${item.label}: ${pct}%. ${item.statusText ?? item.status ?? \"running\"}.`}\n            aria-state={{\n              busy: item.status === \"running\" || item.status === undefined,\n            }}\n          >\n            <Box flexDirection=\"row\" gap={1}>\n              <Text bold color={color}>\n                {label}\n              </Text>\n              {item.statusText && (\n                <Text color={theme.colors.mutedForeground} dimColor>\n                  {item.statusText}\n                </Text>\n              )}\n            </Box>\n            <Box flexDirection=\"row\" gap={1}>\n              <Text color={theme.colors.mutedForeground}>[</Text>\n              <Text color={color}>{bar}</Text>\n              <Text color={theme.colors.mutedForeground}>]</Text>\n              {showPercent && (\n                <Text color={theme.colors.mutedForeground}>\n                  {String(pct).padStart(3)}%\n                </Text>\n              )}\n            </Box>\n          </Box>\n        );\n      })}\n    </Box>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}