{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "folder-carry",
  "title": "Folder carry",
  "description": "Continuous folder transport with shared tab contact geometry.",
  "registryDependencies": [
    "@jbm/tokens",
    "@jbm/scene-geometry",
    "@jbm/folder"
  ],
  "files": [
    {
      "path": "registry/jbm/ui/folder-carry.tsx",
      "content": "import { FolderOutline } from \"./folder\"\nimport { color, font } from \"../lib/tokens\"\nimport { pointOn, unit, type Pt } from \"../lib/geometry\"\n\nexport type FolderGeometry = {\n  x: number\n  y: number\n  w: number\n  h: number\n  tabX: number\n  tabWidth: number\n  tabHeight?: number\n  tabSlope?: number\n}\nexport const folderGrip = (g: FolderGeometry): Pt => ({\n  x: g.tabX + g.tabWidth / 2,\n  y: g.y + ((g.tabHeight ?? 27) * 10) / 27,\n})\nexport const tableFolderGeometry = (at: Pt, width: number): FolderGeometry => {\n  const k = width / 260\n  return {\n    x: at.x + 25 * k,\n    y: at.y + 55 * k,\n    w: 205 * k,\n    h: 150 * k,\n    tabX: at.x + 25 * k,\n    tabWidth: 83 * k,\n    tabHeight: 27 * k,\n    tabSlope: 17 * k,\n  }\n}\n/** Shared geometry for the object and its hand. A caller can supply drawer geometry directly. */\nexport function carriedFolderGeometry(\n  from: FolderGeometry,\n  to: FolderGeometry,\n  path: readonly Pt[],\n  progress: number\n): FolderGeometry {\n  const p = unit(progress),\n    at = pointOn(path, p)\n  const mix = (a: number, b: number) => a + (b - a) * p\n  const tabWidth = mix(from.tabWidth, to.tabWidth)\n  const tabHeight = mix(from.tabHeight ?? 27, to.tabHeight ?? 27)\n  const dx = mix(from.tabX - from.x, to.tabX - to.x)\n  const x = at.x - tabWidth / 2 - dx\n  return {\n    x,\n    y: at.y - (tabHeight * 10) / 27,\n    w: mix(from.w, to.w),\n    h: mix(from.h, to.h),\n    tabX: x + dx,\n    tabWidth,\n    tabHeight,\n    tabSlope: mix(from.tabSlope ?? 17, to.tabSlope ?? 17),\n  }\n}\n/** Render inside an SVG. Uses the same full silhouette as Folder and Cajon. */\nexport function FolderCarry({\n  from,\n  to,\n  path,\n  progress,\n  label,\n}: {\n  from: FolderGeometry\n  to: FolderGeometry\n  path: readonly Pt[]\n  progress: number\n  label?: string\n}) {\n  const g = carriedFolderGeometry(from, to, path, progress)\n  const p = unit(progress),\n    k = (g.tabHeight ?? 27) / 27\n  return (\n    <g\n      role={label ? \"img\" : undefined}\n      aria-label={label ? `Carrying ${label}` : undefined}\n    >\n      <FolderOutline {...g} />\n      <path\n        d={`M${g.x} ${g.y + 40 * k}H${g.x + g.w}`}\n        stroke={color.ink}\n        strokeWidth={2}\n      />\n      {label && (\n        <g>\n          <text\n            x={g.tabX + 8 * k}\n            y={g.y + 15 * k}\n            fontFamily={font.mono}\n            fontSize={9 * k}\n            fill={color.bg}\n            opacity={1 - p}\n          >\n            {label.slice(0, 16)}\n          </text>\n          <text\n            x={g.x + 21 * k}\n            y={g.y + g.h - 25 * k}\n            fontFamily={font.mono}\n            fontSize={16 * k}\n            fontWeight={600}\n            fill={color.bg}\n            opacity={p}\n          >\n            {label.slice(0, 16)}\n          </text>\n        </g>\n      )}\n    </g>\n  )\n}\n",
      "type": "registry:ui",
      "target": "src/jbm/ui/folder-carry.tsx"
    }
  ],
  "type": "registry:ui"
}