{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "canvas-shell",
  "type": "registry:component",
  "title": "Canvas Shell",
  "description": "Layout shell for canvas workspaces with top bar, left rail, right dock, and bottom slot regions.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/canvas-shell/canvas-shell.tsx",
      "content": "import { forwardRef } from \"react\";\n\nimport type { CSSProperties, ReactNode } from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\nimport type { CanvasShellInsets } from \"./canvas-shell-route-config\";\n\nexport type CanvasShellProps = React.ComponentPropsWithoutRef<\"section\"> & {\n  bottomBar?: ReactNode;\n  bottomSlot?: ReactNode;\n  children?: ReactNode;\n  chromeInset?: number | string;\n  contentPadding?: CanvasShellInsets;\n  leftBar?: ReactNode;\n  leftRail?: ReactNode;\n  rightBar?: ReactNode;\n  rightDock?: ReactNode;\n  topBar?: ReactNode;\n};\n\ntype CanvasShellChromeProps = {\n  bottomBar?: ReactNode;\n  inset: string;\n  leftBar?: ReactNode;\n  rightBar?: ReactNode;\n  topBar?: ReactNode;\n};\n\ntype CanvasShellSafeAreaStyle = CSSProperties & {\n  \"--canvas-shell-safe-bottom\": string;\n  \"--canvas-shell-safe-left\": string;\n  \"--canvas-shell-safe-right\": string;\n  \"--canvas-shell-safe-top\": string;\n};\n\nfunction toInsetValue(value: number | string | undefined) {\n  if (typeof value === \"number\") {\n    return `${value}px`;\n  }\n\n  return value;\n}\n\nconst FLOATING_BOTTOM_BAR_FOOTPRINT = \"3.5rem\";\nconst FLOATING_LEFT_BAR_FOOTPRINT = \"4.5rem\";\nconst FLOATING_RIGHT_BAR_FOOTPRINT = \"18rem\";\nconst FLOATING_TOP_BAR_FOOTPRINT = \"3.5rem\";\n\nfunction getReservedInset(\n  inset: string,\n  footprint: string,\n  override: number | string | undefined,\n) {\n  return toInsetValue(override) ?? `calc(${inset} + ${footprint})`;\n}\n\nfunction getSafeAreaInsets({\n  chromeInset,\n  contentPadding,\n  hasBottomBar,\n  hasLeftBar,\n  hasRightBar,\n  hasTopBar,\n}: {\n  chromeInset: number | string;\n  contentPadding?: CanvasShellInsets;\n  hasBottomBar: boolean;\n  hasLeftBar: boolean;\n  hasRightBar: boolean;\n  hasTopBar: boolean;\n}) {\n  const inset = toInsetValue(chromeInset) ?? \"16px\";\n\n  return {\n    bottom: hasBottomBar\n      ? getReservedInset(\n          inset,\n          FLOATING_BOTTOM_BAR_FOOTPRINT,\n          contentPadding?.bottom,\n        )\n      : (toInsetValue(contentPadding?.bottom) ?? inset),\n    left: hasLeftBar\n      ? getReservedInset(\n          inset,\n          FLOATING_LEFT_BAR_FOOTPRINT,\n          contentPadding?.left,\n        )\n      : (toInsetValue(contentPadding?.left) ?? inset),\n    right: hasRightBar\n      ? getReservedInset(\n          inset,\n          FLOATING_RIGHT_BAR_FOOTPRINT,\n          contentPadding?.right,\n        )\n      : (toInsetValue(contentPadding?.right) ?? inset),\n    top: hasTopBar\n      ? getReservedInset(inset, FLOATING_TOP_BAR_FOOTPRINT, contentPadding?.top)\n      : (toInsetValue(contentPadding?.top) ?? inset),\n  };\n}\n\nfunction getSafeAreaStyle(\n  insets: ReturnType<typeof getSafeAreaInsets>,\n): CanvasShellSafeAreaStyle {\n  return {\n    \"--canvas-shell-safe-bottom\": insets.bottom,\n    \"--canvas-shell-safe-left\": insets.left,\n    \"--canvas-shell-safe-right\": insets.right,\n    \"--canvas-shell-safe-top\": insets.top,\n  } satisfies CanvasShellSafeAreaStyle;\n}\n\nconst hasChromeContent = Boolean;\n\ntype CanvasShellChromeAfterProps = Pick<\n  CanvasShellChromeProps,\n  \"bottomBar\" | \"inset\" | \"rightBar\"\n>;\n\nfunction CanvasShellChromeBefore({\n  inset,\n  leftBar,\n  topBar,\n}: Pick<CanvasShellChromeProps, \"inset\" | \"leftBar\" | \"topBar\">) {\n  return (\n    <>\n      {hasChromeContent(topBar) ? (\n        <div\n          className=\"pointer-events-none absolute inset-x-0 z-30\"\n          style={{ top: inset }}\n        >\n          <div\n            className=\"pointer-events-auto mx-auto w-full max-w-[960px]\"\n            style={{ paddingLeft: inset, paddingRight: inset }}\n          >\n            {topBar}\n          </div>\n        </div>\n      ) : null}\n      {hasChromeContent(leftBar) ? (\n        <div\n          className=\"pointer-events-none absolute left-0 z-30 flex\"\n          style={{\n            bottom: \"var(--canvas-shell-safe-bottom)\",\n            left: inset,\n            top: \"var(--canvas-shell-safe-top)\",\n          }}\n        >\n          <div className=\"pointer-events-auto flex\">{leftBar}</div>\n        </div>\n      ) : null}\n    </>\n  );\n}\n\nfunction CanvasShellChromeAfter({\n  bottomBar,\n  inset,\n  rightBar,\n}: CanvasShellChromeAfterProps) {\n  return (\n    <>\n      {hasChromeContent(rightBar) ? (\n        <div\n          className=\"pointer-events-none absolute right-0 z-30 flex\"\n          style={{\n            bottom: \"var(--canvas-shell-safe-bottom)\",\n            right: inset,\n            top: \"var(--canvas-shell-safe-top)\",\n          }}\n        >\n          <div className=\"pointer-events-auto flex\">{rightBar}</div>\n        </div>\n      ) : null}\n      {hasChromeContent(bottomBar) ? (\n        <div\n          className=\"pointer-events-none absolute inset-x-0 z-30\"\n          style={{ bottom: inset }}\n        >\n          <div\n            className=\"pointer-events-auto mx-auto w-full max-w-[960px]\"\n            style={{ paddingLeft: inset, paddingRight: inset }}\n          >\n            {bottomBar}\n          </div>\n        </div>\n      ) : null}\n    </>\n  );\n}\n\nfunction renderLegacyCanvasShell(\n  {\n    bottomBar: _bottomBar,\n    bottomSlot,\n    children,\n    chromeInset: _chromeInset = 16,\n    className,\n    contentPadding: _contentPadding,\n    leftBar: _leftBar,\n    leftRail,\n    rightBar: _rightBar,\n    rightDock,\n    style,\n    topBar,\n    ...props\n  }: CanvasShellProps,\n  ref: React.ForwardedRef<HTMLElement>,\n) {\n  return (\n    <section\n      className={cn(\n        \"flex min-h-[720px] w-full flex-col overflow-hidden rounded-md border border-border bg-background\",\n        className,\n      )}\n      ref={ref}\n      style={style}\n      {...props}\n    >\n      {topBar}\n      <div className=\"grid min-h-0 flex-1 grid-cols-[auto_minmax(0,1fr)_auto] overflow-hidden bg-background\">\n        {leftRail ?? <div />}\n        <div className=\"relative min-h-0 min-w-0 overflow-hidden\">\n          {children}\n        </div>\n        {rightDock ?? <div />}\n      </div>\n      {bottomSlot ? (\n        <div className=\"border-t border-border bg-background px-4 py-2\">\n          {bottomSlot}\n        </div>\n      ) : null}\n    </section>\n  );\n}\n\nfunction renderFloatingContent(\n  children: ReactNode,\n  contentStyle: CSSProperties,\n) {\n  return (\n    <div\n      className=\"relative z-0 h-full w-full min-h-0 min-w-0\"\n      data-slot=\"canvas-shell-content\"\n      style={contentStyle}\n    >\n      <div className=\"h-full w-full min-h-0 min-w-0 overflow-hidden\">\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction renderFloatingCanvasShell(\n  {\n    bottomBar,\n    bottomSlot,\n    children,\n    chromeInset = 16,\n    className,\n    contentPadding,\n    leftBar,\n    leftRail,\n    rightBar,\n    rightDock,\n    style,\n    topBar,\n    ...props\n  }: CanvasShellProps,\n  ref: React.ForwardedRef<HTMLElement>,\n) {\n  const inset = toInsetValue(chromeInset) ?? \"16px\";\n  const resolvedBottomBar = bottomBar ?? bottomSlot;\n  const resolvedLeftBar = leftBar ?? leftRail;\n  const resolvedRightBar = rightBar ?? rightDock;\n\n  const hasTopBar = hasChromeContent(topBar);\n  const hasLeftBar = hasChromeContent(resolvedLeftBar);\n  const hasRightBar = hasChromeContent(resolvedRightBar);\n  const hasBottomBar = hasChromeContent(resolvedBottomBar);\n  const safeAreaInsets = getSafeAreaInsets({\n    chromeInset,\n    contentPadding,\n    hasBottomBar,\n    hasLeftBar,\n    hasRightBar,\n    hasTopBar,\n  });\n  const mergedStyle = {\n    ...getSafeAreaStyle(safeAreaInsets),\n    ...style,\n  } satisfies CSSProperties;\n  const contentStyle = {\n    paddingBottom: \"var(--canvas-shell-safe-bottom)\",\n    paddingLeft: \"var(--canvas-shell-safe-left)\",\n    paddingRight: \"var(--canvas-shell-safe-right)\",\n    paddingTop: \"var(--canvas-shell-safe-top)\",\n  } satisfies CSSProperties;\n\n  return (\n    <section\n      className={cn(\n        \"relative isolate flex min-h-[720px] w-full overflow-hidden bg-[radial-gradient(circle_at_top,hsl(var(--background)/0.94),hsl(var(--muted)/0.6))]\",\n        className,\n      )}\n      ref={ref}\n      style={mergedStyle}\n      {...props}\n    >\n      <div className=\"absolute inset-0 bg-[linear-gradient(180deg,hsl(var(--background)/0.94),hsl(var(--background)/0.8))]\" />\n      <CanvasShellChromeBefore\n        inset={inset}\n        leftBar={hasLeftBar ? resolvedLeftBar : undefined}\n        topBar={hasTopBar ? topBar : undefined}\n      />\n      {renderFloatingContent(children, contentStyle)}\n      <CanvasShellChromeAfter\n        bottomBar={hasBottomBar ? resolvedBottomBar : undefined}\n        inset={inset}\n        rightBar={hasRightBar ? resolvedRightBar : undefined}\n      />\n    </section>\n  );\n}\n\nconst CanvasShell = forwardRef<HTMLElement, CanvasShellProps>((props, ref) => {\n  const { bottomBar, chromeInset, contentPadding, leftBar, rightBar } = props;\n  const hasExplicitChromeInset = Object.prototype.hasOwnProperty.call(\n    props,\n    \"chromeInset\",\n  );\n  const usesFloatingChrome =\n    hasChromeContent(bottomBar) ||\n    hasChromeContent(leftBar) ||\n    hasChromeContent(rightBar) ||\n    contentPadding !== undefined ||\n    (hasExplicitChromeInset && chromeInset !== undefined);\n\n  if (!usesFloatingChrome) {\n    return renderLegacyCanvasShell(props, ref);\n  }\n\n  return renderFloatingCanvasShell(props, ref);\n});\n\nCanvasShell.displayName = \"CanvasShell\";\n\nexport { CanvasShell };\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
