{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "terminal-window",
  "title": "Terminal Window",
  "description": "Window chrome in three conventions: macOS, Windows, or a slim hairline version. Resizable and collapsible.",
  "registryDependencies": [
    "@afterglow/theme"
  ],
  "files": [
    {
      "path": "registry/terminal/components/terminal-window.tsx",
      "content": "\"use client\";\n\nimport {\n  type PointerEvent as ReactPointerEvent,\n  useCallback,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst MIN_WIDTH = 260;\nconst MIN_HEIGHT = 160;\nconst KEY_STEP = 24;\n\nconst MARKS = {\n  close: [\n    \"absolute inset-x-[8%] inset-y-[calc(50%-0.5px)] rotate-45 bg-current\",\n    \"-rotate-45 absolute inset-x-[8%] inset-y-[calc(50%-0.5px)] bg-current\",\n  ],\n  collapse: [\"absolute inset-x-[12%] inset-y-[calc(50%-0.5px)] bg-current\"],\n  zoom: [\n    \"absolute inset-x-[12%] inset-y-[calc(50%-0.5px)] bg-current\",\n    \"absolute inset-x-[calc(50%-0.5px)] inset-y-[12%] bg-current\",\n  ],\n} as const;\n\ntype LightName = keyof typeof MARKS;\ntype Variant = \"macos\" | \"windows\" | \"terminal\";\n\nconst LIGHTS: { name: LightName; label: string; color: string }[] = [\n  { color: \"var(--window-close)\", label: \"Close\", name: \"close\" },\n  { color: \"var(--window-collapse)\", label: \"Collapse\", name: \"collapse\" },\n  { color: \"var(--window-zoom)\", label: \"Zoom\", name: \"zoom\" },\n];\n\nconst CHROME: Record<\n  Variant,\n  {\n    frame: string;\n    bar: string;\n    barTall: string;\n    plate: string;\n    subtitle: string;\n    title: string;\n    content: string;\n    footer: string;\n\n    edgeX: string;\n    edgeY: string;\n    corner: string;\n    grip: string;\n    control: string;\n    markBox: string;\n  }\n> = {\n  macos: {\n    bar: \"border-window-rule bg-window-titlebar p-1.5 min-h-9 justify-between\",\n    barTall: \"min-h-[3.25rem]\",\n    content: \"m-2 border-2 border-window-inset [border-style:inset]\",\n    control:\n      \"relative block size-[0.72rem] border border-window-control p-0 text-transparent transition-colors group-hover/titlebar:text-window-text/72\",\n    corner: \"-right-[3px] -bottom-[3px] size-3.5\",\n    edgeX: \"-right-[3px] w-2\",\n    edgeY: \"-bottom-[3px] h-2\",\n    footer:\n      \"min-h-7 px-2.5 pr-6 text-[0.48rem] border-window-footer text-window-footer-text\",\n    frame:\n      \"border-[3px] border-window-border border-double bg-window-surface text-window-text shadow-window\",\n    grip: \"bg-window-grip\",\n    markBox: \"absolute inset-0\",\n    plate:\n      \"bg-window-surface px-2 py-0.5 shrink justify-items-center text-center\",\n    subtitle: \"text-window-text-muted\",\n    title: \"font-bold text-[0.82rem]\",\n  },\n  terminal: {\n    bar: \"border-line bg-secondary px-2 py-1 min-h-7\",\n    barTall: \"min-h-11\",\n    content: \"\",\n    control:\n      \"relative grid size-[0.9rem] place-items-center border border-line bg-panel p-0 text-phosphor-dim transition-colors hover:border-line-strong hover:text-phosphor-bright\",\n    corner: \"-right-px -bottom-px size-3\",\n    edgeX: \"-right-px w-1.5\",\n    edgeY: \"-bottom-px h-1.5\",\n\n    footer: \"min-h-5 px-2 pr-5 text-[0.4375rem] border-line text-phosphor-dim\",\n    frame: \"border border-line bg-panel text-foreground shadow-panel\",\n    grip: \"bg-[repeating-linear-gradient(-45deg,transparent_0_2px,var(--phosphor-dim)_2px_3px)]\",\n    markBox: \"relative block size-[0.5rem]\",\n    plate: \"mr-auto justify-items-start\",\n    subtitle: \"text-phosphor-dim\",\n    title:\n      \"font-semibold text-1xs text-phosphor-bright uppercase tracking-terminal-lg\",\n  },\n  windows: {\n    bar: \"border-window-rule bg-window-titlebar p-1.5 min-h-9\",\n    barTall: \"min-h-[3.25rem]\",\n    content: \"m-2 border-2 border-window-inset [border-style:inset]\",\n    control:\n      \"relative grid size-[1.15rem] place-items-center border border-t-window-bevel-light border-r-window-bevel-dark border-b-window-bevel-dark border-l-window-bevel-light bg-window-surface p-0 text-window-rule active:border-t-window-bevel-dark active:border-r-window-bevel-light active:border-b-window-bevel-light active:border-l-window-bevel-dark\",\n\n    corner: \"-right-[3px] -bottom-[3px] size-3.5\",\n    edgeX: \"-right-[3px] w-2\",\n    edgeY: \"-bottom-[3px] h-2\",\n    footer:\n      \"min-h-7 px-2.5 pr-6 text-[0.48rem] border-window-footer text-window-footer-text\",\n    frame:\n      \"border-[3px] border-window-border border-double bg-window-surface text-window-text shadow-window\",\n    grip: \"bg-window-grip\",\n    markBox: \"relative block size-[0.55rem]\",\n    plate: \"bg-window-surface px-2 py-0.5 mr-auto justify-items-start\",\n    subtitle: \"text-window-text-muted\",\n    title: \"font-bold text-[0.82rem]\",\n  },\n};\n\nfunction Marks({ name }: { name: LightName }) {\n  return (\n    <>\n      {MARKS[name].map((mark) => (\n        <span className={mark} key={mark} />\n      ))}\n    </>\n  );\n}\n\nfunction Light({\n  light,\n  action,\n  className,\n  children,\n}: {\n  light: (typeof LIGHTS)[number];\n  action?: () => void;\n  className: string;\n  children: React.ReactNode;\n}) {\n  if (!action) {\n    return (\n      <span aria-hidden=\"true\" className={className} title={light.label}>\n        {children}\n      </span>\n    );\n  }\n\n  return (\n    <button\n      aria-label={light.label}\n      className={cn(className, \"cursor-pointer\")}\n      onClick={action}\n      title={light.label}\n      type=\"button\"\n    >\n      {children}\n    </button>\n  );\n}\n\nfunction Controls({\n  variant,\n  action,\n}: {\n  variant: Variant;\n  action: Record<LightName, (() => void) | undefined>;\n}) {\n  const chrome = CHROME[variant];\n\n  return (\n    <>\n      {LIGHTS.map((light) => (\n        <Light\n          action={action[light.name]}\n          className={chrome.control}\n          key={light.name}\n          light={light}\n        >\n          {variant === \"macos\" && (\n            <span\n              aria-hidden=\"true\"\n              className=\"absolute inset-0\"\n              style={{ background: light.color }}\n            />\n          )}\n          <span className={chrome.markBox}>\n            <Marks name={light.name} />\n          </span>\n        </Light>\n      ))}\n    </>\n  );\n}\n\nfunction TerminalWindow({\n  className,\n  children,\n  title,\n  subtitle,\n  footer,\n  variant = \"macos\",\n  resizable = false,\n  collapsible = false,\n  onClose,\n  onZoom,\n  style,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  title: string;\n  subtitle?: string;\n  footer?: React.ReactNode;\n  variant?: Variant;\n  resizable?: boolean;\n  collapsible?: boolean;\n  onClose?: () => void;\n  onZoom?: () => void;\n}) {\n  const frame = useRef<HTMLDivElement>(null);\n  const [size, setSize] = useState<{ width: number; height: number } | null>(\n    null\n  );\n  const [collapsed, setCollapsed] = useState(false);\n  const chrome = CHROME[variant];\n\n  const startResize = useCallback((event: ReactPointerEvent<HTMLElement>) => {\n    const axis = event.currentTarget.dataset.axis as \"x\" | \"y\" | \"both\";\n    const box = frame.current?.getBoundingClientRect();\n    if (!box) {\n      return;\n    }\n\n    event.preventDefault();\n    const handle = event.currentTarget;\n    const originX = event.clientX;\n    const originY = event.clientY;\n\n    try {\n      handle.setPointerCapture(event.pointerId);\n    } catch {\n      // Not every pointer allows capture; the listeners below still work.\n    }\n\n    const move = (moved: PointerEvent) => {\n      setSize({\n        height:\n          axis === \"x\"\n            ? box.height\n            : Math.max(MIN_HEIGHT, box.height + moved.clientY - originY),\n        width:\n          axis === \"y\"\n            ? box.width\n            : Math.max(MIN_WIDTH, box.width + moved.clientX - originX),\n      });\n    };\n\n    const stop = () => {\n      handle.removeEventListener(\"pointermove\", move);\n      handle.removeEventListener(\"pointerup\", stop);\n      handle.removeEventListener(\"pointercancel\", stop);\n    };\n\n    handle.addEventListener(\"pointermove\", move);\n    handle.addEventListener(\"pointerup\", stop);\n    handle.addEventListener(\"pointercancel\", stop);\n  }, []);\n\n  const nudge = useCallback((event: React.KeyboardEvent<HTMLElement>) => {\n    const step = {\n      ArrowDown: [0, KEY_STEP],\n      ArrowLeft: [-KEY_STEP, 0],\n      ArrowRight: [KEY_STEP, 0],\n      ArrowUp: [0, -KEY_STEP],\n    }[event.key];\n    const box = frame.current?.getBoundingClientRect();\n\n    if (!(step && box)) {\n      return;\n    }\n\n    event.preventDefault();\n    setSize({\n      height: Math.max(MIN_HEIGHT, box.height + step[1]),\n      width: Math.max(MIN_WIDTH, box.width + step[0]),\n    });\n  }, []);\n\n  const toggleCollapsed = useCallback(() => setCollapsed((open) => !open), []);\n  const action: Record<LightName, (() => void) | undefined> = {\n    close: onClose,\n    collapse: collapsible ? toggleCollapsed : undefined,\n    zoom: onZoom,\n  };\n\n  return (\n    <div\n      className={cn(\n        \"relative grid\",\n        chrome.frame,\n        collapsed\n          ? \"!h-fit grid-rows-[auto]\"\n          : \"grid-rows-[auto_minmax(0,1fr)_auto]\",\n\n        variant === \"terminal\" && resizable && !(collapsed || footer) && \"pb-3\",\n        className\n      )}\n      data-collapsed={collapsed || undefined}\n      data-slot=\"terminal-window\"\n      data-variant={variant}\n      ref={frame}\n      style={size && !collapsed ? { ...style, ...size } : style}\n      {...props}\n    >\n      <div\n        className={cn(\n          \"group/titlebar flex select-none items-center gap-2\",\n          chrome.bar,\n\n          subtitle && chrome.barTall,\n          collapsed ? \"border-b-0\" : \"border-b-2\",\n          variant === \"terminal\" && !collapsed && \"border-b\"\n        )}\n        data-slot=\"terminal-window-titlebar\"\n      >\n        {variant === \"macos\" && (\n          <div className=\"flex justify-self-start gap-px bg-window-surface p-1\">\n            <Controls action={action} variant={variant} />\n          </div>\n        )}\n\n        <div className={cn(\"grid min-w-0 gap-0.5\", chrome.plate)}>\n          {subtitle ? (\n            <span\n              className={cn(\n                \"font-bold font-mono text-[0.48rem] uppercase tracking-terminal-lg\",\n                chrome.subtitle\n              )}\n            >\n              {subtitle}\n            </span>\n          ) : null}\n          <h2\n            className={cn(\n              \"max-w-full truncate font-mono leading-tight\",\n              chrome.title\n            )}\n          >\n            {title}\n          </h2>\n        </div>\n\n        {/* Mirrors the controls to keep the macOS title centered. */}\n        {variant === \"macos\" && (\n          <div aria-hidden=\"true\" className=\"w-[3.1rem] shrink-0\" />\n        )}\n        {variant === \"windows\" && (\n          <div className=\"-my-1.5 -mr-1.5 flex items-center gap-0.5 self-stretch px-1.5\">\n            <Controls action={action} variant={variant} />\n          </div>\n        )}\n        {variant === \"terminal\" && (\n          <div className=\"flex items-center gap-1\">\n            <Controls action={action} variant={variant} />\n          </div>\n        )}\n      </div>\n\n      {!collapsed && (\n        <>\n          <div\n            className={cn(\n              \"min-h-0 overflow-auto overscroll-contain bg-panel-sunken text-phosphor-bright\",\n              chrome.content\n            )}\n            data-slot=\"terminal-window-content\"\n          >\n            {children}\n          </div>\n\n          {footer ? (\n            <div\n              className={cn(\n                \"flex items-center justify-between gap-4 border-t font-bold font-mono uppercase tracking-terminal-sm\",\n                chrome.footer\n              )}\n              data-slot=\"terminal-window-footer\"\n            >\n              {footer}\n            </div>\n          ) : null}\n        </>\n      )}\n\n      {resizable && !collapsed && (\n        <>\n          {/* Only the corner is keyboard-focusable. */}\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              \"absolute inset-y-0 cursor-ew-resize touch-none\",\n              chrome.edgeX\n            )}\n            data-axis=\"x\"\n            onPointerDown={startResize}\n          />\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              \"absolute inset-x-0 cursor-ns-resize touch-none\",\n              chrome.edgeY\n            )}\n            data-axis=\"y\"\n            onPointerDown={startResize}\n          />\n          <button\n            aria-label=\"Resize window\"\n            className={cn(\n              \"absolute z-1 cursor-nwse-resize touch-none border-0 bg-transparent p-0 outline-none focus-visible:outline-2 focus-visible:outline-phosphor-bright\",\n              chrome.corner\n            )}\n            data-axis=\"both\"\n            onKeyDown={nudge}\n            onPointerDown={startResize}\n            type=\"button\"\n          >\n            <span\n              aria-hidden=\"true\"\n              className={cn(\"block size-full\", chrome.grip)}\n              style={{ clipPath: \"polygon(100% 0, 100% 100%, 0 100%)\" }}\n            />\n          </button>\n        </>\n      )}\n    </div>\n  );\n}\n\nexport { TerminalWindow };\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}