{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pdf-block-resizable-shell",
  "title": "PDF Block Resizable Shell",
  "description": "A reusable resizable left document viewer and right review panel shell for PDF-style blocks.",
  "registryDependencies": [
    "utils",
    "resizable"
  ],
  "files": [
    {
      "path": "components/extend/pdf-block-resizable-shell.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  useDefaultLayout,\n  type GroupImperativeHandle,\n  type LayoutStorage,\n} from \"react-resizable-panels\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  ResizableHandle,\n  ResizablePanel,\n  ResizablePanelGroup,\n} from \"@/components/ui/resizable\"\n\ntype PdfBlockResizableShellProps = {\n  autoSaveId: string\n  className?: string\n  heightClassName?: string\n  initialOrientation?: React.ComponentProps<\n    typeof ResizablePanelGroup\n  >[\"orientation\"]\n  left: React.ReactNode\n  leftDefaultSize?: number\n  leftMinSize?: number\n  right: React.ReactNode\n  rightDefaultSize?: number\n  rightMaxSize?: number\n  rightMinSize?: number\n}\nfunction toPercentSize(size: number) {\n  return `${size}%`\n}\nfunction getLayoutSize(\n  layout: React.ComponentProps<typeof ResizablePanelGroup>[\"defaultLayout\"],\n  panelId: string,\n  fallbackSize: number\n) {\n  const size = layout?.[panelId]\n  return typeof size === \"number\" && Number.isFinite(size) ? size : fallbackSize\n}\nconst HORIZONTAL_LAYOUT_MIN_WIDTH = 900\nfunction useElementWidth<T extends HTMLElement>() {\n  const [node, setNode] = React.useState<T | null>(null)\n  const [width, setWidth] = React.useState<number>()\n  React.useEffect(() => {\n    if (!node || typeof ResizeObserver === \"undefined\") return\n    const updateWidth = () => {\n      const nextWidth = node.getBoundingClientRect().width\n      setWidth((currentWidth) =>\n        currentWidth !== undefined && Math.abs(currentWidth - nextWidth) < 0.5\n          ? currentWidth\n          : nextWidth\n      )\n    }\n    updateWidth()\n    const observer = new ResizeObserver(updateWidth)\n    observer.observe(node)\n    return () => observer.disconnect()\n  }, [node])\n  return [setNode, width] as const\n}\nconst subscribeToHydration = () => () => {}\nfunction useMounted() {\n  return React.useSyncExternalStore(\n    subscribeToHydration,\n    () => true,\n    () => false\n  )\n}\nfunction getSavedLayout(key: string) {\n  if (typeof window === \"undefined\") return null\n  try {\n    return window.localStorage.getItem(key)\n  } catch {\n    return null\n  }\n}\nfunction setSavedLayout(key: string, value: string) {\n  if (typeof window === \"undefined\") return\n  try {\n    window.localStorage.setItem(key, value)\n  } catch {\n    // Ignore storage failures so previews still render in restricted contexts.\n  }\n}\nfunction useHydrationSafeLayoutStorage() {\n  const mounted = useMounted()\n  return React.useMemo<LayoutStorage>(\n    () => ({\n      getItem(key) {\n        return mounted ? getSavedLayout(key) : null\n      },\n      setItem(key, value) {\n        if (mounted) setSavedLayout(key, value)\n      },\n    }),\n    [mounted]\n  )\n}\nexport function PdfBlockResizableShell({\n  autoSaveId,\n  className,\n  heightClassName = \"h-[680px]\",\n  initialOrientation = \"horizontal\",\n  left,\n  leftDefaultSize,\n  leftMinSize,\n  right,\n  rightDefaultSize = 34,\n  rightMaxSize = 52,\n  rightMinSize = 24,\n}: PdfBlockResizableShellProps) {\n  return (\n    <PdfBlockResizableShellWithSavedLayout\n      autoSaveId={autoSaveId}\n      className={className}\n      heightClassName={heightClassName}\n      initialOrientation={initialOrientation}\n      left={left}\n      leftDefaultSize={leftDefaultSize}\n      leftMinSize={leftMinSize}\n      right={right}\n      rightDefaultSize={rightDefaultSize}\n      rightMaxSize={rightMaxSize}\n      rightMinSize={rightMinSize}\n    />\n  )\n}\nfunction PdfBlockResizableShellWithSavedLayout({\n  autoSaveId,\n  className,\n  heightClassName,\n  initialOrientation = \"horizontal\",\n  left,\n  leftDefaultSize,\n  leftMinSize,\n  right,\n  rightDefaultSize = 34,\n  rightMaxSize = 52,\n  rightMinSize = 24,\n}: Required<Pick<PdfBlockResizableShellProps, \"autoSaveId\">> &\n  Omit<PdfBlockResizableShellProps, \"autoSaveId\">) {\n  const [containerRef, containerWidth] = useElementWidth<HTMLDivElement>()\n  const layoutStorage = useHydrationSafeLayoutStorage()\n  const isHorizontal =\n    containerWidth === undefined\n      ? initialOrientation === \"horizontal\"\n      : containerWidth >= HORIZONTAL_LAYOUT_MIN_WIDTH\n  const orientation = isHorizontal ? \"horizontal\" : \"vertical\"\n  const resolvedLeftDefaultSize =\n    leftDefaultSize ?? (isHorizontal ? 100 - rightDefaultSize : 62)\n  const layoutId = `${autoSaveId}-${orientation}`\n  const leftPanelId = `${layoutId}-left`\n  const rightPanelId = `${layoutId}-right`\n  const panelIds = React.useMemo(\n    () => [leftPanelId, rightPanelId],\n    [leftPanelId, rightPanelId]\n  )\n  const { defaultLayout, onLayoutChanged } = useDefaultLayout({\n    id: layoutId,\n    panelIds,\n    storage: layoutStorage,\n  })\n  const resolvedRightDefaultSize = isHorizontal ? rightDefaultSize : 38\n  return (\n    <PdfBlockResizableShellLayout\n      className={className}\n      containerRef={containerRef}\n      defaultLayout={defaultLayout}\n      groupId={autoSaveId}\n      heightClassName={heightClassName}\n      left={left}\n      leftDefaultSize={resolvedLeftDefaultSize}\n      leftMinSize={leftMinSize}\n      leftPanelId={leftPanelId}\n      onLayoutChanged={onLayoutChanged}\n      orientation={orientation}\n      right={right}\n      rightDefaultSize={resolvedRightDefaultSize}\n      rightMaxSize={isHorizontal ? rightMaxSize : 66}\n      rightMinSize={isHorizontal ? rightMinSize : 24}\n      rightPanelId={rightPanelId}\n      targetLayout={{\n        key: `${layoutId}:${defaultLayout ? \"saved\" : \"default\"}`,\n        leftSize: getLayoutSize(\n          defaultLayout,\n          leftPanelId,\n          resolvedLeftDefaultSize\n        ),\n        rightSize: getLayoutSize(\n          defaultLayout,\n          rightPanelId,\n          resolvedRightDefaultSize\n        ),\n      }}\n    />\n  )\n}\nfunction PdfBlockResizableShellLayout({\n  className,\n  containerRef,\n  defaultLayout,\n  groupId,\n  heightClassName,\n  left,\n  leftDefaultSize,\n  leftMinSize,\n  leftPanelId,\n  onLayoutChanged,\n  orientation = \"vertical\",\n  right,\n  rightDefaultSize = 34,\n  rightMaxSize = 52,\n  rightMinSize = 24,\n  rightPanelId,\n  targetLayout,\n}: Omit<PdfBlockResizableShellProps, \"autoSaveId\"> & {\n  containerRef?: React.Ref<HTMLDivElement>\n  defaultLayout?: React.ComponentProps<\n    typeof ResizablePanelGroup\n  >[\"defaultLayout\"]\n  groupId?: string\n  leftPanelId?: string\n  onLayoutChanged?: React.ComponentProps<\n    typeof ResizablePanelGroup\n  >[\"onLayoutChanged\"]\n  orientation?: React.ComponentProps<typeof ResizablePanelGroup>[\"orientation\"]\n  rightPanelId?: string\n  targetLayout?: {\n    key: string\n    leftSize: number\n    rightSize: number\n  }\n}) {\n  const resolvedLeftDefaultSize = leftDefaultSize ?? 62\n  const groupRef = React.useRef<GroupImperativeHandle | null>(null)\n  const appliedTargetLayoutKeyRef = React.useRef<string | null>(null)\n  React.useLayoutEffect(() => {\n    if (!targetLayout || !leftPanelId || !rightPanelId) return\n    const targetLayoutKey = `${targetLayout.key}:${targetLayout.leftSize}:${targetLayout.rightSize}`\n    if (appliedTargetLayoutKeyRef.current === targetLayoutKey) return\n    appliedTargetLayoutKeyRef.current = targetLayoutKey\n    groupRef.current?.setLayout({\n      [leftPanelId]: targetLayout.leftSize,\n      [rightPanelId]: targetLayout.rightSize,\n    })\n  }, [leftPanelId, rightPanelId, targetLayout])\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        heightClassName,\n        \"relative min-h-[420px] overflow-hidden bg-background\",\n        className\n      )}\n    >\n      <ResizablePanelGroup\n        id={groupId}\n        groupRef={groupRef}\n        orientation={orientation}\n        defaultLayout={defaultLayout}\n        onLayoutChanged={onLayoutChanged}\n        className=\"h-full min-h-0\"\n      >\n        <ResizablePanel\n          id={leftPanelId ?? \"left\"}\n          defaultSize={toPercentSize(resolvedLeftDefaultSize)}\n          minSize={toPercentSize(leftMinSize ?? 34)}\n          className=\"min-h-0 min-w-0 overflow-hidden\"\n        >\n          {left}\n        </ResizablePanel>\n        <ResizableHandle className=\"group z-10\" withHandle />\n        <ResizablePanel\n          id={rightPanelId ?? \"right\"}\n          defaultSize={toPercentSize(rightDefaultSize)}\n          minSize={toPercentSize(rightMinSize)}\n          maxSize={toPercentSize(rightMaxSize)}\n          className=\"min-h-0 min-w-0 overflow-hidden\"\n        >\n          {right}\n        </ResizablePanel>\n      </ResizablePanelGroup>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/extend/pdf-block-resizable-shell.tsx"
    }
  ],
  "categories": [
    "documents",
    "blocks"
  ],
  "type": "registry:component",
  "dependencies": [
    "react-resizable-panels@^4.11.2"
  ]
}
