{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/usage-monitor",
  "title": "Usage Monitor",
  "description": "Real-time dashboard with diamond header, progress metrics, burn rate, predictions, and status bar",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/use-interval.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/usage-monitor.tsx",
      "content": "import { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useInterval } from \"@/hooks/use-interval\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\n\nexport interface UsageMonitorProps {\n  refreshInterval?: number;\n  separatorChar?: string;\n  children: ReactNode;\n  isActive?: boolean;\n  \"aria-label\"?: string;\n}\n\nexport interface UsageMonitorHeaderProps {\n  title: string;\n  titleColor?: string;\n  decorator?: string;\n  separatorChar?: string;\n  separatorColor?: string;\n}\n\nexport interface UsageMonitorTagsProps {\n  items: string[];\n  bracketColor?: string;\n  separatorColor?: string;\n}\n\nexport interface UsageMonitorSectionProps {\n  icon?: string;\n  title?: string;\n  subtitle?: string;\n  children: ReactNode;\n}\n\nexport interface UsageMonitorMetricProps {\n  icon?: string;\n  label: string;\n  value: number;\n  max: number;\n  percent: number;\n  status: \"green\" | \"yellow\" | \"red\" | \"blue\";\n  format: \"number\" | \"currency\" | \"duration\" | \"percent\" | \"custom\";\n  formatFn?: (v: number, max: number) => string;\n  barWidth?: number;\n  barFillChar?: string;\n  barEmptyChar?: string;\n  barColor?: string;\n  maxDim?: boolean;\n  showMax?: boolean;\n}\n\nexport interface UsageMonitorDistributionMetricProps {\n  icon?: string;\n  label: string;\n  segments: { label: string; percent: number; color?: string }[];\n  barWidth?: number;\n}\n\nexport interface UsageMonitorStatRowProps {\n  icon?: string;\n  label: string;\n  value: string;\n  valueSuffix?: string;\n  valueColor?: string;\n}\n\nexport interface UsageMonitorPredictionProps {\n  label: string;\n  value: string;\n  valueColor?: string;\n}\n\nexport interface UsageMonitorStatusBarProps {\n  clock?: boolean;\n  clockColor?: string;\n  sessionLabel?: string;\n  sessionColor?: string;\n  exitHint?: string;\n  statusDot?: \"green\" | \"yellow\" | \"red\";\n  separator?: string;\n}\n\nconst formatValue = (\n  value: number,\n  max: number,\n  format: UsageMonitorMetricProps[\"format\"],\n  formatFn?: (v: number, max: number) => string\n): { current: string; maxStr: string } => {\n  if (format === \"custom\" && formatFn) {\n    const result = formatFn(value, max);\n    return { current: result, maxStr: \"\" };\n  }\n  switch (format) {\n    case \"currency\": {\n      return { current: `$${value.toFixed(2)}`, maxStr: `$${max.toFixed(2)}` };\n    }\n    case \"duration\": {\n      const h = Math.floor(value / 60);\n      const m = value % 60;\n      const maxH = Math.floor(max / 60);\n      const maxM = max % 60;\n      return {\n        current: h > 0 ? `${h}h ${m}m` : `${m}m`,\n        maxStr: maxH > 0 ? `${maxH}h ${maxM}m` : `${maxM}m`,\n      };\n    }\n    case \"percent\": {\n      return { current: `${value.toFixed(1)}%`, maxStr: `${max.toFixed(1)}%` };\n    }\n    case \"number\": {\n      return {\n        current: value.toLocaleString(),\n        maxStr: max.toLocaleString(),\n      };\n    }\n    default: {\n      return { current: String(value), maxStr: String(max) };\n    }\n  }\n};\n\nconst statusDotChar = (\n  status: \"green\" | \"yellow\" | \"red\",\n  unicode: boolean\n): {\n  char: string;\n  color: string;\n} => {\n  switch (status) {\n    case \"green\": {\n      return { char: unicode ? \"●\" : \"o\", color: \"green\" };\n    }\n    case \"yellow\": {\n      return { char: unicode ? \"◕\" : \"!\", color: \"yellow\" };\n    }\n    case \"red\": {\n      return { char: unicode ? \"○\" : \"x\", color: \"red\" };\n    }\n    default: {\n      return { char: unicode ? \"●\" : \"o\", color: \"green\" };\n    }\n  }\n};\n\nconst UsageMonitorRoot = ({\n  refreshInterval = 1000,\n  separatorChar,\n  children,\n  isActive = true,\n  \"aria-label\": ariaLabel = \"Usage monitor\",\n}: UsageMonitorProps) => {\n  const unicode = useUnicode();\n  const resolvedSeparatorChar = separatorChar ?? (unicode ? \"─\" : \"-\");\n  const [, setTick] = useState(0);\n  useInterval(() => setTick((t) => t + 1), refreshInterval, { isActive });\n\n  return (\n    <Box flexDirection=\"column\" aria-role=\"list\">\n      <Text aria-label={ariaLabel}>{\"\"}</Text>\n      {React.Children.map(children, (child, i) => (\n        <React.Fragment key={i}>\n          {child}\n          {i < React.Children.count(children) - 1 &&\n            React.isValidElement(child) &&\n            (child.type as unknown as { displayName?: string }).displayName !==\n              \"UsageMonitor.StatusBar\" && (\n              <Text aria-hidden dimColor>\n                {resolvedSeparatorChar.repeat(44)}\n              </Text>\n            )}\n        </React.Fragment>\n      ))}\n    </Box>\n  );\n};\n\nconst UsageMonitorHeader = ({\n  title,\n  titleColor = \"cyan\",\n  decorator,\n  separatorChar,\n  separatorColor,\n}: UsageMonitorHeaderProps) => {\n  const unicode = useUnicode();\n  const resolvedDecorator = decorator ?? (unicode ? \"◆ ✦ ◆ ✦\" : \"* + * +\");\n  const resolvedSeparatorChar = separatorChar ?? (unicode ? \"═\" : \"=\");\n  return (\n    <Box flexDirection=\"column\">\n      <Box flexDirection=\"row\" justifyContent=\"center\">\n        <Text dimColor>{`${resolvedDecorator}  `}</Text>\n        <Text color={titleColor} bold>\n          {title}\n        </Text>\n        <Text dimColor>{`  ${resolvedDecorator}`}</Text>\n      </Box>\n      <Text color={separatorColor} dimColor={!separatorColor}>\n        {resolvedSeparatorChar.repeat(44)}\n      </Text>\n    </Box>\n  );\n};\nUsageMonitorHeader.displayName = \"UsageMonitor.Header\";\n\nconst UsageMonitorTags = ({\n  items,\n  bracketColor,\n  separatorColor,\n}: UsageMonitorTagsProps) => (\n  <Box flexDirection=\"row\" marginBottom={1}>\n    <Text color={bracketColor ?? \"cyan\"}>{\"[ \"}</Text>\n    {items.map((item, i) => (\n      <React.Fragment key={i}>\n        <Text>{item}</Text>\n        {i < items.length - 1 && (\n          <Text color={separatorColor ?? \"cyan\"}>{\" | \"}</Text>\n        )}\n      </React.Fragment>\n    ))}\n    <Text color={bracketColor ?? \"cyan\"}>{\" ]\"}</Text>\n  </Box>\n);\n\nconst UsageMonitorSection = ({\n  icon,\n  title,\n  subtitle,\n  children,\n}: UsageMonitorSectionProps) => (\n  <Box flexDirection=\"column\" marginBottom={1}>\n    {(icon || title) && (\n      <Box flexDirection=\"row\" gap={1}>\n        {icon && <Text>{icon}</Text>}\n        {title && <Text bold>{title}</Text>}\n      </Box>\n    )}\n    {subtitle && <Text dimColor>{`    ${subtitle}`}</Text>}\n    <Box flexDirection=\"column\" paddingTop={1}>\n      {children}\n    </Box>\n  </Box>\n);\n\nconst UsageMonitorMetric = ({\n  icon,\n  label,\n  value,\n  max,\n  percent,\n  status,\n  format,\n  formatFn,\n  barWidth = 22,\n  barFillChar,\n  barEmptyChar,\n  barColor = \"white\",\n  maxDim = false,\n  showMax = true,\n}: UsageMonitorMetricProps) => {\n  const unicode = useUnicode();\n  const resolvedBarFillChar = barFillChar ?? (unicode ? \"█\" : \"#\");\n  const resolvedBarEmptyChar = barEmptyChar ?? (unicode ? \"░\" : \".\");\n  const normalizedPercent = Math.max(0, Math.min(100, percent));\n  const filled = Math.round((normalizedPercent / 100) * barWidth);\n  const empty = barWidth - filled;\n  const bar =\n    resolvedBarFillChar.repeat(filled) + resolvedBarEmptyChar.repeat(empty);\n  const dot = statusDotChar(status as \"green\" | \"yellow\" | \"red\", unicode);\n  const { current, maxStr } = formatValue(value, max, format, formatFn);\n\n  return (\n    <Box\n      flexDirection=\"row\"\n      gap={1}\n      aria-role=\"progressbar\"\n      aria-label={`${label}: ${normalizedPercent.toFixed(1)}%, ${current}${showMax && maxStr ? ` of ${maxStr}` : \"\"}, status ${status}.`}\n      aria-state={{ busy: normalizedPercent < 100 }}\n    >\n      {icon && <Text>{icon}</Text>}\n      <Text dimColor>{label.padEnd(16)}</Text>\n      <Text aria-hidden color={dot.color}>\n        {dot.char}\n      </Text>\n      <Text aria-hidden color={barColor}>{`[${bar}]`}</Text>\n      <Text>{` ${normalizedPercent.toFixed(1)}%`}</Text>\n      <Text>{` ${current}`}</Text>\n      {showMax && maxStr && <Text dimColor={maxDim}>{`/ ${maxStr}`}</Text>}\n    </Box>\n  );\n};\n\nconst UsageMonitorDistributionMetric = ({\n  icon,\n  label,\n  segments,\n  barWidth = 22,\n}: UsageMonitorDistributionMetricProps) => {\n  const unicode = useUnicode();\n  const bars = segments.map((seg) => {\n    const count = Math.round((seg.percent / 100) * barWidth);\n    return { ...seg, count };\n  });\n\n  return (\n    <Box\n      flexDirection=\"row\"\n      gap={1}\n      aria-role=\"listitem\"\n      aria-label={`${label}: ${segments.map((segment) => `${segment.label} ${segment.percent}%`).join(\", \")}`}\n    >\n      {icon && <Text>{icon}</Text>}\n      <Text dimColor>{label.padEnd(16)}</Text>\n      <Text>[</Text>\n      {bars.map((seg, i) => (\n        <Text key={i} color={seg.color ?? \"white\"}>\n          {(unicode ? \"█\" : \"#\").repeat(seg.count)}\n        </Text>\n      ))}\n      <Text>]</Text>\n      {segments.map((seg, i) => (\n        <Text key={i} color={seg.color ?? \"white\"}>\n          {`${seg.label} ${seg.percent}%`}\n          {i < segments.length - 1 ? \" | \" : \"\"}\n        </Text>\n      ))}\n    </Box>\n  );\n};\n\nconst UsageMonitorStats = ({ children }: { children: ReactNode }) => (\n  <Box flexDirection=\"column\" aria-role=\"list\">\n    {children}\n  </Box>\n);\n\nconst UsageMonitorStatRow = ({\n  icon,\n  label,\n  value,\n  valueSuffix,\n  valueColor = \"white\",\n}: UsageMonitorStatRowProps) => (\n  <Box\n    flexDirection=\"row\"\n    gap={1}\n    aria-role=\"listitem\"\n    aria-label={`${label}: ${value}${valueSuffix ? ` ${valueSuffix}` : \"\"}`}\n  >\n    {icon && <Text>{icon}</Text>}\n    <Text dimColor>{label.padEnd(16)}</Text>\n    <Text color={valueColor}>{value}</Text>\n    {valueSuffix && <Text>{` ${valueSuffix}`}</Text>}\n  </Box>\n);\n\nconst UsageMonitorPredictions = ({ children }: { children: ReactNode }) => {\n  const unicode = useUnicode();\n  return (\n    <Box flexDirection=\"column\">\n      <Text bold>{unicode ? \"🔮 Predictions:\" : \"Predictions:\"}</Text>\n      <Box flexDirection=\"column\" paddingLeft={4}>\n        {children}\n      </Box>\n    </Box>\n  );\n};\n\nconst UsageMonitorPrediction = ({\n  label,\n  value,\n  valueColor = \"yellow\",\n}: UsageMonitorPredictionProps) => (\n  <Box flexDirection=\"row\">\n    <Text dimColor>{label.padEnd(24)}</Text>\n    <Text color={valueColor}>{value}</Text>\n  </Box>\n);\n\nconst UsageMonitorStatusBar = ({\n  clock = false,\n  clockColor,\n  sessionLabel,\n  sessionColor = \"green\",\n  exitHint,\n  statusDot: dot = \"green\",\n  separator = \" | \",\n}: UsageMonitorStatusBarProps) => {\n  const unicode = useUnicode();\n  const [time, setTime] = useState(() => new Date().toTimeString().slice(0, 8));\n  useInterval(() => setTime(new Date().toTimeString().slice(0, 8)), 1000);\n\n  const dotInfo = statusDotChar(dot, unicode);\n\n  return (\n    <Box flexDirection=\"row\" paddingTop={1}>\n      {clock && (\n        <Text color={clockColor ?? \"cyan\"}>\n          {unicode ? `⏰ ${time}` : `time ${time}`}\n        </Text>\n      )}\n      {sessionLabel && (\n        <Text color={sessionColor}>\n          {unicode\n            ? `${separator}📄 ${sessionLabel}`\n            : `${separator}session ${sessionLabel}`}\n        </Text>\n      )}\n      {exitHint && <Text dimColor>{`${separator}${exitHint}`}</Text>}\n      <Text color={dotInfo.color}>{`${separator}${dotInfo.char}`}</Text>\n    </Box>\n  );\n};\nUsageMonitorStatusBar.displayName = \"UsageMonitor.StatusBar\";\n\nexport const UsageMonitor = Object.assign(UsageMonitorRoot, {\n  DistributionMetric: UsageMonitorDistributionMetric,\n  Header: UsageMonitorHeader,\n  Metric: UsageMonitorMetric,\n  Prediction: UsageMonitorPrediction,\n  Predictions: UsageMonitorPredictions,\n  Section: UsageMonitorSection,\n  StatRow: UsageMonitorStatRow,\n  Stats: UsageMonitorStats,\n  StatusBar: UsageMonitorStatusBar,\n  Tags: UsageMonitorTags,\n});\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "templates"
  ],
  "type": "registry:ui"
}