{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-viewer",
  "title": "File Viewer",
  "description": "One viewer for any file: detects the type and lazy-loads the matching renderer (PDF, DOCX, XLSX, PPTX, CSV, images) plus inline, virtualized text/code/JSON, Markdown (sandboxed), and HTML (sandboxed). Only the needed parser is ever downloaded.",
  "dependencies": [
    "lucide-react",
    "marked@18.0.5",
    "dompurify@3.4.9",
    "@radix-ui/react-slot@^1.1.1",
    "class-variance-authority@^0.7.1",
    "radix-ui@^1.5.0"
  ],
  "registryDependencies": [
    "@retab/utils",
    "button",
    "@retab/pdf-viewer",
    "@retab/docx-viewer",
    "@retab/image-viewer",
    "@retab/pptx-viewer",
    "@retab/xlsx-viewer",
    "@retab/csv-viewer",
    "@retab/code-viewer",
    "@retab/text-viewer-chenglou",
    "@retab/markdown-viewer",
    "@retab/viewer-controls",
    "@retab/skeleton",
    "separator",
    "@retab/spinner",
    "@retab/email-viewer"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/ui/file-viewer.tsx",
      "content": "\"use client\";\n\nexport type { ViewerSource } from \"./file-viewer-core\";\nexport {\n  FileViewerContent,\n  FileViewerInset,\n  FileViewerLegend,\n  FileViewerViewport,\n  type FileViewerContentProps,\n  type FileViewerInsetProps,\n  type FileViewerLegendProps,\n  type FileViewerViewportProps,\n} from \"./file-viewer-layout\";\nexport {\n  FileViewerSidebar,\n  FileViewerSidebarContent,\n  FileViewerSidebarFooter,\n  FileViewerSidebarHeader,\n  FileViewerSidebarRail,\n  FileViewerSidebarSection,\n  FileViewerSidebarSectionAction,\n  FileViewerSidebarSectionContent,\n  FileViewerSidebarSectionHeader,\n  FileViewerSidebarSectionTitle,\n  FileViewerSidebarSeparator,\n  type FileViewerSidebarProps,\n  type FileViewerSidebarContentProps,\n  type FileViewerSidebarFooterProps,\n  type FileViewerSidebarHeaderProps,\n  type FileViewerSidebarRailProps,\n  type FileViewerSidebarSectionActionProps,\n  type FileViewerSidebarSectionContentProps,\n  type FileViewerSidebarSectionHeaderProps,\n  type FileViewerSidebarSectionProps,\n  type FileViewerSidebarSectionTitleProps,\n  type FileViewerSidebarSeparatorProps,\n} from \"./file-viewer-sidebar\";\nexport {\n  FileViewerDocument,\n  type FileViewerDocumentProps,\n} from \"./file-viewer-document\";\nexport {\n  FileViewerControls,\n  FileViewerHeader,\n  FileViewerMeta,\n  FileViewerSidebarTrigger,\n  FileViewerTitle,\n  type FileViewerControlsProps,\n  type FileViewerHeaderProps,\n  type FileViewerMetaProps,\n  type FileViewerSidebarTriggerProps,\n  type FileViewerTitleProps,\n} from \"./file-viewer-header\";\nexport {\n  FileViewerEmptyState,\n  FileViewerErrorState,\n  FileViewerLoadingState,\n  FileViewerUnavailableState,\n  FileViewerUnsupportedState,\n  type FileViewerErrorStateProps,\n  type FileViewerStateProps,\n  type FileViewerUnsupportedStateProps,\n} from \"./file-viewer-state\";\nexport { useFileViewerResource } from \"./file-viewer-resource-state\";\nexport {\n  FileViewerPreview,\n  type FileViewerPreviewProps,\n} from \"./file-viewer-preview\";\nexport {\n  useFileViewerSidebar,\n  type FileViewerHeaderMode,\n  type FileViewerSidebarValue,\n} from \"./file-viewer-context\";\nexport {\n  FileViewer,\n  type FileViewerLayout,\n  type FileViewerProps,\n} from \"./file-viewer-shell\";\nexport {\n  FileViewerProvider,\n  type FileViewerProviderProps,\n} from \"./file-viewer-provider\";\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-accessibility.ts",
      "content": "\"use client\";\n\nimport type { FileViewerSidebarCollapsible } from \"./file-viewer-context\";\nimport type { FileViewerElements } from \"./file-viewer-elements\";\n\nexport type FileViewerSidebarAccessibilityProps = {\n  \"aria-hidden\"?: true;\n  inert?: true;\n};\n\nexport type FileViewerSidebarTriggerAccessibilityProps = {\n  \"aria-controls\"?: string;\n  \"aria-disabled\"?: true;\n  \"aria-expanded\"?: boolean;\n  \"aria-label\": string;\n};\n\nconst visibleSidebarAccessibilityProps = {};\nconst hiddenSidebarAccessibilityProps = {\n  \"aria-hidden\": true,\n  inert: true,\n} as const;\n\nexport function resolveFileViewerSidebarAccessibilityProps({\n  collapsible,\n  deferInert,\n  isSidebarInteractive,\n}: {\n  collapsible: FileViewerSidebarCollapsible;\n  deferInert?: boolean;\n  isSidebarInteractive: boolean;\n}): FileViewerSidebarAccessibilityProps {\n  if (collapsible === \"none\" || isSidebarInteractive) {\n    return visibleSidebarAccessibilityProps;\n  }\n\n  if (deferInert) {\n    return {\n      \"aria-hidden\": true,\n    };\n  }\n\n  return hiddenSidebarAccessibilityProps;\n}\n\nexport function resolveFileViewerSidebarTriggerAccessibilityProps({\n  ariaLabel,\n  canToggleSidebar,\n  isDisabled,\n  isSidebarInteractive,\n  sidebarId,\n}: {\n  ariaLabel: string;\n  canToggleSidebar: boolean;\n  isDisabled: boolean;\n  isSidebarInteractive: boolean;\n  sidebarId: string;\n}): FileViewerSidebarTriggerAccessibilityProps {\n  return {\n    \"aria-controls\": canToggleSidebar ? sidebarId : undefined,\n    \"aria-disabled\": isDisabled ? true : undefined,\n    // Deliberately tracks interactivity, not the requested state: while the\n    // panel slides open it is still inert, and announcing \"expanded\" would\n    // point assistive tech at a region that cannot yet be used.\n    \"aria-expanded\": canToggleSidebar ? isSidebarInteractive : undefined,\n    \"aria-label\": ariaLabel,\n  };\n}\n\nexport function restoreFileViewerSidebarFocusOnClose({\n  elements,\n  isSidebarInteractive,\n  previousIsSidebarInteractive,\n}: {\n  elements: FileViewerElements;\n  isSidebarInteractive: boolean;\n  previousIsSidebarInteractive: boolean;\n}) {\n  if (isSidebarInteractive || !previousIsSidebarInteractive) return;\n  if (!elements.sidebarElement || !elements.sidebarTriggerElement) return;\n\n  const ownerWindow = elements.sidebarElement.ownerDocument.defaultView;\n  const activeElement = elements.sidebarElement.ownerDocument.activeElement;\n  if (\n    ownerWindow &&\n    activeElement instanceof ownerWindow.HTMLElement &&\n    elements.sidebarElement.contains(activeElement)\n  ) {\n    elements.sidebarTriggerElement.focus({ preventScroll: true });\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-accessibility.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-layout.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  useOptionalFileViewerShellStatic,\n  useFileViewerShellStatic,\n} from \"./file-viewer-context\";\nimport {\n  FileViewerDocumentFrameProvider,\n  type FileViewerDocumentFrameState,\n} from \"./file-viewer-renderer-frame\";\nimport { type FileViewerDocumentAlign } from \"./file-viewer-renderer-contract\";\nimport { useStableElementSize } from \"./viewer-measurement\";\n\nconst FileViewerContentContext = React.createContext(false);\nconst FileViewerInsetContext = React.createContext(false);\nconst FileViewerViewportContext = React.createContext(false);\n\nexport function useIsInsideFileViewerContent(): boolean {\n  return React.useContext(FileViewerContentContext);\n}\n\nexport function useIsInsideFileViewerInset(): boolean {\n  return React.useContext(FileViewerInsetContext);\n}\n\nexport function useIsInsideFileViewerViewport(): boolean {\n  return React.useContext(FileViewerViewportContext);\n}\n\nexport type FileViewerContentProps = React.ComponentProps<\"div\">;\nexport type FileViewerInsetProps = React.ComponentProps<\"div\"> & {\n  align?: FileViewerDocumentAlign;\n  documentFrameClassName?: string;\n  documentFrameStyle?: React.CSSProperties;\n  maxInlineSize?: React.CSSProperties[\"maxInlineSize\"];\n};\nexport type FileViewerLegendProps = React.ComponentProps<\"div\">;\nexport type FileViewerViewportProps = React.ComponentProps<\"div\">;\n\nexport function FileViewerContent({\n  className,\n  ...props\n}: FileViewerContentProps) {\n  const shell = useFileViewerShellStatic(\"FileViewerContent\");\n\n  return (\n    <FileViewerContentContext.Provider value={true}>\n      <div\n        data-file-viewer-sidebar-mode={shell.mode}\n        data-file-viewer-sidebar-side={shell.side}\n        data-slot=\"file-viewer-content\"\n        className={cn(\n          \"relative flex min-h-0 min-w-0 flex-1 overflow-hidden\",\n          className,\n        )}\n        {...props}\n      />\n    </FileViewerContentContext.Provider>\n  );\n}\n\nexport function FileViewerLegend({\n  className,\n  ...props\n}: FileViewerLegendProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-legend\"\n      className={cn(\"min-w-0 flex-shrink-0 border-b\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerInset({\n  align = \"start\",\n  children,\n  className,\n  documentFrameClassName,\n  documentFrameStyle,\n  maxInlineSize,\n  ...props\n}: FileViewerInsetProps) {\n  const shell = useOptionalFileViewerShellStatic();\n  const hasAnimatedFileViewerWidth =\n    shell?.mode === \"inline\" && shell.canToggleSidebar;\n  const documentFrame = useMeasuredFileViewerDocumentFrame({\n    enabled: !hasAnimatedFileViewerWidth,\n  });\n  const frame = React.useMemo<FileViewerDocumentFrameState>(\n    () => ({\n      align,\n      element: documentFrame.element,\n      inlineSize: documentFrame.inlineSize,\n    }),\n    [align, documentFrame.element, documentFrame.inlineSize],\n  );\n\n  return (\n    <FileViewerInsetContext.Provider value={true}>\n      <FileViewerDocumentFrameProvider value={frame}>\n        <div\n          data-slot=\"file-viewer-inset\"\n          className={cn(\n            \"relative flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden\",\n            className,\n          )}\n          {...props}\n        >\n          <div\n            ref={documentFrame.setElement}\n            data-slot=\"file-viewer-document-frame\"\n            className={cn(\n              \"[container-type:inline-size] relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col\",\n              getDocumentFrameAlignClass(align),\n              documentFrameClassName,\n            )}\n            style={\n              {\n                maxInlineSize,\n                ...documentFrameStyle,\n              } as React.CSSProperties\n            }\n          >\n            {children}\n          </div>\n        </div>\n      </FileViewerDocumentFrameProvider>\n    </FileViewerInsetContext.Provider>\n  );\n}\n\nexport const FileViewerViewport = React.forwardRef<\n  HTMLDivElement,\n  FileViewerViewportProps\n>(function FileViewerViewport({ className, style, ...props }, ref) {\n  const isInsideInset = useIsInsideFileViewerInset();\n  const setElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      if (typeof ref === \"function\") {\n        ref(element);\n      } else if (ref) {\n        ref.current = element;\n      }\n    },\n    [ref],\n  );\n\n  if (process.env.NODE_ENV !== \"production\" && !isInsideInset) {\n    throw new Error(\n      \"FileViewerViewport must be rendered inside FileViewerInset.\",\n    );\n  }\n\n  return (\n    <FileViewerViewportContext.Provider value={true}>\n      <div\n        ref={setElement}\n        data-slot=\"file-viewer-viewport\"\n        className={cn(\n          \"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\",\n          className,\n        )}\n        style={\n          {\n            ...style,\n          } as React.CSSProperties\n        }\n        {...props}\n      />\n    </FileViewerViewportContext.Provider>\n  );\n});\n\nfunction useMeasuredFileViewerDocumentFrame({ enabled }: { enabled: boolean }) {\n  const size = useStableElementSize<HTMLDivElement>({\n    enabled,\n  });\n\n  return React.useMemo(\n    () => ({\n      element: size.element,\n      inlineSize: size.width,\n      setElement: size.setElement,\n    }),\n    [size.element, size.setElement, size.width],\n  );\n}\n\nfunction getDocumentFrameAlignClass(align: FileViewerDocumentAlign) {\n  switch (align) {\n    case \"center\":\n      return \"mx-auto\";\n    case \"end\":\n      return \"ml-auto\";\n    case \"start\":\n      return \"mr-auto\";\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-layout.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-context.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { FileCategory } from \"@/lib/viewer-source\";\nimport type { FileViewerElementRegistry } from \"./file-viewer-elements\";\nimport type { FileViewerMotionKernel } from \"./file-viewer-motion-kernel\";\n\nexport type FileViewerSidebarMode = \"inline\" | \"overlay\";\nexport type FileViewerSidebarRequestedMode = \"auto\" | FileViewerSidebarMode;\nexport type FileViewerSidebarState = \"expanded\" | \"collapsed\";\nexport type FileViewerSidebarSide = \"left\" | \"right\";\nexport type FileViewerSidebarCollapsible = \"offcanvas\" | \"none\";\n\nexport type FileViewerHeaderMode = \"inline\" | \"outlets\";\n\nexport const DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH = \"10rem\";\n\nexport type FileViewerSetSidebarOpen = (\n  value: boolean | ((isSidebarOpen: boolean) => boolean),\n) => void;\n\nexport type FileViewerSidebarOpenProps = {\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n};\n\nexport type FileViewerContextValue = {\n  headerMode: FileViewerHeaderMode;\n  hasHeaderOutlets: boolean;\n  isInsideFileViewer: boolean;\n  resourceCategory: FileCategory;\n  sidebarOpenProps: FileViewerSidebarOpenProps;\n};\n\nexport type FileViewerSidebarRegistration = {\n  collapsible: FileViewerSidebarCollapsible;\n  id: string;\n  side: FileViewerSidebarSide;\n  width: string;\n  widthPixels: number;\n};\n\nexport type FileViewerSidebarValue = {\n  canToggleSidebar: boolean;\n  isSidebarInteractive: boolean;\n  isSidebarOpen: boolean;\n  mode: FileViewerSidebarMode;\n  side: FileViewerSidebarSide;\n  sidebarId: string;\n  sidebarState: FileViewerSidebarState;\n  setSidebarOpen: FileViewerSetSidebarOpen;\n  toggleSidebar: () => void;\n};\n\nexport type FileViewerShellStaticContextValue = {\n  canToggleSidebar: boolean;\n  collapsible: FileViewerSidebarCollapsible;\n  elementRegistry: FileViewerElementRegistry;\n  mode: FileViewerSidebarMode;\n  motionDurationMs: number;\n  motionKernel: FileViewerMotionKernel;\n  registerSidebar: (registration: FileViewerSidebarRegistration) => () => void;\n  rootId: string;\n  setSidebarOpen: FileViewerSetSidebarOpen;\n  side: FileViewerSidebarSide;\n  sidebarId: string;\n  sidebarWidth: string;\n  toggleSidebar: () => void;\n};\n\nexport type FileViewerSidebarDynamicContextValue = {\n  isSidebarInteractive: boolean;\n  isSidebarOpen: boolean;\n  isSidebarTransitioning: boolean;\n  sidebarState: FileViewerSidebarState;\n};\n\nexport const FileViewerContext = React.createContext<FileViewerContextValue>({\n  headerMode: \"inline\",\n  hasHeaderOutlets: false,\n  isInsideFileViewer: false,\n  resourceCategory: \"unsupported\",\n  sidebarOpenProps: {},\n});\n\nexport const FileViewerShellStaticContext =\n  React.createContext<FileViewerShellStaticContextValue | null>(null);\n\nexport const FileViewerSidebarDynamicContext =\n  React.createContext<FileViewerSidebarDynamicContextValue | null>(null);\n\nexport function useFileViewerContext() {\n  return React.useContext(FileViewerContext);\n}\n\nexport function useOptionalFileViewerShellStatic() {\n  return React.useContext(FileViewerShellStaticContext);\n}\n\nexport function useFileViewerShellStatic(consumer: string) {\n  const context = React.useContext(FileViewerShellStaticContext);\n  if (!context) {\n    throw new Error(`${consumer} must be rendered inside FileViewer.`);\n  }\n  return context;\n}\n\nexport function useOptionalFileViewerShell() {\n  const staticContext = React.useContext(FileViewerShellStaticContext);\n  const sidebarContext = React.useContext(FileViewerSidebarDynamicContext);\n\n  return React.useMemo(\n    () =>\n      staticContext && sidebarContext\n        ? { ...staticContext, ...sidebarContext }\n        : null,\n    [sidebarContext, staticContext],\n  );\n}\n\nexport function useFileViewerShell(consumer: string) {\n  const context = useOptionalFileViewerShell();\n  if (!context) {\n    throw new Error(`${consumer} must be rendered inside FileViewer.`);\n  }\n  return context;\n}\n\nexport function useFileViewerSidebar(): FileViewerSidebarValue {\n  const fileViewerContext = React.useContext(FileViewerContext);\n  const shellContext = useOptionalFileViewerShell();\n\n  if (!fileViewerContext.isInsideFileViewer || !shellContext) {\n    throw new Error(\"useFileViewerSidebar must be used within FileViewer.\");\n  }\n\n  return shellContext;\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-context.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-core.ts",
      "content": "import {\n  detectCategory,\n  extensionOf,\n  extractName,\n  resolveViewerDescriptor,\n  textPayloadKey,\n  type FileCategory,\n  type ViewerDescriptor,\n  type ViewerSource,\n} from \"@/lib/viewer-source\";\n\nexport type { FileCategory, ViewerSource };\nexport type FileViewerFallbackSize = { width: number; height: number };\n\nexport interface FileViewerCoreProps {\n  source: ViewerSource;\n  category?: FileCategory;\n  className?: string;\n  /** Intrinsic first-frame size for image/TIFF loading skeletons. */\n  fallbackFrameSize?: FileViewerFallbackSize;\n  /** Intrinsic first-slide size for PPTX loading skeletons. */\n  fallbackSlideSize?: FileViewerFallbackSize;\n  isolateStyles?: boolean;\n}\n\nexport type FileDescriptor = ViewerDescriptor;\n\nexport function resolveFileDescriptor({\n  source,\n  category,\n}: FileViewerCoreProps): FileDescriptor {\n  return resolveViewerDescriptor({\n    source,\n    category,\n  });\n}\n\nexport function descriptorResetKey(descriptor: FileDescriptor): string {\n  return [\n    descriptorIdentityResetKey(descriptor),\n    descriptor.displayName,\n    descriptor.mimeType ?? \"\",\n    descriptor.category,\n  ].join(\"\\u0000\");\n}\n\nfunction descriptorIdentityResetKey(descriptor: FileDescriptor): string {\n  if (\n    descriptor.source.kind === \"text\" &&\n    descriptor.source.identityKey == null\n  ) {\n    return textPayloadKey(descriptor.source.text);\n  }\n  return descriptor.identityKey;\n}\n\nexport function isProseTextDescriptor(descriptor: FileDescriptor): boolean {\n  if (descriptor.category !== \"text\") return false;\n\n  const extension = extensionOf(descriptor.fileName);\n  if (extension === \"txt\" || extension === \"text\") return true;\n  return !extension && descriptor.mimeType?.toLowerCase() === \"text/plain\";\n}\n\nexport { detectCategory, extensionOf, extractName };\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-core.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-document.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { FileErrorBoundary, ViewerFallback } from \"./file-viewer-fallback\";\nimport { useFileViewerRequiredResourceState } from \"./file-viewer-resource-state\";\nimport { FileViewerRoute } from \"./file-viewer-route\";\nimport { useIsInsideFileViewerViewport } from \"./file-viewer-layout\";\n\nexport type FileViewerDocumentProps = {\n  className?: string;\n  controls?: boolean;\n};\n\nexport function FileViewerDocument({\n  className,\n  controls = false,\n}: FileViewerDocumentProps) {\n  const isInsideViewport = useIsInsideFileViewerViewport();\n\n  if (process.env.NODE_ENV !== \"production\" && !isInsideViewport) {\n    throw new Error(\n      \"FileViewerDocument must be rendered inside FileViewerViewport.\",\n    );\n  }\n\n  return (\n    <FileViewerDocumentContent\n      className={cn(\"h-full\", className)}\n      controls={controls}\n    />\n  );\n}\n\nfunction FileViewerDocumentContent({\n  className,\n  controls,\n}: Required<Pick<FileViewerDocumentProps, \"controls\">> &\n  Pick<FileViewerDocumentProps, \"className\">) {\n  const {\n    descriptor,\n    descriptorKey,\n    descriptorSignal,\n    fallbackFrameSize,\n    fallbackSlideSize,\n    isClient,\n    isolateStyles,\n    resource,\n  } = useFileViewerRequiredResourceState();\n  const fallback = (\n    <ViewerFallback\n      resource={resource}\n      className={className}\n      bare\n      controls={controls}\n      fallbackFrameSize={fallbackFrameSize}\n      fallbackSlideSize={fallbackSlideSize}\n    />\n  );\n\n  if (!isClient) return fallback;\n\n  return (\n    <FileErrorBoundary\n      key={descriptorKey}\n      descriptor={descriptor}\n      resource={resource}\n      className={className}\n      resetKey={descriptorKey}\n      showDownload={controls}\n    >\n      <React.Suspense fallback={fallback}>\n        <FileViewerRoute\n          className={className}\n          controls={controls}\n          descriptor={descriptor}\n          descriptorSignal={descriptorSignal}\n          fallbackFrameSize={fallbackFrameSize}\n          fallbackSlideSize={fallbackSlideSize}\n          frame=\"none\"\n          isolateStyles={isolateStyles}\n          resource={resource}\n        />\n      </React.Suspense>\n    </FileErrorBoundary>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-document.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-elements.ts",
      "content": "\"use client\";\n\nimport type {\n  FileViewerDocumentSurface,\n  FileViewerMotionKernel,\n} from \"./file-viewer-motion-kernel\";\nimport type { FileViewerMotionFrame } from \"./file-viewer-motion-plan\";\n\nexport const FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT =\n  \"file-viewer:before-layout-motion\";\n\n// The kernel dispatches the before-layout-motion event as a CustomEvent whose\n// detail is its LIVE interactive frame, so renderers can capture pre-commit\n// anchors against what is actually on screen (a mid-flight retarget sees the\n// settled layout plus the in-flight transform).\nexport function readFileViewerBeforeLayoutMotionFrame(\n  event: Event,\n): FileViewerMotionFrame | null {\n  if (!(event instanceof CustomEvent)) return null;\n  const detail: unknown = event.detail;\n  return detail != null && typeof detail === \"object\"\n    ? (detail as FileViewerMotionFrame)\n    : null;\n}\n\nexport type FileViewerElements = {\n  documentSurfaceElement: HTMLElement | null;\n  getDocumentSurfaceMotionProbeElement: (() => HTMLElement | null) | null;\n  sidebarElement: HTMLElement | null;\n  sidebarGapElement: HTMLDivElement | null;\n  sidebarTriggerElement: HTMLElement | null;\n  viewerShellElement: HTMLDivElement | null;\n};\n\nexport type FileViewerElementRegistry = {\n  getElements: () => FileViewerElements;\n  registerDocumentSurface: (surface: FileViewerDocumentSurface) => () => void;\n  registerSidebarElement: (element: HTMLElement | null) => void;\n  registerSidebarGapElement: (element: HTMLDivElement | null) => void;\n  registerSidebarTriggerElement: (element: HTMLElement | null) => void;\n  registerViewerShellElement: (element: HTMLDivElement | null) => void;\n};\n\nexport function createFileViewerElementRegistry({\n  motionKernel,\n  onViewerShellElementChange,\n}: {\n  motionKernel: FileViewerMotionKernel;\n  onViewerShellElementChange: (element: HTMLDivElement | null) => void;\n}): FileViewerElementRegistry {\n  const elements: FileViewerElements = {\n    documentSurfaceElement: null,\n    getDocumentSurfaceMotionProbeElement: null,\n    sidebarElement: null,\n    sidebarGapElement: null,\n    sidebarTriggerElement: null,\n    viewerShellElement: null,\n  };\n  let documentSurfaceRegistration = 0;\n\n  return {\n    getElements: () => elements,\n    registerDocumentSurface: (surface) => {\n      documentSurfaceRegistration += 1;\n      const registration = documentSurfaceRegistration;\n      elements.documentSurfaceElement = surface.element;\n      elements.getDocumentSurfaceMotionProbeElement =\n        surface.getMotionProbeElement ?? null;\n      motionKernel.setDocumentSurface(surface);\n      return () => {\n        if (documentSurfaceRegistration !== registration) return;\n        elements.documentSurfaceElement = null;\n        elements.getDocumentSurfaceMotionProbeElement = null;\n        motionKernel.setDocumentSurface(null);\n      };\n    },\n    registerSidebarElement: (element) => {\n      if (elements.sidebarElement === element) return;\n      elements.sidebarElement = element;\n    },\n    registerSidebarGapElement: (element) => {\n      if (elements.sidebarGapElement === element) return;\n      elements.sidebarGapElement = element;\n      motionKernel.setSidebarGapElement(element);\n    },\n    registerSidebarTriggerElement: (element) => {\n      elements.sidebarTriggerElement = element;\n    },\n    registerViewerShellElement: (element) => {\n      if (elements.viewerShellElement === element) return;\n      elements.viewerShellElement = element;\n      onViewerShellElementChange(element);\n    },\n  };\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-elements.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-fallback.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  fileViewerDiagnostic,\n  summarizeViewerDescriptor,\n  summarizeViewerError,\n  summarizeViewerResource,\n} from \"@/lib/pdf-viewer-diagnostics\";\nimport { cn } from \"@/lib/utils\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\n\nimport { CodeViewerFallback } from \"./code-viewer-chrome\";\nimport { DocxViewerFallback } from \"./docx-viewer-chrome\";\nimport {\n  isProseTextDescriptor,\n  type FileDescriptor,\n  type FileViewerFallbackSize,\n} from \"./file-viewer-core\";\nimport {\n  FileViewerErrorState,\n  FileViewerLoadingState,\n  FileViewerUnsupportedState,\n} from \"./file-viewer-state\";\nimport { ImageViewerFallback } from \"./image-viewer-chrome\";\nimport { PdfViewerFallback } from \"./pdf-viewer-states\";\nimport { PptxViewerFallback } from \"./pptx-viewer-fallback\";\nimport { TextViewerFallback } from \"./text-viewer-chrome\";\nimport { ViewerControlsSkeleton } from \"./viewer-controls\";\nimport { XlsxViewerFallback } from \"./xlsx-viewer-chrome\";\n\nexport function UnsupportedCard({\n  resource,\n  className,\n  bare,\n  message,\n  showDownload = true,\n}: {\n  resource: ViewerResource;\n  className?: string;\n  bare?: boolean;\n  message?: string;\n  showDownload?: boolean;\n}) {\n  return (\n    <FileViewerUnsupportedState\n      className={cn(\n        bare ? \"bg-muted/20 h-full\" : \"bg-muted/30 min-h-64 rounded-xl border\",\n        className,\n      )}\n      description={\n        message ?? `No preview is available for ${resource.fileName}.`\n      }\n      download={resource.originalDownload}\n      fileName={resource.fileName}\n      showDownload={showDownload}\n    />\n  );\n}\n\nexport function ViewerFallback({\n  resource,\n  className,\n  bare = false,\n  controls = true,\n  fallbackFrameSize,\n  fallbackSlideSize,\n}: {\n  resource: ViewerResource;\n  className?: string;\n  bare?: boolean;\n  controls?: boolean;\n  fallbackFrameSize?: FileViewerFallbackSize;\n  fallbackSlideSize?: FileViewerFallbackSize;\n}) {\n  const descriptor = resource.descriptor;\n  const category = descriptor.category;\n\n  // Render the exact per-type skeleton each viewer shows while it parses, so the\n  // SSR + chunk-loading paint is identical to the in-viewer loading state (same\n  // controls row, same body) — no controls popping in and no geometry shift as one\n  // skeleton hands off to the next.\n  switch (category) {\n    case \"pdf\":\n      return (\n        <PdfViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      );\n    case \"docx\":\n      return (\n        <DocxViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      );\n    case \"pptx\":\n      return (\n        <PptxViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n          fallbackSlideSize={fallbackSlideSize}\n        />\n      );\n    case \"image\":\n      return (\n        <ImageViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n          fallbackFrameSize={fallbackFrameSize}\n        />\n      );\n    case \"xlsx\":\n      return (\n        <XlsxViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      );\n    case \"markdown\":\n      return (\n        <TextViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      );\n    case \"text\":\n      // The \"text\" category fans out to a prose viewer or a code viewer; match\n      // whichever the route will pick so the body skeleton (gutter vs none) lines up.\n      return isProseTextDescriptor(descriptor) ? (\n        <TextViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      ) : (\n        <CodeViewerFallback\n          bare={bare}\n          className={className}\n          controls={controls}\n        />\n      );\n    case \"csv\":\n      return (\n        <TextFamilyFallbackFrame bare={bare} className={className}>\n          {controls ? (\n            <ViewerControlsSkeleton title subtitle zoom download />\n          ) : null}\n          <TableBodySkeleton />\n        </TextFamilyFallbackFrame>\n      );\n    case \"html\":\n      return (\n        <TextFamilyFallbackFrame bare={bare} className={className}>\n          {controls ? <ViewerControlsSkeleton zoom /> : null}\n          <div className=\"bg-card min-h-0 flex-1 p-4\">\n            <Skeleton className=\"size-full rounded-md\" />\n          </div>\n        </TextFamilyFallbackFrame>\n      );\n  }\n\n  // Unsupported / unknown categories: a neutral page-sheet placeholder.\n  return (\n    <FileViewerLoadingState\n      description={null}\n      icon={null}\n      title={null}\n      className={cn(\n        \"flex min-h-0 flex-col items-stretch justify-start gap-0 overflow-hidden p-0 text-left\",\n        bare ? \"bg-muted/20 h-full\" : \"bg-muted/20 min-h-64\",\n        className,\n      )}\n    >\n      <div\n        data-slot=\"file-viewer-document-fallback\"\n        className=\"min-h-0 flex-1 overflow-hidden\"\n      >\n        <div className=\"flex flex-col items-center p-4\">\n          <Skeleton\n            aria-hidden\n            className=\"ring-border w-full rounded-none shadow-sm ring-1\"\n            style={{ aspectRatio: \"8.5 / 11\" }}\n          />\n        </div>\n      </div>\n    </FileViewerLoadingState>\n  );\n}\n\nfunction TextFamilyFallbackFrame({\n  bare,\n  className,\n  children,\n}: {\n  bare?: boolean;\n  className?: string;\n  children: React.ReactNode;\n}) {\n  return (\n    <div\n      data-slot=\"file-viewer-document-fallback\"\n      className={cn(\n        \"bg-card flex min-h-0 flex-1 flex-col overflow-hidden\",\n        bare ? \"h-full\" : \"min-h-64\",\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n\nfunction TableBodySkeleton() {\n  const gutter = 52;\n  const colWidth = 150;\n  const cols = 6;\n  const rows = 14;\n  const widths = [70, 45, 88, 56, 62, 78];\n  return (\n    <div\n      aria-hidden\n      className=\"bg-card flex min-h-0 flex-1 flex-col overflow-hidden text-sm\"\n    >\n      <div className=\"bg-muted/60 flex border-b\">\n        <div\n          className=\"shrink-0 border-r\"\n          style={{ width: gutter, height: 33 }}\n        />\n        {Array.from({ length: cols }, (_, c) => (\n          <div\n            key={c}\n            className=\"flex shrink-0 items-center border-r px-3\"\n            style={{ width: colWidth, height: 33 }}\n          >\n            <Skeleton className=\"h-3 w-16\" />\n          </div>\n        ))}\n      </div>\n      <div className=\"min-h-0 flex-1 overflow-hidden\">\n        {Array.from({ length: rows }, (_, r) => (\n          <div key={r} className=\"flex border-b\" style={{ height: 33 }}>\n            <div\n              className=\"flex shrink-0 items-center justify-end border-r px-2\"\n              style={{ width: gutter }}\n            >\n              <Skeleton className=\"h-3 w-4\" />\n            </div>\n            {Array.from({ length: cols }, (_, c) => (\n              <div\n                key={c}\n                className=\"flex shrink-0 items-center border-r px-3\"\n                style={{ width: colWidth }}\n              >\n                <Skeleton\n                  className=\"h-3\"\n                  style={{ width: `${widths[(r + c) % widths.length]}%` }}\n                />\n              </div>\n            ))}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport class FileErrorBoundary extends React.Component<\n  {\n    children: React.ReactNode;\n    descriptor: FileDescriptor;\n    resource: ViewerResource;\n    className?: string;\n    resetKey?: unknown;\n    showDownload?: boolean;\n  },\n  { error: unknown | null }\n> {\n  state: Readonly<{ error: unknown | null }> = { error: null };\n\n  componentDidUpdate(prev: { resetKey?: unknown }) {\n    if (prev.resetKey !== this.props.resetKey && this.state.error) {\n      fileViewerDiagnostic(\"debug\", \"file_viewer_error_boundary_reset\", {\n        descriptor: summarizeViewerDescriptor(this.props.descriptor),\n        resource: summarizeViewerResource(this.props.resource),\n      });\n      this.setState({ error: null });\n    }\n  }\n\n  static getDerivedStateFromError(error: unknown) {\n    return { error };\n  }\n\n  componentDidCatch(error: unknown) {\n    fileViewerDiagnostic(\"error\", \"file_viewer_error_boundary_caught\", {\n      descriptor: summarizeViewerDescriptor(this.props.descriptor),\n      resource: summarizeViewerResource(this.props.resource),\n      error: summarizeViewerError(error),\n    });\n  }\n\n  render() {\n    if (this.state.error != null) {\n      return (\n        <FileViewerErrorState\n          error={this.state.error}\n          format=\"file\"\n          sourceKind={this.props.resource.sourceKind}\n          download={\n            this.props.showDownload === false\n              ? null\n              : this.props.resource.originalDownload\n          }\n          className={this.props.className}\n        />\n      );\n    }\n    return this.props.children;\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-fallback.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-shell.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  FileViewerShellStaticContext,\n  FileViewerSidebarDynamicContext,\n  type FileViewerSidebarRequestedMode,\n} from \"./file-viewer-context\";\nimport {\n  FILE_VIEWER_INLINE_BREAKPOINT,\n  useFileViewerShellController,\n} from \"./file-viewer-shell-controller\";\n\nexport type FileViewerLayout = \"fill\" | \"intrinsic\";\n\nexport type FileViewerProps = React.ComponentProps<\"div\"> & {\n  inlineBreakpoint?: number;\n  layout?: FileViewerLayout;\n  sidebarMode?: FileViewerSidebarRequestedMode;\n};\n\nexport function FileViewer({\n  children,\n  className,\n  inlineBreakpoint = FILE_VIEWER_INLINE_BREAKPOINT,\n  layout = \"fill\",\n  sidebarMode = \"auto\",\n  style,\n  ...props\n}: FileViewerProps) {\n  const controller = useFileViewerShellController({\n    inlineBreakpoint,\n    sidebarMode,\n  });\n\n  return (\n    <FileViewerShellStaticContext.Provider\n      value={controller.shellStaticContext}\n    >\n      <FileViewerSidebarDynamicContext.Provider\n        value={controller.sidebarDynamicContext}\n      >\n        <div\n          ref={controller.setRootElement}\n          data-file-viewer=\"root\"\n          data-file-viewer-header-mode={controller.headerMode}\n          data-file-viewer-layout={layout}\n          data-file-viewer-resource-category={controller.resourceCategory}\n          data-file-viewer-sidebar-collapsible={controller.effectiveCollapsible}\n          data-file-viewer-sidebar-mode={controller.mode}\n          data-file-viewer-sidebar-open={\n            controller.isSidebarOpen ? \"true\" : \"false\"\n          }\n          data-file-viewer-sidebar-side={controller.side}\n          data-file-viewer-sidebar-state={controller.sidebarState}\n          data-slot=\"file-viewer-root\"\n          id={controller.rootId}\n          className={cn(\n            \"relative flex min-h-0 min-w-0 flex-col overflow-hidden\",\n            layout === \"fill\" && \"h-full w-full flex-1\",\n            className,\n          )}\n          style={\n            {\n              overflowAnchor: \"none\",\n              ...style,\n            } as React.CSSProperties\n          }\n          {...props}\n        >\n          {children}\n        </div>\n      </FileViewerSidebarDynamicContext.Provider>\n    </FileViewerShellStaticContext.Provider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-shell.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-shell-controller.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nimport { restoreFileViewerSidebarFocusOnClose } from \"./file-viewer-accessibility\";\nimport {\n  DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,\n  useFileViewerContext,\n  type FileViewerContextValue,\n  type FileViewerSetSidebarOpen,\n  type FileViewerShellStaticContextValue,\n  type FileViewerSidebarCollapsible,\n  type FileViewerSidebarDynamicContextValue,\n  type FileViewerSidebarMode,\n  type FileViewerSidebarOpenProps,\n  type FileViewerSidebarRegistration,\n  type FileViewerSidebarRequestedMode,\n  type FileViewerSidebarSide,\n  type FileViewerSidebarState,\n} from \"./file-viewer-context\";\nimport { createFileViewerElementRegistry } from \"./file-viewer-elements\";\nimport { useFileViewerShellKeyboard } from \"./file-viewer-shell-keyboard\";\nimport {\n  createFileViewerMotionKernel,\n  useFileViewerMotionFrame,\n} from \"./file-viewer-motion-kernel\";\nimport { useFileViewerMotionTelemetry } from \"./viewer-motion-telemetry\";\nimport {\n  FILE_VIEWER_MOTION_DURATION_MS,\n  type FileViewerMotionTarget,\n} from \"./file-viewer-motion-plan\";\nimport {\n  readElementRectSnapshot,\n  useStableElementSize,\n} from \"./viewer-measurement\";\n\nexport const FILE_VIEWER_INLINE_BREAKPOINT = 768;\n\ntype FileViewerSidebarOpenController = {\n  getSidebarOpen: () => boolean;\n  isSidebarOpen: boolean;\n  setSidebarOpen: (isSidebarOpen: boolean) => void;\n};\n\n// Bridges the controlled/uncontrolled `open` prop pair to real React state, so\n// every toggle re-renders the tree and the declarative writers (data\n// attributes, inert/aria, overlay translate classes) stay current.\nfunction useFileViewerSidebarOpenController({\n  collapsible,\n  defaultOpen,\n  onOpenChange,\n  open,\n}: FileViewerSidebarOpenProps & {\n  collapsible: FileViewerSidebarCollapsible;\n}): FileViewerSidebarOpenController {\n  const isControlled = open !== undefined;\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(\n    defaultOpen ?? false,\n  );\n  const isSidebarOpen =\n    collapsible === \"none\" ? true : (open ?? uncontrolledOpen);\n  const openRef = React.useRef(isSidebarOpen);\n  // Not an inline render-time mirror: setSidebarOpen writes this ref eagerly\n  // before a controlled parent applies the prop, and the kernel's flushSync\n  // re-renders mid-click with the stale prop value. Assign only when the\n  // committed value actually changes so the eager write survives that window.\n  useKeyedLayoutEffect(\n    joinEffectKey([\"file-viewer-open-mirror\", isSidebarOpen]),\n    () => {\n      openRef.current = isSidebarOpen;\n    },\n  );\n\n  const getSidebarOpen = React.useCallback(() => openRef.current, []);\n\n  const setSidebarOpen = React.useCallback(\n    (nextIsSidebarOpen: boolean) => {\n      openRef.current = nextIsSidebarOpen;\n      if (!isControlled) {\n        setUncontrolledOpen(nextIsSidebarOpen);\n      }\n      onOpenChange?.(nextIsSidebarOpen);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  return React.useMemo(\n    () => ({\n      getSidebarOpen,\n      isSidebarOpen,\n      setSidebarOpen,\n    }),\n    [getSidebarOpen, isSidebarOpen, setSidebarOpen],\n  );\n}\n\ntype FileViewerSidebarRegistrationController = {\n  canToggleSidebar: boolean;\n  effectiveCollapsible: FileViewerSidebarCollapsible;\n  registerSidebar: (registration: FileViewerSidebarRegistration) => () => void;\n  side: FileViewerSidebarSide;\n  sidebarId: string;\n  sidebarWidth: string;\n  sidebarWidthPixels: number;\n};\n\n// The mounted FileViewerSidebar is the single source of sidebar configuration\n// (collapsible, side, width); the shell only holds neutral defaults until one\n// registers. No sidebar registered means nothing to toggle and no motion, so\n// no fallback width measurement is needed.\nfunction useFileViewerSidebarRegistration({\n  fallbackSidebarId,\n}: {\n  fallbackSidebarId: string;\n}): FileViewerSidebarRegistrationController {\n  const [sidebarRegistration, setSidebarRegistration] =\n    React.useState<FileViewerSidebarRegistration | null>(null);\n  const effectiveCollapsible = sidebarRegistration?.collapsible ?? \"offcanvas\";\n  const side = sidebarRegistration?.side ?? \"left\";\n  const sidebarId = sidebarRegistration?.id ?? fallbackSidebarId;\n  const sidebarWidth =\n    sidebarRegistration?.width ?? DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH;\n  const sidebarWidthPixels = sidebarRegistration?.widthPixels ?? 0;\n  const canToggleSidebar =\n    sidebarRegistration !== null && effectiveCollapsible !== \"none\";\n\n  const registerSidebar = React.useCallback(\n    (registration: FileViewerSidebarRegistration) => {\n      setSidebarRegistration(registration);\n      return () => {\n        setSidebarRegistration((current) =>\n          current?.id === registration.id ? null : current,\n        );\n      };\n    },\n    [],\n  );\n\n  return React.useMemo(\n    () => ({\n      canToggleSidebar,\n      effectiveCollapsible,\n      registerSidebar,\n      side,\n      sidebarId,\n      sidebarWidth,\n      sidebarWidthPixels,\n    }),\n    [\n      canToggleSidebar,\n      effectiveCollapsible,\n      registerSidebar,\n      side,\n      sidebarId,\n      sidebarWidth,\n      sidebarWidthPixels,\n    ],\n  );\n}\n\ntype FileViewerShellControllerOptions = {\n  inlineBreakpoint: number;\n  sidebarMode: FileViewerSidebarRequestedMode;\n};\n\nexport type FileViewerShellController = {\n  effectiveCollapsible: FileViewerSidebarCollapsible;\n  headerMode: FileViewerContextValue[\"headerMode\"];\n  isSidebarOpen: boolean;\n  mode: FileViewerSidebarMode;\n  resourceCategory: FileViewerContextValue[\"resourceCategory\"];\n  rootId: string;\n  setRootElement: (element: HTMLDivElement | null) => void;\n  shellStaticContext: FileViewerShellStaticContextValue;\n  side: FileViewerSidebarSide;\n  sidebarDynamicContext: FileViewerSidebarDynamicContextValue;\n  sidebarState: FileViewerSidebarState;\n};\n\nexport function useFileViewerShellController({\n  inlineBreakpoint,\n  sidebarMode,\n}: FileViewerShellControllerOptions): FileViewerShellController {\n  const context = useFileViewerContext();\n\n  if (!context.isInsideFileViewer) {\n    throw new Error(\"FileViewer must be rendered within FileViewerProvider.\");\n  }\n\n  const rootId = React.useId();\n  const fallbackSidebarId = `${rootId}-sidebar`;\n  // The kernel owns time + style writes only; its settle-hold layout reads are\n  // injected from the quarantined measurement module here at the controller\n  // layer.\n  const motionKernel = React.useMemo(\n    () => createFileViewerMotionKernel({ readElementRectSnapshot }),\n    [],\n  );\n  const size = useStableElementSize<HTMLDivElement>({\n    retainLastNonZero: true,\n  });\n  const elementRegistry = React.useMemo(\n    () =>\n      createFileViewerElementRegistry({\n        motionKernel,\n        onViewerShellElementChange: size.setElement,\n      }),\n    [motionKernel, size.setElement],\n  );\n  const {\n    canToggleSidebar,\n    effectiveCollapsible,\n    registerSidebar,\n    side,\n    sidebarId,\n    sidebarWidth,\n    sidebarWidthPixels,\n  } = useFileViewerSidebarRegistration({\n    fallbackSidebarId,\n  });\n  const openController = useFileViewerSidebarOpenController({\n    ...context.sidebarOpenProps,\n    collapsible: effectiveCollapsible,\n  });\n  const isSidebarOpen = openController.isSidebarOpen;\n  const motionFrame = useFileViewerMotionFrame(motionKernel);\n  const isSidebarTransitioning = motionFrame.phase === \"sliding\";\n  const mode = resolveFileViewerSidebarMode({\n    inlineBreakpoint,\n    requestedMode: sidebarMode,\n    width: size.width,\n  });\n  const isSidebarInteractive = resolveFileViewerSidebarInteractive({\n    canToggleSidebar,\n    collapsible: effectiveCollapsible,\n    isSidebarOpen,\n    isSidebarTransitioning,\n    mode,\n  });\n  const sidebarState: FileViewerSidebarState = isSidebarOpen\n    ? \"expanded\"\n    : \"collapsed\";\n  const motionTarget = React.useMemo<FileViewerMotionTarget>(\n    () => ({\n      shellInlineSize: size.width ?? 0,\n      durationMs: FILE_VIEWER_MOTION_DURATION_MS,\n      mode,\n      open: isSidebarOpen,\n      side,\n      sidebarWidth: sidebarWidthPixels,\n    }),\n    [isSidebarOpen, mode, side, sidebarWidthPixels, size.width],\n  );\n  const motionTargetRef = React.useRef(motionTarget);\n  // Same two-writer shape as openRef above: setSidebarOpen stores the eager\n  // nextTarget before React commits, so only adopt the rendered target when\n  // it is a genuinely new object.\n  useKeyedLayoutEffect(\n    joinEffectKey([\"file-viewer-motion-target-mirror\", motionTarget]),\n    () => {\n      motionTargetRef.current = motionTarget;\n    },\n  );\n\n  const setRootElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      elementRegistry.registerViewerShellElement(element);\n    },\n    [elementRegistry],\n  );\n\n  // Adopt idle geometry (mount, resizes, mode flips) without animating. While\n  // a motion is in flight the kernel owns the target — EXCEPT on a mode flip:\n  // React renders the new mode immediately, so the kernel must be told (it\n  // snaps the motion) or an inline slide keeps writing gap widths against\n  // overlay DOM until settle.\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"file-viewer-sync-target\",\n      motionFrame.mode,\n      motionFrame.phase,\n      motionKernel,\n      motionTarget,\n    ]),\n    () => {\n      if (\n        motionFrame.phase !== \"idle\" &&\n        motionFrame.mode === motionTarget.mode\n      ) {\n        return;\n      }\n      motionKernel.syncTarget(motionTarget);\n    },\n  );\n\n  // Single owner of focus restoration: when the sidebar stops being\n  // interactive while focus is inside it, hand focus back to the trigger.\n  const previousIsSidebarInteractiveRef = React.useRef(isSidebarInteractive);\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"file-viewer-sidebar-focus-restore\",\n      elementRegistry,\n      isSidebarInteractive,\n    ]),\n    () => {\n      restoreFileViewerSidebarFocusOnClose({\n        elements: elementRegistry.getElements(),\n        isSidebarInteractive,\n        previousIsSidebarInteractive: previousIsSidebarInteractiveRef.current,\n      });\n      previousIsSidebarInteractiveRef.current = isSidebarInteractive;\n    },\n  );\n\n  const setSidebarOpen = React.useCallback<FileViewerSetSidebarOpen>(\n    (value) => {\n      // Toggling requires a registered, collapsible sidebar; a non-collapsible\n      // sidebar is pinned open and an unregistered one has nothing to move.\n      if (!canToggleSidebar) return;\n\n      const currentOpen = openController.getSidebarOpen();\n      const nextOpen = typeof value === \"function\" ? value(currentOpen) : value;\n      if (nextOpen === currentOpen) return;\n\n      openController.setSidebarOpen(nextOpen);\n      const nextTarget: FileViewerMotionTarget = {\n        ...motionTargetRef.current,\n        open: nextOpen,\n      };\n      motionTargetRef.current = nextTarget;\n      motionKernel.startMotion(nextTarget);\n    },\n    [canToggleSidebar, motionKernel, openController],\n  );\n  const toggleSidebar = React.useCallback(\n    () => setSidebarOpen((open) => !open),\n    [setSidebarOpen],\n  );\n  const closeSidebar = React.useCallback(\n    () => setSidebarOpen(false),\n    [setSidebarOpen],\n  );\n\n  useFileViewerShellKeyboard({\n    canToggleSidebar,\n    closeSidebar,\n    elementRegistry,\n    isSidebarInteractive,\n    isSidebarOverlayDismissible:\n      mode === \"overlay\" && canToggleSidebar && isSidebarOpen,\n    viewerShellElement: size.element,\n  });\n\n  useFileViewerMotionTelemetry({\n    getElements: elementRegistry.getElements,\n    getIsSidebarOpen: openController.getSidebarOpen,\n    motionDurationMs: FILE_VIEWER_MOTION_DURATION_MS,\n    motionKernel,\n    toggleSidebar,\n  });\n\n  const shellStaticContext = React.useMemo<FileViewerShellStaticContextValue>(\n    () => ({\n      canToggleSidebar,\n      collapsible: effectiveCollapsible,\n      elementRegistry,\n      mode,\n      motionDurationMs: FILE_VIEWER_MOTION_DURATION_MS,\n      motionKernel,\n      registerSidebar,\n      rootId,\n      setSidebarOpen,\n      side,\n      sidebarId,\n      sidebarWidth,\n      toggleSidebar,\n    }),\n    [\n      canToggleSidebar,\n      effectiveCollapsible,\n      elementRegistry,\n      mode,\n      motionKernel,\n      registerSidebar,\n      rootId,\n      setSidebarOpen,\n      side,\n      sidebarId,\n      sidebarWidth,\n      toggleSidebar,\n    ],\n  );\n  const sidebarDynamicContext =\n    React.useMemo<FileViewerSidebarDynamicContextValue>(\n      () => ({\n        isSidebarInteractive,\n        isSidebarOpen,\n        isSidebarTransitioning,\n        sidebarState,\n      }),\n      [\n        isSidebarInteractive,\n        isSidebarOpen,\n        isSidebarTransitioning,\n        sidebarState,\n      ],\n    );\n\n  return {\n    effectiveCollapsible,\n    headerMode: context.headerMode,\n    isSidebarOpen,\n    mode,\n    resourceCategory: context.resourceCategory,\n    rootId,\n    setRootElement,\n    shellStaticContext,\n    side,\n    sidebarDynamicContext,\n    sidebarState,\n  };\n}\n\n// An inline sidebar stays non-interactive while its geometry is in motion:\n// closing makes it inert immediately, opening keeps it inert until the\n// settle publish leaves the \"sliding\" phase.\nfunction resolveFileViewerSidebarInteractive({\n  canToggleSidebar,\n  collapsible,\n  isSidebarOpen,\n  isSidebarTransitioning,\n  mode,\n}: {\n  canToggleSidebar: boolean;\n  collapsible: FileViewerSidebarCollapsible;\n  isSidebarOpen: boolean;\n  isSidebarTransitioning: boolean;\n  mode: FileViewerSidebarMode;\n}): boolean {\n  if (collapsible === \"none\") return true;\n  if (!isSidebarOpen) return false;\n  return !(mode === \"inline\" && canToggleSidebar && isSidebarTransitioning);\n}\n\nfunction resolveFileViewerSidebarMode({\n  inlineBreakpoint,\n  requestedMode,\n  width,\n}: {\n  inlineBreakpoint: number;\n  requestedMode: FileViewerSidebarRequestedMode;\n  width: number | null;\n}): FileViewerSidebarMode {\n  if (requestedMode !== \"auto\") return requestedMode;\n  if (width === null) return \"overlay\";\n  return width >= inlineBreakpoint ? \"inline\" : \"overlay\";\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-shell-controller.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-shell-keyboard.ts",
      "content": "\"use client\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nimport type { FileViewerElementRegistry } from \"./file-viewer-elements\";\n\nexport function shouldCloseFileViewerSidebarOnEscape({\n  canToggleSidebar,\n  event,\n  isSidebarInteractive,\n}: {\n  canToggleSidebar: boolean;\n  event: KeyboardEvent;\n  isSidebarInteractive: boolean;\n}) {\n  return (\n    canToggleSidebar &&\n    isSidebarInteractive &&\n    event.key === \"Escape\" &&\n    !event.defaultPrevented &&\n    !event.repeat\n  );\n}\n\nexport function isFileViewerActiveElementInsideShell({\n  activeElement,\n  viewerShellElement,\n}: {\n  activeElement: Element | null;\n  viewerShellElement: HTMLElement | null;\n}) {\n  return Boolean(\n    activeElement &&\n      viewerShellElement &&\n      viewerShellElement.contains(activeElement),\n  );\n}\n\nexport function useFileViewerShellKeyboard({\n  canToggleSidebar,\n  closeSidebar,\n  elementRegistry,\n  isSidebarInteractive,\n  isSidebarOverlayDismissible,\n  viewerShellElement,\n}: {\n  canToggleSidebar: boolean;\n  closeSidebar: () => void;\n  elementRegistry: FileViewerElementRegistry;\n  isSidebarInteractive: boolean;\n  isSidebarOverlayDismissible: boolean;\n  viewerShellElement: HTMLDivElement | null;\n}) {\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"file-viewer-escape-close\",\n      canToggleSidebar,\n      closeSidebar,\n      elementRegistry,\n      isSidebarInteractive,\n      viewerShellElement,\n    ]),\n    () => {\n      const ownerDocument = viewerShellElement?.ownerDocument;\n      if (!ownerDocument) return;\n\n      const handleKeyDown = (event: KeyboardEvent) => {\n        if (\n          !shouldCloseFileViewerSidebarOnEscape({\n            canToggleSidebar,\n            event,\n            isSidebarInteractive,\n          })\n        ) {\n          return;\n        }\n\n        if (\n          !isFileViewerActiveElementInsideShell({\n            activeElement: ownerDocument.activeElement,\n            viewerShellElement:\n              elementRegistry.getElements().viewerShellElement,\n          })\n        ) {\n          return;\n        }\n\n        event.preventDefault();\n        closeSidebar();\n      };\n\n      ownerDocument.addEventListener(\"keydown\", handleKeyDown);\n      return () => ownerDocument.removeEventListener(\"keydown\", handleKeyDown);\n    },\n  );\n\n  // An overlay sidebar floats above the document, so a pointer press anywhere\n  // outside the panel (and its trigger, whose own click handles the toggle)\n  // dismisses it — the same light-dismiss contract as a popover.\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"file-viewer-overlay-dismiss\",\n      closeSidebar,\n      elementRegistry,\n      isSidebarOverlayDismissible,\n      viewerShellElement,\n    ]),\n    () => {\n      const ownerDocument = viewerShellElement?.ownerDocument;\n      if (!ownerDocument || !isSidebarOverlayDismissible) return;\n\n      const handlePointerDown = (event: PointerEvent) => {\n        const target = event.target;\n        if (!(target instanceof Node)) return;\n        const elements = elementRegistry.getElements();\n        if (elements.sidebarElement?.contains(target)) return;\n        if (elements.sidebarTriggerElement?.contains(target)) return;\n        closeSidebar();\n      };\n\n      ownerDocument.addEventListener(\"pointerdown\", handlePointerDown, {\n        capture: true,\n      });\n      return () =>\n        ownerDocument.removeEventListener(\"pointerdown\", handlePointerDown, {\n          capture: true,\n        });\n    },\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-shell-keyboard.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-sidebar-controller.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nimport { resolveFileViewerSidebarAccessibilityProps } from \"./file-viewer-accessibility\";\nimport {\n  type FileViewerSidebarCollapsible,\n  type FileViewerSidebarMode,\n  type FileViewerSidebarSide,\n  type FileViewerSidebarState,\n  useFileViewerShell,\n} from \"./file-viewer-context\";\nimport { useIsInsideFileViewerContent } from \"./file-viewer-layout\";\nimport { useStableCssLength, useStableElementSize } from \"./viewer-measurement\";\n\nexport type FileViewerSidebarController = {\n  accessibilityProps: ReturnType<\n    typeof resolveFileViewerSidebarAccessibilityProps\n  >;\n  customProperties: React.CSSProperties;\n  isInline: boolean;\n  isSidebarOpen: boolean;\n  mode: FileViewerSidebarMode;\n  panelStyle: React.CSSProperties;\n  resolvedCollapsible: FileViewerSidebarCollapsible;\n  resolvedSide: FileViewerSidebarSide;\n  setSidebarGapElement: (element: HTMLDivElement | null) => void;\n  setSidebarPanelElement: (element: HTMLElement | null) => void;\n  sidebarId: string;\n  sidebarState: FileViewerSidebarState;\n};\n\nexport function useFileViewerSidebarController({\n  collapsible,\n  id,\n  side,\n  style,\n  width,\n}: {\n  collapsible: FileViewerSidebarCollapsible | undefined;\n  id: string | undefined;\n  side: FileViewerSidebarSide | undefined;\n  style: React.CSSProperties | undefined;\n  width: string;\n}): FileViewerSidebarController {\n  const isInsideContent = useIsInsideFileViewerContent();\n  const shell = useFileViewerShell(\"FileViewerSidebar\");\n  const registerSidebar = shell.registerSidebar;\n  // Observed, not measured-once: the panel's resolved pixel width is the same\n  // number the motion kernel animates the gap to, so it must stay fresh across\n  // font-size and container changes or the panel edge and the layout width\n  // drift apart.\n  const sidebarSize = useStableElementSize<HTMLElement>({\n    retainLastNonZero: true,\n  });\n  const reactId = React.useId();\n  const sidebarId = id ?? `${reactId}-file-viewer-sidebar`;\n  const resolvedCollapsible = collapsible ?? shell.collapsible;\n  const resolvedSide = side ?? shell.side;\n  const isSidebarOpen =\n    resolvedCollapsible === \"none\" ? true : shell.isSidebarOpen;\n  const isSidebarInteractive =\n    resolvedCollapsible === \"none\" ? true : shell.isSidebarInteractive;\n  const sidebarState = isSidebarOpen ? \"expanded\" : \"collapsed\";\n  const isInline = shell.mode === \"inline\";\n  const declaredWidthPixels = useStableCssLength({\n    element: sidebarSize.element,\n    value: width,\n  });\n  const widthPixels =\n    declaredWidthPixels > 0 ? declaredWidthPixels : (sidebarSize.width ?? 0);\n  const setSidebarGapElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      shell.elementRegistry.registerSidebarGapElement(element);\n    },\n    [shell.elementRegistry],\n  );\n  const setSidebarSizeElement = sidebarSize.setElement;\n  const setSidebarPanelElement = React.useCallback(\n    (element: HTMLElement | null) => {\n      setSidebarSizeElement(element);\n      shell.elementRegistry.registerSidebarElement(element);\n    },\n    [setSidebarSizeElement, shell.elementRegistry],\n  );\n\n  if (process.env.NODE_ENV !== \"production\" && !isInsideContent) {\n    throw new Error(\n      \"FileViewerSidebar must be rendered inside FileViewerContent.\",\n    );\n  }\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"file-viewer-sidebar-registration\",\n      registerSidebar,\n      resolvedCollapsible,\n      resolvedSide,\n      sidebarId,\n      width,\n      widthPixels,\n    ]),\n    () =>\n      registerSidebar({\n        collapsible: resolvedCollapsible,\n        id: sidebarId,\n        side: resolvedSide,\n        width,\n        widthPixels,\n      }),\n  );\n\n  const accessibilityProps = resolveFileViewerSidebarAccessibilityProps({\n    collapsible: resolvedCollapsible,\n    deferInert:\n      !isSidebarOpen && !isSidebarInteractive && shell.isSidebarTransitioning,\n    isSidebarInteractive,\n  });\n  const customProperties = pickCssCustomProperties(style);\n  const panelStyle: React.CSSProperties = {\n    width,\n    // Overlay slides on a CSS transition; its duration is the same constant\n    // that drives the kernel clock, so the two timelines cannot drift.\n    ...(isInline\n      ? null\n      : { transitionDuration: `${shell.motionDurationMs}ms` }),\n    ...style,\n    ...(isSidebarInteractive ? null : { pointerEvents: \"none\" as const }),\n  };\n\n  // No memo: `panelStyle` and `customProperties` are fresh objects every\n  // render, so the controller value can never be referentially stable anyway.\n  return {\n    accessibilityProps,\n    customProperties,\n    isInline,\n    isSidebarOpen,\n    mode: shell.mode,\n    panelStyle,\n    resolvedCollapsible,\n    resolvedSide,\n    setSidebarGapElement,\n    setSidebarPanelElement,\n    sidebarId,\n    sidebarState,\n  };\n}\n\nfunction pickCssCustomProperties(style: React.CSSProperties | undefined) {\n  if (!style) return {};\n\n  return Object.fromEntries(\n    Object.entries(style).filter(([name]) => name.startsWith(\"--\")),\n  ) as React.CSSProperties;\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-sidebar-controller.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-motion-kernel.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { flushSync } from \"react-dom\";\n\nimport { FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT } from \"./file-viewer-elements\";\nimport {\n  easeFileViewerMotion,\n  FILE_VIEWER_MOTION_DURATION_MS,\n  areFileViewerMotionFramesEqual,\n  areFileViewerMotionRestFramesEqual,\n  createFileViewerIdleMotionFrame,\n  createFileViewerMotionPlan,\n  createFileViewerMotionRestFrame,\n  type FileViewerMotionFrame,\n  type FileViewerMotionPlan,\n  type FileViewerMotionRestFrame,\n  type FileViewerMotionTarget,\n} from \"./file-viewer-motion-plan\";\n\n// The kernel is the single owner of sidebar motion. It holds the clock (one\n// rAF loop from slide start through settle), writes the continuous inline\n// styles, and publishes to React subscribers only at phase edges\n// (idle → sliding → settling → idle). Everything discrete — data attributes,\n// inert/aria, the overlay translate classes — is owned by React renders.\n//\n// Commit-then-relax ordering: the first sliding frame is flushed\n// synchronously inside the toggle's own task, so renderers commit their\n// TARGET layout and rebase scroll before anything paints; the per-tick style\n// writes then only relax the counter-transform to identity. Settle removes a\n// no-op transform — it never commits layout, so no flushSync runs inside rAF.\nexport type FileViewerMotionKernel = {\n  getFlightRecords: () => readonly FileViewerMotionFlightRecord[];\n  getInteractiveSnapshot: () => FileViewerMotionFrame;\n  getSnapshot: () => FileViewerMotionFrame;\n  setDocumentSurface: (surface: FileViewerDocumentSurface | null) => void;\n  setSidebarGapElement: (element: HTMLElement | null) => void;\n  startMotion: (target: FileViewerMotionTarget) => void;\n  subscribe: (listener: () => void) => () => void;\n  syncTarget: (target: FileViewerMotionTarget) => void;\n};\n\n// Always-on flight recorder: every motion leaves a bounded trace (per-tick\n// widths, phase edges, settle holds, inter-frame gaps) so a blink report is\n// diagnosable after the fact without re-instrumenting.\nexport type FileViewerMotionFlightRecord = {\n  fromInlineSize: number;\n  id: number;\n  interrupted: boolean;\n  maxTickGapMs: number;\n  open: boolean;\n  settleHoldFrameCount: number;\n  startedAt: number;\n  ticks: FileViewerMotionFlightTick[];\n  toInlineSize: number;\n};\n\nexport type FileViewerMotionFlightTick = {\n  elapsedMs: number;\n  phase: FileViewerMotionFrame[\"phase\"];\n  sidebarInlineSize: number;\n};\n\nconst FILE_VIEWER_FLIGHT_RECORD_LIMIT = 8;\nconst FILE_VIEWER_FLIGHT_TICK_LIMIT = 240;\n\nexport type FileViewerDocumentSurface = {\n  element: HTMLElement;\n  getMotionProbeElement?: (() => HTMLElement | null) | null;\n  readSettleSnapshot?: FileViewerDocumentSurfaceSettleSnapshotReader | null;\n  resolveMotionStyle?: FileViewerDocumentSurfaceMotionResolver | null;\n};\n\nexport type FileViewerDocumentSurfaceSettleSnapshotReader = () =>\n  | readonly number[]\n  | null\n  | undefined;\n\n// Layout reads are quarantined outside the kernel (viewer-measurement / the\n// frame controller layer). The kernel owns time and style writes only, so the\n// settle-hold rect reader is injected by its creator rather than imported.\nexport type FileViewerElementRectSnapshotReader = (\n  element: HTMLElement | null,\n) => readonly number[];\n\nexport type FileViewerMotionKernelOptions = {\n  readElementRectSnapshot?: FileViewerElementRectSnapshotReader | null;\n};\n\nexport type FileViewerDocumentSurfaceMotionStyle = {\n  customProperties?: Readonly<Record<string, string | null>>;\n  transform: string;\n  transformOrigin: string;\n  willChange: string;\n};\n\nexport type FileViewerDocumentSurfaceMotionResolver = (\n  frame: FileViewerMotionFrame,\n) => FileViewerDocumentSurfaceMotionStyle | null;\n\ntype FileViewerActiveMotion = {\n  durationMs: number;\n  from: FileViewerMotionRestFrame;\n  // The clock re-anchors to the first tick's vsync frame time: startedAt is\n  // stamped inside the toggle's task, but the synchronous slide-start commit\n  // can burn 10ms+ before anything paints, and an ease anchored at the click\n  // lands its first painted frame that deep into the curve.\n  hasFrameClockAnchor: boolean;\n  id: number;\n  startedAt: number;\n  to: FileViewerMotionRestFrame;\n};\n\ntype FileViewerSettleRelease = {\n  idleFrame: FileViewerMotionFrame;\n  lastSnapshot: readonly number[];\n  remainingFrameCount: number;\n  settlingFrame: FileViewerMotionFrame;\n  stableFrameCount: number;\n};\n\nconst FILE_VIEWER_SETTLE_SCROLL_EPSILON_PX = 0.25;\nconst FILE_VIEWER_SETTLE_STABLE_FRAME_COUNT = 2;\nconst FILE_VIEWER_SETTLE_MAX_HOLD_FRAMES = 6;\nconst FILE_VIEWER_SUBPIXEL_ENDPOINT_EPSILON_PX = 1;\n\nexport const DEFAULT_FILE_VIEWER_MOTION_FRAME: FileViewerMotionFrame = {\n  shellInlineSize: 0,\n  durationMs: FILE_VIEWER_MOTION_DURATION_MS,\n  fromInlineSize: 0,\n  layoutInlineSize: 0,\n  mode: \"overlay\",\n  motionId: null,\n  motionProgress: 1,\n  open: false,\n  phase: \"idle\",\n  side: \"left\",\n  sidebarInlineSize: 0,\n  sidebarWidth: 0,\n  toInlineSize: 0,\n};\n\nexport function createFileViewerMotionKernel({\n  readElementRectSnapshot = null,\n}: FileViewerMotionKernelOptions = {}): FileViewerMotionKernel {\n  const listeners = new Set<() => void>();\n  let contractFrame = DEFAULT_FILE_VIEWER_MOTION_FRAME;\n  let interactiveFrame = DEFAULT_FILE_VIEWER_MOTION_FRAME;\n  let target: FileViewerMotionTarget = {\n    shellInlineSize: 0,\n    durationMs: DEFAULT_FILE_VIEWER_MOTION_FRAME.durationMs,\n    mode: DEFAULT_FILE_VIEWER_MOTION_FRAME.mode,\n    open: DEFAULT_FILE_VIEWER_MOTION_FRAME.open,\n    side: DEFAULT_FILE_VIEWER_MOTION_FRAME.side,\n    sidebarWidth: 0,\n  };\n  let documentSurface: FileViewerDocumentSurface | null = null;\n  let documentSurfaceCustomProperties = new Set<string>();\n  let sidebarGapElement: HTMLElement | null = null;\n  let activeMotion: FileViewerActiveMotion | null = null;\n  let settleRelease: FileViewerSettleRelease | null = null;\n  let rafHandle = 0;\n  let settleReleaseHandle = 0;\n  let motionSequence = 0;\n  const flightRecords: FileViewerMotionFlightRecord[] = [];\n  let activeFlightRecord: FileViewerMotionFlightRecord | null = null;\n  let lastFlightTickAt = 0;\n\n  const beginFlightRecord = (motion: FileViewerActiveMotion) => {\n    if (activeFlightRecord && activeFlightRecord.id !== motion.id) {\n      activeFlightRecord.interrupted = true;\n    }\n    activeFlightRecord = {\n      fromInlineSize: motion.from.layoutInlineSize,\n      id: motion.id,\n      interrupted: false,\n      maxTickGapMs: 0,\n      open: motion.to.open,\n      settleHoldFrameCount: 0,\n      startedAt: motion.startedAt,\n      ticks: [],\n      toInlineSize: motion.to.layoutInlineSize,\n    };\n    lastFlightTickAt = motion.startedAt;\n    flightRecords.push(activeFlightRecord);\n    if (flightRecords.length > FILE_VIEWER_FLIGHT_RECORD_LIMIT) {\n      flightRecords.splice(\n        0,\n        flightRecords.length - FILE_VIEWER_FLIGHT_RECORD_LIMIT,\n      );\n    }\n  };\n\n  const recordFlightTick = (frame: FileViewerMotionFrame, now = readNow()) => {\n    const record = activeFlightRecord;\n    if (!record || frame.motionId !== record.id) return;\n    record.maxTickGapMs = Math.max(record.maxTickGapMs, now - lastFlightTickAt);\n    lastFlightTickAt = now;\n    if (frame.phase === \"settling\") record.settleHoldFrameCount += 1;\n    if (record.ticks.length >= FILE_VIEWER_FLIGHT_TICK_LIMIT) return;\n    record.ticks.push({\n      elapsedMs: Math.max(0, now - record.startedAt),\n      phase: frame.phase,\n      sidebarInlineSize: frame.sidebarInlineSize,\n    });\n  };\n\n  const notify = () => {\n    for (const listener of listeners) listener();\n  };\n\n  const publishContractFrame = (\n    nextFrame: FileViewerMotionFrame,\n    { flushSubscribers = false }: { flushSubscribers?: boolean } = {},\n  ) => {\n    if (areFileViewerMotionFramesEqual(contractFrame, nextFrame)) return;\n    contractFrame = nextFrame;\n\n    if (flushSubscribers) {\n      flushSync(notify);\n      return;\n    }\n\n    notify();\n  };\n\n  // The gap's inline size and the document surface's counter-scale must land\n  // in the same frame: two independent CSS transitions (width on the gap,\n  // transform on the surface) can desync under main-thread jank, letting the\n  // document edge drift off the sidebar edge mid-slide. The kernel therefore\n  // writes both here, once per tick.\n  const writeElementStyles = (nextFrame: FileViewerMotionFrame) => {\n    writeSidebarGapStyle(nextFrame);\n    writeDocumentSurfaceStyle(nextFrame);\n  };\n\n  const commit = (\n    nextFrame: FileViewerMotionFrame,\n    { publish = true }: { publish?: boolean } = {},\n  ) => {\n    writeElementStyles(nextFrame);\n    interactiveFrame = nextFrame;\n    if (publish) publishContractFrame(nextFrame);\n  };\n\n  const cancelTick = () => {\n    if (rafHandle === 0) return;\n    getCancelAnimationFrame()(rafHandle);\n    rafHandle = 0;\n  };\n\n  const cancelSettleRelease = () => {\n    settleRelease = null;\n    if (settleReleaseHandle === 0) return;\n    getCancelAnimationFrame()(settleReleaseHandle);\n    settleReleaseHandle = 0;\n  };\n\n  const readMotionSample = (\n    motion: FileViewerActiveMotion,\n    now = readNow(),\n  ): FileViewerMotionFrame => {\n    const rawTimeProgress =\n      motion.durationMs <= 0\n        ? 1\n        : clamp((now - motion.startedAt) / motion.durationMs, 0, 1);\n    const rawMotionProgress = easeFileViewerMotion(rawTimeProgress);\n    const rawSidebarInlineSize = lerp(\n      motion.from.sidebarInlineSize,\n      motion.to.sidebarInlineSize,\n      rawMotionProgress,\n    );\n    const isSubpixelEndpoint =\n      rawMotionProgress > 0.98 &&\n      Math.abs(rawSidebarInlineSize - motion.to.sidebarInlineSize) <=\n        FILE_VIEWER_SUBPIXEL_ENDPOINT_EPSILON_PX;\n    const motionProgress = isSubpixelEndpoint ? 1 : rawMotionProgress;\n    const sidebarInlineSize = isSubpixelEndpoint\n      ? motion.to.sidebarInlineSize\n      : rawSidebarInlineSize;\n    const layoutInlineSize = Math.max(\n      0,\n      motion.to.shellInlineSize - sidebarInlineSize,\n    );\n    const fromInlineSize = motion.from.layoutInlineSize;\n\n    return {\n      shellInlineSize: motion.to.shellInlineSize,\n      durationMs: motion.durationMs,\n      fromInlineSize,\n      layoutInlineSize,\n      mode: motion.to.mode,\n      motionId: motion.id,\n      motionProgress,\n      open: motion.to.open,\n      phase: motionProgress < 1 ? \"sliding\" : \"settling\",\n      side: motion.to.side,\n      sidebarInlineSize,\n      sidebarWidth: motion.to.sidebarWidth,\n      toInlineSize: motion.to.layoutInlineSize,\n    };\n  };\n\n  const settle = () => {\n    if (!activeMotion) return;\n    const finishedMotion = activeMotion;\n    activeMotion = null;\n    cancelTick();\n\n    const idleFrame = createFileViewerIdleMotionFrame(finishedMotion.to);\n    const settlingFrame: FileViewerMotionFrame = {\n      ...idleFrame,\n      fromInlineSize: finishedMotion.from.layoutInlineSize,\n      motionId: finishedMotion.id,\n      phase: \"settling\",\n    };\n\n    // Layout and scroll were committed at slide start; settling only clears\n    // the (now identity) counter-transform and holds until shell geometry\n    // stops moving. Nothing here re-renders geometry, so no flushSync in rAF.\n    commit(settlingFrame, { publish: false });\n    recordFlightTick(settlingFrame);\n    publishContractFrame(settlingFrame);\n    scheduleSettleRelease(settlingFrame, idleFrame);\n  };\n\n  // Ticks sample the clock at the rAF FRAME timestamp, never the callback's\n  // execution time: the frame time is the vsync the paint belongs to, and a\n  // callback running late in a janky frame would otherwise write a position\n  // ahead of the frame's own time axis — a real paint-side velocity excess\n  // (the probes' rule 11, applied to the writer). The first tick also\n  // re-anchors startedAt to its frame time, so the ease starts at the first\n  // paintable frame rather than at the click that precedes the slide-start\n  // commit.\n  const tick = (frameTime: number) => {\n    rafHandle = 0;\n    if (!activeMotion) return;\n    const now = Number.isFinite(frameTime) ? frameTime : readNow();\n    if (!activeMotion.hasFrameClockAnchor) {\n      activeMotion.hasFrameClockAnchor = true;\n      activeMotion.startedAt = now;\n    }\n    const sample = readMotionSample(activeMotion, now);\n    if (sample.motionProgress >= 1) {\n      settle();\n      return;\n    }\n    commit(sample, { publish: false });\n    recordFlightTick(sample, now);\n    scheduleTick();\n  };\n\n  const scheduleTick = () => {\n    if (rafHandle !== 0) return;\n    rafHandle = getRequestAnimationFrame()(tick);\n  };\n\n  const scheduleSettleRelease = (\n    settlingFrame: FileViewerMotionFrame,\n    idleFrame: FileViewerMotionFrame,\n  ) => {\n    cancelSettleRelease();\n    settleRelease = {\n      idleFrame,\n      lastSnapshot: readSettleSnapshot(),\n      remainingFrameCount: FILE_VIEWER_SETTLE_MAX_HOLD_FRAMES,\n      settlingFrame,\n      stableFrameCount: 0,\n    };\n    scheduleSettleReleaseFrame();\n  };\n\n  const scheduleSettleReleaseFrame = () => {\n    if (settleReleaseHandle !== 0) return;\n    settleReleaseHandle = getRequestAnimationFrame()(holdSettleRelease);\n  };\n\n  const holdSettleRelease = () => {\n    settleReleaseHandle = 0;\n    if (!settleRelease) return;\n\n    commit(settleRelease.settlingFrame, { publish: false });\n    recordFlightTick(settleRelease.settlingFrame);\n\n    const snapshot = readSettleSnapshot();\n    const stableFrameCount = areSettleSnapshotsEqual(\n      settleRelease.lastSnapshot,\n      snapshot,\n    )\n      ? settleRelease.stableFrameCount + 1\n      : 0;\n    const remainingFrameCount = settleRelease.remainingFrameCount - 1;\n\n    if (\n      stableFrameCount >= FILE_VIEWER_SETTLE_STABLE_FRAME_COUNT ||\n      remainingFrameCount <= 0\n    ) {\n      const idleFrame = settleRelease.idleFrame;\n      settleRelease = null;\n      // Natural completion: close the flight record so the next motion does\n      // not mark this one interrupted.\n      activeFlightRecord = null;\n      commit(idleFrame);\n      return;\n    }\n\n    settleRelease = {\n      ...settleRelease,\n      lastSnapshot: snapshot,\n      remainingFrameCount,\n      stableFrameCount,\n    };\n    scheduleSettleReleaseFrame();\n  };\n\n  // The event carries the kernel's LIVE frame so renderers can capture their\n  // pre-commit anchor against what is actually on screen — during a mid-flight\n  // retarget that is the settled layout PLUS the in-flight transform, not the\n  // settled layout alone.\n  const dispatchBeforeLayoutMotion = (currentFrame: FileViewerMotionFrame) => {\n    documentSurface?.element.dispatchEvent(\n      new CustomEvent<FileViewerMotionFrame>(\n        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,\n        { detail: currentFrame },\n      ),\n    );\n  };\n\n  const interruptActiveFlightRecord = () => {\n    if (!activeFlightRecord) return;\n    activeFlightRecord.interrupted = true;\n    activeFlightRecord = null;\n  };\n\n  const retarget = (nextTarget: FileViewerMotionTarget, animate: boolean) => {\n    cancelSettleRelease();\n    // Continuity is with what is PAINTED, not with the clock: mid-flight the\n    // screen shows the last tick's commit (`interactiveFrame`), which can be\n    // a frame behind a fresh clock sample. Planning (and the before-motion\n    // capture renderers do off the event detail) from the painted frame keeps\n    // the retarget hand-off pixel-continuous; the new motion simply re-lerps\n    // from the painted geometry.\n    const currentFrame =\n      interactiveFrame.shellInlineSize > 0\n        ? interactiveFrame\n        : createFileViewerIdleMotionFrame(\n            createFileViewerMotionRestFrame(target),\n          );\n    const plan = createFileViewerMotionPlan({\n      animate: animate && !prefersReducedMotion(),\n      currentFrame,\n      nextTarget,\n    });\n    if (shouldDispatchBeforeLayoutMotion(plan)) {\n      dispatchBeforeLayoutMotion(currentFrame);\n    }\n    target = plan.resolvedTarget;\n\n    if (!plan.shouldAnimate) {\n      if (activeMotion) interruptActiveFlightRecord();\n      activeMotion = null;\n      cancelTick();\n      commit(createFileViewerIdleMotionFrame(plan.nextRestFrame));\n      return;\n    }\n\n    motionSequence += 1;\n    activeMotion = {\n      durationMs: plan.resolvedTarget.durationMs,\n      from: { ...plan.currentRestFrame, layoutInlineSize: plan.fromInlineSize },\n      hasFrameClockAnchor: false,\n      id: motionSequence,\n      startedAt: readNow(),\n      to: plan.nextRestFrame,\n    };\n    beginFlightRecord(activeMotion);\n    const startFrame = readMotionSample(activeMotion, activeMotion.startedAt);\n    // Commit the discontinuity while it cannot be seen: flush the first\n    // sliding frame synchronously (inside the toggle's own task) so renderers\n    // lay out at the target width and rebase scroll before first paint, hidden\n    // behind the counter-transform written above in the same task.\n    writeElementStyles(startFrame);\n    interactiveFrame = startFrame;\n    recordFlightTick(startFrame, activeMotion.startedAt);\n    publishContractFrame(startFrame, { flushSubscribers: true });\n    scheduleTick();\n  };\n\n  const syncTarget = (nextTarget: FileViewerMotionTarget) => {\n    const nextRestFrame = createFileViewerMotionRestFrame(nextTarget);\n\n    if (activeMotion) {\n      target = nextTarget;\n      if (areFileViewerMotionRestFramesEqual(activeMotion.to, nextRestFrame)) {\n        return;\n      }\n      // A mode flip mid-motion (breakpoint crossing during the slide) cannot\n      // be animated: React re-renders the new mode immediately, so an inline\n      // slide continuing against overlay DOM (or vice versa) double-moves the\n      // surface. Snap to the new rest geometry instead.\n      if (nextRestFrame.mode !== activeMotion.to.mode) {\n        interruptActiveFlightRecord();\n        activeMotion = null;\n        cancelTick();\n        cancelSettleRelease();\n        commit(createFileViewerIdleMotionFrame(nextRestFrame));\n        return;\n      }\n      retarget(nextTarget, true);\n      return;\n    }\n\n    cancelSettleRelease();\n    target = nextTarget;\n    commit(createFileViewerIdleMotionFrame(nextRestFrame));\n  };\n\n  return {\n    getFlightRecords: () => flightRecords.slice(),\n    getInteractiveSnapshot: () =>\n      activeMotion ? readMotionSample(activeMotion) : interactiveFrame,\n    getSnapshot: () => contractFrame,\n    setDocumentSurface: (surface) => {\n      const previousSurface = documentSurface;\n      if (\n        previousSurface &&\n        (!surface || previousSurface.element !== surface.element)\n      ) {\n        clearDocumentSurfaceStyle(previousSurface.element);\n      }\n      documentSurface = surface;\n      writeDocumentSurfaceStyle(interactiveFrame);\n    },\n    setSidebarGapElement: (element) => {\n      sidebarGapElement = element;\n      writeSidebarGapStyle(interactiveFrame);\n    },\n    startMotion: (nextTarget) => retarget(nextTarget, true),\n    subscribe: (listener) => {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n    syncTarget,\n  };\n\n  function writeSidebarGapStyle(nextFrame: FileViewerMotionFrame) {\n    if (!sidebarGapElement) return;\n\n    // Overlay motion is CSS-owned; relinquish the gap so its `w-0` class is\n    // the only writer outside inline mode.\n    if (nextFrame.mode !== \"inline\") {\n      sidebarGapElement.style.width = \"\";\n      sidebarGapElement.style.flexBasis = \"\";\n      return;\n    }\n\n    sidebarGapElement.style.width = `${nextFrame.sidebarInlineSize}px`;\n    sidebarGapElement.style.flexBasis = `${nextFrame.sidebarInlineSize}px`;\n  }\n\n  function writeDocumentSurfaceStyle(nextFrame: FileViewerMotionFrame) {\n    if (!documentSurface) return;\n\n    const { element, resolveMotionStyle } = documentSurface;\n    const resolvedStyle = resolveMotionStyle?.(nextFrame);\n    if (resolvedStyle) {\n      writeDocumentSurfaceCustomProperties(\n        element,\n        resolvedStyle.customProperties,\n      );\n      element.style.transform = resolvedStyle.transform;\n      element.style.transformOrigin = resolvedStyle.transformOrigin;\n      element.style.willChange = resolvedStyle.willChange;\n      return;\n    }\n\n    // Default (no motion resolver): identity. Fit-width renderers register\n    // the shared commit-then-relax resolver (file-viewer-fit-width-motion);\n    // a surface without one either tracks the live DOM width on its own or\n    // opts out of shell motion entirely, and must not be transformed here.\n    writeDocumentSurfaceCustomProperties(element, null);\n    element.style.transform = \"\";\n    element.style.transformOrigin = \"\";\n    element.style.willChange = \"\";\n  }\n\n  function writeDocumentSurfaceCustomProperties(\n    element: HTMLElement,\n    customProperties:\n      | Readonly<Record<string, string | null>>\n      | null\n      | undefined,\n  ) {\n    const nextNames = new Set(Object.keys(customProperties ?? {}));\n    for (const name of documentSurfaceCustomProperties) {\n      if (!nextNames.has(name)) {\n        element.style.removeProperty(name);\n      }\n    }\n\n    for (const [name, value] of Object.entries(customProperties ?? {})) {\n      if (value == null) {\n        element.style.removeProperty(name);\n      } else {\n        element.style.setProperty(name, value);\n      }\n    }\n\n    documentSurfaceCustomProperties = nextNames;\n  }\n\n  function clearDocumentSurfaceStyle(element: HTMLElement) {\n    element.style.transform = \"\";\n    element.style.transformOrigin = \"\";\n    element.style.willChange = \"\";\n    for (const name of documentSurfaceCustomProperties) {\n      element.style.removeProperty(name);\n    }\n    documentSurfaceCustomProperties = new Set();\n  }\n\n  function readSettleSnapshot(): readonly number[] {\n    const values: number[] = [];\n\n    appendElementRectSnapshot(values, sidebarGapElement);\n    appendElementRectSnapshot(values, documentSurface?.element ?? null);\n\n    try {\n      const surfaceSnapshot = documentSurface?.readSettleSnapshot?.();\n      if (surfaceSnapshot) {\n        values.push(...surfaceSnapshot.map(toSettleSnapshotNumber));\n      }\n    } catch {\n      // A renderer snapshot is diagnostic, not correctness-critical. If a\n      // renderer unmounts while settling, fall back to shell geometry.\n    }\n\n    return values.length > 0 ? values : [0];\n  }\n\n  function appendElementRectSnapshot(\n    values: number[],\n    element: HTMLElement | null,\n  ) {\n    if (!readElementRectSnapshot || !element) return;\n    for (const value of readElementRectSnapshot(element)) {\n      values.push(toSettleSnapshotNumber(value));\n    }\n  }\n}\n\nfunction areSettleSnapshotsEqual(\n  previous: readonly number[],\n  next: readonly number[],\n) {\n  if (previous.length !== next.length) return false;\n  return previous.every(\n    (value, index) =>\n      Math.abs(value - next[index]) <= FILE_VIEWER_SETTLE_SCROLL_EPSILON_PX,\n  );\n}\n\nfunction toSettleSnapshotNumber(value: number) {\n  return Number.isFinite(value) ? value : 0;\n}\n\nfunction shouldDispatchBeforeLayoutMotion({\n  currentRestFrame,\n  nextRestFrame,\n}: FileViewerMotionPlan) {\n  return (\n    currentRestFrame.mode === \"inline\" &&\n    nextRestFrame.mode === \"inline\" &&\n    Math.abs(\n      currentRestFrame.layoutInlineSize - nextRestFrame.layoutInlineSize,\n    ) > 0.001\n  );\n}\n\nexport function useFileViewerMotionFrame(\n  kernel: FileViewerMotionKernel | null | undefined,\n): FileViewerMotionFrame {\n  const subscribe = React.useCallback(\n    (listener: () => void) => kernel?.subscribe(listener) ?? (() => {}),\n    [kernel],\n  );\n  const getSnapshot = React.useCallback(\n    () => kernel?.getSnapshot() ?? DEFAULT_FILE_VIEWER_MOTION_FRAME,\n    [kernel],\n  );\n\n  return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nfunction prefersReducedMotion() {\n  return (\n    typeof matchMedia === \"function\" &&\n    matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  );\n}\n\nfunction readNow() {\n  return typeof performance !== \"undefined\" &&\n    typeof performance.now === \"function\"\n    ? performance.now()\n    : Date.now();\n}\n\nfunction getRequestAnimationFrame() {\n  return (\n    globalThis.requestAnimationFrame ??\n    ((callback: FrameRequestCallback) =>\n      window.setTimeout(() => callback(readNow()), 16))\n  );\n}\n\nfunction getCancelAnimationFrame() {\n  return (\n    globalThis.cancelAnimationFrame ??\n    ((id: number) => {\n      window.clearTimeout(id);\n    })\n  );\n}\n\nfunction lerp(from: number, to: number, progress: number) {\n  return from + (to - from) * progress;\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-motion-kernel.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-motion-telemetry.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport type { FileViewerElementRegistry } from \"./file-viewer-elements\";\nimport type {\n  FileViewerMotionFlightRecord,\n  FileViewerMotionKernel,\n} from \"./file-viewer-motion-kernel\";\n\n// Shell-level motion telemetry, shipped with the component. It measures the\n// FileViewer's own contract — gap monotonicity, surface continuity across the\n// sliding → settling edge, scroll identity, DOM churn, layout shift, main\n// thread — for ANY renderer hosted in the shell, complementing the renderer\n// probes (e.g. __pdfViewerTelemetry's canvas continuity).\ntype FileViewerMotionTelemetryMetricId =\n  | \"blink\"\n  | \"gap-stability\"\n  | \"overshoot\"\n  | \"content-start-snap\"\n  | \"content-overshoot\"\n  | \"settle-snap\"\n  | \"scroll-identity\"\n  | \"dom-mutations\"\n  | \"layout-shift\"\n  | \"main-thread\"\n  | \"cycle-invariance\";\n\nexport type FileViewerMotionTelemetryMetric = {\n  budget: string;\n  detail: string;\n  id: FileViewerMotionTelemetryMetricId;\n  label: string;\n  passed: boolean;\n  value: string;\n};\n\nexport type FileViewerMotionTelemetrySample = {\n  contentHeight: number;\n  contentLeft: number;\n  contentTop: number;\n  contentWidth: number;\n  elapsedMs: number;\n  gapWidth: number;\n  motionPhase: string;\n  scrollHeight: number;\n  scrollTop: number;\n  scrollWidth: number;\n  sidebarState: string | null;\n  sidebarWidth: number;\n  surfaceHeight: number;\n  surfaceLeft: number;\n  surfaceTop: number;\n  surfaceTransform: string;\n  surfaceWidth: number;\n  timestamp: number;\n  visibleCanvasCount: number;\n};\n\nexport type FileViewerMotionTelemetryRun = {\n  action: \"close\" | \"open\";\n  addedNodeCount: number;\n  after: FileViewerMotionTelemetrySample;\n  before: FileViewerMotionTelemetrySample;\n  canvasResizeMutationCount: number;\n  layoutShiftCount: number;\n  layoutShiftScore: number;\n  longTaskCount: number;\n  longTaskDurationMs: number;\n  mutationCount: number;\n  removedNodeCount: number;\n  samples: FileViewerMotionTelemetrySample[];\n  scrollEventCount: number;\n};\n\nexport type FileViewerMotionTelemetryResult = {\n  durationMs: number;\n  metrics: FileViewerMotionTelemetryMetric[];\n  runs: FileViewerMotionTelemetryRun[];\n  sampledFrameCount: number;\n  status: \"failed\" | \"passed\";\n};\n\nexport type FileViewerMotionTelemetryRuntime = {\n  getFlightRecords: () => readonly FileViewerMotionFlightRecord[];\n  getLastResult: () => FileViewerMotionTelemetryResult | null;\n  readSample: () => FileViewerMotionTelemetrySample | null;\n  run: (options?: {\n    settleFrameCount?: number;\n  }) => Promise<FileViewerMotionTelemetryResult | null>;\n};\n\ndeclare global {\n  interface Window {\n    __fileViewerMotionTelemetry?: FileViewerMotionTelemetryRuntime;\n  }\n}\n\nconst FILE_VIEWER_TELEMETRY_DEFAULT_SETTLE_FRAMES = 16;\nconst FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX = 1;\nconst FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX = 1;\nconst FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX = 1.5;\nconst FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX = 1;\nconst FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX = 1.5;\nconst FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX = 1;\nconst FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX = 2;\nconst FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS = 34;\nconst FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS = 250;\n\ntype FileViewerMotionTelemetryHost = {\n  getElements: FileViewerElementRegistry[\"getElements\"];\n  getIsSidebarOpen: () => boolean;\n  motionDurationMs: number;\n  motionKernel: FileViewerMotionKernel;\n  toggleSidebar: () => void;\n};\n\nexport function useFileViewerMotionTelemetry(\n  host: FileViewerMotionTelemetryHost,\n) {\n  const hostRef = React.useRef(host);\n  const lastResultRef = React.useRef<FileViewerMotionTelemetryResult | null>(\n    null,\n  );\n  const isRunningRef = React.useRef(false);\n  hostRef.current = host;\n\n  useKeyedMountEffect(joinEffectKey([\"file-viewer-motion-telemetry\"]), () => {\n    if (typeof window === \"undefined\") return;\n\n    const previousRuntime = window.__fileViewerMotionTelemetry;\n    const runtime: FileViewerMotionTelemetryRuntime = {\n      getFlightRecords: () => hostRef.current.motionKernel.getFlightRecords(),\n      getLastResult: () => lastResultRef.current,\n      readSample: () =>\n        readFileViewerMotionTelemetrySample(hostRef.current, performance.now()),\n      run: async (options) => {\n        // A telemetry run drives real toggles; a concurrent run would\n        // interleave motions and score garbage. One at a time.\n        if (isRunningRef.current) return null;\n        isRunningRef.current = true;\n        try {\n          const result = await runFileViewerMotionTelemetry(hostRef, options);\n          lastResultRef.current = result;\n          if (result) logFileViewerMotionTelemetryResult(result);\n          return result;\n        } finally {\n          isRunningRef.current = false;\n        }\n      },\n    };\n\n    window.__fileViewerMotionTelemetry = runtime;\n\n    return () => {\n      if (window.__fileViewerMotionTelemetry === runtime) {\n        window.__fileViewerMotionTelemetry = previousRuntime;\n      }\n    };\n  });\n}\n\nasync function runFileViewerMotionTelemetry(\n  hostRef: React.MutableRefObject<FileViewerMotionTelemetryHost>,\n  options?: { settleFrameCount?: number },\n): Promise<FileViewerMotionTelemetryResult | null> {\n  const host = hostRef.current;\n  const elements = host.getElements();\n  if (!elements.viewerShellElement || !elements.documentSurfaceElement) {\n    return null;\n  }\n\n  const startedAt = performance.now();\n  const settleFrameCount =\n    options?.settleFrameCount ?? FILE_VIEWER_TELEMETRY_DEFAULT_SETTLE_FRAMES;\n  const firstAction = host.getIsSidebarOpen() ? \"close\" : \"open\";\n  const secondAction = firstAction === \"close\" ? \"open\" : \"close\";\n  const runs: FileViewerMotionTelemetryRun[] = [];\n\n  runs.push(\n    await sampleFileViewerMotionRun({\n      action: firstAction,\n      hostRef,\n      settleFrameCount,\n    }),\n  );\n  await waitForFileViewerTelemetryFrames(6);\n  runs.push(\n    await sampleFileViewerMotionRun({\n      action: secondAction,\n      hostRef,\n      settleFrameCount,\n    }),\n  );\n\n  const metrics = collectFileViewerMotionTelemetryMetrics(runs);\n\n  return {\n    durationMs: Math.max(0, performance.now() - startedAt),\n    metrics,\n    runs,\n    sampledFrameCount: runs.reduce(\n      (count, run) => count + run.samples.length,\n      0,\n    ),\n    status: metrics.every((metric) => metric.passed) ? \"passed\" : \"failed\",\n  };\n}\n\nasync function sampleFileViewerMotionRun({\n  action,\n  hostRef,\n  settleFrameCount,\n}: {\n  action: \"close\" | \"open\";\n  hostRef: React.MutableRefObject<FileViewerMotionTelemetryHost>;\n  settleFrameCount: number;\n}): Promise<FileViewerMotionTelemetryRun> {\n  const host = hostRef.current;\n  const startedAt = performance.now();\n  const before =\n    readFileViewerMotionTelemetrySample(host, startedAt) ??\n    createEmptyFileViewerMotionTelemetrySample();\n  const trackers = createFileViewerMotionTelemetryTrackers(host);\n  const samples: FileViewerMotionTelemetrySample[] = [];\n\n  host.toggleSidebar();\n  samples.push(readFileViewerMotionTelemetrySample(host, startedAt) ?? before);\n\n  const sampleUntil = host.motionDurationMs + settleFrameCount * (1000 / 60);\n  while (performance.now() - startedAt < sampleUntil) {\n    await waitForFileViewerTelemetryFrames(1);\n    const sample = readFileViewerMotionTelemetrySample(\n      hostRef.current,\n      startedAt,\n    );\n    if (sample) samples.push(sample);\n  }\n\n  await waitForFileViewerTelemetryFrames(2);\n  const after =\n    readFileViewerMotionTelemetrySample(hostRef.current, startedAt) ?? before;\n  trackers.disconnect();\n\n  return {\n    action,\n    addedNodeCount: trackers.addedNodeCount,\n    after,\n    before,\n    canvasResizeMutationCount: trackers.canvasResizeMutationCount,\n    layoutShiftCount: trackers.layoutShiftCount,\n    layoutShiftScore: trackers.layoutShiftScore,\n    longTaskCount: trackers.longTaskCount,\n    longTaskDurationMs: trackers.longTaskDurationMs,\n    mutationCount: trackers.mutationCount,\n    removedNodeCount: trackers.removedNodeCount,\n    samples,\n    scrollEventCount: trackers.scrollEventCount,\n  };\n}\n\nfunction createEmptyFileViewerMotionTelemetrySample(): FileViewerMotionTelemetrySample {\n  return {\n    contentHeight: 0,\n    contentLeft: 0,\n    contentTop: 0,\n    contentWidth: 0,\n    elapsedMs: 0,\n    gapWidth: 0,\n    motionPhase: \"idle\",\n    scrollHeight: 0,\n    scrollTop: 0,\n    scrollWidth: 0,\n    sidebarState: null,\n    sidebarWidth: 0,\n    surfaceHeight: 0,\n    surfaceLeft: 0,\n    surfaceTop: 0,\n    surfaceTransform: \"\",\n    surfaceWidth: 0,\n    timestamp: 0,\n    visibleCanvasCount: 0,\n  };\n}\n\nfunction readFileViewerMotionTelemetrySample(\n  host: FileViewerMotionTelemetryHost,\n  startedAt: number,\n): FileViewerMotionTelemetrySample | null {\n  const elements = host.getElements();\n  const surfaceElement = elements.documentSurfaceElement;\n  if (!surfaceElement) return null;\n\n  const gapRect = elements.sidebarGapElement?.getBoundingClientRect();\n  const sidebarRect = elements.sidebarElement?.getBoundingClientRect();\n  const surfaceRect = surfaceElement.getBoundingClientRect();\n  const contentElement = readFileViewerMotionProbeElement(elements);\n  const contentRect = contentElement\n    ? readFileViewerMotionProbeRect(contentElement, elements.viewerShellElement)\n    : null;\n  const scroller = findFileViewerTelemetryScroller(surfaceElement);\n  const frame = host.motionKernel.getInteractiveSnapshot();\n  const visibleCanvasCount = countFileViewerVisibleCanvases(surfaceElement);\n\n  return {\n    contentHeight: contentRect?.height ?? surfaceRect.height,\n    contentLeft: contentRect?.left ?? surfaceRect.left,\n    contentTop: contentRect?.top ?? surfaceRect.top,\n    contentWidth: contentRect?.width ?? surfaceRect.width,\n    elapsedMs: Math.max(0, performance.now() - startedAt),\n    gapWidth: gapRect?.width ?? 0,\n    motionPhase: frame.phase,\n    scrollHeight: scroller?.scrollHeight ?? 0,\n    scrollTop: scroller?.scrollTop ?? 0,\n    scrollWidth: scroller?.scrollWidth ?? 0,\n    sidebarState:\n      elements.sidebarElement?.getAttribute(\"data-file-viewer-sidebar-state\") ??\n      null,\n    sidebarWidth: sidebarRect?.width ?? 0,\n    surfaceHeight: surfaceRect.height,\n    surfaceLeft: surfaceRect.left,\n    surfaceTop: surfaceRect.top,\n    surfaceTransform: getComputedStyle(surfaceElement).transform,\n    surfaceWidth: surfaceRect.width,\n    timestamp: performance.now(),\n    visibleCanvasCount,\n  };\n}\n\nfunction readFileViewerMotionProbeElement(\n  elements: ReturnType<FileViewerElementRegistry[\"getElements\"]>,\n) {\n  try {\n    return elements.getDocumentSurfaceMotionProbeElement?.() ?? null;\n  } catch {\n    return null;\n  }\n}\n\nfunction readFileViewerMotionProbeRect(\n  element: HTMLElement,\n  boundaryElement: HTMLElement | null,\n) {\n  const rect = element.getBoundingClientRect();\n  let left = rect.left;\n  let right = rect.right;\n\n  // getBoundingClientRect reports the transformed content box even when an\n  // ancestor removes part of it at paint. Intersect horizontal clipping\n  // ancestors so telemetry measures the pixels a sidebar resize can actually\n  // show, while leaving vertical viewport clipping (ordinary document\n  // scrolling) out of this horizontal motion signal.\n  let ancestor = element.parentElement;\n  while (ancestor) {\n    const style = getComputedStyle(ancestor);\n    if (clipsFileViewerMotionProbeInline(style)) {\n      const ancestorRect = ancestor.getBoundingClientRect();\n      left = Math.max(left, ancestorRect.left);\n      right = Math.min(right, ancestorRect.right);\n    }\n    if (ancestor === boundaryElement) break;\n    ancestor = ancestor.parentElement;\n  }\n\n  return {\n    height: rect.height,\n    left,\n    top: rect.top,\n    width: Math.max(0, right - left),\n  };\n}\n\nfunction clipsFileViewerMotionProbeInline(style: CSSStyleDeclaration) {\n  const contain = style.contain.split(/\\s+/);\n  return (\n    style.overflowX === \"auto\" ||\n    style.overflowX === \"clip\" ||\n    style.overflowX === \"hidden\" ||\n    style.overflowX === \"scroll\" ||\n    contain.includes(\"content\") ||\n    contain.includes(\"paint\") ||\n    contain.includes(\"strict\")\n  );\n}\n\nfunction findFileViewerTelemetryScroller(element: HTMLElement) {\n  let candidate: HTMLElement | null = element.parentElement;\n  while (candidate) {\n    const overflowY = getComputedStyle(candidate).overflowY;\n    if (overflowY === \"auto\" || overflowY === \"scroll\") return candidate;\n    candidate = candidate.parentElement;\n  }\n  return null;\n}\n\nfunction countFileViewerVisibleCanvases(surfaceElement: HTMLElement) {\n  let count = 0;\n  for (const canvas of surfaceElement.querySelectorAll(\"canvas\")) {\n    if (!(canvas instanceof HTMLCanvasElement)) continue;\n    if (canvas.width <= 0 || canvas.height <= 0) continue;\n    const rect = canvas.getBoundingClientRect();\n    if (rect.width <= 0 || rect.height <= 0) continue;\n    count += 1;\n  }\n  return count;\n}\n\nfunction createFileViewerMotionTelemetryTrackers(\n  host: FileViewerMotionTelemetryHost,\n) {\n  const elements = host.getElements();\n  const rootElement = elements.viewerShellElement;\n  const scroller = elements.documentSurfaceElement\n    ? findFileViewerTelemetryScroller(elements.documentSurfaceElement)\n    : null;\n\n  const trackers = {\n    addedNodeCount: 0,\n    canvasResizeMutationCount: 0,\n    layoutShiftCount: 0,\n    layoutShiftScore: 0,\n    longTaskCount: 0,\n    longTaskDurationMs: 0,\n    mutationCount: 0,\n    removedNodeCount: 0,\n    scrollEventCount: 0,\n    disconnect: () => {},\n  };\n\n  const cleanups: (() => void)[] = [];\n\n  if (rootElement && typeof MutationObserver === \"function\") {\n    const mutationObserver = new MutationObserver((records) => {\n      for (const record of records) {\n        trackers.mutationCount += 1;\n        trackers.addedNodeCount += record.addedNodes.length;\n        trackers.removedNodeCount += record.removedNodes.length;\n        if (\n          record.type === \"attributes\" &&\n          record.target instanceof HTMLCanvasElement &&\n          (record.attributeName === \"width\" ||\n            record.attributeName === \"height\")\n        ) {\n          trackers.canvasResizeMutationCount += 1;\n        }\n      }\n    });\n    mutationObserver.observe(rootElement, {\n      attributeFilter: [\"width\", \"height\", \"style\", \"data-pdf-render-status\"],\n      attributes: true,\n      childList: true,\n      subtree: true,\n    });\n    cleanups.push(() => mutationObserver.disconnect());\n  }\n\n  const layoutShiftObserver = observeFileViewerPerformanceEntries(\n    \"layout-shift\",\n    (entry) => {\n      const layoutShift = entry as PerformanceEntry & {\n        hadRecentInput?: boolean;\n        value?: number;\n      };\n      if (layoutShift.hadRecentInput) return;\n      trackers.layoutShiftCount += 1;\n      trackers.layoutShiftScore += layoutShift.value ?? 0;\n    },\n  );\n  if (layoutShiftObserver) cleanups.push(layoutShiftObserver);\n\n  const longTaskObserver = observeFileViewerPerformanceEntries(\n    \"longtask\",\n    (entry) => {\n      trackers.longTaskCount += 1;\n      trackers.longTaskDurationMs += entry.duration;\n    },\n  );\n  if (longTaskObserver) cleanups.push(longTaskObserver);\n\n  if (scroller) {\n    const handleScroll = () => {\n      trackers.scrollEventCount += 1;\n    };\n    scroller.addEventListener(\"scroll\", handleScroll, { passive: true });\n    cleanups.push(() => scroller.removeEventListener(\"scroll\", handleScroll));\n  }\n\n  trackers.disconnect = () => {\n    for (const cleanup of cleanups) cleanup();\n  };\n\n  return trackers;\n}\n\nfunction observeFileViewerPerformanceEntries(\n  type: string,\n  onEntry: (entry: PerformanceEntry) => void,\n) {\n  if (typeof PerformanceObserver !== \"function\") return null;\n  const supported = PerformanceObserver.supportedEntryTypes;\n  if (Array.isArray(supported) && !supported.includes(type)) return null;\n\n  try {\n    const observer = new PerformanceObserver((list) => {\n      for (const entry of list.getEntries()) onEntry(entry);\n    });\n    observer.observe({ type, buffered: false });\n    return () => observer.disconnect();\n  } catch {\n    return null;\n  }\n}\n\nfunction waitForFileViewerTelemetryFrames(frameCount: number) {\n  return new Promise<void>((resolve) => {\n    let remaining = Math.max(1, frameCount);\n    const step = () => {\n      remaining -= 1;\n      if (remaining <= 0) {\n        resolve();\n        return;\n      }\n      requestAnimationFrame(step);\n    };\n    requestAnimationFrame(step);\n  });\n}\n\nfunction collectFileViewerMotionTelemetryMetrics(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric[] {\n  return [\n    collectFileViewerBlinkMetric(runs),\n    collectFileViewerGapStabilityMetric(runs),\n    collectFileViewerOvershootMetric(runs),\n    collectFileViewerContentStartSnapMetric(runs),\n    collectFileViewerContentOvershootMetric(runs),\n    collectFileViewerSettleSnapMetric(runs),\n    collectFileViewerScrollIdentityMetric(runs),\n    collectFileViewerDomMutationsMetric(runs),\n    collectFileViewerLayoutShiftMetric(runs),\n    collectFileViewerMainThreadMetric(runs),\n    collectFileViewerCycleInvarianceMetric(runs),\n  ];\n}\n\nfunction collectFileViewerContentStartSnapMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let snapPx = 0;\n\n  for (const run of runs) {\n    const before = getFileViewerTelemetryContentEdges(run.before);\n    const first = run.samples[0]\n      ? getFileViewerTelemetryContentEdges(run.samples[0])\n      : null;\n    if (!before || !first) continue;\n    for (let index = 0; index < before.length; index += 1) {\n      snapPx = Math.max(snapPx, Math.abs(first[index]! - before[index]!));\n    }\n  }\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX}px`,\n    detail:\n      \"The synchronous target-layout commit must paint exactly where the pre-toggle content was; any first-sample deviation is an opening snap or ancestor clip.\",\n    id: \"content-start-snap\",\n    label: \"Content start snap\",\n    passed: snapPx <= FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(snapPx, 2)}px`,\n  };\n}\n\nfunction collectFileViewerContentOvershootMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let overshootPx = 0;\n\n  for (const run of runs) {\n    const before = getFileViewerTelemetryContentEdges(run.before);\n    const after = getFileViewerTelemetryContentEdges(run.after);\n    if (!before || !after) continue;\n\n    for (const sample of run.samples) {\n      const edges = getFileViewerTelemetryContentEdges(sample);\n      if (!edges) continue;\n      for (let index = 0; index < edges.length; index += 1) {\n        const low = Math.min(before[index]!, after[index]!);\n        const high = Math.max(before[index]!, after[index]!);\n        overshootPx = Math.max(\n          overshootPx,\n          low - edges[index]!,\n          edges[index]! - high,\n        );\n      }\n    }\n  }\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX}px`,\n    detail:\n      \"The visible content edges must stay inside their endpoint corridor; an excursion outside it is the first-frame back-and-forth wobble.\",\n    id: \"content-overshoot\",\n    label: \"Content overshoot\",\n    passed: overshootPx <= FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(Math.max(0, overshootPx), 2)}px`,\n  };\n}\n\nfunction getFileViewerTelemetryContentEdges(\n  sample: FileViewerMotionTelemetrySample,\n): readonly [number, number, number, number] | null {\n  if (sample.contentWidth <= 0 || sample.contentHeight <= 0) return null;\n  return [\n    sample.contentLeft,\n    sample.contentTop,\n    sample.contentLeft + sample.contentWidth,\n    sample.contentTop + sample.contentHeight,\n  ];\n}\n\nfunction collectFileViewerBlinkMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let blinkFrameCount = 0;\n  for (const run of runs) {\n    // Canvas-backed renderers must never lose all visible pixels mid-motion;\n    // renderers without canvases (text-like) hold both endpoints at 0 and the\n    // check degrades to surface-box presence.\n    const endpointCanvasFloor = Math.min(\n      run.before.visibleCanvasCount,\n      run.after.visibleCanvasCount,\n    );\n    for (const sample of run.samples) {\n      const surfaceMissing =\n        sample.surfaceWidth <= 0 || sample.surfaceHeight <= 0;\n      const canvasDropout =\n        endpointCanvasFloor > 0 && sample.visibleCanvasCount === 0;\n      if (surfaceMissing || canvasDropout) blinkFrameCount += 1;\n    }\n  }\n\n  return {\n    budget: \"0 blink frames\",\n    detail:\n      \"A blink frame has a collapsed document surface or loses every visible canvas while both endpoints have rendered content.\",\n    id: \"blink\",\n    label: \"Blink\",\n    passed: blinkFrameCount === 0,\n    value: `${blinkFrameCount} frames`,\n  };\n}\n\nfunction collectFileViewerGapStabilityMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let reversalCount = 0;\n  for (const run of runs) {\n    const widths = run.samples.map((sample) => sample.gapWidth);\n    const direction = Math.sign((widths.at(-1) ?? 0) - (widths.at(0) ?? 0));\n    if (direction === 0) continue;\n    for (let index = 1; index < widths.length; index += 1) {\n      const step = widths[index] - widths[index - 1];\n      if (\n        Math.sign(step) === -direction &&\n        Math.abs(step) > FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX\n      ) {\n        reversalCount += 1;\n      }\n    }\n  }\n\n  return {\n    budget: `0 reversals over ${FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX}px`,\n    detail:\n      \"The sidebar gap width must progress monotonically toward its target.\",\n    id: \"gap-stability\",\n    label: \"Gap stability\",\n    passed: reversalCount === 0,\n    value: `${reversalCount} reversals`,\n  };\n}\n\nfunction collectFileViewerOvershootMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let overshootPx = 0;\n  for (const run of runs) {\n    const endpoints = [\n      run.samples.at(0)?.gapWidth ?? 0,\n      run.samples.at(-1)?.gapWidth ?? 0,\n    ];\n    const low = Math.min(...endpoints);\n    const high = Math.max(...endpoints);\n    for (const sample of run.samples) {\n      overshootPx = Math.max(\n        overshootPx,\n        low - sample.gapWidth,\n        sample.gapWidth - high,\n      );\n    }\n  }\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX}px`,\n    detail:\n      \"The gap width must stay inside the interval spanned by its endpoints.\",\n    id: \"overshoot\",\n    label: \"Overshoot\",\n    passed: overshootPx <= FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(Math.max(0, overshootPx), 2)}px`,\n  };\n}\n\n// The settle-boundary blink: once the transform leaves the sliding phase it\n// is identity over the already-settled layout, so every post-sliding sample's\n// surface box must already equal the final rest box. Deviation here is the\n// \"reach the target, then move again\" wobble. (Comparing against the final\n// box — not the previous frame — keeps dropped frames, whose mid-flight box\n// coordinates legitimately sweep with the transform's anchor term, out of\n// this metric; main-thread covers those.)\nfunction collectFileViewerSettleSnapMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let snapPx = 0;\n  for (const run of runs) {\n    let sawSliding = false;\n    // Content position, not box position: late page-size measurements can\n    // resize the stage after the motion — vertically the scroll layer\n    // compensates scrollTop in the same commit, horizontally the pages stay\n    // centered — so the box moves while the pixels do not. The\n    // content-invariant observables are surfaceTop + scrollTop (vertical)\n    // and the stage center (horizontal).\n    const afterContentTop = run.after.surfaceTop + run.after.scrollTop;\n    const afterContentCenter =\n      run.after.surfaceLeft + run.after.surfaceWidth / 2;\n    for (const sample of run.samples) {\n      if (sample.motionPhase === \"sliding\") {\n        sawSliding = true;\n        continue;\n      }\n      if (!sawSliding) continue;\n      // A sample can catch the frame where the kernel has advanced to\n      // settling but the last sliding tick's transform is still applied; its\n      // box sweeps with the anchor term while the viewport pixels hold. The\n      // metric asserts rest AFTER the transform clears.\n      if (\n        sample.surfaceTransform !== \"none\" &&\n        sample.surfaceTransform !== \"\"\n      ) {\n        continue;\n      }\n      snapPx = Math.max(\n        snapPx,\n        Math.abs(\n          sample.surfaceLeft + sample.surfaceWidth / 2 - afterContentCenter,\n        ),\n        Math.abs(sample.surfaceTop + sample.scrollTop - afterContentTop),\n      );\n    }\n  }\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX}px settle deviation`,\n    detail:\n      \"After the sliding phase ends the surface box must already sit at its final rest position; deviation is the settle-boundary wobble.\",\n    id: \"settle-snap\",\n    label: \"Settle snap\",\n    passed: snapPx <= FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(snapPx, 2)}px`,\n  };\n}\n\nfunction collectFileViewerScrollIdentityMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  let scrollRangePx = 0;\n  for (const run of runs) {\n    // Layout and scroll commit in the toggle's own task, so every sampled\n    // frame (the first sample runs after the toggle) sees the final scrollTop.\n    const tops = run.samples.map((sample) => sample.scrollTop);\n    if (tops.length === 0) continue;\n    scrollRangePx = Math.max(\n      scrollRangePx,\n      Math.max(...tops) - Math.min(...tops),\n    );\n  }\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX}px scroll range`,\n    detail:\n      \"scrollTop is rebased once at slide start and must hold for the rest of the motion.\",\n    id: \"scroll-identity\",\n    label: \"Scroll identity\",\n    passed: scrollRangePx <= FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(scrollRangePx, 2)}px`,\n  };\n}\n\nfunction collectFileViewerDomMutationsMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  const canvasResizeMutationCount = runs.reduce(\n    (count, run) => count + run.canvasResizeMutationCount,\n    0,\n  );\n  const mutationCount = runs.reduce(\n    (count, run) => count + run.mutationCount,\n    0,\n  );\n\n  return {\n    budget: \"0 canvas resizes\",\n    detail:\n      \"Raster headroom is prepared before motion, so no canvas backing store may resize (and wipe) during it. Total mutations are reported for diagnosis.\",\n    id: \"dom-mutations\",\n    label: \"DOM mutations\",\n    passed: canvasResizeMutationCount === 0,\n    value: `${canvasResizeMutationCount} canvas resizes / ${mutationCount} total`,\n  };\n}\n\nfunction collectFileViewerLayoutShiftMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  const score = runs.reduce((total, run) => total + run.layoutShiftScore, 0);\n  const count = runs.reduce((total, run) => total + run.layoutShiftCount, 0);\n\n  return {\n    budget: \"diagnostic\",\n    detail:\n      \"Cumulative layout-shift entries during motion (the intentional sidebar reflow is included; read alongside settle-snap).\",\n    id: \"layout-shift\",\n    label: \"Layout shift\",\n    passed: true,\n    value: `${count} shifts / ${formatFileViewerTelemetryNumber(score, 4)}`,\n  };\n}\n\nfunction collectFileViewerMainThreadMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  const gaps: number[] = [];\n  for (const run of runs) {\n    for (let index = 1; index < run.samples.length; index += 1) {\n      gaps.push(\n        run.samples[index].timestamp - run.samples[index - 1].timestamp,\n      );\n    }\n  }\n  const sortedGaps = gaps.slice().sort((left, right) => left - right);\n  const p95 =\n    sortedGaps.length > 0\n      ? sortedGaps[\n          Math.min(sortedGaps.length - 1, Math.floor(sortedGaps.length * 0.95))\n        ]\n      : 0;\n  const max = sortedGaps.at(-1) ?? 0;\n  const longTaskDurationMs = runs.reduce(\n    (total, run) => total + run.longTaskDurationMs,\n    0,\n  );\n\n  const passed =\n    p95 <= FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS &&\n    max <= FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS;\n\n  return {\n    budget: `p95 <= ${FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS}ms, max <= ${FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS}ms`,\n    detail:\n      \"Frame-to-frame sample gaps during motion; a long gap is a dropped frame the user sees as stutter. Long-task time is reported alongside.\",\n    id: \"main-thread\",\n    label: \"Main thread\",\n    passed,\n    value: `p95 ${formatFileViewerTelemetryNumber(p95, 1)}ms / max ${formatFileViewerTelemetryNumber(max, 1)}ms / ${formatFileViewerTelemetryNumber(longTaskDurationMs, 1)}ms long tasks`,\n  };\n}\n\nfunction collectFileViewerCycleInvarianceMetric(\n  runs: readonly FileViewerMotionTelemetryRun[],\n): FileViewerMotionTelemetryMetric {\n  if (runs.length < 2) {\n    return {\n      budget: `<= ${FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX}px`,\n      detail: \"Needs a full toggle cycle.\",\n      id: \"cycle-invariance\",\n      label: \"Cycle invariance\",\n      passed: true,\n      value: \"n/a\",\n    };\n  }\n\n  const first = runs[0].before;\n  const last = runs[runs.length - 1].after;\n  const drift = Math.max(\n    Math.abs(first.gapWidth - last.gapWidth),\n    Math.abs(first.surfaceLeft - last.surfaceLeft),\n    Math.abs(first.surfaceWidth - last.surfaceWidth),\n    Math.abs(first.scrollTop - last.scrollTop),\n  );\n\n  return {\n    budget: `<= ${FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX}px`,\n    detail:\n      \"A full open/close cycle must return the gap, surface box, and scroll position to their starting values.\",\n    id: \"cycle-invariance\",\n    label: \"Cycle invariance\",\n    passed: drift <= FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX,\n    value: `${formatFileViewerTelemetryNumber(drift, 2)}px`,\n  };\n}\n\nfunction logFileViewerMotionTelemetryResult(\n  result: FileViewerMotionTelemetryResult,\n) {\n  const summary = {\n    durationMs: Number(result.durationMs.toFixed(1)),\n    failedMetricIds: result.metrics\n      .filter((metric) => !metric.passed)\n      .map((metric) => metric.id),\n    metrics: result.metrics,\n    sampledFrameCount: result.sampledFrameCount,\n    status: result.status,\n  };\n  console.info(\n    `[file-viewer:motion-telemetry] result ${JSON.stringify(summary)}`,\n  );\n}\n\nfunction formatFileViewerTelemetryNumber(\n  value: number,\n  fractionDigits: number,\n) {\n  return Number.isFinite(value) ? Number(value.toFixed(fractionDigits)) : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-motion-telemetry.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-header.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { PanelLeft, PanelRight } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"./button\";\nimport { Spinner } from \"./spinner\";\nimport { useFileViewerControlsState } from \"./file-viewer-provider\";\nimport { useFileViewerRequiredResourceState } from \"./file-viewer-resource-state\";\nimport {\n  useFileViewerContext,\n  useFileViewerShell,\n} from \"./file-viewer-context\";\nimport { resolveFileViewerSidebarTriggerAccessibilityProps } from \"./file-viewer-accessibility\";\nimport { ViewerControls } from \"./viewer-controls\";\nimport { ViewerHeaderOutlet } from \"./viewer-header-outlet\";\n\ntype ButtonProps = React.ComponentProps<typeof Button> & {\n  loading?: boolean;\n};\n\nexport type FileViewerHeaderProps = React.ComponentProps<\"div\">;\nexport type FileViewerTitleProps = React.ComponentProps<\"span\">;\nexport type FileViewerMetaProps = React.ComponentProps<\"span\"> & {\n  visibility?: \"responsive\" | \"visible\";\n};\nexport type FileViewerControlsProps = Omit<\n  React.ComponentProps<typeof ViewerControls>,\n  | \"downloads\"\n  | \"loading\"\n  | \"position\"\n  | \"rotate\"\n  | \"subtitle\"\n  | \"title\"\n  | \"zoom\"\n>;\nexport type FileViewerSidebarTriggerProps = ButtonProps;\n\nexport function FileViewerHeader({\n  children,\n  className,\n  ...props\n}: FileViewerHeaderProps) {\n  const { hasHeaderOutlets, headerMode } = useFileViewerContext();\n  const content = children ?? <FileViewerDefaultHeaderContent />;\n\n  if (headerMode === \"outlets\" && hasHeaderOutlets) {\n    return <>{content}</>;\n  }\n\n  return (\n    <div\n      data-slot=\"file-viewer-header\"\n      className={cn(\n        \"bg-card flex min-h-10 shrink-0 flex-wrap items-center gap-2 border-b px-2 py-1 sm:flex-nowrap sm:py-0\",\n        className,\n      )}\n      {...props}\n    >\n      {content}\n    </div>\n  );\n}\n\nfunction FileViewerDefaultHeaderContent() {\n  const { hasHeaderOutlets, headerMode } = useFileViewerContext();\n\n  if (headerMode === \"outlets\" && hasHeaderOutlets) {\n    return (\n      <>\n        <FileViewerTitle />\n        <FileViewerMeta />\n        <FileViewerControls />\n      </>\n    );\n  }\n\n  return (\n    <>\n      <div\n        data-slot=\"file-viewer-header-title-group\"\n        className=\"flex min-w-0 flex-1 items-baseline gap-2\"\n      >\n        <FileViewerTitle />\n        <FileViewerMeta />\n      </div>\n      <FileViewerControls />\n    </>\n  );\n}\n\nexport function FileViewerTitle({ className, ...props }: FileViewerTitleProps) {\n  const { hasHeaderOutlets, headerMode } = useFileViewerContext();\n  const { descriptor } = useFileViewerRequiredResourceState();\n  const title = (\n    <span\n      data-slot=\"file-viewer-title\"\n      className={cn(\n        \"text-foreground min-w-0 truncate text-[13px] leading-5 font-medium\",\n        className,\n      )}\n      title={descriptor.displayName}\n      {...props}\n    >\n      {descriptor.displayName}\n    </span>\n  );\n\n  if (headerMode === \"outlets\" && hasHeaderOutlets) {\n    return <ViewerHeaderOutlet name=\"titleGroup\">{title}</ViewerHeaderOutlet>;\n  }\n\n  return title;\n}\n\nexport function FileViewerMeta({\n  className,\n  visibility = \"responsive\",\n  ...props\n}: FileViewerMetaProps) {\n  const { hasHeaderOutlets, headerMode } = useFileViewerContext();\n  const { descriptor, resource } = useFileViewerRequiredResourceState();\n  const metaText =\n    resource.mimeType || descriptor.mimeType || descriptor.category;\n\n  if (!metaText) return null;\n\n  const meta = (\n    <span\n      data-slot=\"file-viewer-meta\"\n      className={cn(\n        \"text-muted-foreground min-w-0 shrink truncate text-xs\",\n        visibility === \"responsive\" && \"hidden sm:inline\",\n        className,\n      )}\n      {...props}\n    >\n      {metaText}\n    </span>\n  );\n\n  if (headerMode === \"outlets\" && hasHeaderOutlets) {\n    return <ViewerHeaderOutlet name=\"titleGroup\">{meta}</ViewerHeaderOutlet>;\n  }\n\n  return meta;\n}\n\nexport function FileViewerControls({\n  className,\n  extra,\n  ...props\n}: FileViewerControlsProps) {\n  const { resource } = useFileViewerRequiredResourceState();\n  const controlsState = useFileViewerControlsState();\n  const downloads = controlsState?.downloads ?? [resource.originalDownload];\n  const { hasHeaderOutlets, headerMode } = useFileViewerContext();\n\n  const controls = (\n    <ViewerControls\n      {...props}\n      data-file-viewer-controls={controlsState ? \"ready\" : \"idle\"}\n      data-slot=\"file-viewer-controls\"\n      className={cn(\n        \"ms-auto h-8 min-w-0 border-b-0 bg-transparent px-0\",\n        className,\n      )}\n      downloads={downloads}\n      extra={extra ?? controlsState?.extra}\n      loading={controlsState?.loading ?? false}\n      position={controlsState?.position ?? null}\n      rotate={controlsState?.rotate ?? null}\n      zoom={controlsState?.zoom ?? null}\n    />\n  );\n\n  if (headerMode === \"outlets\" && hasHeaderOutlets) {\n    return <ViewerHeaderOutlet name=\"controls\">{controls}</ViewerHeaderOutlet>;\n  }\n\n  return controls;\n}\n\nexport function FileViewerSidebarTrigger({\n  className,\n  disabled,\n  loading,\n  onClick,\n  children,\n  size = \"icon\",\n  variant = \"ghost\",\n  \"aria-label\": ariaLabel = \"Toggle sidebar\",\n  ...props\n}: FileViewerSidebarTriggerProps) {\n  const {\n    canToggleSidebar,\n    elementRegistry,\n    mode,\n    side,\n    isSidebarInteractive,\n    sidebarId,\n    sidebarState,\n    toggleSidebar,\n  } = useFileViewerShell(\"FileViewerSidebarTrigger\");\n  const isDisabled = Boolean(disabled || loading || !canToggleSidebar);\n  const Icon = side === \"right\" ? PanelRight : PanelLeft;\n  const setTriggerElement = React.useCallback(\n    (element: HTMLButtonElement | null) => {\n      elementRegistry.registerSidebarTriggerElement(element);\n    },\n    [elementRegistry],\n  );\n  const accessibilityProps = resolveFileViewerSidebarTriggerAccessibilityProps({\n    ariaLabel,\n    canToggleSidebar,\n    isDisabled,\n    isSidebarInteractive,\n    sidebarId,\n  });\n\n  return (\n    <Button\n      ref={setTriggerElement}\n      {...accessibilityProps}\n      className={cn(\"size-8\", className)}\n      data-file-viewer-sidebar-mode={mode}\n      data-file-viewer-sidebar-side={side}\n      data-file-viewer-sidebar-state={sidebarState}\n      data-side={side}\n      data-slot=\"file-viewer-sidebar-trigger\"\n      disabled={isDisabled}\n      onClick={(event) => {\n        if (isDisabled) {\n          event.preventDefault();\n          return;\n        }\n\n        onClick?.(event);\n        if (!event.defaultPrevented) {\n          toggleSidebar();\n        }\n      }}\n      size={size}\n      variant={variant}\n      {...props}\n    >\n      {loading ? <Spinner className=\"size-4 animate-spin\" /> : null}\n      {children ?? (\n        <>\n          {loading ? null : <Icon />}\n          <span className=\"sr-only\">Toggle sidebar</span>\n        </>\n      )}\n    </Button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-header.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-html-viewer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  viewerContentRenderKey,\n  type ViewerResource,\n} from \"@/lib/viewer-resource\";\n\nimport { ViewerFallback } from \"./file-viewer-fallback\";\nimport { getHtmlViewerSrcDoc } from \"./file-viewer-html-srcdoc-cache\";\nimport { loadTextResource } from \"./file-viewer-text-resource\";\nimport { isAbortError } from \"./viewer-abortable-request\";\nimport { useViewerControlsRegistration } from \"./viewer-controls\";\nimport { useZoom, ZoomActions } from \"./viewer-zoom\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\ntype HtmlLoadState =\n  | { status: \"loading\"; key: unknown }\n  | { status: \"loaded\"; key: unknown; html: string }\n  | { status: \"error\"; key: unknown; error: unknown };\n\nexport function FileViewerHtmlContent({\n  resource,\n  className,\n  bare,\n  controls = true,\n  descriptorSignal,\n}: {\n  resource: ViewerResource;\n  className?: string;\n  bare?: boolean;\n  controls?: boolean;\n  descriptorSignal: AbortSignal;\n}) {\n  if (resource.content.payload.kind === \"text\") {\n    return (\n      <FileViewerHtmlContentFrame\n        key={viewerContentRenderKey(resource.content)}\n        resource={resource}\n        html={resource.content.payload.text}\n        className={className}\n        bare={bare}\n        controls={controls}\n      />\n    );\n  }\n  return (\n    <HtmlFileResource\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      descriptorSignal={descriptorSignal}\n    />\n  );\n}\n\nfunction HtmlFileResource({\n  resource,\n  className,\n  bare,\n  controls,\n  descriptorSignal,\n}: {\n  resource: ViewerResource;\n  className?: string;\n  bare?: boolean;\n  controls: boolean;\n  descriptorSignal: AbortSignal;\n}) {\n  const content = resource.content;\n  const contentKey = content.key;\n  const [state, setState] = React.useState<HtmlLoadState>({\n    status: \"loading\",\n    key: contentKey,\n  });\n\n  useKeyedMountEffect(\n    joinEffectKey([content, contentKey, descriptorSignal, resource.fileName]),\n    () => {\n      let active = true;\n      const controller = new AbortController();\n      const abortLocal = () => controller.abort();\n      setState({ status: \"loading\", key: contentKey });\n\n      if (descriptorSignal.aborted) {\n        abortLocal();\n      } else {\n        descriptorSignal.addEventListener(\"abort\", abortLocal, { once: true });\n      }\n\n      loadTextResource({\n        content,\n        fileName: resource.fileName,\n        signal: controller.signal,\n      }).then(\n        (html) => {\n          if (active && !controller.signal.aborted) {\n            setState({ status: \"loaded\", key: contentKey, html });\n          }\n        },\n        (error: unknown) => {\n          if (!active || isAbortError(error)) return;\n          setState({ status: \"error\", key: contentKey, error });\n        },\n      );\n\n      return () => {\n        active = false;\n        descriptorSignal.removeEventListener(\"abort\", abortLocal);\n        abortLocal();\n      };\n    },\n  );\n\n  if (state.key !== contentKey) {\n    return (\n      <ViewerFallback resource={resource} className={className} bare={bare} />\n    );\n  }\n  if (state.status === \"error\") {\n    throw state.error;\n  }\n  if (state.status === \"loading\") {\n    return (\n      <ViewerFallback resource={resource} className={className} bare={bare} />\n    );\n  }\n\n  const { html } = state;\n  return (\n    <FileViewerHtmlContentFrame\n      key={contentKey}\n      resource={resource}\n      html={html}\n      className={className}\n      bare={bare}\n      controls={controls}\n    />\n  );\n}\n\nfunction FileViewerHtmlContentFrame({\n  resource,\n  html,\n  className,\n  bare,\n  controls,\n}: {\n  resource: ViewerResource;\n  html: string;\n  className?: string;\n  bare?: boolean;\n  controls: boolean;\n}) {\n  const fileName = resource.fileName;\n  const { scale, zoom, reset } = useZoom();\n  useHtmlControlsRegistration({ reset, scale, zoom });\n\n  return (\n    <div\n      data-slot=\"html-file-viewer-content\"\n      className={cn(\n        \"bg-card flex min-h-0 flex-1 flex-col overflow-hidden\",\n        bare ? \"h-full\" : \"min-h-64\",\n        className,\n      )}\n    >\n      {controls ? (\n        <HtmlContentToolbar scale={scale} zoom={zoom} reset={reset} />\n      ) : null}\n      <SandboxedDoc\n        contentKey={resource.content.key}\n        html={html}\n        title={fileName}\n        scale={scale}\n      />\n    </div>\n  );\n}\n\nfunction useHtmlControlsRegistration({\n  reset,\n  scale,\n  zoom,\n}: {\n  reset: () => void;\n  scale: number;\n  zoom: (factor: number) => void;\n}) {\n  const onControlsChange = useViewerControlsRegistration();\n\n  useKeyedMountEffect(\n    joinEffectKey([onControlsChange, reset, scale, zoom]),\n    () => {\n      if (!onControlsChange) return;\n\n      onControlsChange({\n        zoom: {\n          scale,\n          onZoomOut: () => zoom(1 / 1.2),\n          onZoomIn: () => zoom(1.2),\n          onReset: reset,\n        },\n      });\n\n      return () => onControlsChange(null);\n    },\n  );\n}\n\nfunction HtmlContentToolbar({\n  scale,\n  zoom,\n  reset,\n}: {\n  scale: number;\n  zoom: (factor: number) => void;\n  reset: () => void;\n}) {\n  return (\n    <div className=\"bg-card flex h-10 shrink-0 items-center justify-end gap-1 border-b px-2\">\n      <ZoomActions scale={scale} zoom={zoom} reset={reset} />\n    </div>\n  );\n}\n\nfunction SandboxedDoc({\n  contentKey,\n  html,\n  title,\n  scale = 1,\n}: {\n  contentKey: string;\n  html: string;\n  title: string;\n  scale?: number;\n}) {\n  const { containerRef, shouldMountIframe } =\n    useLazySandboxedIframeMount(contentKey);\n  const srcDoc = React.useMemo(\n    () =>\n      shouldMountIframe\n        ? getHtmlViewerSrcDoc({\n            contentKey,\n            html,\n          })\n        : \"\",\n    [contentKey, html, shouldMountIframe],\n  );\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"bg-document flex min-h-0 flex-1 flex-col overflow-auto\"\n    >\n      {shouldMountIframe ? (\n        <iframe\n          sandbox=\"\"\n          srcDoc={srcDoc}\n          title={title}\n          className=\"bg-document h-full min-h-0 w-full flex-1 border-0\"\n          style={{ zoom: scale }}\n        />\n      ) : null}\n    </div>\n  );\n}\n\nfunction useLazySandboxedIframeMount(resetKey: string) {\n  const containerRef = React.useRef<HTMLDivElement>(null);\n  const [shouldMountIframe, setShouldMountIframe] = React.useState(false);\n  const mountEffectKey = shouldMountIframe\n    ? null\n    : joinEffectKey([\"html-iframe-visible\", resetKey]);\n\n  useKeyedMountEffect(mountEffectKey, () => {\n    if (typeof IntersectionObserver === \"undefined\") {\n      setShouldMountIframe(true);\n      return;\n    }\n\n    const node = containerRef.current;\n    if (!node) {\n      setShouldMountIframe(true);\n      return;\n    }\n\n    let observer: IntersectionObserver | null = null;\n    try {\n      observer = new IntersectionObserver(\n        (entries) => {\n          if (\n            entries.some(\n              (entry) => entry.isIntersecting || entry.intersectionRatio > 0,\n            )\n          ) {\n            setShouldMountIframe(true);\n            observer?.disconnect();\n          }\n        },\n        { rootMargin: \"512px\" },\n      );\n    } catch {\n      setShouldMountIframe(true);\n      return;\n    }\n\n    observer.observe(node);\n\n    return () => observer.disconnect();\n  });\n\n  return { containerRef, shouldMountIframe };\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-html-viewer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-html-srcdoc-cache.ts",
      "content": "import { lruGet, lruSet } from \"./viewer-lru-cache\";\n\nconst HTML_VIEWER_SRC_DOC_IDENTITIES_PER_CONTENT = 2;\n\nconst HTML_VIEWER_DOCUMENT_STYLE = `<style data-retab-html-viewer-padding>\nhtml {\n  background: white;\n}\nbody {\n  box-sizing: border-box;\n  margin: 0;\n  min-width: 0;\n  padding: 1rem;\n}\nbody > :first-child {\n  margin-block-start: 0;\n}\nbody > :last-child {\n  margin-block-end: 0;\n}\n</style>`;\n\ninterface HtmlViewerSrcDocCacheEntry {\n  readonly html: string;\n  readonly srcDoc: string;\n}\n\ninterface HtmlViewerSrcDocCacheBucket {\n  readonly entries: HtmlViewerSrcDocCacheEntry[];\n}\n\nconst htmlViewerSrcDocCache = new Map<string, HtmlViewerSrcDocCacheBucket>();\nlet htmlViewerSrcDocCacheHitsForTests = 0;\nlet htmlViewerSrcDocCacheMissesForTests = 0;\n\nexport function getHtmlViewerSrcDoc({\n  contentKey,\n  html,\n}: {\n  contentKey: string;\n  html: string;\n}) {\n  const bucket = lruGet(htmlViewerSrcDocCache, contentKey);\n  const cached = bucket?.entries.find((entry) => entry.html === html);\n\n  if (cached) {\n    htmlViewerSrcDocCacheHitsForTests += 1;\n    return cached.srcDoc;\n  }\n\n  htmlViewerSrcDocCacheMissesForTests += 1;\n  const srcDoc = createHtmlViewerSrcDoc(html);\n  const entries = [...(bucket?.entries ?? []), { html, srcDoc }].slice(\n    -HTML_VIEWER_SRC_DOC_IDENTITIES_PER_CONTENT,\n  );\n\n  lruSet(htmlViewerSrcDocCache, contentKey, { entries });\n  return srcDoc;\n}\n\nexport function clearHtmlViewerSrcDocCacheForTests() {\n  htmlViewerSrcDocCache.clear();\n  htmlViewerSrcDocCacheHitsForTests = 0;\n  htmlViewerSrcDocCacheMissesForTests = 0;\n}\n\nexport function htmlViewerSrcDocCacheStatsForTests() {\n  let identities = 0;\n  for (const bucket of htmlViewerSrcDocCache.values()) {\n    identities += bucket.entries.length;\n  }\n\n  return {\n    contentKeys: htmlViewerSrcDocCache.size,\n    hits: htmlViewerSrcDocCacheHitsForTests,\n    identities,\n    misses: htmlViewerSrcDocCacheMissesForTests,\n  };\n}\n\nfunction createHtmlViewerSrcDoc(html: string) {\n  const headOpenTag = /<head(?:\\s[^>]*)?>/i;\n  const htmlOpenTag = /<html(?:\\s[^>]*)?>/i;\n\n  if (headOpenTag.test(html)) {\n    return html.replace(\n      headOpenTag,\n      (tag) => `${tag}${HTML_VIEWER_DOCUMENT_STYLE}`,\n    );\n  }\n  if (htmlOpenTag.test(html)) {\n    return html.replace(\n      htmlOpenTag,\n      (tag) => `${tag}<head>${HTML_VIEWER_DOCUMENT_STYLE}</head>`,\n    );\n  }\n  return `${HTML_VIEWER_DOCUMENT_STYLE}${html}`;\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-html-srcdoc-cache.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-preview.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  FileViewerContent,\n  FileViewerInset,\n  FileViewerViewport,\n} from \"./file-viewer-layout\";\nimport { FileViewerDocument } from \"./file-viewer-document\";\nimport { FileViewer } from \"./file-viewer-shell\";\nimport { FileViewerProvider } from \"./file-viewer-provider\";\nimport { type FileViewerResourceOptions } from \"./file-viewer-resource-state\";\n\nexport type FileViewerPreviewProps = FileViewerResourceOptions & {\n  className?: string;\n};\n\nexport function FileViewerPreview({\n  category,\n  className,\n  fallbackFrameSize,\n  fallbackSlideSize,\n  isolateStyles,\n  source,\n}: FileViewerPreviewProps) {\n  return (\n    <FileViewerProvider\n      category={category}\n      fallbackFrameSize={fallbackFrameSize}\n      fallbackSlideSize={fallbackSlideSize}\n      isolateStyles={isolateStyles}\n      source={source}\n    >\n      <FileViewer\n        data-file-viewer-mode=\"preview\"\n        className={cn(\"h-full min-h-0\", className)}\n      >\n        <FileViewerContent>\n          <FileViewerInset>\n            <FileViewerViewport>\n              <FileViewerDocument controls />\n            </FileViewerViewport>\n          </FileViewerInset>\n        </FileViewerContent>\n      </FileViewer>\n    </FileViewerProvider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-preview.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-provider.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport type { FileCategory, ViewerSource } from \"@/lib/viewer-source\";\n\nimport {\n  FileViewerContext,\n  type FileViewerContextValue,\n  type FileViewerHeaderMode,\n} from \"./file-viewer-context\";\nimport type { FileViewerFallbackSize } from \"./file-viewer-core\";\nimport {\n  FileViewerResourceProvider,\n  useFileViewerResourceState,\n} from \"./file-viewer-resource-state\";\nimport {\n  ViewerControlsRegistrationProvider,\n  type ViewerControlsState,\n} from \"./viewer-controls\";\nimport { warnViewerDevelopmentOnce } from \"./viewer-diagnostics\";\nimport {\n  useViewerHeaderOutletAvailable,\n  useViewerHeaderOutletsAvailable,\n} from \"./viewer-header-outlet\";\n\nexport type FileViewerControlsState = ViewerControlsState;\n\ntype FileViewerControlsRegistration = {\n  descriptorKey: string;\n  state: FileViewerControlsState | null;\n};\n\ntype FileViewerControlsValue = {\n  controlsState: FileViewerControlsState | null;\n};\n\nconst FileViewerControlsContext =\n  React.createContext<FileViewerControlsValue | null>(null);\n\nfunction useFileViewerControlsController(descriptorKey: string): {\n  controlsValue: FileViewerControlsValue;\n  handleControlsChange: (state: FileViewerControlsState | null) => void;\n} {\n  const [controlsRegistration, setControlsRegistration] =\n    React.useState<FileViewerControlsRegistration>(() => ({\n      descriptorKey,\n      state: null,\n    }));\n  const controlsState =\n    controlsRegistration.descriptorKey === descriptorKey\n      ? controlsRegistration.state\n      : null;\n  const handleControlsChange = React.useCallback(\n    (state: FileViewerControlsState | null) => {\n      setControlsRegistration({ descriptorKey, state });\n    },\n    [descriptorKey],\n  );\n  const controlsValue = React.useMemo<FileViewerControlsValue>(\n    () => ({\n      controlsState,\n    }),\n    [controlsState],\n  );\n\n  return {\n    controlsValue,\n    handleControlsChange,\n  };\n}\n\nexport function useFileViewerControlsState() {\n  const context = React.useContext(FileViewerControlsContext);\n  if (!context) {\n    throw new Error(\"File viewer controls must be used within FileViewer.\");\n  }\n  return context.controlsState;\n}\n\nexport type FileViewerProviderProps = {\n  children: React.ReactNode;\n  source: ViewerSource;\n  category?: FileCategory;\n  fallbackFrameSize?: FileViewerFallbackSize;\n  fallbackSlideSize?: FileViewerFallbackSize;\n  isolateStyles?: boolean;\n  headerMode?: FileViewerHeaderMode;\n  defaultSidebarOpen?: boolean;\n  sidebarOpen?: boolean;\n  onSidebarOpenChange?: (open: boolean) => void;\n};\n\nexport function FileViewerProvider({\n  children,\n  source,\n  category,\n  fallbackFrameSize,\n  fallbackSlideSize,\n  isolateStyles,\n  headerMode,\n  defaultSidebarOpen,\n  sidebarOpen,\n  onSidebarOpenChange,\n}: FileViewerProviderProps) {\n  const diagnosticViewerId = React.useId();\n  const resourceState = useFileViewerResourceState({\n    category,\n    fallbackFrameSize,\n    fallbackSlideSize,\n    isolateStyles,\n    source,\n  });\n  const controlsController = useFileViewerControlsController(\n    resourceState.descriptorKey,\n  );\n  const hasTitleGroupOutlet = useViewerHeaderOutletAvailable(\"titleGroup\");\n  const hasControlsOutlet = useViewerHeaderOutletAvailable(\"controls\");\n  const hasHeaderOutlets = useViewerHeaderOutletsAvailable();\n  const resolvedHeaderMode = headerMode ?? \"inline\";\n  const contextValue = React.useMemo<FileViewerContextValue>(\n    () => ({\n      headerMode: resolvedHeaderMode,\n      hasHeaderOutlets: resolvedHeaderMode === \"outlets\" && hasHeaderOutlets,\n      isInsideFileViewer: true,\n      resourceCategory: resourceState.descriptor.category,\n      sidebarOpenProps: {\n        defaultOpen: defaultSidebarOpen,\n        onOpenChange: onSidebarOpenChange,\n        open: sidebarOpen,\n      },\n    }),\n    [\n      defaultSidebarOpen,\n      hasHeaderOutlets,\n      onSidebarOpenChange,\n      resolvedHeaderMode,\n      resourceState.descriptor.category,\n      sidebarOpen,\n    ],\n  );\n  const hasExactlyOneHeaderOutlet =\n    resolvedHeaderMode === \"outlets\" &&\n    hasTitleGroupOutlet !== hasControlsOutlet;\n\n  useKeyedMountEffect(\n    hasExactlyOneHeaderOutlet\n      ? joinEffectKey([\n          \"file-viewer-header-outlet-single-mount\",\n          diagnosticViewerId,\n          hasTitleGroupOutlet,\n          hasControlsOutlet,\n        ])\n      : null,\n    () => {\n      warnViewerDevelopmentOnce({\n        code: \"file_viewer_header_outlet_single_mount\",\n        message: \"file viewer has exactly one header outlet mounted.\",\n        rootId: diagnosticViewerId,\n        details: {\n          controlsOutletAvailable: hasControlsOutlet,\n          titleGroupOutletAvailable: hasTitleGroupOutlet,\n        },\n      });\n    },\n  );\n\n  return (\n    <FileViewerResourceProvider value={resourceState}>\n      <FileViewerControlsContext.Provider\n        value={controlsController.controlsValue}\n      >\n        <ViewerControlsRegistrationProvider\n          onControlsChange={controlsController.handleControlsChange}\n        >\n          <FileViewerContext.Provider value={contextValue}>\n            {children}\n          </FileViewerContext.Provider>\n        </ViewerControlsRegistrationProvider>\n      </FileViewerControlsContext.Provider>\n    </FileViewerResourceProvider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-provider.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-renderer-contract.ts",
      "content": "\"use client\";\n\nimport {\n  getFileViewerMotionRasterInlineSize,\n  type FileViewerMotionFrame,\n  type FileViewerMotionPhase,\n} from \"./file-viewer-motion-plan\";\nimport type { ViewerDocumentTransition } from \"./viewer-types\";\n\nexport type FileViewerDocumentAlign = \"start\" | \"center\" | \"end\";\n\n// Physical inline direction of the document frame (computed CSS `direction`).\n// The fit-width motion transform works on the physical X axis, so it needs\n// the direction to model where auto-margin alignment actually puts the stage.\nexport type FileViewerInlineDirection = \"ltr\" | \"rtl\";\n\n// `phase` is the motion clock's state; `documentTransition` is the single\n// spelling of the policies derived from it. Renderers read layout/scroll/\n// visual decisions from the transition, never from duplicated top-level\n// fields. `isTransitioning` is shorthand for `phase === \"sliding\"`.\nexport type FileViewerRendererFrame = {\n  align: FileViewerDocumentAlign;\n  canToggleSidebar: boolean;\n  direction: FileViewerInlineDirection;\n  documentTransition: ViewerDocumentTransition;\n  element: HTMLDivElement | null;\n  fromInlineSize: number | null;\n  isTransitioning: boolean;\n  layoutInlineSize: number | null;\n  motionDurationMs: number;\n  phase: FileViewerMotionPhase;\n  rasterInlineSize: number | null;\n  settledInlineSize: number | null;\n  shellInlineSize: number | null;\n  toInlineSize: number | null;\n  usesShellGeometry: boolean;\n};\n\nexport function resolveFileViewerRendererLayoutInlineSize({\n  fallbackInlineSize,\n  rendererFrame,\n}: {\n  fallbackInlineSize: number | null;\n  rendererFrame: FileViewerRendererFrame;\n}) {\n  const fallbackSize = resolveMeasuredInlineSize(fallbackInlineSize);\n\n  // Commit-then-relax: the renderer lays out at the motion's TARGET width for\n  // the entire motion (layoutPolicy \"target\" from the first sliding frame).\n  // The in-flight visual is the surface motion transform reprojecting that\n  // settled layout, so settle never commits layout.\n  if (\n    rendererFrame.documentTransition.layoutPolicy === \"target\" &&\n    rendererFrame.toInlineSize != null\n  ) {\n    return rendererFrame.toInlineSize;\n  }\n\n  return rendererFrame.layoutInlineSize ?? fallbackSize;\n}\n\nexport function createFileViewerRendererFrame({\n  align,\n  canToggleSidebar,\n  direction = \"ltr\",\n  element,\n  fallbackInlineSize,\n  motionFrame,\n  motionDurationMs,\n  usesShellGeometry,\n}: {\n  align: FileViewerDocumentAlign;\n  canToggleSidebar: boolean;\n  direction?: FileViewerInlineDirection;\n  element: HTMLDivElement | null;\n  fallbackInlineSize: number | null;\n  motionFrame: FileViewerMotionFrame;\n  motionDurationMs: number;\n  usesShellGeometry: boolean;\n}): FileViewerRendererFrame {\n  const measuredInlineSize = resolveMeasuredInlineSize(fallbackInlineSize);\n  const shellInlineSize = usesShellGeometry\n    ? motionFrame.shellInlineSize\n    : null;\n  const layoutInlineSize = usesShellGeometry\n    ? motionFrame.layoutInlineSize\n    : measuredInlineSize;\n  const settledInlineSize = usesShellGeometry\n    ? motionFrame.toInlineSize\n    : measuredInlineSize;\n  const rasterInlineSize = usesShellGeometry\n    ? getFileViewerMotionRasterInlineSize(motionFrame)\n    : layoutInlineSize;\n  const fromInlineSize = usesShellGeometry\n    ? motionFrame.fromInlineSize\n    : settledInlineSize;\n  const toInlineSize = usesShellGeometry\n    ? motionFrame.toInlineSize\n    : settledInlineSize;\n  const documentTransition = createFileViewerRendererTransition({\n    motionFrame,\n    usesShellGeometry,\n  });\n\n  const phase = usesShellGeometry ? motionFrame.phase : \"idle\";\n\n  return {\n    align,\n    canToggleSidebar,\n    direction,\n    documentTransition,\n    element,\n    fromInlineSize,\n    isTransitioning: phase === \"sliding\",\n    layoutInlineSize,\n    motionDurationMs,\n    phase,\n    rasterInlineSize,\n    settledInlineSize: settledInlineSize ?? layoutInlineSize,\n    shellInlineSize,\n    toInlineSize: toInlineSize ?? layoutInlineSize,\n    usesShellGeometry,\n  };\n}\n\nfunction resolveMeasuredInlineSize(inlineSize: number | null | undefined) {\n  return inlineSize != null && Number.isFinite(inlineSize) && inlineSize > 0\n    ? inlineSize\n    : null;\n}\n\nfunction createFileViewerRendererTransition({\n  motionFrame,\n  usesShellGeometry,\n}: {\n  motionFrame: FileViewerMotionFrame;\n  usesShellGeometry: boolean;\n}): ViewerDocumentTransition {\n  if (!usesShellGeometry) {\n    return {\n      layoutPolicy: \"live\",\n      scrollPolicy: \"preserve\",\n      source: \"none\",\n      transitionId: null,\n      visualPolicy: \"none\",\n    };\n  }\n\n  switch (motionFrame.phase) {\n    // Sliding commits the TARGET layout immediately (inside the toggle's own\n    // task, before first paint) and rebases scroll to the reading anchor in\n    // the same commit; the shell transform hides the jump. Settling then has\n    // no layout or scroll work left — it only clears the identity transform.\n    case \"sliding\":\n      return {\n        layoutPolicy: \"target\",\n        scrollPolicy: \"rebase\",\n        source: \"viewer-shell\",\n        transitionId: motionFrame.motionId,\n        visualPolicy: \"shell-transform\",\n      };\n    case \"settling\":\n      return {\n        layoutPolicy: \"target\",\n        scrollPolicy: \"rebase\",\n        source: \"viewer-shell\",\n        transitionId: motionFrame.motionId,\n        visualPolicy: \"shell-transform\",\n      };\n    case \"idle\":\n      return {\n        layoutPolicy: \"live\",\n        scrollPolicy: \"preserve\",\n        source: \"none\",\n        transitionId: null,\n        visualPolicy: \"none\",\n      };\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-renderer-contract.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-renderer-frame.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  useOptionalFileViewerShell,\n  useOptionalFileViewerShellStatic,\n  useFileViewerShellStatic,\n} from \"./file-viewer-context\";\nimport type { FileViewerDocumentSurface } from \"./file-viewer-motion-kernel\";\nimport {\n  createFileViewerRendererFrame,\n  type FileViewerDocumentAlign,\n  type FileViewerRendererFrame,\n} from \"./file-viewer-renderer-contract\";\nimport { useFileViewerMotionFrame } from \"./file-viewer-motion-kernel\";\nimport { useViewerInlineDirection } from \"./viewer-measurement\";\n\nexport type FileViewerRendererEnvironment = {\n  registerDocumentSurface: (surface: FileViewerDocumentSurface) => () => void;\n  usesShellGeometry: boolean;\n};\n\nexport type FileViewerDocumentFrameState = {\n  align: FileViewerDocumentAlign;\n  element: HTMLDivElement | null;\n  inlineSize: number | null;\n};\n\nconst FileViewerDocumentFrameContext =\n  React.createContext<FileViewerDocumentFrameState | null>(null);\n\nexport function FileViewerDocumentFrameProvider({\n  children,\n  value,\n}: {\n  children: React.ReactNode;\n  value: FileViewerDocumentFrameState;\n}) {\n  return (\n    <FileViewerDocumentFrameContext.Provider value={value}>\n      {children}\n    </FileViewerDocumentFrameContext.Provider>\n  );\n}\n\nexport function useOptionalFileViewerDocumentFrame(): FileViewerDocumentFrameState | null {\n  return React.useContext(FileViewerDocumentFrameContext);\n}\n\nexport function useOptionalFileViewerRendererEnvironment(): FileViewerRendererEnvironment {\n  const { elementRegistry, usesShellGeometry } =\n    useFileViewerRendererEnvironmentState();\n  const registerDocumentSurface = React.useCallback(\n    (surface: FileViewerDocumentSurface) =>\n      elementRegistry?.registerDocumentSurface(surface) ?? (() => {}),\n    [elementRegistry],\n  );\n\n  return React.useMemo(\n    () => ({\n      registerDocumentSurface,\n      usesShellGeometry,\n    }),\n    [registerDocumentSurface, usesShellGeometry],\n  );\n}\n\nexport type FileViewerSidebarMotion = {\n  /** True when the shell animates the sidebar (inline mode with a toggle). */\n  isMotionManaged: boolean;\n  isSidebarInteractive: boolean;\n  isSidebarOpen: boolean;\n  isSidebarTransitioning: boolean;\n};\n\nexport function useOptionalFileViewerSidebarMotion(): FileViewerSidebarMotion | null {\n  const shell = useOptionalFileViewerShell();\n\n  return React.useMemo(\n    () =>\n      shell\n        ? {\n            isMotionManaged: shell.mode === \"inline\" && shell.canToggleSidebar,\n            isSidebarInteractive: shell.isSidebarInteractive,\n            isSidebarOpen: shell.isSidebarOpen,\n            isSidebarTransitioning: shell.isSidebarTransitioning,\n          }\n        : null,\n    [shell],\n  );\n}\n\nexport function useFileViewerRendererFrame({\n  fallbackInlineSize,\n}: {\n  fallbackInlineSize?: number | null;\n} = {}): FileViewerRendererFrame {\n  useFileViewerShellStatic(\"useFileViewerRendererFrame\");\n  return useResolvedFileViewerRendererFrame({\n    fallbackInlineSize,\n    required: true,\n  });\n}\n\nexport function useOptionalFileViewerRendererFrame({\n  fallbackInlineSize,\n}: {\n  fallbackInlineSize?: number | null;\n} = {}): FileViewerRendererFrame {\n  return useResolvedFileViewerRendererFrame({\n    fallbackInlineSize,\n    required: false,\n  });\n}\n\nfunction useResolvedFileViewerRendererFrame({\n  fallbackInlineSize,\n  required,\n}: {\n  fallbackInlineSize?: number | null;\n  required: boolean;\n}): FileViewerRendererFrame {\n  const { motionFrame, shell, usesShellGeometry } =\n    useFileViewerRendererEnvironmentState();\n  const documentFrame = useOptionalFileViewerDocumentFrame();\n\n  if (required && !documentFrame) {\n    throw new Error(\n      \"useFileViewerRendererFrame must be used within FileViewerInset.\",\n    );\n  }\n\n  const fallbackSize =\n    fallbackInlineSize != null && Number.isFinite(fallbackInlineSize)\n      ? fallbackInlineSize\n      : null;\n\n  // The fit-width motion transform is a physical-X computation, so renderers\n  // need the frame's computed CSS `direction` alongside its logical align.\n  const direction = useViewerInlineDirection(documentFrame?.element ?? null);\n\n  return React.useMemo(\n    () =>\n      createFileViewerRendererFrame({\n        align: documentFrame?.align ?? \"center\",\n        canToggleSidebar: shell?.canToggleSidebar ?? false,\n        direction,\n        element: documentFrame?.element ?? null,\n        fallbackInlineSize: documentFrame?.inlineSize ?? fallbackSize,\n        motionFrame,\n        motionDurationMs: shell?.motionDurationMs ?? 0,\n        usesShellGeometry,\n      }),\n    [\n      direction,\n      documentFrame?.align,\n      documentFrame?.element,\n      documentFrame?.inlineSize,\n      fallbackSize,\n      shell?.canToggleSidebar,\n      motionFrame,\n      shell?.motionDurationMs,\n      usesShellGeometry,\n    ],\n  );\n}\n\nfunction useFileViewerRendererEnvironmentState() {\n  const shell = useOptionalFileViewerShellStatic();\n  const motionFrame = useFileViewerMotionFrame(shell?.motionKernel);\n  const usesShellGeometry = Boolean(\n    shell &&\n      motionFrame.shellInlineSize > 0 &&\n      shell.mode === \"inline\" &&\n      (shell.canToggleSidebar || shell.collapsible === \"none\"),\n  );\n\n  return React.useMemo(\n    () => ({\n      elementRegistry: shell?.elementRegistry,\n      motionFrame,\n      shell,\n      usesShellGeometry,\n    }),\n    [motionFrame, shell, usesShellGeometry],\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-renderer-frame.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-resource-state.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport {\n  fileViewerDiagnostic,\n  nextFileViewerSessionId,\n  summarizeDiagnosticId,\n  summarizeViewerDescriptor,\n  summarizeViewerResource,\n} from \"@/lib/pdf-viewer-diagnostics\";\nimport {\n  createViewerResource,\n  type ViewerResource,\n} from \"@/lib/viewer-resource\";\n\nimport {\n  descriptorResetKey,\n  type FileDescriptor,\n  type FileViewerFallbackSize,\n  type FileViewerCoreProps,\n} from \"./file-viewer-core\";\nimport { useIsClient } from \"./use-is-client\";\n\nexport type FileViewerResourceOptions = Pick<\n  FileViewerCoreProps,\n  | \"category\"\n  | \"fallbackFrameSize\"\n  | \"fallbackSlideSize\"\n  | \"isolateStyles\"\n  | \"source\"\n>;\n\nexport type FileViewerResourceState = {\n  descriptor: FileDescriptor;\n  descriptorKey: string;\n  descriptorSignal: AbortSignal;\n  fallbackFrameSize?: FileViewerFallbackSize;\n  fallbackSlideSize?: FileViewerFallbackSize;\n  isClient: boolean;\n  isolateStyles: boolean;\n  resource: ViewerResource;\n};\n\nconst FileViewerResourceContext =\n  React.createContext<FileViewerResourceState | null>(null);\n\n// Per-descriptor cancellation signal. Aborting is deferred a macrotask so a\n// keyed remount can keep reusing a shared resource request through React\n// StrictMode's mount/unmount/mount sequence.\nfunction useDescriptorSignal(descriptorKey: string): AbortSignal {\n  const controller = React.useMemo(() => {\n    void descriptorKey;\n    return new AbortController();\n  }, [descriptorKey]);\n  const abortTimerRef = React.useRef<{\n    controller: AbortController;\n    timer: ReturnType<typeof setTimeout>;\n  } | null>(null);\n\n  useKeyedLayoutEffect(joinEffectKey([controller]), () => {\n    const descriptorKeyHash = summarizeDiagnosticId(descriptorKey);\n    fileViewerDiagnostic(\"debug\", \"file_viewer_descriptor_signal_active\", {\n      descriptorKeyHash,\n    });\n    if (abortTimerRef.current?.controller === controller) {\n      clearTimeout(abortTimerRef.current.timer);\n      abortTimerRef.current = null;\n      fileViewerDiagnostic(\n        \"debug\",\n        \"file_viewer_descriptor_signal_abort_cancelled\",\n        { descriptorKeyHash },\n      );\n    }\n\n    return () => {\n      fileViewerDiagnostic(\"debug\", \"file_viewer_descriptor_abort_scheduled\", {\n        descriptorKeyHash,\n        isSignalAborted: controller.signal.aborted,\n      });\n      const abortTimer = {\n        controller,\n        timer: setTimeout(() => {\n          fileViewerDiagnostic(\"debug\", \"file_viewer_descriptor_abort_fired\", {\n            descriptorKeyHash,\n            isSignalAborted: controller.signal.aborted,\n          });\n          controller.abort();\n          if (abortTimerRef.current === abortTimer) {\n            abortTimerRef.current = null;\n          }\n        }, 0),\n      };\n      abortTimerRef.current = abortTimer;\n    };\n  });\n\n  return controller.signal;\n}\n\nexport function useFileViewerResourceState({\n  category,\n  fallbackFrameSize,\n  fallbackSlideSize,\n  isolateStyles = false,\n  source,\n}: FileViewerResourceOptions): FileViewerResourceState {\n  const isClient = useIsClient();\n  const resource = React.useMemo(\n    () => createViewerResource(source, category),\n    [source, category],\n  );\n  const descriptor = resource.descriptor;\n  const descriptorKey = descriptorResetKey(descriptor);\n  const viewerSessionId = React.useMemo(() => {\n    void descriptorKey;\n    return nextFileViewerSessionId(\"fv\");\n  }, [descriptorKey]);\n  const descriptorSignal = useDescriptorSignal(descriptorKey);\n\n  useKeyedLayoutEffect(joinEffectKey([descriptorKey, viewerSessionId]), () => {\n    fileViewerDiagnostic(\"info\", \"file_viewer_resource_mount\", {\n      descriptor: summarizeViewerDescriptor(descriptor),\n      descriptorKeyHash: summarizeDiagnosticId(descriptorKey),\n      isClient,\n      isolateStyles,\n      resource: summarizeViewerResource(resource),\n      viewerSessionId,\n    });\n    return () => {\n      fileViewerDiagnostic(\"debug\", \"file_viewer_resource_unmount\", {\n        descriptor: summarizeViewerDescriptor(descriptor),\n        descriptorKeyHash: summarizeDiagnosticId(descriptorKey),\n        resource: summarizeViewerResource(resource),\n        viewerSessionId,\n      });\n    };\n  });\n\n  return React.useMemo<FileViewerResourceState>(\n    () => ({\n      descriptor,\n      descriptorKey,\n      descriptorSignal,\n      fallbackFrameSize,\n      fallbackSlideSize,\n      isClient,\n      isolateStyles,\n      resource,\n    }),\n    [\n      descriptor,\n      descriptorKey,\n      descriptorSignal,\n      fallbackFrameSize,\n      fallbackSlideSize,\n      isClient,\n      isolateStyles,\n      resource,\n    ],\n  );\n}\n\nexport function FileViewerResourceProvider({\n  children,\n  value,\n}: {\n  children: React.ReactNode;\n  value: FileViewerResourceState;\n}) {\n  return React.createElement(\n    FileViewerResourceContext.Provider,\n    { value },\n    children,\n  );\n}\n\nexport function useFileViewerRequiredResourceState(): FileViewerResourceState {\n  const context = React.useContext(FileViewerResourceContext);\n  if (!context) {\n    throw new Error(\"File viewer parts must be used within FileViewer.\");\n  }\n  return context;\n}\n\nexport function useOptionalFileViewerResource(): ViewerResource | null {\n  return React.useContext(FileViewerResourceContext)?.resource ?? null;\n}\n\nexport function useFileViewerResource(): ViewerResource {\n  const resource = useOptionalFileViewerResource();\n  if (!resource) {\n    throw new Error(\"useFileViewerResource must be used within FileViewer.\");\n  }\n  return resource;\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-resource-state.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-route.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { inferCsvDialect } from \"@/lib/csv\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\nimport { type ViewerResource } from \"@/lib/viewer-resource\";\nimport {\n  fileViewerDiagnostic,\n  summarizeViewerDescriptor,\n  summarizeViewerSource,\n} from \"@/lib/pdf-viewer-diagnostics\";\n\nimport {\n  isProseTextDescriptor,\n  type FileCategory,\n  type FileDescriptor,\n  type FileViewerFallbackSize,\n} from \"./file-viewer-core\";\nimport { UnsupportedCard } from \"./file-viewer-fallback\";\nimport { FileViewerHtmlContent } from \"./file-viewer-html-viewer\";\nimport {\n  getFileViewerPrewarmTarget,\n  scheduleFileViewerRendererPrewarm,\n} from \"./file-viewer-prewarm\";\n\nconst PdfResourceContent = React.lazy(() =>\n  import(\"@/components/ui/pdf-viewer\").then((m) => ({\n    default: m.PdfResourceContent,\n  })),\n);\nconst DocxResourceContent = React.lazy(() =>\n  import(\"@/components/ui/docx-viewer\").then((m) => ({\n    default: m.DocxResourceContent,\n  })),\n);\nconst ImageResourceContent = React.lazy(() =>\n  import(\"@/components/ui/image-viewer\").then((m) => ({\n    default: m.ImageResourceContent,\n  })),\n);\nconst PptxResourceContent = React.lazy(() =>\n  import(\"@/components/ui/pptx-viewer\").then((m) => ({\n    default: m.PptxResourceContent,\n  })),\n);\nconst XlsxResourceContent = React.lazy(() =>\n  import(\"@/components/ui/xlsx-viewer\").then((m) => ({\n    default: m.XlsxResourceContent,\n  })),\n);\nconst ProseTextViewer = React.lazy(() =>\n  import(\"@/components/ui/text-viewer\").then((m) => ({\n    default: m.TextResourceContent,\n  })),\n);\nconst MarkdownViewer = React.lazy(() =>\n  import(\"@/components/ui/markdown-viewer\").then((m) => ({\n    default: m.MarkdownResourceContent,\n  })),\n);\nconst CodeTextViewer = React.lazy(() =>\n  import(\"@/components/ui/code-viewer\").then((m) => ({\n    default: m.CodeResourceContent,\n  })),\n);\nconst EmailResourceContent = React.lazy(() =>\n  import(\"@/components/ui/email-viewer\").then((m) => ({\n    default: m.EmailResourceContent,\n  })),\n);\nconst CsvResourceContent = React.lazy(() =>\n  import(\"@/components/ui/csv-viewer\").then((m) => ({\n    default: m.CsvResourceContent,\n  })),\n);\n\nfunction FileViewerCsvContent({\n  resource,\n  className,\n  bare,\n  controls = false,\n  isolateStyles,\n}: {\n  resource: ViewerResource;\n  className?: string;\n  bare?: boolean;\n  controls?: boolean;\n  isolateStyles?: boolean;\n}) {\n  const dialect = inferCsvDialect({\n    src: resource.content.directUrl ?? undefined,\n    fileName: resource.fileName,\n    mimeType: resource.mimeType,\n  });\n  return (\n    <div\n      data-slot=\"csv-file-viewer-content\"\n      className={cn(\n        \"bg-card flex min-h-0 flex-1 flex-col overflow-hidden\",\n        bare ? \"h-full\" : \"min-h-64\",\n        className,\n      )}\n    >\n      <CsvResourceContent\n        resource={resource}\n        dialect={dialect}\n        fillHeight\n        frame={!bare}\n        controls={controls}\n        isolateStyles={isolateStyles}\n      />\n    </div>\n  );\n}\n\nexport type FileViewerRouteProps = {\n  className?: string;\n  descriptor: FileDescriptor;\n  descriptorSignal: AbortSignal;\n  fallbackFrameSize?: FileViewerFallbackSize;\n  fallbackSlideSize?: FileViewerFallbackSize;\n  frame?: \"contained\" | \"none\";\n  isolateStyles: boolean;\n  resource: ViewerResource;\n  controls: boolean;\n};\n\ntype RenderContext = {\n  descriptor: FileDescriptor;\n  resource: ViewerResource;\n  className?: string;\n  bare: boolean;\n  controls: boolean;\n  fallbackFrameSize?: FileViewerFallbackSize;\n  fallbackSlideSize?: FileViewerFallbackSize;\n  isolateStyles: boolean;\n  descriptorSignal: AbortSignal;\n};\n\n// Categories a text-kind source (raw text, no blob/url backing) can render.\nconst TEXT_SOURCE_CATEGORIES = new Set<FileCategory>([\n  \"csv\",\n  \"markdown\",\n  \"html\",\n  \"email\",\n  \"text\",\n]);\n\nconst RENDERERS: Partial<\n  Record<FileCategory, (ctx: RenderContext) => React.ReactNode>\n> = {\n  pdf: ({ resource, className, bare, controls }) => (\n    <PdfResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n    />\n  ),\n  docx: ({ resource, className, bare, controls }) => (\n    <DocxResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n    />\n  ),\n  image: ({ resource, className, bare, controls, fallbackFrameSize }) => (\n    <ImageResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n      fallbackFrameSize={fallbackFrameSize}\n    />\n  ),\n  pptx: ({ resource, className, bare, controls, fallbackSlideSize }) => (\n    <PptxResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n      fallbackSlideSize={fallbackSlideSize}\n    />\n  ),\n  xlsx: ({ resource, className, bare, controls, isolateStyles }) => (\n    <XlsxResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n      isolateStyles={isolateStyles}\n    />\n  ),\n  csv: ({ resource, className, bare, controls, isolateStyles }) => (\n    <FileViewerCsvContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      isolateStyles={isolateStyles}\n    />\n  ),\n  html: ({ resource, className, bare, controls, descriptorSignal }) => (\n    <FileViewerHtmlContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      descriptorSignal={descriptorSignal}\n    />\n  ),\n  email: ({ resource, className, bare, controls, descriptorSignal }) => (\n    <EmailResourceContent\n      resource={resource}\n      className={className}\n      bare={bare}\n      controls={controls}\n      download\n      descriptorSignal={descriptorSignal}\n    />\n  ),\n  markdown: ({ resource, className, bare, controls }) => (\n    <MarkdownViewer\n      resource={resource}\n      className={className}\n      controls={controls}\n      download\n      bare={bare}\n      urlFragmentNavigation={false}\n    />\n  ),\n  text: renderTextViewer,\n};\n\nexport function FileViewerRoute({\n  descriptor,\n  className,\n  frame = \"contained\",\n  isolateStyles,\n  descriptorSignal,\n  fallbackFrameSize,\n  fallbackSlideSize,\n  resource,\n  controls,\n}: FileViewerRouteProps) {\n  const { category } = descriptor;\n  const bare = frame === \"none\";\n  const isTextSource = descriptor.source.kind === \"text\";\n\n  const renderer = RENDERERS[category];\n  const textSourceAllowed =\n    !isTextSource || TEXT_SOURCE_CATEGORIES.has(category);\n  const isRouteRenderable = Boolean(renderer) && textSourceAllowed;\n  const prewarmTarget = getFileViewerPrewarmTarget({\n    descriptor,\n    isRouteRenderable,\n  });\n  useKeyedMountEffect(\n    prewarmTarget\n      ? joinEffectKey([\"file-viewer-renderer-prewarm\", prewarmTarget])\n      : null,\n    () => {\n      if (!prewarmTarget) return;\n      return scheduleFileViewerRendererPrewarm(prewarmTarget);\n    },\n  );\n\n  const routeDetails = {\n    bare,\n    category,\n    hasRenderer: Boolean(renderer),\n    isTextSource,\n    isolateStyles,\n    source: summarizeViewerSource(descriptor.source),\n    descriptor: summarizeViewerDescriptor(descriptor),\n    controls,\n  };\n  if (renderer && textSourceAllowed) {\n    fileViewerDiagnostic(\"debug\", \"file_viewer_route_renderer_selected\", {\n      ...routeDetails,\n      textSourceAllowed,\n    });\n    return renderer({\n      descriptor,\n      resource,\n      className,\n      bare,\n      controls,\n      fallbackFrameSize,\n      fallbackSlideSize,\n      isolateStyles,\n      descriptorSignal,\n    });\n  }\n\n  fileViewerDiagnostic(\"warn\", \"file_viewer_route_unsupported\", {\n    ...routeDetails,\n    textSourceAllowed,\n  });\n  return (\n    <UnsupportedCard\n      resource={resource}\n      className={className}\n      bare={bare}\n      showDownload={controls}\n    />\n  );\n}\n\nfunction renderTextViewer({\n  descriptor,\n  resource,\n  className,\n  bare,\n  controls,\n}: RenderContext): React.ReactNode {\n  if (isProseTextDescriptor(descriptor)) {\n    return (\n      <ProseTextViewer\n        resource={resource}\n        className={className}\n        controls={controls}\n        download\n        bare={bare}\n        mode=\"text\"\n      />\n    );\n  }\n  return (\n    <CodeTextViewer\n      resource={resource}\n      className={className}\n      controls={controls}\n      download\n      bare={bare}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-route.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-prewarm.ts",
      "content": "import {\n  isProseTextDescriptor,\n  type FileDescriptor,\n  type FileCategory,\n} from \"./file-viewer-core\";\n\nexport type FileViewerPrewarmTarget =\n  | \"pdf\"\n  | \"docx\"\n  | \"image\"\n  | \"pptx\"\n  | \"xlsx\"\n  | \"markdown\"\n  | \"email\"\n  | \"text\"\n  | \"code\";\n\ntype FileViewerPrewarmStatus = \"scheduled\" | \"running\" | \"complete\";\n\ntype FileViewerIdleWindow = Window &\n  typeof globalThis & {\n    cancelIdleCallback?: Window[\"cancelIdleCallback\"];\n    requestIdleCallback?: Window[\"requestIdleCallback\"];\n  };\n\ntype FileViewerIdleTask =\n  | { kind: \"idle\"; id: number }\n  | { kind: \"timeout\"; id: ReturnType<typeof setTimeout> };\n\ntype FileViewerPrewarmRecord = {\n  browserWindow: FileViewerIdleWindow;\n  isCancelled: boolean;\n  status: FileViewerPrewarmStatus;\n  subscribers: number;\n  task: FileViewerIdleTask | null;\n};\n\nconst CATEGORY_PREWARM_TARGETS: Partial<\n  Record<FileCategory, FileViewerPrewarmTarget>\n> = {\n  pdf: \"pdf\",\n  docx: \"docx\",\n  image: \"image\",\n  pptx: \"pptx\",\n  xlsx: \"xlsx\",\n  markdown: \"markdown\",\n  email: \"email\",\n};\n\nconst PREWARM_LOADERS: Record<FileViewerPrewarmTarget, () => Promise<void>> = {\n  pdf: () => import(\"@/components/ui/pdf-viewer\").then(() => undefined),\n  docx: () => import(\"@/components/ui/docx-viewer\").then(() => undefined),\n  image: () => import(\"@/components/ui/image-viewer\").then(() => undefined),\n  pptx: () =>\n    import(\"@/components/ui/pptx-viewer\").then((module) => {\n      const preloadViewer = (module as { preloadPptxViewer?: () => void })\n        .preloadPptxViewer;\n      preloadViewer?.();\n    }),\n  xlsx: () => import(\"@/components/ui/xlsx-viewer\").then(() => undefined),\n  markdown: () =>\n    import(\"@/components/ui/markdown-viewer\").then(() => undefined),\n  email: () => import(\"@/components/ui/email-viewer\").then(() => undefined),\n  text: () =>\n    import(\"@/components/ui/text-viewer-chenglou\").then(() => undefined),\n  code: () => import(\"@/components/ui/code-viewer\").then(() => undefined),\n};\n\nconst prewarmRecords = new Map<\n  FileViewerPrewarmTarget,\n  FileViewerPrewarmRecord\n>();\n\nexport function getFileViewerPrewarmTarget({\n  descriptor,\n  isRouteRenderable,\n}: {\n  descriptor: FileDescriptor;\n  isRouteRenderable: boolean;\n}): FileViewerPrewarmTarget | null {\n  if (!isRouteRenderable) return null;\n  if (descriptor.category === \"text\") {\n    return isProseTextDescriptor(descriptor) ? \"text\" : \"code\";\n  }\n  return CATEGORY_PREWARM_TARGETS[descriptor.category] ?? null;\n}\n\nexport function scheduleFileViewerRendererPrewarm(\n  target: FileViewerPrewarmTarget,\n): () => void {\n  if (typeof window === \"undefined\") return noop;\n\n  const existingRecord = prewarmRecords.get(target);\n  if (existingRecord) {\n    if (existingRecord.status !== \"scheduled\") return noop;\n    existingRecord.subscribers += 1;\n    return () => releaseScheduledPrewarm(target, existingRecord);\n  }\n\n  const browserWindow = window as FileViewerIdleWindow;\n  const record: FileViewerPrewarmRecord = {\n    browserWindow,\n    isCancelled: false,\n    status: \"scheduled\",\n    subscribers: 1,\n    task: null,\n  };\n  record.task = scheduleIdleTask(browserWindow, () => {\n    if (record.isCancelled) return;\n    record.status = \"running\";\n    record.task = null;\n    void PREWARM_LOADERS[target]()\n      .then(() => {\n        if (prewarmRecords.get(target) === record) {\n          record.status = \"complete\";\n        }\n      })\n      .catch(() => {\n        if (prewarmRecords.get(target) === record) {\n          prewarmRecords.delete(target);\n        }\n      });\n  });\n  prewarmRecords.set(target, record);\n\n  return () => releaseScheduledPrewarm(target, record);\n}\n\nexport function resetFileViewerRendererPrewarmForTests() {\n  prewarmRecords.clear();\n}\n\nfunction releaseScheduledPrewarm(\n  target: FileViewerPrewarmTarget,\n  record: FileViewerPrewarmRecord,\n) {\n  if (prewarmRecords.get(target) !== record || record.status !== \"scheduled\") {\n    return;\n  }\n  record.subscribers -= 1;\n  if (record.subscribers > 0) return;\n\n  record.isCancelled = true;\n  if (record.task) cancelIdleTask(record.browserWindow, record.task);\n  prewarmRecords.delete(target);\n}\n\nfunction scheduleIdleTask(\n  browserWindow: FileViewerIdleWindow,\n  callback: () => void,\n): FileViewerIdleTask {\n  if (browserWindow.requestIdleCallback) {\n    return {\n      kind: \"idle\",\n      id: browserWindow.requestIdleCallback(callback, { timeout: 600 }),\n    };\n  }\n  return { kind: \"timeout\", id: setTimeout(callback, 120) };\n}\n\nfunction cancelIdleTask(\n  browserWindow: FileViewerIdleWindow,\n  task: FileViewerIdleTask,\n) {\n  if (task.kind === \"idle\") {\n    browserWindow.cancelIdleCallback?.(task.id);\n    return;\n  }\n  clearTimeout(task.id);\n}\n\nfunction noop() {}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-prewarm.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-state.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  AlertCircle,\n  FileQuestion,\n  Inbox,\n  Loader2,\n  WifiOff,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { ViewerDownloadAction } from \"@/lib/viewer-download-actions\";\nimport {\n  toViewerErrorInfo,\n  type ViewerErrorContext,\n} from \"@/lib/viewer-errors\";\n\nimport { Button } from \"./button\";\nimport { Skeleton } from \"./skeleton\";\nimport { ViewerDownloadButton } from \"./viewer-download\";\n\ntype FileViewerStateKind =\n  | \"empty\"\n  | \"error\"\n  | \"loading\"\n  | \"unavailable\"\n  | \"unsupported\";\n\nexport type FileViewerStateProps = Omit<\n  React.ComponentProps<\"div\">,\n  \"title\"\n> & {\n  action?: React.ReactNode;\n  description?: React.ReactNode;\n  icon?: React.ReactNode;\n  title?: React.ReactNode;\n};\n\nexport type FileViewerErrorStateProps = FileViewerStateProps &\n  ViewerErrorContext & {\n    canDownload?: boolean;\n    download?: ViewerDownloadAction | null;\n    error?: unknown;\n    onRetry?: () => void;\n  };\n\nexport type FileViewerUnsupportedStateProps = FileViewerStateProps & {\n  download?: ViewerDownloadAction | null;\n  fileName?: string;\n  showDownload?: boolean;\n};\n\nfunction stateSlot(kind: FileViewerStateKind) {\n  return `file-viewer-${kind}-state`;\n}\n\nfunction renderErrorDescription(error: unknown): React.ReactNode {\n  if (error instanceof Error && error.message) return error.message;\n  if (typeof error === \"string\" && error) return error;\n  return undefined;\n}\n\nfunction FileViewerStateFrame({\n  action,\n  children,\n  className,\n  description,\n  icon,\n  kind,\n  role,\n  title,\n  ...props\n}: FileViewerStateProps & {\n  kind: FileViewerStateKind;\n  role?: React.AriaRole;\n}) {\n  return (\n    <div\n      data-slot={stateSlot(kind)}\n      role={role}\n      className={cn(\n        \"text-muted-foreground flex min-h-48 min-w-0 flex-col items-center justify-center gap-3 p-6 text-center text-sm\",\n        className,\n      )}\n      {...props}\n    >\n      {icon ? (\n        <div\n          aria-hidden\n          data-slot=\"file-viewer-state-icon\"\n          className=\"text-muted-foreground flex size-9 items-center justify-center\"\n        >\n          {icon}\n        </div>\n      ) : null}\n      <div className=\"flex max-w-md min-w-0 flex-col items-center gap-1\">\n        {title ? (\n          <p\n            data-slot=\"file-viewer-state-title\"\n            className=\"text-foreground text-sm font-medium\"\n          >\n            {title}\n          </p>\n        ) : null}\n        {description ? (\n          <p\n            data-slot=\"file-viewer-state-description\"\n            className=\"text-muted-foreground text-sm\"\n          >\n            {description}\n          </p>\n        ) : null}\n      </div>\n      {children}\n      {action ? (\n        <div\n          data-slot=\"file-viewer-state-action\"\n          className=\"flex items-center justify-center gap-2\"\n        >\n          {action}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n\nexport function FileViewerLoadingState({\n  children,\n  description = \"Preparing the preview.\",\n  icon = <Loader2 className=\"size-5 animate-spin\" />,\n  title = \"Loading file\",\n  ...props\n}: FileViewerStateProps) {\n  return (\n    <FileViewerStateFrame\n      aria-busy=\"true\"\n      description={description}\n      icon={icon}\n      kind=\"loading\"\n      title={title}\n      {...props}\n    >\n      {children ?? (\n        <div\n          aria-hidden\n          data-slot=\"file-viewer-state-skeleton\"\n          className=\"flex w-full max-w-xs flex-col gap-2\"\n        >\n          <Skeleton className=\"h-2 w-full\" />\n          <Skeleton className=\"h-2 w-3/4 self-center\" />\n        </div>\n      )}\n    </FileViewerStateFrame>\n  );\n}\n\nexport function FileViewerUnavailableState({\n  description = \"The file is not available right now.\",\n  icon = <WifiOff className=\"size-5\" />,\n  title = \"File unavailable\",\n  ...props\n}: FileViewerStateProps) {\n  return (\n    <FileViewerStateFrame\n      description={description}\n      icon={icon}\n      kind=\"unavailable\"\n      role=\"status\"\n      title={title}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerEmptyState({\n  description = \"Choose a file to preview.\",\n  icon = <Inbox className=\"size-5\" />,\n  title = \"No file selected\",\n  ...props\n}: FileViewerStateProps) {\n  return (\n    <FileViewerStateFrame\n      description={description}\n      icon={icon}\n      kind=\"empty\"\n      role=\"status\"\n      title={title}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerErrorState({\n  action,\n  canDownload,\n  description,\n  download,\n  error,\n  format = \"file\",\n  icon = <AlertCircle className=\"size-5\" />,\n  onRetry,\n  retry,\n  sourceKind,\n  title,\n  ...props\n}: FileViewerErrorStateProps) {\n  const info =\n    error === undefined\n      ? null\n      : toViewerErrorInfo(error, {\n          canDownload: canDownload ?? Boolean(download && !download.isDisabled),\n          format,\n          retry,\n          sourceKind,\n        });\n  const showRetry = Boolean(info?.isRetryable && onRetry);\n  const showDownload = Boolean(\n    info?.isDownloadUseful && download && !download.isDisabled,\n  );\n  const resolvedAction =\n    action ??\n    (showRetry || showDownload ? (\n      <>\n        {showRetry ? (\n          <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n            Retry\n          </Button>\n        ) : null}\n        {showDownload && download ? (\n          <ViewerDownloadButton\n            action={download}\n            variant={showRetry ? \"ghost\" : \"outline\"}\n            size=\"sm\"\n            className=\"\"\n            showLabel\n          />\n        ) : null}\n      </>\n    ) : null);\n\n  return (\n    <FileViewerStateFrame\n      action={resolvedAction}\n      data-error-domain={info?.domain}\n      data-error-format={info?.format}\n      data-error-kind={info?.kind}\n      data-error-message={info?.message}\n      description={\n        description ?? info?.userMessage ?? renderErrorDescription(error)\n      }\n      icon={icon}\n      kind=\"error\"\n      role=\"alert\"\n      title={title ?? \"Could not preview file\"}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerUnsupportedState({\n  description,\n  download,\n  fileName,\n  icon = <FileQuestion className=\"size-5\" />,\n  showDownload = true,\n  title = \"Preview unsupported\",\n  ...props\n}: FileViewerUnsupportedStateProps) {\n  const resolvedDescription =\n    description ??\n    (fileName\n      ? `No preview is available for ${fileName}.`\n      : \"No preview is available for this file.\");\n\n  return (\n    <FileViewerStateFrame\n      action={\n        showDownload && download && !download.isDisabled ? (\n          <ViewerDownloadButton\n            action={download}\n            variant=\"outline\"\n            size=\"sm\"\n            className=\"\"\n            showLabel\n          />\n        ) : undefined\n      }\n      description={resolvedDescription}\n      icon={icon}\n      kind=\"unsupported\"\n      role=\"status\"\n      title={title}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-state.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-sidebar.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,\n  type FileViewerSidebarCollapsible,\n  type FileViewerSidebarSide,\n  useFileViewerShell,\n} from \"./file-viewer-context\";\nimport { useFileViewerSidebarController } from \"./file-viewer-sidebar-controller\";\nimport { Separator } from \"./separator\";\n\nexport type FileViewerSidebarProps = React.ComponentProps<\"aside\"> & {\n  collapsible?: FileViewerSidebarCollapsible;\n  innerClassName?: string;\n  side?: FileViewerSidebarSide;\n  width?: string;\n};\nexport type FileViewerSidebarContentProps = React.ComponentProps<\"div\">;\nexport type FileViewerSidebarFooterProps = React.ComponentProps<\"div\">;\nexport type FileViewerSidebarHeaderProps = React.ComponentProps<\"div\">;\nexport type FileViewerSidebarRailProps = React.ComponentProps<\"button\"> & {\n  asChild?: boolean;\n};\nexport type FileViewerSidebarSectionProps = React.ComponentProps<\"section\">;\nexport type FileViewerSidebarSectionActionProps =\n  React.ComponentProps<\"button\"> & {\n    asChild?: boolean;\n  };\nexport type FileViewerSidebarSectionContentProps = React.ComponentProps<\"div\">;\nexport type FileViewerSidebarSectionHeaderProps = React.ComponentProps<\"div\">;\nexport type FileViewerSidebarSectionTitleProps = React.ComponentProps<\"h3\">;\nexport type FileViewerSidebarSeparatorProps = React.ComponentProps<\n  typeof Separator\n>;\n\nexport function FileViewerSidebar({\n  children,\n  className,\n  collapsible,\n  id,\n  innerClassName,\n  side,\n  style,\n  width = DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,\n  ...props\n}: FileViewerSidebarProps) {\n  const sidebar = useFileViewerSidebarController({\n    collapsible,\n    id,\n    side,\n    style,\n    width,\n  });\n\n  return (\n    <div\n      ref={sidebar.setSidebarGapElement}\n      data-file-viewer-sidebar-collapsible={sidebar.resolvedCollapsible}\n      data-file-viewer-sidebar-mode={sidebar.mode}\n      data-file-viewer-sidebar-open={sidebar.isSidebarOpen ? \"true\" : \"false\"}\n      data-file-viewer-sidebar-side={sidebar.resolvedSide}\n      data-file-viewer-sidebar-state={sidebar.sidebarState}\n      data-slot=\"file-viewer-sidebar-gap\"\n      className={cn(\n        \"group/file-viewer-sidebar z-30 min-h-0 min-w-0 shrink-0 overflow-hidden\",\n        sidebar.isInline\n          ? \"relative h-full\"\n          : \"absolute inset-y-0 w-0 overflow-visible\",\n        sidebar.resolvedSide === \"left\" ? \"order-first\" : \"order-last\",\n        !sidebar.isInline && sidebar.resolvedSide === \"left\" && \"left-0\",\n        !sidebar.isInline && sidebar.resolvedSide === \"right\" && \"right-0\",\n      )}\n      style={\n        {\n          ...sidebar.customProperties,\n        } as React.CSSProperties\n      }\n    >\n      <aside\n        ref={sidebar.setSidebarPanelElement}\n        id={sidebar.sidebarId}\n        data-file-viewer-sidebar-collapsible={sidebar.resolvedCollapsible}\n        data-file-viewer-sidebar-mode={sidebar.mode}\n        data-file-viewer-sidebar-open={sidebar.isSidebarOpen ? \"true\" : \"false\"}\n        data-file-viewer-sidebar-side={sidebar.resolvedSide}\n        data-file-viewer-sidebar-state={sidebar.sidebarState}\n        data-slot=\"file-viewer-sidebar\"\n        className={cn(\n          \"bg-background absolute inset-y-0 flex min-w-0 flex-col overflow-hidden\",\n          sidebar.resolvedSide === \"left\"\n            ? \"right-0 border-r\"\n            : \"left-0 border-l\",\n          !sidebar.isInline && \"shadow-lg transition-transform ease-out\",\n          !sidebar.isInline &&\n            !sidebar.isSidebarOpen &&\n            sidebar.resolvedSide === \"left\" &&\n            \"-translate-x-full\",\n          !sidebar.isInline &&\n            !sidebar.isSidebarOpen &&\n            sidebar.resolvedSide === \"right\" &&\n            \"translate-x-full\",\n          className,\n        )}\n        style={sidebar.panelStyle}\n        {...props}\n        {...sidebar.accessibilityProps}\n      >\n        <FileViewerSidebarInner className={innerClassName}>\n          {children}\n        </FileViewerSidebarInner>\n      </aside>\n    </div>\n  );\n}\n\nconst FileViewerSidebarInner = React.memo(function FileViewerSidebarInner({\n  children,\n  className,\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-inner\"\n      className={cn(\n        \"flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden\",\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n});\n\nexport function FileViewerSidebarContent({\n  className,\n  ...props\n}: FileViewerSidebarContentProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-content\"\n      className={cn(\n        \"flex min-h-0 min-w-0 flex-1 flex-col overflow-auto\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarHeader({\n  className,\n  ...props\n}: FileViewerSidebarHeaderProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-header\"\n      className={cn(\n        \"flex min-h-10 min-w-0 shrink-0 items-center gap-2 border-b px-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarFooter({\n  className,\n  ...props\n}: FileViewerSidebarFooterProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-footer\"\n      className={cn(\n        \"flex min-h-10 min-w-0 shrink-0 items-center gap-2 border-t px-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarRail({\n  asChild = false,\n  className,\n  onClick,\n  type = \"button\",\n  ...props\n}: FileViewerSidebarRailProps) {\n  const Comp = asChild ? Slot.Root : \"button\";\n  const sidebar = useFileViewerShell(\"FileViewerSidebarRail\");\n\n  return (\n    <Comp\n      aria-disabled={sidebar.canToggleSidebar ? undefined : true}\n      aria-label=\"Toggle sidebar\"\n      data-file-viewer-sidebar-mode={sidebar.mode}\n      data-file-viewer-sidebar-side={sidebar.side}\n      data-file-viewer-sidebar-state={sidebar.sidebarState}\n      data-side={sidebar.side}\n      data-slot=\"file-viewer-sidebar-rail\"\n      type={asChild ? undefined : type}\n      onClick={(event) => {\n        if (!sidebar.canToggleSidebar) {\n          event.preventDefault();\n          return;\n        }\n\n        onClick?.(event);\n        if (!event.defaultPrevented) {\n          sidebar.toggleSidebar();\n        }\n      }}\n      className={cn(\n        \"hover:bg-muted absolute inset-y-0 z-40 hidden w-2 transition-colors after:absolute after:inset-y-0 after:left-1/2 after:w-4 after:-translate-x-1/2 md:block\",\n        sidebar.side === \"left\" ? \"right-0\" : \"left-0\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSection({\n  className,\n  ...props\n}: FileViewerSidebarSectionProps) {\n  return (\n    <section\n      data-slot=\"file-viewer-sidebar-section\"\n      className={cn(\"relative flex w-full min-w-0 flex-col p-3\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSectionHeader({\n  className,\n  ...props\n}: FileViewerSidebarSectionHeaderProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-section-header\"\n      className={cn(\n        \"flex min-h-8 min-w-0 shrink-0 items-center gap-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSectionTitle({\n  className,\n  ...props\n}: FileViewerSidebarSectionTitleProps) {\n  return (\n    <h3\n      data-slot=\"file-viewer-sidebar-section-title\"\n      className={cn(\n        \"text-muted-foreground min-w-0 flex-1 truncate px-1 text-xs font-medium\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSectionAction({\n  asChild = false,\n  className,\n  type = \"button\",\n  ...props\n}: FileViewerSidebarSectionActionProps) {\n  const Comp = asChild ? Slot.Root : \"button\";\n\n  return (\n    <Comp\n      data-slot=\"file-viewer-sidebar-section-action\"\n      type={asChild ? undefined : type}\n      className={cn(\n        \"ring-ring text-muted-foreground hover:bg-accent hover:text-accent-foreground flex size-7 shrink-0 items-center justify-center rounded-md outline-hidden transition-[background-color,color,box-shadow] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSectionContent({\n  className,\n  ...props\n}: FileViewerSidebarSectionContentProps) {\n  return (\n    <div\n      data-slot=\"file-viewer-sidebar-section-content\"\n      className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function FileViewerSidebarSeparator({\n  className,\n  ...props\n}: FileViewerSidebarSeparatorProps) {\n  return (\n    <Separator\n      data-slot=\"file-viewer-sidebar-separator\"\n      className={cn(\"mx-3 w-auto\", className)}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-sidebar.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-motion-plan.ts",
      "content": "\"use client\";\n\nimport type {\n  FileViewerSidebarMode,\n  FileViewerSidebarSide,\n} from \"./file-viewer-context\";\n\nexport const FILE_VIEWER_MOTION_EPSILON = 0.5;\n\n// The one duration for every sidebar motion timeline — the kernel clock, the\n// motion targets, and the overlay panel's CSS transition all read this value.\nexport const FILE_VIEWER_MOTION_DURATION_MS = 150;\n\n// The one easing for every sidebar motion timeline. Cubic ease-out: the slide\n// decelerates into rest. Linear progress ends at full velocity, and content\n// far from the reading anchor (the bottom of a tall fit-width document)\n// travels several pixels per millisecond straight into a hard stop — a\n// visible jolt the anchor line never shows.\nexport function easeFileViewerMotion(timeProgress: number) {\n  return 1 - (1 - timeProgress) ** 3;\n}\n\nexport type FileViewerMotionPhase = \"idle\" | \"sliding\" | \"settling\";\n\nexport type FileViewerMotionTarget = {\n  shellInlineSize: number;\n  durationMs: number;\n  mode: FileViewerSidebarMode;\n  open: boolean;\n  side: FileViewerSidebarSide;\n  sidebarWidth: number;\n};\n\nexport type FileViewerMotionFrame = {\n  shellInlineSize: number;\n  durationMs: number;\n  fromInlineSize: number;\n  layoutInlineSize: number;\n  mode: FileViewerSidebarMode;\n  motionId: number | null;\n  motionProgress: number;\n  open: boolean;\n  phase: FileViewerMotionPhase;\n  side: FileViewerSidebarSide;\n  sidebarInlineSize: number;\n  sidebarWidth: number;\n  toInlineSize: number;\n};\n\nexport type FileViewerMotionRestFrame = Pick<\n  FileViewerMotionFrame,\n  | \"shellInlineSize\"\n  | \"durationMs\"\n  | \"layoutInlineSize\"\n  | \"mode\"\n  | \"open\"\n  | \"side\"\n  | \"sidebarInlineSize\"\n  | \"sidebarWidth\"\n>;\n\nexport type FileViewerMotionPlan = {\n  currentRestFrame: FileViewerMotionRestFrame;\n  fromInlineSize: number;\n  nextRestFrame: FileViewerMotionRestFrame;\n  resolvedTarget: FileViewerMotionTarget;\n  shouldAnimate: boolean;\n};\n\nexport function createFileViewerMotionRestFrame(\n  target: FileViewerMotionTarget,\n): FileViewerMotionRestFrame {\n  const shellInlineSize = target.shellInlineSize;\n  const sidebarInlineSize =\n    target.mode === \"inline\" && target.open\n      ? Math.min(target.sidebarWidth, shellInlineSize)\n      : 0;\n\n  return {\n    shellInlineSize,\n    durationMs: target.durationMs,\n    layoutInlineSize: Math.max(0, shellInlineSize - sidebarInlineSize),\n    mode: target.mode,\n    open: target.open,\n    side: target.side,\n    sidebarInlineSize,\n    sidebarWidth: target.sidebarWidth,\n  };\n}\n\nexport function createFileViewerIdleMotionFrame(\n  restFrame: FileViewerMotionRestFrame,\n): FileViewerMotionFrame {\n  return {\n    ...restFrame,\n    fromInlineSize: restFrame.layoutInlineSize,\n    motionId: null,\n    motionProgress: 1,\n    phase: \"idle\",\n    toInlineSize: restFrame.layoutInlineSize,\n  };\n}\n\nexport function getFileViewerMotionRasterInlineSize(\n  frame: Pick<\n    FileViewerMotionFrame,\n    \"fromInlineSize\" | \"layoutInlineSize\" | \"toInlineSize\"\n  >,\n): number {\n  return Math.max(\n    frame.fromInlineSize,\n    frame.toInlineSize,\n    frame.layoutInlineSize,\n  );\n}\n\nexport function createFileViewerMotionPlan({\n  animate,\n  currentFrame,\n  nextTarget,\n}: {\n  animate: boolean;\n  currentFrame: FileViewerMotionFrame;\n  nextTarget: FileViewerMotionTarget;\n}): FileViewerMotionPlan {\n  const resolvedTarget = resolveFileViewerMotionTarget({\n    currentFrame,\n    nextTarget,\n  });\n  const currentRestFrame = pickFileViewerMotionRestFrame(currentFrame);\n  const nextRestFrame = createFileViewerMotionRestFrame(resolvedTarget);\n  // The motion's visual origin is what is on screen RIGHT NOW: for a fresh\n  // motion that is the rest layout; for a mid-flight retarget it is the live\n  // interpolated width, so the new motion's first frame (and every renderer's\n  // anchor solve) continues from the picture the reader is looking at rather\n  // than the interrupted motion's origin.\n  const fromInlineSize =\n    currentFrame.phase === \"sliding\"\n      ? currentFrame.layoutInlineSize\n      : currentRestFrame.layoutInlineSize;\n  const shouldAnimate =\n    animate &&\n    resolvedTarget.mode === \"inline\" &&\n    currentFrame.shellInlineSize > 0 &&\n    Math.abs(\n      currentRestFrame.sidebarInlineSize - nextRestFrame.sidebarInlineSize,\n    ) > FILE_VIEWER_MOTION_EPSILON &&\n    resolvedTarget.durationMs > 0;\n\n  return {\n    currentRestFrame,\n    fromInlineSize,\n    nextRestFrame,\n    resolvedTarget,\n    shouldAnimate,\n  };\n}\n\nexport function areFileViewerMotionRestFramesEqual(\n  previous: FileViewerMotionRestFrame,\n  next: FileViewerMotionRestFrame,\n) {\n  return (\n    areFileViewerMotionNumbersEqual(\n      previous.shellInlineSize,\n      next.shellInlineSize,\n    ) &&\n    previous.durationMs === next.durationMs &&\n    areFileViewerMotionNumbersEqual(\n      previous.layoutInlineSize,\n      next.layoutInlineSize,\n    ) &&\n    previous.mode === next.mode &&\n    previous.open === next.open &&\n    previous.side === next.side &&\n    areFileViewerMotionNumbersEqual(\n      previous.sidebarInlineSize,\n      next.sidebarInlineSize,\n    ) &&\n    areFileViewerMotionNumbersEqual(previous.sidebarWidth, next.sidebarWidth)\n  );\n}\n\nexport function areFileViewerMotionFramesEqual(\n  previous: FileViewerMotionFrame,\n  next: FileViewerMotionFrame,\n) {\n  return (\n    areFileViewerMotionRestFramesEqual(previous, next) &&\n    previous.motionId === next.motionId &&\n    areFileViewerMotionNumbersEqual(\n      previous.motionProgress,\n      next.motionProgress,\n    ) &&\n    previous.phase === next.phase &&\n    areFileViewerMotionNumbersEqual(\n      previous.fromInlineSize,\n      next.fromInlineSize,\n    ) &&\n    areFileViewerMotionNumbersEqual(previous.toInlineSize, next.toInlineSize)\n  );\n}\n\nfunction pickFileViewerMotionRestFrame(\n  frame: FileViewerMotionFrame,\n): FileViewerMotionRestFrame {\n  return {\n    shellInlineSize: frame.shellInlineSize,\n    durationMs: frame.durationMs,\n    layoutInlineSize: frame.layoutInlineSize,\n    mode: frame.mode,\n    open: frame.open,\n    side: frame.side,\n    sidebarInlineSize: frame.sidebarInlineSize,\n    sidebarWidth: frame.sidebarWidth,\n  };\n}\n\nfunction resolveFileViewerMotionTarget({\n  currentFrame,\n  nextTarget,\n}: {\n  currentFrame: FileViewerMotionFrame;\n  nextTarget: FileViewerMotionTarget;\n}): FileViewerMotionTarget {\n  if (\n    nextTarget.mode !== \"overlay\" ||\n    currentFrame.mode !== \"inline\" ||\n    currentFrame.shellInlineSize <= 0\n  ) {\n    return nextTarget;\n  }\n\n  return {\n    ...nextTarget,\n    shellInlineSize:\n      nextTarget.shellInlineSize > 0\n        ? nextTarget.shellInlineSize\n        : currentFrame.shellInlineSize,\n    mode: \"inline\",\n  };\n}\n\nfunction areFileViewerMotionNumbersEqual(previous: number, next: number) {\n  return Math.abs(previous - next) <= 0.001;\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-motion-plan.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-text-resource.ts",
      "content": "import type {\n  ViewerContentIdentity,\n  ViewerContentText,\n} from \"@/lib/viewer-resource\";\n\nimport {\n  abortError,\n  isAbortError,\n  subscribeToAbortableRequest,\n  type SharedAbortableRequest,\n} from \"./viewer-abortable-request\";\nimport { lruGet } from \"./viewer-lru-cache\";\n\nasync function timed<T>(label: string, fn: () => Promise<T>): Promise<T> {\n  const on =\n    typeof globalThis !== \"undefined\" &&\n    (globalThis as { __FILE_VIEWER_PROFILE__?: boolean })\n      .__FILE_VIEWER_PROFILE__;\n  if (!on) return fn();\n  const t0 = performance.now();\n  try {\n    return await fn();\n  } finally {\n    console.log(\n      `[file-viewer] ${label} ${(performance.now() - t0).toFixed(1)}ms`,\n    );\n  }\n}\n\nexport interface TextResourceSubscription {\n  content: ViewerContentIdentity & ViewerContentText;\n  fileName: string;\n  signal: AbortSignal;\n}\n\nexport interface TextResourceCache {\n  load(sub: TextResourceSubscription): Promise<string>;\n  clear(): void;\n  size(): number;\n}\n\nexport function createTextResourceCache(maxEntries = 12): TextResourceCache {\n  const requests = new Map<string, SharedAbortableRequest<string>>();\n\n  function remove(contentKey: string) {\n    requests.delete(contentKey);\n  }\n\n  function pruneInactiveEntries() {\n    while (requests.size > maxEntries) {\n      let pruned = false;\n      for (const [contentKey, entry] of requests) {\n        if (!entry.settled && entry.subscribers.size > 0) continue;\n        requests.delete(contentKey);\n        if (!entry.settled) entry.controller.abort();\n        pruned = true;\n        break;\n      }\n      if (!pruned) break;\n    }\n  }\n\n  function entryFor({\n    content,\n    fileName,\n  }: TextResourceSubscription): SharedAbortableRequest<string> {\n    const contentKey = content.key;\n    const existingEntry = lruGet(requests, contentKey);\n    if (existingEntry) return existingEntry;\n\n    const controller = new AbortController();\n    let entry: SharedAbortableRequest<string> | null = null;\n    const promise = timed(`text:fetch ${fileName}`, () =>\n      content.readText({ signal: controller.signal }),\n    )\n      .catch((error: unknown) => {\n        if (!isAbortError(error)) requests.delete(contentKey);\n        throw error;\n      })\n      .finally(() => {\n        if (entry) {\n          entry.settled = true;\n          pruneInactiveEntries();\n        }\n      });\n\n    entry = {\n      controller,\n      promise,\n      subscriberPromises: new WeakMap(),\n      subscribers: new Set(),\n      settled: false,\n    };\n\n    requests.set(contentKey, entry);\n    return entry;\n  }\n\n  return {\n    load(sub) {\n      if (sub.signal.aborted) return Promise.reject(abortError());\n\n      const contentKey = sub.content.key;\n      const entry = entryFor(sub);\n      const promise = subscribeToAbortableRequest(entry, sub.signal, () =>\n        remove(contentKey),\n      );\n      pruneInactiveEntries();\n      return promise;\n    },\n    clear() {\n      for (const entry of requests.values()) entry.controller.abort();\n      requests.clear();\n    },\n    size() {\n      return requests.size;\n    },\n  };\n}\n\nexport const textResource = createTextResourceCache();\n\nexport function loadTextResource(\n  sub: TextResourceSubscription,\n): Promise<string> {\n  return textResource.load(sub);\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-text-resource.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer.tsx",
      "content": "\"use client\";\n\nexport { ViewerBody } from \"./viewer-body\";\nexport type { ViewerSidebarTriggerProps } from \"./viewer-chrome\";\nexport {\n  ViewerFrame,\n  ViewerHeader,\n  ViewerSidebarTrigger,\n} from \"./viewer-chrome\";\nexport {\n  ViewerRoot,\n  useOptionalViewerSidebar,\n  useViewerSidebar,\n} from \"./viewer-root\";\nexport { ViewerSidebar } from \"./viewer-sidebar\";\nexport { ViewerSurface, ViewerViewport } from \"./viewer-surface\";\nexport type {\n  ViewerBodyProps,\n  ViewerDataAttributes,\n  ViewerFrameProps,\n  ViewerHeaderProps,\n  ViewerRootProps,\n  ViewerSidebarCollapsible,\n  ViewerSidebarStateValue,\n  ViewerSidebarMode,\n  ViewerSidebarProps,\n  ViewerSidebarRequestedMode,\n  ViewerSidebarSide,\n  ViewerSidebarSlotNames,\n  ViewerSidebarState,\n  ViewerStateAttributeNamespace,\n  ViewerStateAttributeSlot,\n  ViewerStateAttributeValues,\n  ViewerSurfaceProps,\n  ViewerViewportProps,\n} from \"./viewer-types\";\n",
      "type": "registry:ui",
      "target": "@ui/viewer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-body.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  createViewerStateAttributes,\n  readViewerElementSize,\n  useOptionalViewerSidebarRegistration,\n  useViewerDevelopmentLayoutWarning,\n} from \"./viewer-internals\";\nimport { useOptionalViewerSidebar } from \"./viewer-root\";\nimport { warnViewerDevelopmentOnce } from \"./viewer-diagnostics\";\nimport type { ViewerBodyProps } from \"./viewer-types\";\n\nexport function ViewerBody({ className, ...props }: ViewerBodyProps) {\n  const bodyRef = React.useRef<HTMLDivElement | null>(null);\n  const [bodyElement, setBodyElement] = React.useState<HTMLDivElement | null>(\n    null,\n  );\n  const sidebarState = useOptionalViewerSidebar();\n  const sidebarRegistration = useOptionalViewerSidebarRegistration();\n  const namespacedBodyStateAttributes = createViewerStateAttributes(\n    sidebarRegistration?.stateNamespace,\n    \"body\",\n    {\n      hasSidebar: sidebarRegistration?.hasSidebar === true,\n      sidebarMode: sidebarState?.mode,\n      sidebarOpen: sidebarState?.open,\n      sidebarSide: sidebarRegistration?.sidebarSide,\n      sidebarState: sidebarState?.state,\n    },\n  );\n\n  useViewerDevelopmentLayoutWarning({\n    enabled: Boolean(sidebarRegistration),\n    elements: () => [\n      sidebarRegistration?.getRootElement() ?? null,\n      bodyRef.current,\n    ],\n    inspect: () => {\n      const rootElement = sidebarRegistration?.getRootElement() ?? null;\n      const bodyElement = bodyRef.current;\n      const rootSize = readViewerElementSize(rootElement);\n      const bodySize = readViewerElementSize(bodyElement);\n\n      if (rootSize.width > 0 && bodySize.width === 0) {\n        warnViewerDevelopmentOnce({\n          code: \"viewer_body_zero_width\",\n          message:\n            \"viewer root has nonzero width but viewer body width is zero.\",\n          rootId: sidebarRegistration?.rootId ?? \"unknown-root\",\n          details: {\n            bodyHeight: bodySize.height,\n            bodySlot: bodyElement?.dataset.slot,\n            bodyWidth: bodySize.width,\n            rootHeight: rootSize.height,\n            rootSlot: rootElement?.dataset.slot,\n            rootWidth: rootSize.width,\n          },\n        });\n      }\n    },\n    keyParts: [\n      \"viewer-body-zero-width\",\n      sidebarRegistration?.rootId,\n      sidebarRegistration?.getRootElement(),\n      bodyRef.current,\n    ],\n  });\n  const setBodyRef = React.useCallback((element: HTMLDivElement | null) => {\n    bodyRef.current = element;\n    setBodyElement(element);\n  }, []);\n\n  useKeyedLayoutEffect(\n    bodyElement && sidebarRegistration\n      ? joinEffectKey([\n          \"viewer-body:register-geometry\",\n          bodyElement,\n          sidebarRegistration.registerBody,\n        ])\n      : null,\n    () => {\n      if (!bodyElement || !sidebarRegistration) return;\n      return sidebarRegistration.registerBody(bodyElement);\n    },\n  );\n\n  return (\n    <div\n      ref={setBodyRef}\n      data-slot=\"viewer-body\"\n      data-viewer-has-sidebar={\n        sidebarRegistration?.hasSidebar === true ? \"true\" : \"false\"\n      }\n      data-viewer-sidebar-mode={sidebarState?.mode}\n      data-viewer-sidebar-open={\n        sidebarState ? (sidebarState.open ? \"true\" : \"false\") : undefined\n      }\n      data-viewer-sidebar-side={sidebarRegistration?.sidebarSide}\n      data-viewer-sidebar-state={sidebarState?.state}\n      data-viewer-slot=\"body\"\n      {...namespacedBodyStateAttributes}\n      className={cn(\n        \"relative flex min-h-0 min-w-0 flex-1 overflow-hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-body.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-chrome.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { PanelLeft, PanelRight } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"./button\";\nimport { Spinner } from \"./spinner\";\nimport {\n  isAriaDisabled,\n  useViewerSidebarState,\n  useViewerSidebarRegistrationContext,\n} from \"./viewer-internals\";\nimport type { ViewerFrameProps, ViewerHeaderProps } from \"./viewer-types\";\n\n// Stock Button no longer exports ButtonProps or a `loading` prop. ViewerSidebarTrigger\n// keeps its own `loading` API, so we define ButtonProps locally as the stock Button's\n// props plus the retab `loading` flag (translated to disabled + Spinner at the leaf).\ntype ButtonProps = React.ComponentProps<typeof Button> & {\n  loading?: boolean;\n};\n\nexport type ViewerSidebarTriggerProps = ButtonProps;\n\nexport function ViewerFrame({ className, ...props }: ViewerFrameProps) {\n  return (\n    <div\n      data-slot=\"viewer-frame\"\n      className={cn(\n        \"bg-background relative min-h-0 overflow-hidden rounded-xl border\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function ViewerHeader({ className, ...props }: ViewerHeaderProps) {\n  return (\n    <div\n      data-slot=\"viewer-header\"\n      className={cn(\"bg-card flex-shrink-0 border-b\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ViewerSidebarTrigger({\n  className,\n  disabled,\n  loading,\n  onClick,\n  onPointerDown,\n  children,\n  size = \"icon\",\n  variant = \"ghost\",\n  \"aria-label\": ariaLabel = \"Toggle sidebar\",\n  \"aria-disabled\": ariaDisabled,\n  ...props\n}: ViewerSidebarTriggerProps) {\n  const {\n    hasSidebar,\n    rootId,\n    sidebarId,\n    sidebarSide,\n    setLastTriggerElement,\n  } = useViewerSidebarRegistrationContext(\"ViewerSidebarTrigger\");\n  const sidebarState = useViewerSidebarState(\"ViewerSidebarTrigger\");\n  const { canToggleSidebar, open, state, toggleSidebar } = sidebarState;\n  const isDisabled = Boolean(\n    disabled || loading || isAriaDisabled(ariaDisabled) || !canToggleSidebar,\n  );\n  const Icon = sidebarSide === \"right\" ? PanelRight : PanelLeft;\n\n  return (\n    <Button\n      aria-controls={hasSidebar ? sidebarId : undefined}\n      aria-disabled={isDisabled ? true : ariaDisabled}\n      aria-expanded={hasSidebar ? open : undefined}\n      aria-label={ariaLabel}\n      className={cn(\"size-8\", className)}\n      data-side={sidebarSide}\n      data-slot=\"viewer-sidebar-trigger\"\n      data-state={state}\n      data-viewer-root-id={rootId}\n      data-viewer-sidebar-trigger=\"\"\n      disabled={isDisabled}\n      onClick={(event) => {\n        setLastTriggerElement(event.currentTarget);\n        if (isDisabled) {\n          event.preventDefault();\n          return;\n        }\n\n        onClick?.(event);\n        if (!event.defaultPrevented) {\n          toggleSidebar();\n        }\n      }}\n      onPointerDown={(event) => {\n        setLastTriggerElement(event.currentTarget);\n        onPointerDown?.(event);\n      }}\n      size={size}\n      variant={variant}\n      {...props}\n    >\n      {loading ? <Spinner className=\"size-4 animate-spin\" /> : null}\n      {children ?? (\n        <>\n          {loading ? null : <Icon />}\n          <span className=\"sr-only\">Toggle sidebar</span>\n        </>\n      )}\n    </Button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-chrome.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-diagnostics.ts",
      "content": "\"use client\";\n\nconst emittedViewerDevelopmentWarnings = new Set<string>();\n\nexport type ViewerDevelopmentWarningDetails = Record<string, unknown>;\n\nexport function warnViewerDevelopmentOnce({\n  code,\n  details,\n  message,\n  rootId,\n}: {\n  code: string;\n  details?: ViewerDevelopmentWarningDetails;\n  message: string;\n  rootId: string;\n}) {\n  if (process.env.NODE_ENV === \"production\") return;\n\n  const warningKey = `${rootId}:${code}`;\n  if (emittedViewerDevelopmentWarnings.has(warningKey)) return;\n  emittedViewerDevelopmentWarnings.add(warningKey);\n\n  console.warn(`[file-viewer] ${message}`, {\n    code,\n    rootId,\n    ...details,\n  });\n}\n\nexport function resetViewerDevelopmentWarningsForTests() {\n  emittedViewerDevelopmentWarnings.clear();\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-diagnostics.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-header-outlet.tsx",
      "content": "\"use client\";\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nexport type ViewerHeaderOutletName = \"titleGroup\" | \"controls\";\n\ntype ViewerHeaderOutletContextValue = {\n  outlets: Partial<Record<ViewerHeaderOutletName, HTMLElement | null>>;\n  registerContent?: (name: ViewerHeaderOutletName) => () => void;\n};\n\nconst ViewerHeaderOutletContext =\n  createContext<ViewerHeaderOutletContextValue | null>(null);\n\nexport function useViewerHeaderOutletAvailable(name: ViewerHeaderOutletName) {\n  const context = useContext(ViewerHeaderOutletContext);\n  return Boolean(context?.outlets[name]);\n}\n\nexport function useViewerHeaderOutletsAvailable(\n  names: ViewerHeaderOutletName[] = [\"titleGroup\", \"controls\"],\n) {\n  const context = useContext(ViewerHeaderOutletContext);\n  if (!context) return false;\n  return names.every((name) => Boolean(context.outlets[name]));\n}\n\nexport function useViewerHeaderOutlets() {\n  return useViewerHeaderOutletsAvailable();\n}\n\nexport function ViewerHeaderOutlet({\n  children,\n  name,\n}: {\n  children: ReactNode;\n  name: ViewerHeaderOutletName;\n}) {\n  const context = useContext(ViewerHeaderOutletContext);\n  const target = context?.outlets[name] ?? null;\n  const outletMountKey = target ? `viewer-header-outlet:${name}` : null;\n\n  useKeyedMountEffect(outletMountKey, () =>\n    target ? context?.registerContent?.(name) : undefined,\n  );\n\n  if (!target) return null;\n\n  return createPortal(children, target);\n}\n\nexport function ViewerHeaderOutletStart({ children }: { children: ReactNode }) {\n  const hasHeaderOutlets = useViewerHeaderOutlets();\n  if (!hasHeaderOutlets) return <>{children}</>;\n  return <ViewerHeaderOutlet name=\"titleGroup\">{children}</ViewerHeaderOutlet>;\n}\n\nexport function ViewerHeaderOutletEnd({ children }: { children: ReactNode }) {\n  const hasHeaderOutlets = useViewerHeaderOutlets();\n  if (!hasHeaderOutlets) return <>{children}</>;\n  return <ViewerHeaderOutlet name=\"controls\">{children}</ViewerHeaderOutlet>;\n}\n\nexport function ViewerHeaderOutletProvider({\n  children,\n  outlets,\n  registerContent,\n}: {\n  children: ReactNode;\n  outlets: Partial<Record<ViewerHeaderOutletName, HTMLElement | null>>;\n  registerContent?: (name: ViewerHeaderOutletName) => () => void;\n}) {\n  const value = useMemo(\n    () => ({ outlets, registerContent }),\n    [outlets, registerContent],\n  );\n\n  return (\n    <ViewerHeaderOutletContext.Provider value={value}>\n      {children}\n    </ViewerHeaderOutletContext.Provider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-header-outlet.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-internals.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nimport type {\n  ViewerDataAttributes,\n  ViewerPortalContainmentAttributes,\n  ViewerSidebarStateValue,\n  ViewerGeometrySnapshot,\n  ViewerGeometryStore,\n  ViewerGeometryTarget,\n  ViewerSidebarRegistrationState,\n  ViewerSidebarRequestedMode,\n  ViewerSidebarMode,\n  ViewerStateAttributeNamespace,\n  ViewerStateAttributeSlot,\n  ViewerStateAttributeValues,\n  ViewerSurfaceMeasurement,\n} from \"./viewer-types\";\n\nexport const VIEWER_SIDEBAR_INLINE_BREAKPOINT = 768;\nexport const VIEWER_SIDEBAR_WIDTH = \"10rem\";\nexport const VIEWER_ROOT_ID_ATTRIBUTE = \"data-viewer-root-id\";\nexport const VIEWER_PORTAL_ROOT_ID_ATTRIBUTE = \"data-viewer-portal-root-id\";\n\nconst VIEWER_SIDEBAR_MODE_HYSTERESIS = 16;\n\nconst DEFAULT_VIEWER_GEOMETRY_SNAPSHOT: ViewerGeometrySnapshot = {\n  bodyInlineSize: 0,\n  documentInlineSize: 0,\n  hasMeasuredBody: false,\n  isTransitioning: false,\n  mode: \"overlay\",\n  open: false,\n  progress: 1,\n  sidebarGapTransition: \"width\",\n  sidebarInlineSize: 0,\n  sidebarWidth: 0,\n  side: \"left\",\n  state: \"collapsed\",\n  transitionPhase: \"idle\",\n};\n\nconst ViewerSidebarStateContext =\n  React.createContext<ViewerSidebarStateValue | null>(null);\nconst ViewerSidebarRegistrationContext =\n  React.createContext<ViewerSidebarRegistrationState | null>(null);\nconst ViewerSurfaceMeasurementContext =\n  React.createContext<ViewerSurfaceMeasurement | null>(null);\nconst ViewerViewportPresenceContext = React.createContext(false);\nconst VIEWER_GEOMETRY_TRANSITION_MS = 150;\n\ntype ViewerGeometryBaseSnapshot = Pick<\n  ViewerGeometrySnapshot,\n  | \"bodyInlineSize\"\n  | \"documentInlineSize\"\n  | \"hasMeasuredBody\"\n  | \"mode\"\n  | \"open\"\n  | \"sidebarGapTransition\"\n  | \"sidebarInlineSize\"\n  | \"sidebarWidth\"\n  | \"side\"\n  | \"state\"\n>;\n\nexport function ViewerSidebarStateProvider({\n  children,\n  value,\n}: {\n  children: React.ReactNode;\n  value: ViewerSidebarStateValue;\n}) {\n  return React.createElement(\n    ViewerSidebarStateContext.Provider,\n    { value },\n    children,\n  );\n}\n\nexport function ViewerSidebarRegistrationProvider({\n  children,\n  value,\n}: {\n  children: React.ReactNode;\n  value: ViewerSidebarRegistrationState;\n}) {\n  return React.createElement(\n    ViewerSidebarRegistrationContext.Provider,\n    { value },\n    children,\n  );\n}\n\nexport function ViewerSurfaceMeasurementProvider({\n  children,\n  value,\n}: {\n  children: React.ReactNode;\n  value: ViewerSurfaceMeasurement;\n}) {\n  return React.createElement(\n    ViewerSurfaceMeasurementContext.Provider,\n    { value },\n    children,\n  );\n}\n\nexport function ViewerViewportPresenceProvider({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return React.createElement(\n    ViewerViewportPresenceContext.Provider,\n    { value: true },\n    children,\n  );\n}\n\nexport function useOptionalViewerSidebarState(): ViewerSidebarStateValue | null {\n  return React.useContext(ViewerSidebarStateContext);\n}\n\nexport function useViewerSidebarState(\n  consumer: string,\n): ViewerSidebarStateValue {\n  const context = useOptionalViewerSidebarState();\n  if (!context) {\n    throw new Error(`${consumer} must be used within a ViewerRoot.`);\n  }\n  return context;\n}\n\nexport function useOptionalViewerSidebarRegistration(): ViewerSidebarRegistrationState | null {\n  return React.useContext(ViewerSidebarRegistrationContext);\n}\n\nexport function useOptionalViewerSurfaceMeasurementContext(): ViewerSurfaceMeasurement | null {\n  return React.useContext(ViewerSurfaceMeasurementContext);\n}\n\nexport function useViewerViewportPresence(): boolean {\n  return React.useContext(ViewerViewportPresenceContext);\n}\n\nexport function useViewerGeometrySnapshot(\n  geometryStore: ViewerGeometryStore,\n): ViewerGeometrySnapshot {\n  return React.useSyncExternalStore(\n    geometryStore.subscribe,\n    geometryStore.getSnapshot,\n    geometryStore.getSnapshot,\n  );\n}\n\nexport function createViewerGeometryStore(): ViewerGeometryStore {\n  const listeners = new Set<() => void>();\n  let snapshot = DEFAULT_VIEWER_GEOMETRY_SNAPSHOT;\n  let transitionTargetSnapshot: ViewerGeometryBaseSnapshot | null = null;\n  let transitionFrame = 0;\n  let transitionStartTime: number | null = null;\n  let transitionStartSidebarInlineSize =\n    DEFAULT_VIEWER_GEOMETRY_SNAPSHOT.sidebarInlineSize;\n\n  const notify = () => {\n    for (const listener of listeners) listener();\n  };\n\n  const commitSnapshot = (nextSnapshot: ViewerGeometrySnapshot) => {\n    if (areViewerGeometrySnapshotsEqual(snapshot, nextSnapshot)) return;\n\n    snapshot = nextSnapshot;\n    notify();\n  };\n\n  const cancelTransitionFrame = () => {\n    if (transitionFrame === 0) return;\n    const cancelAnimationFrame =\n      globalThis.cancelAnimationFrame ??\n      (typeof window === \"undefined\" ? undefined : window.cancelAnimationFrame);\n    cancelAnimationFrame?.(transitionFrame);\n    transitionFrame = 0;\n  };\n\n  const commitGeometryProgress = (progress: number) => {\n    if (!transitionTargetSnapshot) return;\n\n    const sidebarInlineSize = lerp(\n      transitionStartSidebarInlineSize,\n      transitionTargetSnapshot.sidebarInlineSize,\n      progress,\n    );\n\n    commitSnapshot({\n      ...transitionTargetSnapshot,\n      documentInlineSize: getViewerGeometryDocumentInlineSize({\n        bodyInlineSize: transitionTargetSnapshot.bodyInlineSize,\n        mode: transitionTargetSnapshot.mode,\n        sidebarGapTransition: transitionTargetSnapshot.sidebarGapTransition,\n        sidebarInlineSize,\n      }),\n      isTransitioning: progress < 1,\n      progress,\n      sidebarInlineSize,\n      transitionPhase: progress < 1 ? \"sliding\" : \"idle\",\n    });\n\n    if (progress >= 1) {\n      transitionTargetSnapshot = null;\n    }\n  };\n\n  const scheduleTransitionFrame = () => {\n    const requestAnimationFrame =\n      globalThis.requestAnimationFrame ??\n      (typeof window === \"undefined\" ? undefined : window.requestAnimationFrame);\n\n    if (typeof requestAnimationFrame !== \"function\") {\n      commitGeometryProgress(1);\n      return;\n    }\n\n    transitionFrame = requestAnimationFrame((timestamp) => {\n      transitionFrame = 0;\n      if (!transitionTargetSnapshot) return;\n      if (transitionStartTime === null) transitionStartTime = timestamp;\n\n      const progress = clamp01(\n        (timestamp - transitionStartTime) / VIEWER_GEOMETRY_TRANSITION_MS,\n      );\n      commitGeometryProgress(progress);\n\n      if (progress < 1) scheduleTransitionFrame();\n    });\n  };\n\n  const startTransition = (targetSnapshot: ViewerGeometryBaseSnapshot) => {\n    cancelTransitionFrame();\n\n    transitionTargetSnapshot = targetSnapshot;\n    transitionStartSidebarInlineSize = snapshot.sidebarInlineSize;\n    transitionStartTime = readViewerGeometryNow();\n\n    commitGeometryProgress(0);\n    scheduleTransitionFrame();\n  };\n\n  return {\n    getSnapshot: () => snapshot,\n    setTarget: (target) => {\n      const resolvedSidebarWidth =\n        Number.isFinite(target.sidebarWidth) && target.sidebarWidth > 0\n          ? target.sidebarWidth\n          : readViewerGeometrySidebarWidth(target);\n      const sidebarWidth =\n        resolvedSidebarWidth > 0 ? resolvedSidebarWidth : snapshot.sidebarWidth;\n      const measuredBodyInlineSize = readViewerGeometryBodyInlineSize(target);\n      const bodyInlineSize =\n        measuredBodyInlineSize > 0\n          ? measuredBodyInlineSize\n          : snapshot.bodyInlineSize;\n      const hasMeasuredBody =\n        measuredBodyInlineSize > 0 || snapshot.hasMeasuredBody;\n      const sidebarInlineSize = getViewerGeometryTargetSidebarInlineSize({\n        mode: target.mode,\n        open: target.open,\n        sidebarGapTransition: target.sidebarGapTransition,\n        sidebarWidth,\n      });\n      const nextBaseSnapshot: ViewerGeometryBaseSnapshot = {\n        bodyInlineSize,\n        documentInlineSize: getViewerGeometryDocumentInlineSize({\n          bodyInlineSize,\n          mode: target.mode,\n          sidebarGapTransition: target.sidebarGapTransition,\n          sidebarInlineSize,\n        }),\n        hasMeasuredBody,\n        mode: target.mode,\n        open: target.open,\n        sidebarGapTransition: target.sidebarGapTransition,\n        sidebarInlineSize,\n        sidebarWidth,\n        side: target.side,\n        state: target.state,\n      };\n\n      if (\n        snapshot.isTransitioning &&\n        transitionTargetSnapshot &&\n        areViewerGeometryTargetsEqual(\n          transitionTargetSnapshot,\n          nextBaseSnapshot,\n        )\n      ) {\n        return;\n      }\n\n      const shouldAnimate = shouldAnimateViewerGeometry({\n        previousSnapshot: snapshot,\n        targetSnapshot: nextBaseSnapshot,\n      });\n\n      if (!shouldAnimate) {\n        cancelTransitionFrame();\n        transitionTargetSnapshot = null;\n        commitSnapshot({\n          ...nextBaseSnapshot,\n          isTransitioning: false,\n          progress: 1,\n          transitionPhase: \"idle\",\n        });\n        return;\n      }\n\n      startTransition(nextBaseSnapshot);\n    },\n    subscribe: (listener) => {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n  };\n}\n\nexport function resolveSidebarMode({\n  requestedMode,\n  width,\n  inlineBreakpoint,\n}: {\n  requestedMode: ViewerSidebarRequestedMode;\n  width: number | null;\n  inlineBreakpoint: number;\n}): ViewerSidebarMode {\n  if (requestedMode !== \"auto\") return requestedMode;\n  if (width === null) return \"overlay\";\n  return width >= inlineBreakpoint ? \"inline\" : \"overlay\";\n}\n\nexport function resolveMeasuredSidebarMode({\n  currentMode,\n  hasMeasured,\n  inlineBreakpoint,\n  requestedMode,\n  width,\n}: {\n  currentMode: ViewerSidebarMode;\n  hasMeasured: boolean;\n  inlineBreakpoint: number;\n  requestedMode: ViewerSidebarRequestedMode;\n  width: number;\n}): ViewerSidebarMode {\n  if (requestedMode !== \"auto\") return requestedMode;\n  if (!hasMeasured) {\n    return width >= inlineBreakpoint ? \"inline\" : \"overlay\";\n  }\n\n  if (currentMode === \"inline\") {\n    return width < inlineBreakpoint - VIEWER_SIDEBAR_MODE_HYSTERESIS\n      ? \"overlay\"\n      : \"inline\";\n  }\n\n  return width > inlineBreakpoint + VIEWER_SIDEBAR_MODE_HYSTERESIS\n    ? \"inline\"\n    : \"overlay\";\n}\n\nexport function isAriaDisabled(value: unknown): boolean {\n  return value === true || value === \"true\";\n}\n\nexport function pickCssCustomProperties(\n  style: React.CSSProperties | undefined,\n): React.CSSProperties {\n  if (!style) return {};\n\n  return Object.fromEntries(\n    Object.entries(style).filter(([name]) => name.startsWith(\"--\")),\n  ) as React.CSSProperties;\n}\n\nfunction isViewerStateNamespaceEnabled(\n  namespace: ViewerStateAttributeNamespace | undefined,\n  slot: ViewerStateAttributeSlot,\n): namespace is ViewerStateAttributeNamespace {\n  if (!namespace) return false;\n  return namespace.slots?.[slot] ?? true;\n}\n\nexport function createViewerStateAttributes(\n  namespace: ViewerStateAttributeNamespace | undefined,\n  slot: ViewerStateAttributeSlot,\n  values: ViewerStateAttributeValues,\n): ViewerDataAttributes {\n  if (!isViewerStateNamespaceEnabled(namespace, slot)) return {};\n\n  const attributes: ViewerDataAttributes = {};\n  const prefix = namespace.prefix;\n\n  if (values.hasSidebar !== undefined) {\n    attributes[`data-${prefix}-has-sidebar`] = values.hasSidebar\n      ? \"true\"\n      : \"false\";\n  }\n  if (values.sidebarCollapsible !== undefined) {\n    attributes[`data-${prefix}-sidebar-collapsible`] =\n      values.sidebarCollapsible;\n  }\n  if (values.sidebarMode !== undefined) {\n    attributes[`data-${prefix}-sidebar-mode`] = values.sidebarMode;\n  }\n  if (values.sidebarOpen !== undefined) {\n    attributes[`data-${prefix}-sidebar-open`] = values.sidebarOpen\n      ? \"true\"\n      : \"false\";\n  }\n  if (values.sidebarSide !== undefined) {\n    attributes[`data-${prefix}-sidebar-side`] = values.sidebarSide;\n  }\n  if (values.sidebarState !== undefined) {\n    attributes[`data-${prefix}-sidebar-state`] = values.sidebarState;\n  }\n\n  return attributes;\n}\n\nexport function createViewerSlotAttributes(\n  namespace: ViewerStateAttributeNamespace | undefined,\n  slot: ViewerStateAttributeSlot,\n  value: string | undefined,\n): ViewerDataAttributes {\n  if (!value || !isViewerStateNamespaceEnabled(namespace, slot)) return {};\n  return {\n    [`data-${namespace.prefix}-slot`]: value,\n  };\n}\n\nexport function createViewerPortalContainmentAttributes(\n  rootId: string,\n): ViewerPortalContainmentAttributes {\n  return {\n    [VIEWER_PORTAL_ROOT_ID_ATTRIBUTE]: rootId,\n  };\n}\n\nexport function readViewerElementSize(element: HTMLElement | null) {\n  if (!element) return { height: 0, width: 0 };\n\n  const rect =\n    typeof element.getBoundingClientRect === \"function\"\n      ? element.getBoundingClientRect()\n      : null;\n  return {\n    height: rect?.height || element.clientHeight || 0,\n    width: rect?.width || element.clientWidth || 0,\n  };\n}\n\nfunction readViewerGeometrySidebarWidth({\n  rootElement,\n  sidebarElement,\n}: ViewerGeometryTarget) {\n  const elementSize = readViewerElementSize(sidebarElement);\n  if (Number.isFinite(elementSize.width) && elementSize.width > 0) {\n    return elementSize.width;\n  }\n\n  return readViewerSidebarDeclaredWidth(rootElement, sidebarElement);\n}\n\nfunction readViewerGeometryBodyInlineSize({\n  bodyElement,\n  rootElement,\n}: ViewerGeometryTarget) {\n  const bodySize = readViewerElementSize(bodyElement);\n  if (Number.isFinite(bodySize.width) && bodySize.width > 0) {\n    return bodySize.width;\n  }\n\n  const rootSize = readViewerElementSize(rootElement);\n  return Number.isFinite(rootSize.width) && rootSize.width > 0\n    ? rootSize.width\n    : 0;\n}\n\nfunction readViewerSidebarDeclaredWidth(\n  rootElement: HTMLElement | null,\n  sidebarElement: HTMLElement | null,\n) {\n  return (\n    readViewerCssLength(rootElement, \"--viewer-sidebar-width\") ??\n    readViewerCssLength(sidebarElement, \"--viewer-sidebar-width\") ??\n    0\n  );\n}\n\nfunction readViewerCssLength(\n  element: HTMLElement | null,\n  propertyName: string,\n) {\n  if (!element || typeof window === \"undefined\") return null;\n\n  const styleValue = element.style.getPropertyValue(propertyName);\n  const computedValue =\n    window.getComputedStyle(element).getPropertyValue(propertyName);\n  return parseViewerCssLength(styleValue) ?? parseViewerCssLength(computedValue);\n}\n\nfunction parseViewerCssLength(value: string) {\n  const trimmedValue = value.trim();\n  if (!trimmedValue.endsWith(\"px\")) return null;\n\n  const length = Number.parseFloat(trimmedValue);\n  return Number.isFinite(length) && length > 0 ? length : null;\n}\n\nfunction areViewerGeometrySnapshotsEqual(\n  previous: ViewerGeometrySnapshot,\n  next: ViewerGeometrySnapshot,\n) {\n  return (\n    previous.bodyInlineSize === next.bodyInlineSize &&\n    previous.documentInlineSize === next.documentInlineSize &&\n    previous.hasMeasuredBody === next.hasMeasuredBody &&\n    previous.isTransitioning === next.isTransitioning &&\n    previous.mode === next.mode &&\n    previous.open === next.open &&\n    previous.progress === next.progress &&\n    previous.sidebarGapTransition === next.sidebarGapTransition &&\n    previous.sidebarInlineSize === next.sidebarInlineSize &&\n    previous.sidebarWidth === next.sidebarWidth &&\n    previous.side === next.side &&\n    previous.state === next.state &&\n    previous.transitionPhase === next.transitionPhase\n  );\n}\n\nfunction areViewerGeometryTargetsEqual(\n  previous: ViewerGeometryBaseSnapshot,\n  next: ViewerGeometryBaseSnapshot,\n) {\n  return (\n    previous.bodyInlineSize === next.bodyInlineSize &&\n    previous.documentInlineSize === next.documentInlineSize &&\n    previous.hasMeasuredBody === next.hasMeasuredBody &&\n    previous.mode === next.mode &&\n    previous.open === next.open &&\n    previous.sidebarGapTransition === next.sidebarGapTransition &&\n    previous.sidebarInlineSize === next.sidebarInlineSize &&\n    previous.sidebarWidth === next.sidebarWidth &&\n    previous.side === next.side &&\n    previous.state === next.state\n  );\n}\n\nfunction shouldAnimateViewerGeometry({\n  previousSnapshot,\n  targetSnapshot,\n}: {\n  previousSnapshot: ViewerGeometrySnapshot;\n  targetSnapshot: ViewerGeometryBaseSnapshot;\n}) {\n  return (\n    previousSnapshot.sidebarWidth > 0 &&\n    targetSnapshot.mode === \"inline\" &&\n    targetSnapshot.sidebarGapTransition === \"width\" &&\n    targetSnapshot.sidebarWidth > 0 &&\n    hasViewerGeometryAnimationSupport() &&\n    !prefersReducedViewerGeometryMotion() &&\n    !areViewerGeometryInlineSizesEqual(\n      previousSnapshot.sidebarInlineSize,\n      targetSnapshot.sidebarInlineSize,\n    )\n  );\n}\n\nfunction getViewerGeometryTargetSidebarInlineSize(\n  snapshot: Pick<\n    ViewerGeometryBaseSnapshot,\n    \"mode\" | \"open\" | \"sidebarGapTransition\" | \"sidebarWidth\"\n  >,\n) {\n  return snapshot.mode === \"inline\" && snapshot.open && snapshot.sidebarWidth > 0\n    ? snapshot.sidebarWidth\n    : 0;\n}\n\nfunction getViewerGeometryDocumentInlineSize({\n  bodyInlineSize,\n  mode,\n  sidebarGapTransition,\n  sidebarInlineSize,\n}: {\n  bodyInlineSize: number;\n  mode: ViewerSidebarMode;\n  sidebarGapTransition: string;\n  sidebarInlineSize: number;\n}) {\n  if (bodyInlineSize <= 0) return 0;\n  if (mode !== \"inline\" || sidebarGapTransition !== \"width\") {\n    return bodyInlineSize;\n  }\n  return Math.max(1, bodyInlineSize - sidebarInlineSize);\n}\n\nfunction areViewerGeometryInlineSizesEqual(\n  previousInlineSize: number,\n  nextInlineSize: number,\n) {\n  return Math.abs(previousInlineSize - nextInlineSize) < 0.5;\n}\n\nfunction hasViewerGeometryAnimationSupport() {\n  const requestAnimationFrame =\n    globalThis.requestAnimationFrame ??\n    (typeof window === \"undefined\" ? undefined : window.requestAnimationFrame);\n  return typeof requestAnimationFrame === \"function\";\n}\n\nfunction prefersReducedViewerGeometryMotion() {\n  return (\n    typeof window !== \"undefined\" &&\n    typeof window.matchMedia === \"function\" &&\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  );\n}\n\nfunction readViewerGeometryNow() {\n  const performanceNow =\n    globalThis.performance?.now ??\n    (typeof window === \"undefined\" ? undefined : window.performance?.now);\n\n  return typeof performanceNow === \"function\"\n    ? performanceNow.call(globalThis.performance ?? window.performance)\n    : Date.now();\n}\n\nfunction lerp(start: number, end: number, progress: number) {\n  return start + (end - start) * progress;\n}\n\nfunction clamp01(value: number) {\n  if (!Number.isFinite(value)) return 1;\n  return Math.min(1, Math.max(0, value));\n}\n\nexport function elementContainsTarget(\n  element: HTMLElement | null | undefined,\n  target: unknown,\n): boolean {\n  if (!element || target === null || target === undefined) return false;\n  if (element === target) return true;\n\n  if (typeof element.contains === \"function\") {\n    try {\n      if (element.contains(target as Node)) return true;\n    } catch {\n      // Some test and embedded DOM hosts do not share a Node prototype chain.\n    }\n  }\n\n  if (typeof target !== \"object\") return false;\n\n  let current: unknown = (target as { parentNode?: unknown }).parentNode;\n  while (current && typeof current === \"object\") {\n    if (current === element) return true;\n    current = (current as { parentNode?: unknown }).parentNode;\n  }\n\n  return false;\n}\n\nexport function targetHasViewerPortalContainment({\n  rootId,\n  target,\n}: {\n  rootId: string;\n  target: unknown;\n}): boolean {\n  if (!target || typeof target !== \"object\") return false;\n\n  if (typeof (target as { closest?: unknown }).closest === \"function\") {\n    try {\n      const ownerElement = (\n        target as {\n          closest: (selector: string) => {\n            getAttribute?: (attributeName: string) => string | null;\n          } | null;\n        }\n      ).closest(`[${VIEWER_PORTAL_ROOT_ID_ATTRIBUTE}]`);\n\n      if (\n        ownerElement?.getAttribute?.(VIEWER_PORTAL_ROOT_ID_ATTRIBUTE) === rootId\n      ) {\n        return true;\n      }\n    } catch {\n      // Some test and embedded DOM hosts do not support Element.closest.\n    }\n  }\n\n  let current: unknown = target;\n  while (current && typeof current === \"object\") {\n    const getAttribute = (current as { getAttribute?: unknown }).getAttribute;\n    if (\n      typeof getAttribute === \"function\" &&\n      getAttribute.call(current, VIEWER_PORTAL_ROOT_ID_ATTRIBUTE) === rootId\n    ) {\n      return true;\n    }\n    current = (current as { parentNode?: unknown }).parentNode;\n  }\n\n  return false;\n}\n\nexport function targetViewerRootId(target: unknown): string | null {\n  if (!target || typeof target !== \"object\") return null;\n\n  if (typeof (target as { closest?: unknown }).closest === \"function\") {\n    try {\n      const rootElement = (\n        target as {\n          closest: (selector: string) => {\n            getAttribute?: (attributeName: string) => string | null;\n          } | null;\n        }\n      ).closest(`[${VIEWER_ROOT_ID_ATTRIBUTE}]`);\n      const rootId = rootElement?.getAttribute?.(VIEWER_ROOT_ID_ATTRIBUTE);\n      if (rootId) return rootId;\n    } catch {\n      // Some test and embedded DOM hosts do not support Element.closest.\n    }\n  }\n\n  let current: unknown = target;\n  while (current && typeof current === \"object\") {\n    const getAttribute = (current as { getAttribute?: unknown }).getAttribute;\n    if (typeof getAttribute === \"function\") {\n      const rootId = getAttribute.call(current, VIEWER_ROOT_ID_ATTRIBUTE);\n      if (typeof rootId === \"string\" && rootId) {\n        return rootId;\n      }\n    }\n    current = (current as { parentNode?: unknown }).parentNode;\n  }\n\n  return null;\n}\n\nexport function findClosestViewerBody(\n  element: HTMLDivElement | null,\n): HTMLElement | null {\n  if (!element || typeof element.closest !== \"function\") return null;\n  return element.closest<HTMLElement>('[data-viewer-slot=\"body\"]');\n}\n\nexport function useViewerDevelopmentLayoutWarning({\n  enabled = true,\n  elements,\n  inspect,\n  keyParts,\n}: {\n  enabled?: boolean;\n  elements: () => Array<HTMLElement | null>;\n  inspect: () => void;\n  keyParts: unknown[];\n}) {\n  useKeyedLayoutEffect(enabled ? joinEffectKey(keyParts) : null, () => {\n    if (process.env.NODE_ENV === \"production\" || !enabled) return;\n\n    const observedElements = elements().filter(\n      (element): element is HTMLElement => Boolean(element),\n    );\n\n    let frame: number | null = null;\n    const run = () => {\n      frame = null;\n      inspect();\n    };\n    const schedule = () => {\n      if (frame !== null) return;\n\n      if (typeof requestAnimationFrame === \"function\") {\n        frame = requestAnimationFrame(run);\n        return;\n      }\n\n      run();\n    };\n\n    schedule();\n\n    const ResizeObserverConstructor = globalThis.ResizeObserver;\n    if (\n      observedElements.length === 0 ||\n      typeof ResizeObserverConstructor === \"undefined\"\n    ) {\n      return () => {\n        if (frame !== null && typeof cancelAnimationFrame === \"function\") {\n          cancelAnimationFrame(frame);\n        }\n      };\n    }\n\n    const observer = new ResizeObserverConstructor(schedule);\n    for (const element of observedElements) {\n      observer.observe(element);\n    }\n\n    return () => {\n      if (frame !== null && typeof cancelAnimationFrame === \"function\") {\n        cancelAnimationFrame(frame);\n      }\n      observer.disconnect();\n    };\n  });\n}\n\nexport function useViewerSidebarRegistrationContext(\n  consumer: string,\n): ViewerSidebarRegistrationState {\n  const context = useOptionalViewerSidebarRegistration();\n  if (!context) {\n    throw new Error(`${consumer} must be used within a ViewerRoot.`);\n  }\n  return context;\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-internals.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-measurement.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport type StableElementSize<Element extends HTMLElement = HTMLElement> = {\n  element: Element | null;\n  hasMeasured: boolean;\n  height: number | null;\n  setElement: React.RefCallback<Element>;\n  width: number | null;\n};\n\nexport type StableElementSizeOptions = {\n  enabled?: boolean;\n  observe?: boolean;\n  retainLastNonZero?: boolean;\n};\n\nexport type StableCssLengthOptions = {\n  element: HTMLElement | null;\n  retainLastNonZero?: boolean;\n  value: string;\n};\n\ntype MeasuredSize = {\n  height: number | null;\n  width: number | null;\n};\n\ntype RawMeasuredSize = {\n  height: number;\n  width: number;\n};\n\n// DOM layout reads for viewer chrome are quarantined in this module. The\n// file-viewer motion kernel (time + style writes only) receives this reader by\n// injection from the frame controller instead of touching layout APIs itself.\nexport function readElementRectSnapshot(\n  element: HTMLElement | null,\n): readonly number[] {\n  if (!element) return [];\n  const rect = element.getBoundingClientRect();\n  return [rect.left, rect.top, rect.width, rect.height];\n}\n\n// Computed CSS inline direction of an element, sampled when it attaches (a\n// runtime `dir` flip is picked up on the next mount). The fit-width motion\n// transform works on the physical X axis, so the renderer frame needs to\n// know which edge auto-margin alignment pins the stage to.\nexport function useViewerInlineDirection(\n  element: HTMLElement | null,\n): \"ltr\" | \"rtl\" {\n  const [direction, setDirection] = React.useState<\"ltr\" | \"rtl\">(\"ltr\");\n\n  useKeyedLayoutEffect(element ? joinEffectKey([element]) : null, () => {\n    if (!element) return;\n    setDirection(getComputedStyle(element).direction === \"rtl\" ? \"rtl\" : \"ltr\");\n  });\n\n  return direction;\n}\n\nfunction readElementSize(element: HTMLElement): RawMeasuredSize {\n  const rect =\n    typeof element.getBoundingClientRect === \"function\"\n      ? element.getBoundingClientRect()\n      : null;\n\n  return {\n    height: rect?.height || element.clientHeight,\n    width: rect?.width || element.clientWidth,\n  };\n}\n\nfunction resolveMeasuredElementSize({\n  currentSize,\n  nextSize,\n  retainLastNonZero,\n}: {\n  currentSize: MeasuredSize;\n  nextSize: RawMeasuredSize;\n  retainLastNonZero: boolean;\n}): MeasuredSize {\n  const width =\n    Number.isFinite(nextSize.width) &&\n    (!retainLastNonZero || nextSize.width > 0)\n      ? nextSize.width\n      : currentSize.width;\n  const height =\n    Number.isFinite(nextSize.height) &&\n    (!retainLastNonZero || nextSize.height > 0)\n      ? nextSize.height\n      : currentSize.height;\n\n  if (currentSize.width === width && currentSize.height === height) {\n    return currentSize;\n  }\n\n  return { height, width };\n}\n\nexport function useStableElementSize<Element extends HTMLElement = HTMLElement>(\n  options: StableElementSizeOptions = {},\n): StableElementSize<Element> {\n  const enabled = options.enabled ?? true;\n  const observe = options.observe ?? true;\n  const retainLastNonZero = options.retainLastNonZero ?? false;\n  const [element, setElementState] = React.useState<Element | null>(null);\n  const [size, setSize] = React.useState<MeasuredSize>({\n    height: null,\n    width: null,\n  });\n  const hasMeasured = size.height !== null || size.width !== null;\n\n  const setElement = React.useCallback((nextElement: Element | null) => {\n    setElementState(nextElement);\n  }, []);\n\n  useKeyedLayoutEffect(enabled ? null : \"reset\", () => {\n    setSize({ height: null, width: null });\n  });\n\n  useKeyedLayoutEffect(\n    enabled && element\n      ? joinEffectKey([element, observe, retainLastNonZero])\n      : null,\n    () => {\n      if (!element) return;\n\n      setSize((currentSize) =>\n        resolveMeasuredElementSize({\n          currentSize,\n          nextSize: readElementSize(element),\n          retainLastNonZero,\n        }),\n      );\n\n      const ResizeObserverConstructor = observe\n        ? globalThis.ResizeObserver\n        : undefined;\n      if (typeof ResizeObserverConstructor === \"undefined\") return;\n\n      let frame = 0;\n      let latestSize = readElementSize(element);\n      const observer = new ResizeObserverConstructor((entries) => {\n        for (const entry of entries) {\n          latestSize = readElementSize(entry.target as HTMLElement);\n        }\n\n        if (frame) return;\n        frame = requestAnimationFrame(() => {\n          frame = 0;\n          setSize((currentSize) =>\n            resolveMeasuredElementSize({\n              currentSize,\n              nextSize: latestSize,\n              retainLastNonZero,\n            }),\n          );\n        });\n      });\n\n      observer.observe(element);\n\n      return () => {\n        if (frame) cancelAnimationFrame(frame);\n        observer.disconnect();\n      };\n    },\n  );\n\n  return React.useMemo(\n    () => ({\n      element,\n      hasMeasured,\n      height: size.height,\n      setElement,\n      width: size.width,\n    }),\n    [element, hasMeasured, setElement, size.height, size.width],\n  );\n}\n\nexport function useStableCssLength({\n  element,\n  retainLastNonZero = true,\n  value,\n}: StableCssLengthOptions) {\n  const [resolvedLength, setResolvedLength] = React.useState(0);\n\n  useKeyedLayoutEffect(\n    value ? joinEffectKey([element, retainLastNonZero, value]) : null,\n    () => {\n      const nextLength = resolveCssLength(value, element);\n\n      setResolvedLength((currentLength) => {\n        if (retainLastNonZero && nextLength <= 0) return currentLength;\n        return areCssLengthsEqual(currentLength, nextLength)\n          ? currentLength\n          : nextLength;\n      });\n    },\n  );\n\n  return resolvedLength;\n}\n\nfunction resolveCssLength(value: string, element: HTMLElement | null) {\n  const trimmedValue = value.trim();\n  const pixelMatch = trimmedValue.match(/^(-?\\d+(?:\\.\\d+)?)px$/);\n  if (pixelMatch) return Math.max(0, Number(pixelMatch[1]));\n\n  if (typeof window === \"undefined\") return 0;\n\n  const remMatch = trimmedValue.match(/^(-?\\d+(?:\\.\\d+)?)rem$/);\n  if (remMatch) {\n    return (\n      Math.max(0, Number(remMatch[1])) *\n      readComputedFontSize(window.document.documentElement)\n    );\n  }\n\n  const emMatch = trimmedValue.match(/^(-?\\d+(?:\\.\\d+)?)em$/);\n  if (emMatch) {\n    return Math.max(0, Number(emMatch[1])) * readComputedFontSize(element);\n  }\n\n  const measuringElement = window.document.createElement(\"div\");\n  measuringElement.style.contain = \"strict\";\n  measuringElement.style.inlineSize = trimmedValue;\n  measuringElement.style.position = \"absolute\";\n  measuringElement.style.visibility = \"hidden\";\n  (element ?? window.document.body).appendChild(measuringElement);\n  const width = readElementInlineSize(measuringElement);\n  measuringElement.remove();\n  return width;\n}\n\nfunction readElementInlineSize(element: HTMLElement) {\n  const rect =\n    typeof element.getBoundingClientRect === \"function\"\n      ? element.getBoundingClientRect()\n      : null;\n  const width = rect?.width || element.clientWidth || 0;\n  return Number.isFinite(width) && width > 0 ? width : 0;\n}\n\nfunction readComputedFontSize(element: Element | null) {\n  if (typeof window === \"undefined\") return 16;\n  const fontSize = element ? window.getComputedStyle(element).fontSize : \"16px\";\n  const value = Number.parseFloat(fontSize);\n  return Number.isFinite(value) && value > 0 ? value : 16;\n}\n\nfunction areCssLengthsEqual(previous: number, next: number) {\n  return Math.abs(previous - next) <= 0.001;\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-measurement.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-root.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  createViewerStateAttributes,\n  createViewerPortalContainmentAttributes,\n  createViewerGeometryStore,\n  elementContainsTarget,\n  readViewerElementSize,\n  resolveMeasuredSidebarMode,\n  resolveSidebarMode,\n  targetHasViewerPortalContainment,\n  targetViewerRootId,\n  useOptionalViewerSidebarRegistration,\n  useOptionalViewerSidebarState,\n  useViewerSidebarState,\n  VIEWER_SIDEBAR_INLINE_BREAKPOINT,\n  VIEWER_SIDEBAR_WIDTH,\n  ViewerSidebarRegistrationProvider,\n  ViewerSidebarStateProvider,\n} from \"./viewer-internals\";\nimport type {\n  ViewerRootDiagnostics,\n  ViewerPortalContainmentAttributes,\n  ViewerRootProps,\n  ViewerSidebarStateValue,\n  ViewerSidebarMode,\n  ViewerSidebarRegistration,\n  ViewerSidebarRegistrationState,\n  ViewerSidebarState,\n} from \"./viewer-types\";\n\nexport function useViewerSidebar(): ViewerSidebarStateValue {\n  return useViewerSidebarState(\"useViewerSidebar\");\n}\n\nexport function useOptionalViewerSidebar(): ViewerSidebarStateValue | null {\n  return useOptionalViewerSidebarState();\n}\n\nexport function useViewerPortalContainmentAttributes(): ViewerPortalContainmentAttributes {\n  const context = useOptionalViewerSidebarRegistration();\n  if (!context) {\n    throw new Error(\n      \"useViewerPortalContainmentAttributes must be used within a ViewerRoot.\",\n    );\n  }\n\n  return React.useMemo(\n    () => createViewerPortalContainmentAttributes(context.rootId),\n    [context.rootId],\n  );\n}\n\nexport function useOptionalViewerRootDiagnostics(): ViewerRootDiagnostics | null {\n  const context = useOptionalViewerSidebarRegistration();\n  if (!context) return null;\n\n  const geometry = context.geometryStore.getSnapshot();\n  return {\n    getRootElement: context.getRootElement,\n    layoutSignature: [\n      context.hasSidebar ? \"sidebar\" : \"no-sidebar\",\n      context.sidebarSide,\n      geometry.mode,\n      geometry.open ? \"open\" : \"closed\",\n      geometry.state,\n    ].join(\":\"),\n    rootId: context.rootId,\n  };\n}\n\nexport function ViewerRoot({\n  className,\n  style,\n  defaultOpen = false,\n  open: openProp,\n  onOpenChange,\n  mode = \"auto\",\n  inlineBreakpoint = VIEWER_SIDEBAR_INLINE_BREAKPOINT,\n  sidebarSide = \"left\",\n  sidebarCollapsible = \"offcanvas\",\n  sidebarGapTransition = \"width\",\n  stateNamespace,\n  ...props\n}: ViewerRootProps) {\n  const rootRef = React.useRef<HTMLDivElement | null>(null);\n  const reactId = React.useId();\n  const rootId = `${reactId}-viewer-root`;\n  const fallbackSidebarId = `${reactId}-viewer-sidebar`;\n  const isControlled = openProp !== undefined;\n  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n  const open = openProp ?? internalOpen;\n  const openRef = React.useRef(open);\n  openRef.current = open;\n  const hasMeasuredSidebarWidthRef = React.useRef(false);\n  const lastTriggerElementRef = React.useRef<HTMLElement | null>(null);\n  const registeredSidebarRef = React.useRef<ViewerSidebarRegistration | null>(\n    null,\n  );\n  const bodyElementRef = React.useRef<HTMLElement | null>(null);\n  const geometryStore = React.useMemo(() => createViewerGeometryStore(), []);\n  const [registeredSidebar, setRegisteredSidebar] =\n    React.useState<ViewerSidebarRegistration | null>(null);\n  const [bodyElement, setBodyElement] = React.useState<HTMLElement | null>(\n    null,\n  );\n  const [resolvedSidebarMode, setResolvedSidebarMode] =\n    React.useState<ViewerSidebarMode>(() =>\n      resolveSidebarMode({\n        requestedMode: mode,\n        width: null,\n        inlineBreakpoint,\n      }),\n    );\n\n  const commitViewerGeometryTarget = React.useCallback(\n    (nextOpen: boolean) => {\n      const sidebarRegistration = registeredSidebarRef.current;\n      const nextEffectiveOpen =\n        sidebarRegistration?.collapsible === \"none\" ? true : nextOpen;\n      const nextState: ViewerSidebarState = nextEffectiveOpen\n        ? \"expanded\"\n        : \"collapsed\";\n\n      geometryStore.setTarget({\n        bodyElement: bodyElementRef.current,\n        mode: resolvedSidebarMode,\n        open: nextEffectiveOpen,\n        rootElement: rootRef.current,\n        sidebarElement: sidebarRegistration?.element ?? null,\n        sidebarGapTransition,\n        sidebarWidth: sidebarRegistration?.widthPixels ?? 0,\n        side: sidebarRegistration?.side ?? sidebarSide,\n        state: nextState,\n      });\n    },\n    [\n      geometryStore,\n      resolvedSidebarMode,\n      sidebarGapTransition,\n      sidebarSide,\n    ],\n  );\n\n  useKeyedLayoutEffect(joinEffectKey([inlineBreakpoint, mode]), () => {\n    if (mode !== \"auto\") {\n      setResolvedSidebarMode((currentMode) =>\n        currentMode === mode ? currentMode : mode,\n      );\n      return;\n    }\n\n    const element = rootRef.current;\n    const ResizeObserverConstructor = globalThis.ResizeObserver;\n    if (!element || typeof ResizeObserverConstructor === \"undefined\") return;\n\n    const updateMode = () => {\n      const nextWidth = readViewerElementSize(element).width;\n      if (nextWidth === 0) return;\n\n      setResolvedSidebarMode((currentMode) => {\n        const nextMode = resolveMeasuredSidebarMode({\n          currentMode,\n          hasMeasured: hasMeasuredSidebarWidthRef.current,\n          requestedMode: mode,\n          width: nextWidth,\n          inlineBreakpoint,\n        });\n\n        hasMeasuredSidebarWidthRef.current = true;\n        return currentMode === nextMode ? currentMode : nextMode;\n      });\n    };\n\n    updateMode();\n\n    const observer = new ResizeObserverConstructor(updateMode);\n    observer.observe(element);\n\n    return () => observer.disconnect();\n  });\n\n  const setOpen = React.useCallback(\n    (value: boolean | ((open: boolean) => boolean)) => {\n      const previousOpen = isControlled ? open : openRef.current;\n      const nextOpen =\n        typeof value === \"function\" ? value(previousOpen) : value;\n\n      if (nextOpen === previousOpen) {\n        return;\n      }\n\n      commitViewerGeometryTarget(nextOpen);\n\n      if (!isControlled) {\n        openRef.current = nextOpen;\n        setInternalOpen(nextOpen);\n      }\n      onOpenChange?.(nextOpen);\n    },\n    [commitViewerGeometryTarget, isControlled, onOpenChange, open],\n  );\n\n  const registerSidebar = React.useCallback(\n    (registration: ViewerSidebarRegistration) => {\n      const currentRegistration = registeredSidebarRef.current;\n\n      if (\n        currentRegistration &&\n        currentRegistration.instanceId !== registration.instanceId\n      ) {\n        throw new Error(\n          \"ViewerRoot supports one primary ViewerSidebar. Use a nested ViewerRoot for a complete nested viewer, or put secondary content inside ViewerSurface.\",\n        );\n      }\n\n      registeredSidebarRef.current = registration;\n      setRegisteredSidebar(registration);\n\n      return () => {\n        if (\n          registeredSidebarRef.current?.instanceId !== registration.instanceId\n        ) {\n          return;\n        }\n\n        registeredSidebarRef.current = null;\n        setRegisteredSidebar(null);\n      };\n    },\n    [],\n  );\n  const registerBody = React.useCallback((element: HTMLElement) => {\n    bodyElementRef.current = element;\n    setBodyElement(element);\n\n    return () => {\n      if (bodyElementRef.current !== element) return;\n      bodyElementRef.current = null;\n      setBodyElement(null);\n    };\n  }, []);\n\n  const toggleSidebar = React.useCallback(() => {\n    setOpen((currentOpen) => !currentOpen);\n  }, [setOpen]);\n\n  const hasSidebar = registeredSidebar !== null;\n  const effectiveOpen = registeredSidebar?.collapsible === \"none\" ? true : open;\n  const state: ViewerSidebarState = effectiveOpen ? \"expanded\" : \"collapsed\";\n  const canToggleSidebar =\n    hasSidebar && registeredSidebar.collapsible !== \"none\";\n  const sidebarId = registeredSidebar?.id ?? fallbackSidebarId;\n  const resolvedSidebarSide = registeredSidebar?.side ?? sidebarSide;\n  const sidebarWidth = registeredSidebar?.width ?? VIEWER_SIDEBAR_WIDTH;\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      effectiveOpen,\n      registeredSidebar?.element,\n      registeredSidebar?.widthPixels,\n      bodyElement,\n      resolvedSidebarMode,\n      resolvedSidebarSide,\n      rootRef.current,\n      sidebarGapTransition,\n      geometryStore,\n      state,\n    ]),\n    () => {\n      geometryStore.setTarget({\n        bodyElement,\n        mode: resolvedSidebarMode,\n        open: effectiveOpen,\n        rootElement: rootRef.current,\n        sidebarElement: registeredSidebar?.element ?? null,\n        sidebarGapTransition,\n        sidebarWidth: registeredSidebar?.widthPixels ?? 0,\n        side: resolvedSidebarSide,\n        state,\n      });\n    },\n  );\n  useKeyedLayoutEffect(\n    bodyElement\n      ? joinEffectKey([bodyElement, commitViewerGeometryTarget, effectiveOpen])\n      : null,\n    () => {\n      if (!bodyElement) return;\n      const ResizeObserverConstructor = globalThis.ResizeObserver;\n      if (typeof ResizeObserverConstructor === \"undefined\") {\n        commitViewerGeometryTarget(effectiveOpen);\n        return;\n      }\n\n      const observer = new ResizeObserverConstructor(() => {\n        commitViewerGeometryTarget(effectiveOpen);\n      });\n      observer.observe(bodyElement);\n      commitViewerGeometryTarget(effectiveOpen);\n\n      return () => observer.disconnect();\n    },\n  );\n  const namespacedRootStateAttributes = createViewerStateAttributes(\n    stateNamespace,\n    \"root\",\n    {\n      hasSidebar,\n      sidebarCollapsible: registeredSidebar?.collapsible ?? sidebarCollapsible,\n      sidebarMode: resolvedSidebarMode,\n      sidebarOpen: effectiveOpen,\n      sidebarSide: resolvedSidebarSide,\n      sidebarState: state,\n    },\n  );\n  const setLastTriggerElement = React.useCallback(\n    (element: HTMLElement | null) => {\n      lastTriggerElementRef.current = element;\n    },\n    [],\n  );\n  const getRootElement = React.useCallback(() => rootRef.current, []);\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      canToggleSidebar,\n      open,\n      registeredSidebar,\n      resolvedSidebarMode,\n      rootId,\n      setOpen,\n    ]),\n    () => {\n      if (\n        !open ||\n        !canToggleSidebar ||\n        resolvedSidebarMode !== \"overlay\" ||\n        typeof document === \"undefined\"\n      ) {\n        return;\n      }\n\n      const handleKeyDown = (event: KeyboardEvent) => {\n        if (event.key === \"Escape\") {\n          if (event.defaultPrevented) {\n            return;\n          }\n\n          const activeElement = document.activeElement;\n          if (\n            activeElement &&\n            rootRef.current &&\n            !elementContainsTarget(rootRef.current, activeElement) &&\n            !elementContainsTarget(registeredSidebar?.element, activeElement) &&\n            !targetHasViewerPortalContainment({\n              rootId,\n              target: activeElement,\n            })\n          ) {\n            return;\n          }\n\n          setOpen(false);\n          const triggerElement =\n            lastTriggerElementRef.current?.isConnected === true\n              ? lastTriggerElementRef.current\n              : rootRef.current?.querySelector<HTMLElement>(\n                  \"[data-viewer-sidebar-trigger]\",\n                );\n          if (typeof triggerElement?.focus === \"function\") {\n            triggerElement.focus();\n          }\n        }\n      };\n      const handlePointerDown = (event: PointerEvent) => {\n        const target = event.target;\n\n        if (elementContainsTarget(registeredSidebar?.element, target)) {\n          return;\n        }\n\n        if (targetHasViewerPortalContainment({ rootId, target })) {\n          return;\n        }\n\n        const targetRootId = targetViewerRootId(target);\n        if (targetRootId && targetRootId !== rootId) {\n          return;\n        }\n\n        if (\n          target &&\n          typeof (target as { closest?: unknown }).closest === \"function\"\n        ) {\n          const triggerElement = (\n            target as unknown as {\n              closest: (selector: string) => HTMLElement | null;\n            }\n          ).closest(\"[data-viewer-sidebar-trigger]\");\n          if (triggerElement?.dataset.viewerRootId === rootId) {\n            return;\n          }\n        }\n\n        setOpen(false);\n      };\n\n      document.addEventListener(\"keydown\", handleKeyDown);\n      document.addEventListener(\"pointerdown\", handlePointerDown);\n\n      return () => {\n        document.removeEventListener(\"keydown\", handleKeyDown);\n        document.removeEventListener(\"pointerdown\", handlePointerDown);\n      };\n    },\n  );\n\n  const sidebarStateContext = React.useMemo<ViewerSidebarStateValue>(\n    () => ({\n      state,\n      open: effectiveOpen,\n      setOpen,\n      toggleSidebar,\n      canToggleSidebar,\n      mode: resolvedSidebarMode,\n      side: resolvedSidebarSide,\n    }),\n    [\n      state,\n      effectiveOpen,\n      setOpen,\n      toggleSidebar,\n      canToggleSidebar,\n      resolvedSidebarMode,\n      resolvedSidebarSide,\n    ],\n  );\n\n  const sidebarRegistrationContext =\n    React.useMemo<ViewerSidebarRegistrationState>(\n      () => ({\n        defaultSidebarCollapsible: sidebarCollapsible,\n        defaultSidebarSide: sidebarSide,\n        geometryStore,\n        getRootElement,\n        hasSidebar,\n        registerBody,\n        registerSidebar,\n        rootId,\n        sidebarId,\n        sidebarGapTransition,\n        sidebarSide: resolvedSidebarSide,\n        setLastTriggerElement,\n        stateNamespace,\n      }),\n      [\n        geometryStore,\n        hasSidebar,\n        sidebarCollapsible,\n        resolvedSidebarSide,\n        getRootElement,\n        registerBody,\n        registerSidebar,\n        rootId,\n        sidebarId,\n        sidebarGapTransition,\n        sidebarSide,\n        setLastTriggerElement,\n        stateNamespace,\n      ],\n    );\n\n  return (\n    <ViewerSidebarStateProvider value={sidebarStateContext}>\n      <ViewerSidebarRegistrationProvider value={sidebarRegistrationContext}>\n        <div\n          ref={rootRef}\n          data-slot=\"viewer-root\"\n          data-viewer-root-id={rootId}\n          data-viewer-has-sidebar={hasSidebar ? \"true\" : \"false\"}\n          data-viewer-sidebar-collapsible={\n            registeredSidebar?.collapsible ?? sidebarCollapsible\n          }\n          data-viewer-sidebar-mode={resolvedSidebarMode}\n          data-viewer-sidebar-open={effectiveOpen ? \"true\" : \"false\"}\n          data-viewer-sidebar-side={resolvedSidebarSide}\n          data-viewer-sidebar-state={state}\n          data-viewer-slot=\"root\"\n          {...namespacedRootStateAttributes}\n          className={cn(\n            \"relative flex min-h-0 min-w-0 flex-col overflow-hidden\",\n            className,\n          )}\n          style={\n            {\n              overflowAnchor: \"none\",\n              ...style,\n              \"--viewer-sidebar-width\": sidebarWidth,\n            } as React.CSSProperties\n          }\n          {...props}\n        />\n      </ViewerSidebarRegistrationProvider>\n    </ViewerSidebarStateProvider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-root.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-sidebar.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  createViewerSlotAttributes,\n  createViewerStateAttributes,\n  pickCssCustomProperties,\n  readViewerElementSize,\n  useViewerDevelopmentLayoutWarning,\n  useViewerSidebarState,\n  useViewerGeometrySnapshot,\n  useViewerSidebarRegistrationContext,\n  VIEWER_SIDEBAR_WIDTH,\n} from \"./viewer-internals\";\nimport { warnViewerDevelopmentOnce } from \"./viewer-diagnostics\";\nimport type { ViewerSidebarProps, ViewerSidebarState } from \"./viewer-types\";\n\nexport function ViewerSidebar({\n  children,\n  className,\n  innerClassName,\n  namespacedSlot,\n  namespacedSlotNames,\n  side: sideProp,\n  slotNames,\n  collapsible: collapsibleProp,\n  width = VIEWER_SIDEBAR_WIDTH,\n  style,\n  id: idProp,\n  ...props\n}: ViewerSidebarProps) {\n  const registrationContext =\n    useViewerSidebarRegistrationContext(\"ViewerSidebar\");\n  const sidebarState = useViewerSidebarState(\"ViewerSidebar\");\n  const {\n    defaultSidebarCollapsible,\n    defaultSidebarSide,\n    getRootElement,\n    registerSidebar,\n    rootId,\n    sidebarGapTransition,\n    geometryStore,\n  } = registrationContext;\n  const geometry = useViewerGeometrySnapshot(geometryStore);\n  const reactId = React.useId();\n  const instanceId = `${reactId}-viewer-sidebar-instance`;\n  const generatedSidebarId = `${reactId}-viewer-sidebar`;\n  const sidebarId = idProp ?? generatedSidebarId;\n  const sidebarRef = React.useRef<HTMLElement | null>(null);\n  const [transitionsReady, setTransitionsReady] = React.useState(false);\n  const collapsible = collapsibleProp ?? defaultSidebarCollapsible;\n  const side = sideProp ?? defaultSidebarSide;\n  const open = collapsible === \"none\" ? true : sidebarState.open;\n  const state: ViewerSidebarState = open ? \"expanded\" : \"collapsed\";\n  const mode = sidebarState.mode;\n  const visualOpen = mode === \"inline\" ? geometry.open : open;\n  const visualState: ViewerSidebarState = visualOpen\n    ? \"expanded\"\n    : \"collapsed\";\n  const isCollapsed = collapsible !== \"none\" && !open;\n  const isOpening =\n    collapsible !== \"none\" &&\n    open &&\n    geometry.isTransitioning;\n  const frameSlot = props[\"data-slot\"] ?? \"viewer-sidebar\";\n  const sidebarProps = { ...props };\n  delete sidebarProps[\"data-slot\"];\n  const styleWithoutWidth: React.CSSProperties = { ...style };\n  delete styleWithoutWidth.width;\n  const customProperties = pickCssCustomProperties(style);\n  const frameStyle = {\n    ...customProperties,\n    \"--viewer-sidebar-width\": width,\n  } as React.CSSProperties;\n  const inlineFrameStyle = {\n    ...frameStyle,\n    flexBasis: `${geometry.sidebarInlineSize}px`,\n    maxWidth: `${geometry.sidebarInlineSize}px`,\n    width: `${geometry.sidebarInlineSize}px`,\n  } as React.CSSProperties;\n  const ariaLabel = sidebarProps[\"aria-label\"];\n  const ariaLabelledBy = sidebarProps[\"aria-labelledby\"];\n  const hasAccessibleName =\n    (typeof ariaLabel === \"string\"\n      ? ariaLabel.trim().length > 0\n      : Boolean(ariaLabel)) ||\n    (typeof ariaLabelledBy === \"string\"\n      ? ariaLabelledBy.trim().length > 0\n      : Boolean(ariaLabelledBy));\n\n  useViewerDevelopmentLayoutWarning({\n    enabled: mode === \"inline\" && open,\n    elements: () => [getRootElement(), sidebarRef.current],\n    inspect: () => {\n      const rootElement = getRootElement();\n      const sidebarElement = sidebarRef.current;\n      const rootSize = readViewerElementSize(rootElement);\n      const sidebarSize = readViewerElementSize(sidebarElement);\n\n      if (rootSize.width > 0 && sidebarSize.width > rootSize.width) {\n        warnViewerDevelopmentOnce({\n          code: \"viewer_sidebar_wider_than_root\",\n          message: \"inline viewer sidebar is wider than its root.\",\n          rootId,\n          details: {\n            rootSlot: rootElement?.dataset.slot,\n            rootWidth: rootSize.width,\n            sidebarMode: mode,\n            sidebarOpen: open,\n            sidebarSide: side,\n            sidebarSlot: sidebarElement?.dataset.slot,\n            sidebarWidth: sidebarSize.width,\n          },\n        });\n      }\n    },\n    keyParts: [\n      \"viewer-sidebar-wider-than-root\",\n      rootId,\n      mode,\n      open,\n      side,\n      width,\n      getRootElement(),\n      sidebarRef.current,\n    ],\n  });\n\n  useMountEffect(() => {\n    if (\n      typeof window === \"undefined\" ||\n      typeof window.requestAnimationFrame !== \"function\"\n    ) {\n      setTransitionsReady(true);\n      return;\n    }\n    let secondFrame = 0;\n    const firstFrame = window.requestAnimationFrame(() => {\n      secondFrame = window.requestAnimationFrame(() => {\n        setTransitionsReady(true);\n      });\n    });\n\n    return () => {\n      window.cancelAnimationFrame(firstFrame);\n      window.cancelAnimationFrame(secondFrame);\n    };\n  });\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      collapsible,\n      instanceId,\n      registerSidebar,\n      side,\n      sidebarId,\n      width,\n    ]),\n    () => {\n      const element = sidebarRef.current;\n\n      if (!element) {\n        return;\n      }\n\n      return registerSidebar({\n        collapsible,\n        element,\n        id: sidebarId,\n        instanceId,\n        side,\n        width,\n        widthPixels: resolveViewerSidebarWidthPixels(element, width),\n      });\n    },\n  );\n\n  useKeyedLayoutEffect(\n    mode === \"overlay\" && open && !hasAccessibleName\n      ? joinEffectKey([\n          \"viewer-overlay-sidebar-missing-accessible-name\",\n          rootId,\n          sidebarId,\n          side,\n        ])\n      : null,\n    () => {\n      warnViewerDevelopmentOnce({\n        code: \"viewer_overlay_sidebar_missing_accessible_name\",\n        message: \"overlay viewer sidebar is missing an accessible name.\",\n        rootId,\n        details: {\n          sidebarId,\n          sidebarMode: mode,\n          sidebarSide: side,\n        },\n      });\n    },\n  );\n\n  useKeyedLayoutEffect(\n    isCollapsed\n      ? joinEffectKey([\n          \"viewer-sidebar:move-focus-on-close\",\n          getRootElement,\n          sidebarId,\n        ])\n      : null,\n    () => {\n      moveFocusOutOfViewerSidebar(\n        sidebarRef.current,\n        getRootElement(),\n        sidebarId,\n      );\n    },\n  );\n\n  const hiddenProps = isCollapsed || isOpening\n    ? ({\n        \"aria-hidden\": true,\n        inert: true,\n      } as React.HTMLAttributes<HTMLElement>)\n    : {};\n  const namespacedSidebarStateAttributes = createViewerStateAttributes(\n    registrationContext.stateNamespace,\n    \"sidebar\",\n    {\n      sidebarCollapsible: collapsible,\n      sidebarMode: mode,\n      sidebarOpen: visualOpen,\n      sidebarSide: side,\n      sidebarState: visualState,\n    },\n  );\n  const frameAttributes = {\n    \"data-slot\": frameSlot,\n    \"data-collapsible\": collapsible,\n    \"data-mode\": mode,\n    \"data-side\": side,\n    \"data-state\": visualState,\n    \"data-viewer-sidebar-transitions\": transitionsReady ? \"ready\" : undefined,\n    \"data-viewer-sidebar-mode\": mode,\n    \"data-viewer-sidebar-open\": visualOpen ? \"true\" : \"false\",\n    \"data-viewer-sidebar-state\": visualState,\n    \"data-viewer-slot\": \"sidebar\",\n    ...createViewerSlotAttributes(\n      registrationContext.stateNamespace,\n      \"sidebar\",\n      namespacedSlot,\n    ),\n    ...namespacedSidebarStateAttributes,\n  };\n  const sidebarElement = (\n    <aside\n      ref={sidebarRef}\n      id={sidebarId}\n      data-slot={slotNames?.container ?? \"viewer-sidebar-container\"}\n      data-collapsible={collapsible}\n      data-mode={mode}\n      data-side={side}\n      data-state={visualState}\n      data-viewer-sidebar-transitions={transitionsReady ? \"ready\" : undefined}\n      data-viewer-sidebar-mode={mode}\n      data-viewer-sidebar-open={visualOpen ? \"true\" : \"false\"}\n      data-viewer-sidebar-state={visualState}\n      data-viewer-slot=\"sidebar-container\"\n      {...createViewerSlotAttributes(\n        registrationContext.stateNamespace,\n        \"sidebar\",\n        namespacedSlotNames?.container,\n      )}\n      {...namespacedSidebarStateAttributes}\n        className={cn(\n          className,\n          \"z-30 flex w-(--viewer-sidebar-width) min-w-0 overflow-hidden\",\n          mode === \"inline\" &&\n            \"absolute inset-y-0 h-full min-w-(--viewer-sidebar-width) flex-shrink-0 transition-none\",\n        mode === \"inline\" && side === \"left\" && \"right-0\",\n        mode === \"inline\" && side === \"right\" && \"left-0\",\n        mode === \"overlay\" &&\n          \"absolute inset-y-0 transition-none data-[viewer-sidebar-transitions=ready]:transition-[translate,width,border-color] data-[viewer-sidebar-transitions=ready]:duration-150 data-[viewer-sidebar-transitions=ready]:ease-out\",\n        mode === \"overlay\" && side === \"left\" && \"left-0\",\n        mode === \"overlay\" && side === \"right\" && \"right-0\",\n        collapsible === \"offcanvas\" && mode === \"overlay\" && \"shadow-lg\",\n        isCollapsed && \"pointer-events-none\",\n        collapsible === \"offcanvas\" &&\n          mode === \"overlay\" &&\n          !visualOpen &&\n          side === \"left\" &&\n          \"pointer-events-none -translate-x-full border-transparent\",\n        collapsible === \"offcanvas\" &&\n          mode === \"overlay\" &&\n          !visualOpen &&\n          side === \"right\" &&\n          \"pointer-events-none translate-x-full border-transparent\",\n      )}\n      style={\n        {\n          ...styleWithoutWidth,\n          \"--viewer-sidebar-width\": width,\n        } as React.CSSProperties\n      }\n      {...sidebarProps}\n      {...hiddenProps}\n    >\n      <div\n        data-slot={slotNames?.inner ?? \"viewer-sidebar-inner\"}\n        data-viewer-slot=\"sidebar-inner\"\n        {...createViewerSlotAttributes(\n          registrationContext.stateNamespace,\n          \"sidebar\",\n          namespacedSlotNames?.inner,\n        )}\n        className={cn(\n          \"flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden\",\n          innerClassName,\n        )}\n      >\n        {children}\n      </div>\n    </aside>\n  );\n\n  if (mode === \"inline\") {\n    return (\n      <div\n        {...frameAttributes}\n        className={cn(\n          \"group/viewer-sidebar relative z-30 min-h-0 flex-shrink-0 overflow-hidden transition-none\",\n          side === \"right\" && \"order-last\",\n        )}\n        style={inlineFrameStyle}\n      >\n        {sidebarElement}\n      </div>\n    );\n  }\n\n  return (\n    <div\n      {...frameAttributes}\n      className={cn(\n        \"group/viewer-sidebar relative z-30 min-h-0 flex-shrink-0\",\n        side === \"right\" && \"order-last\",\n      )}\n      style={frameStyle}\n    >\n      <div\n        data-slot={slotNames?.gap ?? \"viewer-sidebar-gap\"}\n        data-viewer-slot=\"sidebar-gap\"\n        {...createViewerSlotAttributes(\n          registrationContext.stateNamespace,\n          \"sidebar\",\n          namespacedSlotNames?.gap,\n        )}\n        className={cn(\n          \"relative h-full w-(--viewer-sidebar-width) flex-shrink-0\",\n          sidebarGapTransition === \"width\" &&\n            \"transition-[width] duration-150 ease-out\",\n          collapsible === \"offcanvas\" && mode === \"overlay\" && \"w-0\",\n        )}\n      />\n      {sidebarElement}\n    </div>\n  );\n}\n\nfunction moveFocusOutOfViewerSidebar(\n  sidebarElement: HTMLElement | null,\n  rootElement: HTMLElement | null,\n  sidebarId: string,\n) {\n  if (typeof document === \"undefined\" || !sidebarElement) return;\n\n  const activeElement = document.activeElement;\n  if (\n    !(activeElement instanceof HTMLElement) ||\n    !sidebarElement.contains(activeElement)\n  ) {\n    return;\n  }\n\n  const trigger = findViewerSidebarTrigger(rootElement, sidebarId);\n  if (trigger) {\n    trigger.focus({ preventScroll: true });\n    return;\n  }\n\n  activeElement.blur();\n}\n\nfunction findViewerSidebarTrigger(\n  rootElement: HTMLElement | null,\n  sidebarId: string,\n) {\n  if (!rootElement) return null;\n\n  for (const element of rootElement.querySelectorAll<HTMLElement>(\n    \"[aria-controls]\",\n  )) {\n    if (element.getAttribute(\"aria-controls\") === sidebarId) {\n      return element;\n    }\n  }\n\n  return null;\n}\n\nfunction resolveViewerSidebarWidthPixels(element: HTMLElement, width: string) {\n  const declaredWidth = parseViewerSidebarCssLength(width, element);\n  if (declaredWidth !== null) return declaredWidth;\n\n  if (typeof window !== \"undefined\") {\n    const computedWidth = parseViewerSidebarCssLength(\n      window.getComputedStyle(element).width,\n      element,\n    );\n    if (computedWidth !== null) return computedWidth;\n  }\n\n  const measuredWidth = readViewerElementSize(element).width;\n  return Number.isFinite(measuredWidth) && measuredWidth > 0\n    ? measuredWidth\n    : 0;\n}\n\nfunction parseViewerSidebarCssLength(value: string, element: HTMLElement) {\n  const trimmedValue = value.trim();\n  const length = Number.parseFloat(trimmedValue);\n  if (!Number.isFinite(length) || length <= 0) return null;\n\n  if (trimmedValue.endsWith(\"px\")) return length;\n  if (trimmedValue.endsWith(\"rem\")) {\n    return length * readRootFontSize();\n  }\n  if (trimmedValue.endsWith(\"em\")) {\n    return length * readElementFontSize(element);\n  }\n\n  return null;\n}\n\nfunction readRootFontSize() {\n  if (typeof window === \"undefined\") return 16;\n\n  const rootFontSize = Number.parseFloat(\n    window.getComputedStyle(document.documentElement).fontSize,\n  );\n  return Number.isFinite(rootFontSize) && rootFontSize > 0\n    ? rootFontSize\n    : 16;\n}\n\nfunction readElementFontSize(element: HTMLElement) {\n  if (typeof window === \"undefined\") return 16;\n\n  const elementFontSize = Number.parseFloat(\n    window.getComputedStyle(element).fontSize,\n  );\n  return Number.isFinite(elementFontSize) && elementFontSize > 0\n    ? elementFontSize\n    : readRootFontSize();\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-sidebar.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-surface.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  findClosestViewerBody,\n  readViewerElementSize,\n  useOptionalViewerSidebarRegistration,\n  useOptionalViewerSurfaceMeasurementContext,\n  useViewerDevelopmentLayoutWarning,\n  useViewerViewportPresence,\n  ViewerSurfaceMeasurementProvider,\n  ViewerViewportPresenceProvider,\n} from \"./viewer-internals\";\nimport { useOptionalViewerRootDiagnostics } from \"./viewer-root\";\nimport { warnViewerDevelopmentOnce } from \"./viewer-diagnostics\";\nimport { useStableElementSize } from \"./viewer-measurement\";\nimport type {\n  ViewerDocumentFrameAlign,\n  ViewerDocumentFrameProps,\n  ViewerGeometryStore,\n  ViewerSurfaceMeasurement,\n  ViewerSurfaceProps,\n  ViewerViewportProps,\n  ViewerGeometrySnapshot,\n} from \"./viewer-types\";\n\nexport type ViewerDocumentFrameLayout = {\n  activeInlineSize: number | null;\n  isTransitioning: boolean;\n  maxInlineSize: number | null;\n  settledInlineSize: number | null;\n};\n\nexport type ViewerDocumentFrameState = {\n  align: ViewerDocumentFrameAlign;\n  element: HTMLDivElement | null;\n  geometryStore: ViewerGeometryStore | null;\n  inlineSize: number | null;\n};\n\nconst ViewerDocumentFrameContext =\n  React.createContext<ViewerDocumentFrameState | null>(null);\n\nfunction getDocumentFrameAlignClass(align: ViewerDocumentFrameAlign) {\n  switch (align) {\n    case \"center\":\n      return \"mx-auto\";\n    case \"end\":\n      return \"ml-auto\";\n    case \"start\":\n      return \"mr-auto\";\n  }\n}\n\nexport function useOptionalViewerDocumentFrame(): ViewerDocumentFrameState | null {\n  return React.useContext(ViewerDocumentFrameContext);\n}\n\nexport function useViewerDocumentFrameLayout({\n  documentFrame,\n  fallbackInlineSize,\n}: {\n  documentFrame: ViewerDocumentFrameState | null;\n  fallbackInlineSize: number | null;\n}): ViewerDocumentFrameLayout {\n  const element = documentFrame?.element ?? null;\n  const geometryStore = documentFrame?.geometryStore ?? null;\n  const snapshotCacheRef = React.useRef<ViewerDocumentFrameLayout | null>(null);\n  const subscribe = React.useCallback(\n    (listener: () => void) =>\n      geometryStore ? geometryStore.subscribe(listener) : () => {},\n    [geometryStore],\n  );\n  const getSnapshot = React.useCallback(() => {\n    const geometry = geometryStore?.getSnapshot() ?? null;\n    const previousLayout = snapshotCacheRef.current;\n    const nextLayout =\n      geometry && geometry.hasMeasuredBody\n        ? createViewerDocumentFrameLayoutFromGeometry({\n            element,\n            fallbackInlineSize,\n            geometry,\n          })\n        : createViewerDocumentFrameFallbackLayout(fallbackInlineSize);\n\n    if (\n      previousLayout &&\n      areViewerDocumentFrameLayoutsEqual(previousLayout, nextLayout)\n    ) {\n      return previousLayout;\n    }\n\n    snapshotCacheRef.current = nextLayout;\n    return nextLayout;\n  }, [element, fallbackInlineSize, geometryStore]);\n  const activeLayout = React.useSyncExternalStore(\n    subscribe,\n    getSnapshot,\n    getSnapshot,\n  );\n  const [settledInlineSize, setSettledInlineSize] = React.useState<\n    number | null\n  >(() => activeLayout.activeInlineSize);\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"viewer-document-frame-layout:settled-inline-size\",\n      activeLayout.activeInlineSize,\n      activeLayout.isTransitioning,\n      geometryStore,\n    ]),\n    () => {\n      if (activeLayout.isTransitioning) return;\n\n      setSettledInlineSize((currentInlineSize) =>\n        currentInlineSize === activeLayout.activeInlineSize\n          ? currentInlineSize\n          : activeLayout.activeInlineSize,\n      );\n    },\n  );\n  return React.useMemo(\n    () => ({\n      ...activeLayout,\n      settledInlineSize:\n        settledInlineSize ?? activeLayout.activeInlineSize ?? null,\n    }),\n    [activeLayout, settledInlineSize],\n  );\n}\n\nexport function ViewerDocumentFrame({\n  align = \"start\",\n  children,\n  className,\n  maxInlineSize,\n  style,\n  ...props\n}: ViewerDocumentFrameProps) {\n  const size = useStableElementSize<HTMLDivElement>({\n    retainLastNonZero: true,\n  });\n  const sidebarRegistration = useOptionalViewerSidebarRegistration();\n  const value = React.useMemo<ViewerDocumentFrameState>(\n    () => ({\n      align,\n      element: size.element,\n      geometryStore: sidebarRegistration?.geometryStore ?? null,\n      inlineSize: size.width,\n    }),\n    [align, sidebarRegistration?.geometryStore, size.element, size.width],\n  );\n\n  return (\n    <ViewerDocumentFrameContext.Provider value={value}>\n      <div\n        ref={size.setElement}\n        data-slot=\"viewer-document-frame\"\n        className={cn(\n          \"[container-type:inline-size] relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col\",\n          getDocumentFrameAlignClass(align),\n          className,\n        )}\n        style={\n          {\n            maxInlineSize,\n            ...style,\n          } as React.CSSProperties\n        }\n        {...props}\n      >\n        {children}\n      </div>\n    </ViewerDocumentFrameContext.Provider>\n  );\n}\n\nfunction createViewerDocumentFrameLayoutFromGeometry({\n  element,\n  fallbackInlineSize,\n  geometry,\n}: {\n  element: HTMLDivElement | null;\n  fallbackInlineSize: number | null;\n  geometry: ViewerGeometrySnapshot;\n}): ViewerDocumentFrameLayout {\n  const maxCssInlineSize = element\n    ? readViewerDocumentFrameMaxInlineSize(element)\n    : null;\n  const measuredFrameInlineSize =\n    fallbackInlineSize != null &&\n    Number.isFinite(fallbackInlineSize) &&\n    fallbackInlineSize > 0\n      ? fallbackInlineSize\n      : null;\n  const usesSidebarGeometry =\n    geometry.sidebarWidth > 0 ||\n    geometry.sidebarInlineSize > 0 ||\n    geometry.isTransitioning;\n  const activeSourceInlineSize = usesSidebarGeometry\n    ? geometry.documentInlineSize\n    : (measuredFrameInlineSize ?? geometry.documentInlineSize);\n  const maxSourceInlineSize = usesSidebarGeometry\n    ? Math.max(geometry.bodyInlineSize, activeSourceInlineSize)\n    : activeSourceInlineSize;\n  const activeInlineSize = constrainViewerDocumentFrameInlineSize(\n    activeSourceInlineSize,\n    maxCssInlineSize,\n  );\n  const maxInlineSize = constrainViewerDocumentFrameInlineSize(\n    maxSourceInlineSize,\n    maxCssInlineSize,\n  );\n\n  return {\n    activeInlineSize,\n    isTransitioning: geometry.isTransitioning,\n    maxInlineSize,\n    settledInlineSize: null,\n  };\n}\n\nfunction createViewerDocumentFrameFallbackLayout(\n  fallbackInlineSize: number | null,\n): ViewerDocumentFrameLayout {\n  const inlineSize =\n    fallbackInlineSize != null &&\n    Number.isFinite(fallbackInlineSize) &&\n    fallbackInlineSize > 0\n      ? fallbackInlineSize\n      : null;\n\n  return {\n    activeInlineSize: inlineSize,\n    isTransitioning: false,\n    maxInlineSize: inlineSize,\n    settledInlineSize: inlineSize,\n  };\n}\n\nfunction areViewerDocumentFrameLayoutsEqual(\n  previousLayout: ViewerDocumentFrameLayout,\n  nextLayout: ViewerDocumentFrameLayout,\n) {\n  return (\n    previousLayout.activeInlineSize === nextLayout.activeInlineSize &&\n    previousLayout.isTransitioning === nextLayout.isTransitioning &&\n    previousLayout.maxInlineSize === nextLayout.maxInlineSize\n  );\n}\n\nfunction readViewerDocumentFrameMaxInlineSize(element: HTMLElement) {\n  if (typeof window === \"undefined\") return null;\n\n  const value = window.getComputedStyle(element).maxInlineSize;\n  if (!value || value === \"none\") return null;\n\n  const size = Number.parseFloat(value);\n  return Number.isFinite(size) && size > 0 ? size : null;\n}\n\nfunction constrainViewerDocumentFrameInlineSize(\n  inlineSize: number,\n  maxInlineSize: number | null,\n) {\n  const safeInlineSize =\n    Number.isFinite(inlineSize) && inlineSize > 0 ? inlineSize : 0;\n  const constrainedInlineSize =\n    maxInlineSize == null\n      ? safeInlineSize\n      : Math.min(safeInlineSize, maxInlineSize);\n  return constrainedInlineSize > 0 ? constrainedInlineSize : null;\n}\n\nfunction useViewerSurfaceMeasurementValue(): ViewerSurfaceMeasurement {\n  const size = useStableElementSize<HTMLDivElement>({\n    retainLastNonZero: true,\n  });\n\n  return React.useMemo(\n    () => ({\n      hasMeasured: size.hasMeasured,\n      setViewportElement: size.setElement,\n      viewportElement: size.element,\n      viewportHeight: size.height,\n      viewportWidth: size.width,\n    }),\n    [size.element, size.hasMeasured, size.height, size.setElement, size.width],\n  );\n}\n\nexport function useOptionalViewerSurfaceMeasurement(): ViewerSurfaceMeasurement | null {\n  return useOptionalViewerSurfaceMeasurementContext();\n}\n\nexport function useViewerSurfaceMeasurement(): ViewerSurfaceMeasurement {\n  const context = useOptionalViewerSurfaceMeasurement();\n  if (!context) {\n    throw new Error(\n      \"useViewerSurfaceMeasurement must be used within a ViewerSurface.\",\n    );\n  }\n  return context;\n}\n\nexport function useIsInsideViewerViewport(): boolean {\n  return useViewerViewportPresence();\n}\n\nexport function ViewerSurface({ className, ...props }: ViewerSurfaceProps) {\n  const measurement = useViewerSurfaceMeasurementValue();\n\n  return (\n    <ViewerSurfaceMeasurementProvider value={measurement}>\n      <div\n        data-slot=\"viewer-surface\"\n        data-viewer-surface=\"inset\"\n        className={cn(\n          \"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\",\n          className,\n        )}\n        {...props}\n      />\n    </ViewerSurfaceMeasurementProvider>\n  );\n}\n\nexport const ViewerViewport = React.forwardRef<\n  HTMLDivElement,\n  ViewerViewportProps\n>(function ViewerViewport({ children, className, ...props }, forwardedRef) {\n  const measurement = useOptionalViewerSurfaceMeasurement();\n  const rootDiagnostics = useOptionalViewerRootDiagnostics();\n  const viewportRef = React.useRef<HTMLDivElement | null>(null);\n  const setViewportElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      viewportRef.current = element;\n      measurement?.setViewportElement(element);\n\n      if (typeof forwardedRef === \"function\") {\n        forwardedRef(element);\n        return;\n      }\n\n      if (forwardedRef) {\n        forwardedRef.current = element;\n      }\n    },\n    [forwardedRef, measurement],\n  );\n\n  useViewerDevelopmentLayoutWarning({\n    enabled: Boolean(rootDiagnostics),\n    elements: () => [\n      findClosestViewerBody(viewportRef.current),\n      viewportRef.current,\n    ],\n    inspect: () => {\n      const viewportElement = viewportRef.current;\n      const bodyElement = findClosestViewerBody(viewportElement);\n      const bodySize = readViewerElementSize(bodyElement);\n      const viewportSize = readViewerElementSize(viewportElement);\n\n      if (bodySize.width > 0 && viewportSize.width === 0) {\n        warnViewerDevelopmentOnce({\n          code: \"viewer_viewport_zero_width\",\n          message:\n            \"viewer body has nonzero width but surface viewport width is zero.\",\n          rootId: rootDiagnostics?.rootId ?? \"unknown-root\",\n          details: {\n            bodyHeight: bodySize.height,\n            bodySlot: bodyElement?.dataset.slot,\n            bodyWidth: bodySize.width,\n            viewportHeight: viewportSize.height,\n            viewportSlot: viewportElement?.dataset.slot,\n            viewportWidth: viewportSize.width,\n          },\n        });\n      }\n    },\n    keyParts: [\n      \"viewer-viewport-zero-width\",\n      rootDiagnostics?.rootId,\n      viewportRef.current,\n    ],\n  });\n\n  return (\n    <div\n      ref={setViewportElement}\n      data-slot=\"viewer-viewport\"\n      className={cn(\n        \"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\",\n        className,\n      )}\n      {...props}\n    >\n      <ViewerViewportPresenceProvider>\n        {children}\n      </ViewerViewportPresenceProvider>\n    </div>\n  );\n});\n",
      "type": "registry:ui",
      "target": "@ui/viewer-surface.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-types.ts",
      "content": "import type * as React from \"react\";\n\nexport type ViewerSidebarMode = \"inline\" | \"overlay\";\nexport type ViewerSidebarRequestedMode = \"auto\" | ViewerSidebarMode;\nexport type ViewerSidebarGapTransition = \"width\" | \"none\";\nexport type ViewerSidebarState = \"expanded\" | \"collapsed\";\nexport type ViewerSidebarSide = \"left\" | \"right\";\nexport type ViewerSidebarCollapsible = \"offcanvas\" | \"none\";\nexport type ViewerDocumentFrameAlign = \"start\" | \"center\" | \"end\";\nexport type ViewerGeometryTransitionPhase = \"idle\" | \"sliding\";\n\nexport type ViewerDocumentReadingAnchorInput = {\n  scrollTop: number;\n  viewportBlockSize: number;\n};\n\nexport type ViewerDocumentReadingAnchorTarget<Anchor> = {\n  anchor: Anchor;\n  viewportBlockSize: number;\n};\n\nexport type ViewerDocumentTransitionSource =\n  | \"none\"\n  | \"viewer-shell\"\n  | \"document-layout\";\n\n// Commit-then-relax: layout commits its target inside the motion's first\n// frame and scroll rebases in the same commit, so there is no frozen layout\n// and no deferred scroll left in the vocabulary.\nexport type ViewerDocumentLayoutPolicy = \"live\" | \"target\";\nexport type ViewerDocumentScrollPolicy = \"preserve\" | \"rebase\";\nexport type ViewerDocumentVisualPolicy =\n  | \"none\"\n  | \"document-flip\"\n  | \"shell-transform\";\n\nexport type ViewerDocumentTransition = {\n  layoutPolicy: ViewerDocumentLayoutPolicy;\n  scrollPolicy: ViewerDocumentScrollPolicy;\n  source: ViewerDocumentTransitionSource;\n  transitionId: number | string | null;\n  visualPolicy: ViewerDocumentVisualPolicy;\n};\n\nexport type ViewerDocumentLayoutModel<Anchor> = {\n  blockSize: number;\n  captureReadingAnchor: (\n    input: ViewerDocumentReadingAnchorInput,\n  ) => Anchor | null;\n  getReadingAnchorScrollTop: (\n    target: ViewerDocumentReadingAnchorTarget<Anchor>,\n  ) => number | null;\n  inlineSize: number;\n  isTransitioning?: boolean;\n  transition?: ViewerDocumentTransition;\n};\n\nexport type ViewerDocumentPhysicalScrollPosition = {\n  physicalScrollTop: number;\n  scrollPageOffset: number;\n};\n\nexport type ViewerDocumentResolvedScrollTarget = {\n  left?: number;\n  top: number;\n};\n\nexport type ViewerDocumentScrollMapper = {\n  getLogicalScrollTop: (input: {\n    blockSize: number;\n    physicalScrollTop: number;\n    scrollPageOffset: number;\n    viewportBlockSize: number;\n  }) => number;\n  getPhysicalScrollSize: (input: {\n    blockSize: number;\n    viewportBlockSize: number;\n  }) => number;\n  resolvePhysicalScrollPosition: (input: {\n    blockSize: number;\n    logicalScrollTop: number;\n    scrollPageOffset: number;\n    viewportBlockSize: number;\n  }) => ViewerDocumentPhysicalScrollPosition;\n};\n\nexport type ViewerDocumentScrollMetrics = {\n  physicalScrollSize: number;\n  physicalScrollTop: number;\n  scrollPageOffset: number;\n  scrollTop: number;\n  viewportBlockSize: number;\n};\n\nexport type ViewerDocumentScrollTargetResolver<Anchor, Target> = (input: {\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  scrollTop: number;\n  target: Target;\n  viewportElement: HTMLDivElement;\n}) => ViewerDocumentResolvedScrollTarget | null;\n\n// A zoom step is the one geometry change whose intent is \"zoom the camera\",\n// not \"keep my reading position\": it re-anchors the viewport CENTER on both\n// axes and relaxes a FLIP about that fixed point. `capture` runs in the zoom\n// gesture's own task against the pre-zoom layout and painted DOM;\n// `resolveScrollTarget` and `play` run inside the geometry commit against the\n// post-zoom layout (commit-then-relax).\nexport type ViewerDocumentZoomMotionBypassReason =\n  | \"resolve-failed\"\n  | \"shell-transition\"\n  | \"stale-intent\";\n\nexport type ViewerDocumentZoomMotionController<Transaction = unknown> = {\n  capture: (input: {\n    scrollTop: number;\n    viewportElement: HTMLDivElement;\n  }) => Transaction | null;\n  /**\n   * Telemetry tap: a captured zoom intent reached a geometry commit but the\n   * zoom lane declined it. Without this the bypass is invisible — the commit\n   * falls back to the reading-anchor restore and no flight is recorded.\n   */\n  noteBypass?: (reason: ViewerDocumentZoomMotionBypassReason) => void;\n  resolveScrollTarget: (input: {\n    transaction: Transaction;\n    viewportElement: HTMLDivElement;\n  }) => ViewerDocumentResolvedScrollTarget | null;\n  play: (input: {\n    transaction: Transaction;\n    viewportElement: HTMLDivElement;\n  }) => (() => void) | null;\n};\n\nexport type ViewerGeometrySnapshot = {\n  bodyInlineSize: number;\n  documentInlineSize: number;\n  hasMeasuredBody: boolean;\n  isTransitioning: boolean;\n  mode: ViewerSidebarMode;\n  open: boolean;\n  progress: number;\n  sidebarGapTransition: ViewerSidebarGapTransition;\n  sidebarInlineSize: number;\n  sidebarWidth: number;\n  side: ViewerSidebarSide;\n  state: ViewerSidebarState;\n  transitionPhase: ViewerGeometryTransitionPhase;\n};\n\nexport type ViewerGeometryStore = {\n  getSnapshot: () => ViewerGeometrySnapshot;\n  setTarget: (target: ViewerGeometryTarget) => void;\n  subscribe: (listener: () => void) => () => void;\n};\n\nexport type ViewerGeometryTarget = {\n  bodyElement: HTMLElement | null;\n  mode: ViewerSidebarMode;\n  open: boolean;\n  rootElement: HTMLElement | null;\n  sidebarElement: HTMLElement | null;\n  sidebarGapTransition: ViewerSidebarGapTransition;\n  sidebarWidth: number;\n  side: ViewerSidebarSide;\n  state: ViewerSidebarState;\n};\n\nexport type ViewerSidebarStateValue = {\n  state: ViewerSidebarState;\n  open: boolean;\n  setOpen: (value: boolean | ((open: boolean) => boolean)) => void;\n  toggleSidebar: () => void;\n  canToggleSidebar: boolean;\n  mode: ViewerSidebarMode;\n  side: ViewerSidebarSide;\n};\n\nexport type ViewerRootProps = React.ComponentProps<\"div\"> & {\n  defaultOpen?: boolean;\n  inlineBreakpoint?: number;\n  mode?: ViewerSidebarRequestedMode;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n  sidebarCollapsible?: ViewerSidebarCollapsible;\n  sidebarGapTransition?: ViewerSidebarGapTransition;\n  sidebarSide?: ViewerSidebarSide;\n  stateNamespace?: ViewerStateAttributeNamespace;\n};\n\nexport type ViewerFrameProps = React.ComponentProps<\"div\">;\nexport type ViewerHeaderProps = React.ComponentProps<\"div\">;\nexport type ViewerBodyProps = React.ComponentProps<\"div\">;\nexport type ViewerSurfaceProps = React.ComponentProps<\"div\">;\nexport type ViewerViewportProps = React.ComponentProps<\"div\">;\nexport type ViewerDocumentFrameProps = React.ComponentProps<\"div\"> & {\n  align?: ViewerDocumentFrameAlign;\n  maxInlineSize?: React.CSSProperties[\"maxInlineSize\"];\n};\n\nexport type ViewerStateAttributeNamespace = {\n  prefix: string;\n  slots?: {\n    body?: boolean;\n    root?: boolean;\n    sidebar?: boolean;\n  };\n};\n\nexport type ViewerSidebarRegistration = {\n  collapsible: ViewerSidebarCollapsible;\n  element: HTMLElement;\n  id: string;\n  instanceId: string;\n  side: ViewerSidebarSide;\n  width: string;\n  widthPixels: number;\n};\n\nexport type ViewerPortalContainmentAttributes = {\n  \"data-viewer-portal-root-id\": string;\n};\n\nexport type ViewerSidebarRegistrationState = {\n  defaultSidebarCollapsible: ViewerSidebarCollapsible;\n  defaultSidebarSide: ViewerSidebarSide;\n  geometryStore: ViewerGeometryStore;\n  getRootElement: () => HTMLElement | null;\n  hasSidebar: boolean;\n  registerBody: (element: HTMLElement) => () => void;\n  registerSidebar: (registration: ViewerSidebarRegistration) => () => void;\n  rootId: string;\n  sidebarId: string;\n  sidebarGapTransition: ViewerSidebarGapTransition;\n  sidebarSide: ViewerSidebarSide;\n  setLastTriggerElement: (element: HTMLElement | null) => void;\n  stateNamespace?: ViewerStateAttributeNamespace;\n};\n\nexport type ViewerRootDiagnostics = {\n  getRootElement: () => HTMLElement | null;\n  layoutSignature: string;\n  rootId: string;\n};\n\nexport type ViewerSurfaceMeasurement = {\n  hasMeasured: boolean;\n  setViewportElement: React.RefCallback<HTMLDivElement>;\n  viewportElement: HTMLDivElement | null;\n  viewportHeight: number | null;\n  viewportWidth: number | null;\n};\n\nexport type ViewerSidebarSlotNames = {\n  container?: string;\n  gap?: string;\n  inner?: string;\n};\n\nexport type ViewerStateAttributeSlot = \"body\" | \"root\" | \"sidebar\";\nexport type ViewerStateAttributeValues = {\n  hasSidebar?: boolean;\n  sidebarCollapsible?: ViewerSidebarCollapsible;\n  sidebarMode?: ViewerSidebarMode;\n  sidebarOpen?: boolean;\n  sidebarSide?: ViewerSidebarSide;\n  sidebarState?: ViewerSidebarState;\n};\nexport type ViewerDataAttributes = Record<`data-${string}`, string | undefined>;\n\nexport type ViewerSidebarProps = React.ComponentProps<\"aside\"> &\n  ViewerDataAttributes & {\n    side?: ViewerSidebarSide;\n    collapsible?: ViewerSidebarCollapsible;\n    innerClassName?: string;\n    namespacedSlot?: string;\n    namespacedSlotNames?: ViewerSidebarSlotNames;\n    slotNames?: ViewerSidebarSlotNames;\n    width?: string;\n  };\n",
      "type": "registry:ui",
      "target": "@ui/viewer-types.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-zoom.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Maximize, Minus, Plus } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\n\nfunction IconButton({\n  label,\n  children,\n  ...props\n}: React.ComponentProps<typeof Button> & { label: string }) {\n  return (\n    <Button\n      variant=\"ghost\"\n      size=\"iconSm\"\n      className=\"size-7\"\n      aria-label={label}\n      title={label}\n      {...props}\n    >\n      {children}\n    </Button>\n  );\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n\nexport function useZoom() {\n  const [scale, setScale] = React.useState(1);\n  const zoom = React.useCallback(\n    (factor: number) => setScale((s) => clamp(s * factor, 0.1, 5)),\n    [],\n  );\n  const reset = React.useCallback(() => setScale(1), []);\n  return { scale, zoom, reset };\n}\n\nexport function ZoomActions({\n  scale,\n  zoom,\n  reset,\n}: {\n  scale: number;\n  zoom: (factor: number) => void;\n  reset: () => void;\n}) {\n  return (\n    <>\n      <IconButton label=\"Zoom out\" onClick={() => zoom(1 / 1.2)}>\n        <Minus />\n      </IconButton>\n      <span className=\"text-muted-foreground w-12 text-center text-xs tabular-nums\">\n        {Math.round(scale * 100)}%\n      </span>\n      <IconButton label=\"Zoom in\" onClick={() => zoom(1.2)}>\n        <Plus />\n      </IconButton>\n      <IconButton label=\"Actual size\" onClick={reset}>\n        <Maximize />\n      </IconButton>\n    </>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-zoom.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-abortable-request.ts",
      "content": "export interface SharedAbortableRequest<T> {\n  controller: AbortController;\n  promise: Promise<T>;\n  subscriberPromises: WeakMap<AbortSignal, Promise<T>>;\n  subscribers: Set<AbortSignal>;\n  settled: boolean;\n}\n\nexport function abortError(): DOMException {\n  return new DOMException(\"Aborted\", \"AbortError\");\n}\n\nexport function isAbortError(error: unknown): boolean {\n  return (\n    (error instanceof DOMException && error.name === \"AbortError\") ||\n    (error instanceof Error && error.name === \"AbortError\")\n  );\n}\n\nexport function promiseForSignal<T>(\n  map: WeakMap<AbortSignal, Promise<T>>,\n  signal: AbortSignal,\n  create: () => Promise<T>,\n): Promise<T> {\n  if (signal.aborted) return Promise.reject(abortError());\n\n  const existingPromise = map.get(signal);\n  if (existingPromise) return existingPromise;\n\n  const promise = create().catch((error: unknown) => {\n    map.delete(signal);\n    throw error;\n  });\n  map.set(signal, promise);\n  return promise;\n}\n\nexport function subscribeToAbortableRequest<T>(\n  entry: SharedAbortableRequest<T>,\n  signal: AbortSignal,\n  onNoSubscribers: () => void,\n): Promise<T> {\n  return promiseForSignal(entry.subscriberPromises, signal, () => {\n    entry.subscribers.add(signal);\n\n    return new Promise<T>((resolve, reject) => {\n      let done = false;\n\n      const cleanup = () => {\n        signal.removeEventListener(\"abort\", onAbort);\n        entry.subscribers.delete(signal);\n      };\n\n      const onAbort = () => {\n        if (done) return;\n        done = true;\n        cleanup();\n        if (!entry.settled && entry.subscribers.size === 0) {\n          entry.controller.abort();\n          onNoSubscribers();\n        }\n        reject(abortError());\n      };\n\n      signal.addEventListener(\"abort\", onAbort, { once: true });\n\n      entry.promise.then(\n        (value) => {\n          if (done) return;\n          done = true;\n          cleanup();\n          resolve(value);\n        },\n        (error: unknown) => {\n          if (done) return;\n          done = true;\n          cleanup();\n          reject(error);\n        },\n      );\n    });\n  });\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-abortable-request.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-lru-cache.ts",
      "content": "export const VIEWER_LRU_CACHE_MAX = 12;\n\nexport function lruGet<K, V>(map: Map<K, V>, key: K): V | undefined {\n  const value = map.get(key);\n  if (value !== undefined) {\n    map.delete(key);\n    map.set(key, value);\n  }\n  return value;\n}\n\nexport function lruSet<K, V>(\n  map: Map<K, V>,\n  key: K,\n  value: V,\n  onEvict?: (key: K, value: V) => void,\n  max = VIEWER_LRU_CACHE_MAX,\n) {\n  map.delete(key);\n  map.set(key, value);\n  while (map.size > max) {\n    const oldest = map.keys().next().value as K | undefined;\n    if (oldest === undefined) break;\n    const dropped = map.get(oldest);\n    map.delete(oldest);\n    if (dropped !== undefined) onEvict?.(oldest, dropped);\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-lru-cache.ts"
    },
    {
      "path": "registry/new-york-v4/lib/pdf-viewer-diagnostics.ts",
      "content": "const FILE_VIEWER_DIAGNOSTIC_STORAGE_KEY = \"retab-file-viewer-debug\";\nconst PDF_DIAGNOSTIC_STORAGE_KEY = \"retab-pdf-debug\";\nconst PDF_HEADER = [0x25, 0x50, 0x44, 0x46, 0x2d];\nconst T0 = typeof performance !== \"undefined\" ? performance.now() : 0;\nconst seqRef = { n: 0 };\nconst sessionSeqRef = { n: 0 };\n\nexport type FileViewerDiagnosticLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\nexport type PdfViewerDiagnosticLevel = FileViewerDiagnosticLevel;\n\nexport type FileViewerDiagnosticData = Record<string, unknown>;\nexport type PdfViewerDiagnosticData = FileViewerDiagnosticData;\n\nfunction isTruthyDiagnosticFlag(value: string | null): boolean {\n  return value === \"1\" || value === \"true\";\n}\n\nexport function isFileViewerDiagnosticsEnabled(): boolean {\n  if (typeof window === \"undefined\") return false;\n  try {\n    const explicit = window.localStorage.getItem(\n      FILE_VIEWER_DIAGNOSTIC_STORAGE_KEY,\n    );\n    if (explicit !== null) return isTruthyDiagnosticFlag(explicit);\n    const legacyPdf = window.localStorage.getItem(PDF_DIAGNOSTIC_STORAGE_KEY);\n    if (legacyPdf !== null) return isTruthyDiagnosticFlag(legacyPdf);\n    return false;\n  } catch {\n    return false;\n  }\n}\n\nexport function isPdfViewerDiagnosticsEnabled(): boolean {\n  return isFileViewerDiagnosticsEnabled();\n}\n\nexport function fileViewerDiagnostic(\n  level: FileViewerDiagnosticLevel,\n  event: string,\n  data?: FileViewerDiagnosticData,\n): void {\n  if (!isFileViewerDiagnosticsEnabled()) return;\n  const seq = ++seqRef.n;\n  const elapsedMs =\n    (typeof performance !== \"undefined\" ? performance.now() : 0) - T0;\n  const method = console[level] ?? console.log;\n  method(\n    `%cfile-viewer%c #${seq} %c${elapsedMs.toFixed(1)}ms%c ${event}`,\n    \"background:#0f766e;color:#fff;padding:0 4px;border-radius:3px;font-weight:bold\",\n    \"color:#888\",\n    \"color:#06c\",\n    \"color:inherit\",\n    data ?? \"\",\n  );\n}\n\nexport function pdfViewerDiagnostic(\n  level: PdfViewerDiagnosticLevel,\n  event: string,\n  data?: PdfViewerDiagnosticData,\n): void {\n  fileViewerDiagnostic(level, event, data);\n}\n\nexport function viewerDiagnosticNow(): number {\n  return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nexport function viewerDiagnosticDurationMs(startedAt: number): number {\n  return Math.max(0, Math.round(viewerDiagnosticNow() - startedAt));\n}\n\nexport function nextFileViewerSessionId(prefix = \"fv\"): string {\n  sessionSeqRef.n += 1;\n  return `${prefix}_${sessionSeqRef.n.toString(36)}`;\n}\n\nexport function fileBufferDiagnostics(buffer: ArrayBuffer | Uint8Array) {\n  const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n  const head = bytes.slice(0, Math.min(16, bytes.byteLength));\n\n  return {\n    byteLength: bytes.byteLength,\n    headHex: bytesToHex(head),\n    looksLikeHtml: looksLikeHtml(bytes),\n    pdfHeaderOffset: pdfHeaderOffset(bytes),\n    tiffHeader: tiffHeader(bytes),\n  };\n}\n\nexport function pdfBufferDiagnostics(buffer: ArrayBuffer | Uint8Array) {\n  return fileBufferDiagnostics(buffer);\n}\n\nexport function summarizeViewerError(error: unknown, depth = 0): unknown {\n  if (!(error instanceof Error)) {\n    const value = String(error);\n    return {\n      type: typeof error,\n      valueHash: hashString(value),\n      valuePreview: redactDiagnosticText(value, 160),\n    };\n  }\n\n  const details: PdfViewerDiagnosticData = {\n    name: error.name,\n    message: redactDiagnosticText(error.message, 240),\n    messageHash: hashString(error.message),\n  };\n\n  const maybeCode = (error as { code?: unknown }).code;\n  if (maybeCode !== undefined) details.code = maybeCode;\n\n  const maybeStatus = (error as { status?: unknown }).status;\n  if (maybeStatus !== undefined) details.status = maybeStatus;\n\n  const maybeReason = (error as { reason?: unknown }).reason;\n  if (maybeReason !== undefined) details.reason = maybeReason;\n\n  const cause = (error as { cause?: unknown }).cause;\n  if (cause !== undefined && depth < 1) {\n    details.cause = summarizeViewerError(cause, depth + 1);\n  }\n\n  return details;\n}\n\nexport function summarizePdfError(error: unknown, depth = 0): unknown {\n  return summarizeViewerError(error, depth);\n}\n\nexport function safeUrlSummary(url: string | null | undefined) {\n  if (!url) return null;\n  try {\n    const currentOrigin =\n      typeof window !== \"undefined\" && window.location?.origin\n        ? window.location.origin\n        : \"http://local\";\n    const parsed = new URL(url, currentOrigin);\n    return {\n      origin:\n        typeof window !== \"undefined\" &&\n        window.location?.origin &&\n        parsed.origin === window.location.origin\n          ? \"same-origin\"\n          : parsed.origin,\n      path: redactUrlPath(parsed.pathname),\n      hasSearch: parsed.search.length > 0,\n    };\n  } catch {\n    return {\n      valueHash: hashString(url),\n      valuePreview: redactDiagnosticText(url, 160),\n    };\n  }\n}\n\nexport function safePdfUrlSummary(url: string | null | undefined) {\n  return safeUrlSummary(url);\n}\n\nexport function responseHeadersDiagnostics(headers: Headers) {\n  const retabFileId = headers.get(\"x-retab-file-id\");\n  return {\n    contentLength: headers.get(\"content-length\"),\n    contentRange: headers.get(\"content-range\"),\n    contentType: headers.get(\"content-type\"),\n    requestId: headers.get(\"x-request-id\"),\n    retabFileIdHash: summarizeDiagnosticId(retabFileId),\n    retabPreviewKind: headers.get(\"x-retab-preview-kind\"),\n  };\n}\n\nexport function summarizeViewerReadOptions(options?: {\n  signal?: AbortSignal;\n  maxBytes?: number;\n  maxLines?: number;\n}) {\n  return {\n    hasSignal: Boolean(options?.signal),\n    isSignalAborted: options?.signal?.aborted ?? false,\n    maxBytes: options?.maxBytes,\n    maxLines: options?.maxLines,\n  };\n}\n\nexport function summarizeViewerByteRange(range: {\n  start: number;\n  end: number;\n}) {\n  return {\n    start: range.start,\n    end: range.end,\n    requestedBytes: range.end >= range.start ? range.end - range.start + 1 : 0,\n  };\n}\n\nexport function summarizeViewerContent(content: {\n  key: string;\n  sourceKind: string;\n  directUrl?: string | null;\n  mimeType?: string;\n  payload?: {\n    kind: string;\n    url?: string;\n    blob?: Blob;\n    text?: string;\n  };\n}) {\n  return {\n    keyHash: summarizeDiagnosticId(content.key),\n    sourceKind: content.sourceKind,\n    mimeType: content.mimeType,\n    directUrl: safeUrlSummary(content.directUrl),\n    payload: content.payload\n      ? summarizeViewerPayload(content.payload)\n      : undefined,\n  };\n}\n\nexport function summarizeViewerKeys(keys: {\n  load: string;\n  presentation: string;\n  resource: string;\n}) {\n  return {\n    loadHash: summarizeDiagnosticId(keys.load),\n    presentationHash: summarizeDiagnosticId(keys.presentation),\n    resourceHash: summarizeDiagnosticId(keys.resource),\n  };\n}\n\nexport function summarizeViewerResource(resource: {\n  descriptor?: {\n    category: string;\n    displayName: string;\n    fileName: string;\n    identityKey: string;\n    mimeType?: string;\n  };\n  fileName: string;\n  identityKey: string;\n  mimeType?: string;\n  sourceKind: string;\n  keys: {\n    load: string;\n    presentation: string;\n    resource: string;\n  };\n  content?: {\n    key: string;\n    sourceKind: string;\n    directUrl?: string | null;\n    mimeType?: string;\n    payload?: {\n      kind: string;\n      url?: string;\n      blob?: Blob;\n      text?: string;\n    };\n  };\n}) {\n  return {\n    fileName: summarizeFileName(resource.fileName),\n    identityKeyHash: summarizeDiagnosticId(resource.identityKey),\n    mimeType: resource.mimeType,\n    sourceKind: resource.sourceKind,\n    descriptor: resource.descriptor\n      ? summarizeViewerDescriptor(resource.descriptor)\n      : undefined,\n    content: resource.content\n      ? summarizeViewerContent(resource.content)\n      : undefined,\n    keys: summarizeViewerKeys(resource.keys),\n  };\n}\n\nexport function summarizeViewerSource(source: {\n  kind: string;\n  fileName?: string;\n  mimeType?: string;\n  identityKey?: string;\n  downloadUrl?: string;\n  url?: string;\n  text?: string;\n  blob?: Blob;\n}) {\n  return {\n    kind: source.kind,\n    fileName: summarizeFileName(source.fileName),\n    extension: extensionOf(source.fileName),\n    mimeType: source.mimeType ?? source.blob?.type,\n    identityKeyHash: summarizeDiagnosticId(source.identityKey),\n    url: safeUrlSummary(source.url),\n    downloadUrl: safeUrlSummary(source.downloadUrl),\n    textLength: source.kind === \"text\" ? (source.text?.length ?? 0) : undefined,\n    blobSize: source.blob?.size,\n    blobType: source.blob?.type || undefined,\n  };\n}\n\nexport function summarizeViewerDescriptor(descriptor: {\n  category: string;\n  displayName: string;\n  fileName: string;\n  identityKey: string;\n  mimeType?: string;\n}) {\n  return {\n    category: descriptor.category,\n    displayName: summarizeFileName(descriptor.displayName),\n    fileName: summarizeFileName(descriptor.fileName),\n    extension: extensionOf(descriptor.fileName),\n    mimeType: descriptor.mimeType,\n    identityKeyHash: summarizeDiagnosticId(descriptor.identityKey),\n    extensionCategory: categoryFromExtension(extensionOf(descriptor.fileName)),\n    mimeCategory: categoryFromMime(descriptor.mimeType),\n  };\n}\n\nexport function viewerCategoryMismatch(descriptor: {\n  fileName: string;\n  mimeType?: string;\n}) {\n  const extensionCategory = categoryFromExtension(\n    extensionOf(descriptor.fileName),\n  );\n  const mimeCategory = categoryFromMime(descriptor.mimeType);\n  if (\n    !extensionCategory ||\n    !mimeCategory ||\n    extensionCategory === mimeCategory\n  ) {\n    return null;\n  }\n  return {\n    extension: extensionOf(descriptor.fileName),\n    extensionCategory,\n    mimeCategory,\n    mimeType: descriptor.mimeType,\n  };\n}\n\nexport function truncate(value: string, maxLength: number): string {\n  if (value.length <= maxLength) return value;\n  return `${value.slice(0, maxLength)}...`;\n}\n\nexport function summarizeDiagnosticId(value: string | null | undefined) {\n  if (!value) return null;\n  return {\n    hash: hashString(value),\n    length: value.length,\n  };\n}\n\nfunction summarizeFileName(name: string | null | undefined) {\n  if (!name) return null;\n  return {\n    extension: extensionOf(name),\n    hash: hashString(name),\n    length: name.length,\n  };\n}\n\nfunction redactUrlPath(pathname: string) {\n  const segments = pathname.split(\"/\").map((segment, index, parts) => {\n    if (!segment) return segment;\n    const previous = parts[index - 1];\n    if (previous === \"files\") return \":file_id\";\n    if (previous === \"primitive-executions\") return \":primitive_execution_id\";\n    if (previous === \"runs\") return \":run_id\";\n    if (previous === \"workflows\") return \":workflow_id\";\n    return redactIdentifierSegment(segment);\n  });\n  return segments.join(\"/\");\n}\n\nfunction redactIdentifierSegment(segment: string) {\n  if (\n    /^(file|pexec|run|wf|doc|artifact|clss|parse|split|part|edit|extr)[_-]/i.test(\n      segment,\n    )\n  ) {\n    return `:${segment.split(/[_-]/, 1)[0].toLowerCase()}_id`;\n  }\n  if (segment.length >= 18 && /^[a-z0-9_-]+$/i.test(segment)) {\n    return \":id\";\n  }\n  return segment;\n}\n\nfunction redactDiagnosticText(value: string, maxLength: number) {\n  return truncate(\n    value\n      .replace(/https?:\\/\\/\\S+/gi, \"[url]\")\n      .replace(/data:[^\\s\"'<>]+/gi, \"[data-url]\")\n      .replace(/[?&][^=\\s\"'<>]+=[^\\s\"'<>]+/g, \"[query]\"),\n    maxLength,\n  );\n}\n\nfunction hashString(value: string): string {\n  let hash = 2166136261;\n  for (let index = 0; index < value.length; index += 1) {\n    hash ^= value.charCodeAt(index);\n    hash = Math.imul(hash, 16777619);\n  }\n  return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\nfunction summarizeViewerPayload(payload: {\n  kind: string;\n  url?: string;\n  blob?: Blob;\n  text?: string;\n}) {\n  if (payload.kind === \"url\") {\n    return {\n      kind: payload.kind,\n      url: safeUrlSummary(payload.url),\n    };\n  }\n  if (payload.kind === \"blob\") {\n    return {\n      kind: payload.kind,\n      blobSize: payload.blob?.size,\n      blobType: payload.blob?.type || undefined,\n    };\n  }\n  if (payload.kind === \"text\") {\n    return {\n      kind: payload.kind,\n      textLength: payload.text?.length ?? 0,\n    };\n  }\n  return { kind: payload.kind };\n}\n\nfunction pdfHeaderOffset(bytes: Uint8Array): number {\n  const limit = Math.min(bytes.byteLength, 1024);\n  for (let i = 0; i <= limit - PDF_HEADER.length; i += 1) {\n    let matches = true;\n    for (let offset = 0; offset < PDF_HEADER.length; offset += 1) {\n      if (bytes[i + offset] !== PDF_HEADER[offset]) {\n        matches = false;\n        break;\n      }\n    }\n    if (matches) return i;\n  }\n  return -1;\n}\n\nfunction tiffHeader(bytes: Uint8Array): \"little_endian\" | \"big_endian\" | null {\n  if (bytes.byteLength < 4) return null;\n  if (\n    bytes[0] === 0x49 &&\n    bytes[1] === 0x49 &&\n    bytes[2] === 0x2a &&\n    bytes[3] === 0x00\n  ) {\n    return \"little_endian\";\n  }\n  if (\n    bytes[0] === 0x4d &&\n    bytes[1] === 0x4d &&\n    bytes[2] === 0x00 &&\n    bytes[3] === 0x2a\n  ) {\n    return \"big_endian\";\n  }\n  return null;\n}\n\nfunction looksLikeHtml(bytes: Uint8Array): boolean {\n  const sample = new TextDecoder()\n    .decode(bytes.slice(0, Math.min(bytes.byteLength, 64)))\n    .trimStart()\n    .toLowerCase();\n  return sample.startsWith(\"<!doctype html\") || sample.startsWith(\"<html\");\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n  return Array.from(bytes)\n    .map((byte) => byte.toString(16).padStart(2, \"0\"))\n    .join(\"\");\n}\n\nfunction extensionOf(name: string | null | undefined): string | null {\n  if (!name) return null;\n  const clean = name.split(/[?#]/)[0];\n  const base = clean.split(\"/\").pop() ?? clean;\n  const dot = base.lastIndexOf(\".\");\n  return dot > 0 ? base.slice(dot + 1).toLowerCase() : null;\n}\n\nfunction categoryFromExtension(extension: string | null): string | null {\n  if (!extension) return null;\n  if (extension === \"pdf\") return \"pdf\";\n  if (extension === \"tif\" || extension === \"tiff\") return \"image\";\n  if (\n    extension === \"png\" ||\n    extension === \"jpg\" ||\n    extension === \"jpeg\" ||\n    extension === \"gif\" ||\n    extension === \"webp\" ||\n    extension === \"avif\" ||\n    extension === \"bmp\" ||\n    extension === \"svg\" ||\n    extension === \"ico\"\n  ) {\n    return \"image\";\n  }\n  if (extension === \"docx\") return \"docx\";\n  if (extension === \"xlsx\" || extension === \"xls\" || extension === \"xlsm\") {\n    return \"xlsx\";\n  }\n  if (extension === \"pptx\") return \"pptx\";\n  if (extension === \"csv\" || extension === \"tsv\") return \"csv\";\n  if (extension === \"md\" || extension === \"markdown\") return \"markdown\";\n  if (extension === \"html\" || extension === \"htm\") return \"html\";\n  return null;\n}\n\nfunction categoryFromMime(mimeType: string | null | undefined): string | null {\n  if (!mimeType) return null;\n  const mime = mimeType.toLowerCase().split(\";\")[0].trim();\n  if (mime === \"application/pdf\") return \"pdf\";\n  if (mime.startsWith(\"image/\")) return \"image\";\n  if (mime.includes(\"wordprocessingml\")) return \"docx\";\n  if (mime.includes(\"spreadsheet\") || mime.includes(\"ms-excel\")) return \"xlsx\";\n  if (mime.includes(\"presentation\") || mime.includes(\"ms-powerpoint\")) {\n    return \"pptx\";\n  }\n  if (mime === \"text/csv\" || mime === \"text/tab-separated-values\") return \"csv\";\n  if (mime === \"text/markdown\") return \"markdown\";\n  if (mime === \"text/html\") return \"html\";\n  if (mime.startsWith(\"text/\")) return \"text\";\n  return null;\n}\n",
      "type": "registry:lib",
      "target": "@lib/pdf-viewer-diagnostics.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-download-actions.ts",
      "content": "export type ViewerDownloadOrigin = \"original\" | \"derived\";\n\nexport type ViewerDownloadPayload =\n  | { kind: \"href\"; href: string }\n  | { kind: \"blob\"; blob: Blob }\n  | { kind: \"text\"; text: string; mimeType?: string }\n  | { kind: \"none\" };\n\nexport interface ViewerDownloadAction {\n  id: string;\n  label: string;\n  fileName: string;\n  origin: ViewerDownloadOrigin;\n  isDisabled?: boolean;\n  getPayload: (options?: {\n    signal?: AbortSignal;\n  }) => ViewerDownloadPayload | Promise<ViewerDownloadPayload>;\n}\n\nexport type ViewerDownloadErrorKind =\n  | \"disabled\"\n  | \"aborted\"\n  | \"payload_failed\"\n  | \"unsupported\";\n\nexport class ViewerDownloadError extends Error {\n  readonly kind: ViewerDownloadErrorKind;\n  readonly actionId: string;\n  override readonly cause?: unknown;\n\n  constructor({\n    actionId,\n    kind,\n    message,\n    cause,\n  }: {\n    actionId: string;\n    kind: ViewerDownloadErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerDownloadError\";\n    this.actionId = actionId;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport function createHrefDownloadAction({\n  id,\n  label = \"Download\",\n  href,\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  href: string;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"href\", href }),\n  };\n}\n\nexport function createBlobDownloadAction({\n  id,\n  label = \"Download\",\n  blob,\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  blob: Blob;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"blob\", blob }),\n  };\n}\n\nexport function createTextDownloadAction({\n  id,\n  label = \"Download\",\n  text,\n  fileName,\n  mimeType,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  text: string;\n  fileName: string;\n  mimeType?: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"text\", text, mimeType }),\n  };\n}\n\nexport function createDisabledDownloadAction({\n  id,\n  label = \"Download\",\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    isDisabled: true,\n    getPayload: () => ({ kind: \"none\" }),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-download-actions.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-errors.ts",
      "content": "export type ViewerFormat =\n  | \"pdf\"\n  | \"image\"\n  | \"text\"\n  | \"csv\"\n  | \"docx\"\n  | \"xlsx\"\n  | \"pptx\"\n  | \"file\";\n\nexport type ViewerErrorDomain =\n  | \"resource\"\n  | \"format\"\n  | \"state\"\n  | \"unsupported\"\n  | \"unknown\";\n\nexport type ResourceErrorKind =\n  | \"fetch_failed\"\n  | \"http_error\"\n  | \"aborted\"\n  | \"invalid_range\"\n  | \"partial_content\"\n  | \"too_large\"\n  | \"unsupported_capability\"\n  | \"unknown\";\n\nexport type ResourceTooLargeReason = \"bytes\" | \"lines\";\n\nexport class ResourceError extends Error {\n  readonly domain = \"resource\";\n  readonly kind: ResourceErrorKind;\n  readonly status?: number;\n  readonly tooLargeReason?: ResourceTooLargeReason;\n  override readonly cause?: unknown;\n\n  constructor({\n    kind,\n    message,\n    status,\n    tooLargeReason,\n    cause,\n  }: {\n    kind: ResourceErrorKind;\n    message: string;\n    status?: number;\n    tooLargeReason?: ResourceTooLargeReason;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ResourceError\";\n    this.kind = kind;\n    this.status = status;\n    this.tooLargeReason = tooLargeReason;\n    this.cause = cause;\n  }\n}\n\nexport type ViewerFormatErrorKind =\n  | \"bounds\"\n  | \"decode_failed\"\n  | \"disposed\"\n  | \"index_out_of_range\"\n  | \"load_failed\"\n  | \"parse_failed\"\n  | \"render_failed\"\n  | \"worker_failed\"\n  | \"unknown\";\n\nexport interface ViewerFormatErrorMapperOptions {\n  kind: ViewerFormatErrorKind;\n  message: string;\n}\n\nexport class ViewerFormatError extends Error {\n  readonly domain = \"format\";\n  readonly format: ViewerFormat;\n  readonly kind: ViewerFormatErrorKind;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    kind,\n    message,\n    cause,\n  }: {\n    format: ViewerFormat;\n    kind: ViewerFormatErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerFormatError\";\n    this.format = format;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport type ViewerStateErrorKind =\n  | \"invalid_bounds\"\n  | \"invalid_target\"\n  | \"out_of_range\"\n  | \"stale_resource\"\n  | \"unknown\";\n\nexport class ViewerStateError extends Error {\n  readonly domain = \"state\";\n  readonly format?: ViewerFormat;\n  readonly kind: ViewerStateErrorKind;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    kind,\n    message,\n    cause,\n  }: {\n    format?: ViewerFormat;\n    kind: ViewerStateErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerStateError\";\n    this.format = format;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport class ViewerUnsupportedError extends Error {\n  readonly domain = \"unsupported\";\n  readonly format?: ViewerFormat;\n  readonly sourceKind?: string;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    sourceKind,\n    message,\n    cause,\n  }: {\n    format?: ViewerFormat;\n    sourceKind?: string;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerUnsupportedError\";\n    this.format = format;\n    this.sourceKind = sourceKind;\n    this.cause = cause;\n  }\n}\n\nexport interface ViewerErrorInfo {\n  domain: ViewerErrorDomain;\n  format?: ViewerFormat;\n  kind: string;\n  message: string;\n  status?: number;\n  isRetryable: boolean;\n  isDownloadUseful: boolean;\n  userMessage: string;\n  cause?: unknown;\n}\n\nexport interface ViewerErrorContext {\n  format?: ViewerFormat;\n  sourceKind?: \"url\" | \"blob\" | \"text\";\n  canDownload?: boolean;\n  retry?: \"auto\" | \"always\" | \"never\";\n}\n\nexport function isAbortError(error: unknown): boolean {\n  return (\n    (error instanceof DOMException && error.name === \"AbortError\") ||\n    (error instanceof Error && error.name === \"AbortError\")\n  );\n}\n\nexport function isResourceError(error: unknown): error is ResourceError {\n  return (\n    error instanceof ResourceError ||\n    isErrorLike(error, \"ResourceError\", \"resource\")\n  );\n}\n\nexport function isViewerFormatError(\n  error: unknown,\n): error is ViewerFormatError {\n  return (\n    error instanceof ViewerFormatError ||\n    isErrorLike(error, \"ViewerFormatError\", \"format\")\n  );\n}\n\nexport function isViewerStateError(error: unknown): error is ViewerStateError {\n  return (\n    error instanceof ViewerStateError ||\n    isErrorLike(error, \"ViewerStateError\", \"state\")\n  );\n}\n\nexport function isViewerUnsupportedError(\n  error: unknown,\n): error is ViewerUnsupportedError {\n  return (\n    error instanceof ViewerUnsupportedError ||\n    isErrorLike(error, \"ViewerUnsupportedError\", \"unsupported\")\n  );\n}\n\nexport function toViewerErrorInfo(\n  error: unknown,\n  context: ViewerErrorContext = {},\n): ViewerErrorInfo {\n  const canDownload = context.canDownload ?? true;\n\n  if (isResourceError(error)) {\n    return {\n      domain: \"resource\",\n      format: context.format,\n      kind: error.kind,\n      message: error.message,\n      status: error.status,\n      isRetryable: retryable(\n        context,\n        resourceErrorDefaultRetry(error, context),\n      ),\n      isDownloadUseful: canDownload && error.kind !== \"aborted\",\n      userMessage: resourceErrorUserMessage(error),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerFormatError(error)) {\n    const format = error.format ?? context.format;\n    return {\n      domain: \"format\",\n      format,\n      kind: error.kind,\n      message: error.message,\n      isRetryable: retryable(\n        context,\n        formatErrorDefaultRetry(error, context, format),\n      ),\n      isDownloadUseful: canDownload,\n      userMessage: formatErrorUserMessage(format, error.kind, error),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerStateError(error)) {\n    return {\n      domain: \"state\",\n      format: error.format ?? context.format,\n      kind: error.kind,\n      message: error.message,\n      isRetryable: retryable(context, false),\n      isDownloadUseful: canDownload,\n      userMessage: stateErrorUserMessage(error.kind),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerUnsupportedError(error)) {\n    return {\n      domain: \"unsupported\",\n      format: error.format ?? context.format,\n      kind: \"unsupported\",\n      message: error.message,\n      isRetryable: retryable(context, false),\n      isDownloadUseful: canDownload,\n      userMessage: \"This file cannot be previewed here.\",\n      cause: error.cause,\n    };\n  }\n\n  if (isAbortError(error)) {\n    return {\n      domain: \"resource\",\n      format: context.format,\n      kind: \"aborted\",\n      message: \"Loading was cancelled.\",\n      isRetryable: retryable(context, false),\n      isDownloadUseful: false,\n      userMessage: \"Loading was cancelled.\",\n      cause: error,\n    };\n  }\n\n  const message = error instanceof Error ? error.message : String(error);\n  return {\n    domain: \"unknown\",\n    format: context.format,\n    kind: \"unknown\",\n    message,\n    isRetryable: retryable(context, unknownErrorDefaultRetry(context)),\n    isDownloadUseful: canDownload,\n    userMessage: fallbackUserMessage(context.format),\n    cause: error,\n  };\n}\n\nfunction isErrorLike(error: unknown, name: string, domain: ViewerErrorDomain) {\n  if (!error || typeof error !== \"object\") return false;\n  const candidate = error as {\n    name?: unknown;\n    domain?: unknown;\n    kind?: unknown;\n  };\n  return (\n    (candidate.name === name || candidate.domain === domain) &&\n    typeof candidate.kind === \"string\"\n  );\n}\n\nfunction retryable(context: ViewerErrorContext, fallback: boolean) {\n  if (context.retry === \"always\") return true;\n  if (context.retry === \"never\") return false;\n  return fallback;\n}\n\nfunction resourceErrorDefaultRetry(\n  error: ResourceError,\n  context: ViewerErrorContext,\n) {\n  if (error.kind === \"aborted\") return false;\n  if (error.kind === \"invalid_range\") return false;\n  if (error.kind === \"too_large\") return false;\n  if (error.kind === \"unsupported_capability\") return false;\n  return context.sourceKind === \"url\";\n}\n\nfunction formatErrorDefaultRetry(\n  error: ViewerFormatError,\n  context: ViewerErrorContext,\n  format: ViewerFormat | undefined,\n) {\n  if (format === \"text\" && error.kind === \"bounds\") return false;\n  if (error.kind === \"disposed\") return false;\n  if (error.kind === \"index_out_of_range\") return false;\n  if (format === \"docx\") return true;\n  return context.sourceKind === \"url\";\n}\n\nfunction unknownErrorDefaultRetry(context: ViewerErrorContext) {\n  if (context.format === \"docx\") return true;\n  return context.sourceKind === \"url\";\n}\n\nfunction resourceErrorUserMessage(error: ResourceError) {\n  if (error.kind === \"http_error\") {\n    return error.status\n      ? `Failed to load file: ${error.status}.`\n      : \"Couldn't load this file.\";\n  }\n  if (error.kind === \"fetch_failed\") return \"Couldn't load this file.\";\n  if (error.kind === \"aborted\") return \"Loading was cancelled.\";\n  if (error.kind === \"invalid_range\") return \"This source range is invalid.\";\n  if (error.kind === \"too_large\") {\n    return error.tooLargeReason === \"lines\"\n      ? \"This file has too many lines to preview.\"\n      : \"This file is too large to preview.\";\n  }\n  if (error.kind === \"partial_content\") {\n    return \"This source returned partial content and cannot be previewed here.\";\n  }\n  if (error.kind === \"unsupported_capability\") {\n    return \"This source cannot be previewed here.\";\n  }\n  return \"Couldn't load this file.\";\n}\n\nfunction formatErrorUserMessage(\n  format: ViewerFormat | undefined,\n  kind: string,\n  error?: unknown,\n) {\n  if (format === \"pdf\") return \"Couldn't load this PDF.\";\n  if (format === \"image\") {\n    if (kind === \"index_out_of_range\")\n      return \"This image page is out of range.\";\n    if (kind === \"decode_failed\") return \"Couldn't decode this image.\";\n    return \"Couldn't load this image.\";\n  }\n  if (format === \"text\") {\n    if (kind === \"render_failed\") return \"Couldn't render this text file.\";\n    if (kind === \"bounds\") {\n      const boundsError = error as {\n        reason?: unknown;\n        boundName?: unknown;\n      };\n      if (boundsError.reason === \"lines\") {\n        return \"This text file has too many lines to preview.\";\n      }\n      if (boundsError.reason === \"bytes\") {\n        return \"This text file is too large to preview.\";\n      }\n      if (typeof boundsError.boundName === \"string\") {\n        return \"Text viewer bounds are invalid.\";\n      }\n    }\n    return \"Couldn't load this text file.\";\n  }\n  if (format === \"csv\") return \"Couldn't parse this table.\";\n  if (format === \"docx\") return \"Couldn't render this document.\";\n  if (format === \"xlsx\") return \"Couldn't parse this spreadsheet.\";\n  if (format === \"pptx\") {\n    if (kind === \"render_failed\") return \"Couldn't render this slide.\";\n    return \"Couldn't load this presentation.\";\n  }\n  return \"Couldn't load this file.\";\n}\n\nfunction stateErrorUserMessage(kind: ViewerStateErrorKind) {\n  if (kind === \"invalid_bounds\") return \"Viewer bounds are invalid.\";\n  if (kind === \"invalid_target\") return \"The requested target is invalid.\";\n  if (kind === \"out_of_range\") return \"The requested item is out of range.\";\n  if (kind === \"stale_resource\") return \"This viewer state is no longer valid.\";\n  return \"Couldn't load this file.\";\n}\n\nfunction fallbackUserMessage(format: ViewerFormat | undefined) {\n  if (format === \"pdf\") return \"Couldn't load this PDF.\";\n  if (format === \"image\") return \"Couldn't load this image.\";\n  if (format === \"text\") return \"Couldn't load this text file.\";\n  if (format === \"csv\") return \"Couldn't parse this table.\";\n  if (format === \"docx\") return \"Couldn't load this document.\";\n  if (format === \"xlsx\") return \"Couldn't load this spreadsheet.\";\n  if (format === \"pptx\") return \"Couldn't load this presentation.\";\n  return \"Couldn't load this file.\";\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-errors.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-resource.ts",
      "content": "import {\n  createBlobDownloadAction,\n  createHrefDownloadAction,\n  createTextDownloadAction,\n  type ViewerDownloadAction,\n} from \"@/lib/viewer-download-actions\";\nimport {\n  isAbortError,\n  ResourceError,\n  type ResourceTooLargeReason,\n} from \"@/lib/viewer-errors\";\nimport {\n  resolveViewerDescriptor,\n  textPayloadKey,\n  type BlobViewerSource,\n  type FileCategory,\n  type TextSource,\n  type UrlViewerSource,\n  type ViewerDescriptor,\n  type ViewerSource,\n} from \"@/lib/viewer-source\";\n\nexport interface ResourceReadOptions {\n  cache?: RequestCache;\n  signal?: AbortSignal;\n}\n\nexport interface TextReadOptions extends ResourceReadOptions {\n  maxBytes?: number;\n  maxLines?: number;\n}\n\nexport interface ByteRange {\n  start: number;\n  end: number;\n}\n\nexport interface ByteRangeResult {\n  buffer: ArrayBuffer;\n  contentRange?: {\n    start: number;\n    end: number;\n    total: number | null;\n  };\n  isComplete: boolean;\n}\n\nexport interface ViewerResourceKeys {\n  readonly load: string;\n  readonly presentation: string;\n  readonly resource: string;\n}\n\nexport type ViewerResourcePayload =\n  | { kind: \"url\"; url: string }\n  | { kind: \"blob\"; blob: Blob }\n  | { kind: \"text\"; text: string };\n\nexport interface ViewerResourceContent {\n  readonly key: string;\n  readonly sourceKind: ViewerSource[\"kind\"];\n  readonly directUrl: string | null;\n  readonly mimeType?: string;\n  readonly payload: ViewerResourcePayload;\n  readBlob(options?: ResourceReadOptions): Promise<Blob>;\n  readBytes(options?: ResourceReadOptions): Promise<ArrayBuffer>;\n  readText(options?: TextReadOptions): Promise<string>;\n  readStream(\n    options?: ResourceReadOptions,\n  ): Promise<ReadableStream<Uint8Array>>;\n  readRange(\n    range: ByteRange,\n    options?: ResourceReadOptions,\n  ): Promise<ByteRangeResult>;\n}\n\nexport type ViewerContentIdentity = Pick<\n  ViewerResourceContent,\n  \"key\" | \"sourceKind\"\n>;\n\nexport type ViewerContentDirectUrl = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"directUrl\">;\n\nexport type ViewerContentPayload = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"payload\">;\n\nexport type ViewerContentMime = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"mimeType\">;\n\nexport type ViewerContentBlob = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readBlob\">;\n\nexport type ViewerContentBytes = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readBytes\">;\n\nexport type ViewerContentText = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readText\">;\n\nexport type ViewerContentStream = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readStream\">;\n\nexport type ViewerContentRange = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readRange\">;\n\nexport interface ViewerResource {\n  readonly descriptor: ViewerDescriptor;\n  readonly sourceKind: ViewerSource[\"kind\"];\n  readonly keys: ViewerResourceKeys;\n  readonly identityKey: string;\n  readonly fileName: string;\n  readonly mimeType?: string;\n  readonly content: ViewerResourceContent;\n  readonly originalDownload: ViewerDownloadAction;\n}\n\nconst URL_RESOURCE_REGISTRY_MAX = 128;\nconst TEXT_RESOURCE_REGISTRY_MAX = 64;\n// LF, CR, CRLF, LINE SEPARATOR (U+2028), and PARAGRAPH SEPARATOR (U+2029) — the\n// ECMAScript LineTerminator set, matching what a browser breaks on in a\n// `white-space: pre` block. Kept in sync with text-viewer-resource's splitter.\nconst TEXT_LINE_BREAK_PATTERN = /\\r\\n|[\\n\\r\\u2028\\u2029]/g;\n\nconst urlViewerResourceRegistry = new Map<string, ViewerResource>();\nconst urlViewerResourceContentRegistry = new Map<\n  string,\n  ViewerResourceContent\n>();\nconst textViewerResourceRegistry = new Map<string, ViewerResource>();\nconst textViewerResourceContentRegistry = new Map<\n  string,\n  ViewerResourceContent\n>();\nlet blobViewerResourceRegistry = new WeakMap<\n  Blob,\n  Map<string, ViewerResource>\n>();\nlet blobViewerResourceContentRegistry = new WeakMap<\n  Blob,\n  Map<string, ViewerResourceContent>\n>();\nconst blobObjectKeys = new WeakMap<Blob, string>();\nlet nextBlobObjectKey = 0;\n\nexport function createViewerResource(\n  source: ViewerSource,\n  category?: FileCategory,\n): ViewerResource {\n  const descriptor = resolveViewerDescriptor({ source, category });\n  const keys = viewerResourceKeys(source, descriptor);\n\n  if (source.kind === \"url\") {\n    return internUrlResource(source, descriptor, keys);\n  }\n  if (source.kind === \"blob\") {\n    return internBlobResource(source, descriptor, keys);\n  }\n  return internTextResource(source, descriptor, keys);\n}\n\nexport function clearViewerResourceRegistryForTests() {\n  urlViewerResourceRegistry.clear();\n  urlViewerResourceContentRegistry.clear();\n  textViewerResourceRegistry.clear();\n  textViewerResourceContentRegistry.clear();\n  blobViewerResourceRegistry = new WeakMap();\n  blobViewerResourceContentRegistry = new WeakMap();\n}\n\nfunction internUrlResource(\n  source: UrlViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const cached = urlViewerResourceRegistry.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createUrlResource(source, descriptor, keys);\n  urlViewerResourceRegistry.set(keys.resource, resource);\n  pruneUrlResourceRegistry();\n  return resource;\n}\n\nfunction internBlobResource(\n  source: BlobViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  let resources = blobViewerResourceRegistry.get(source.blob);\n  if (!resources) {\n    resources = new Map();\n    blobViewerResourceRegistry.set(source.blob, resources);\n  }\n\n  const cached = resources.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createBlobResource(source, descriptor, keys);\n  resources.set(keys.resource, resource);\n  return resource;\n}\n\nfunction internTextResource(\n  source: TextSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const cached = textViewerResourceRegistry.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createTextResource(source, descriptor, keys);\n  textViewerResourceRegistry.set(keys.resource, resource);\n  pruneTextResourceRegistry();\n  return resource;\n}\n\nfunction pruneUrlResourceRegistry() {\n  while (urlViewerResourceRegistry.size > URL_RESOURCE_REGISTRY_MAX) {\n    const firstKey = urlViewerResourceRegistry.keys().next().value;\n    if (!firstKey) return;\n    urlViewerResourceRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneUrlResourceContentRegistry() {\n  while (urlViewerResourceContentRegistry.size > URL_RESOURCE_REGISTRY_MAX) {\n    const firstKey = urlViewerResourceContentRegistry.keys().next().value;\n    if (!firstKey) return;\n    urlViewerResourceContentRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneTextResourceRegistry() {\n  while (textViewerResourceRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {\n    const firstKey = textViewerResourceRegistry.keys().next().value;\n    if (!firstKey) return;\n    textViewerResourceRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneTextResourceContentRegistry() {\n  while (textViewerResourceContentRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {\n    const firstKey = textViewerResourceContentRegistry.keys().next().value;\n    if (!firstKey) return;\n    textViewerResourceContentRegistry.delete(firstKey);\n  }\n}\n\nfunction viewerResourceKeys(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n): ViewerResourceKeys {\n  const load = viewerResourceLoadKey(source, descriptor);\n  const presentation = viewerResourcePresentationKey(source, descriptor);\n  return {\n    load,\n    presentation,\n    resource: [load, presentation].join(\"\\u0000\"),\n  };\n}\n\nfunction viewerResourceLoadKey(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n) {\n  return [\n    source.kind,\n    source.identityKey ?? \"\",\n    sourceMimeType(source) ?? \"\",\n    directLoadCacheKey(source),\n    payloadCacheKey(source, descriptor),\n  ].join(\"\\u0000\");\n}\n\nfunction viewerResourcePresentationKey(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n) {\n  return [\n    descriptor.category,\n    descriptor.displayName,\n    descriptor.fileName,\n    descriptor.mimeType ?? \"\",\n    downloadCacheKey(source),\n  ].join(\"\\u0000\");\n}\n\nfunction directLoadCacheKey(source: ViewerSource) {\n  return source.kind === \"url\" ? source.url : \"\";\n}\n\nfunction downloadCacheKey(source: ViewerSource) {\n  if (source.kind === \"text\") return \"\";\n  return source.downloadUrl ?? \"\";\n}\n\nfunction payloadCacheKey(source: ViewerSource, descriptor: ViewerDescriptor) {\n  if (source.kind === \"url\") return \"\";\n  if (source.kind === \"blob\") return blobObjectKey(source.blob);\n  return source.identityKey ? \"\" : descriptor.identityKey;\n}\n\nexport function viewerResourceRenderKey(resource: ViewerResource): string {\n  const load = [\n    resource.sourceKind,\n    resource.identityKey,\n    resource.mimeType ?? resource.content.mimeType ?? \"\",\n    resource.content.directUrl ?? \"\",\n    viewerContentRenderKey(resource.content),\n  ].join(\"\\u0000\");\n\n  return [load, resource.keys.presentation].join(\"\\u0000\");\n}\n\nexport function viewerContentRenderKey(content: ViewerResourceContent): string {\n  if (content.payload.kind === \"text\")\n    return textPayloadKey(content.payload.text);\n  return content.key;\n}\n\nfunction sourceMimeType(source: ViewerSource) {\n  if (source.kind === \"blob\") return source.mimeType ?? source.blob.type;\n  return source.mimeType;\n}\n\nfunction blobObjectKey(blob: Blob) {\n  let key = blobObjectKeys.get(blob);\n  if (!key) {\n    nextBlobObjectKey += 1;\n    key = `blob-object:${nextBlobObjectKey}`;\n    blobObjectKeys.set(blob, key);\n  }\n  return key;\n}\n\nexport function blobSource(\n  bytes: Blob | ArrayBuffer | Uint8Array,\n  metadata: {\n    identityKey: string;\n    fileName?: string;\n    mimeType?: string;\n    downloadUrl?: string;\n  },\n): BlobViewerSource {\n  const blob =\n    bytes instanceof Blob\n      ? bytes\n      : new Blob(\n          [bytes instanceof ArrayBuffer ? bytes : new Uint8Array(bytes)],\n          {\n            type: metadata.mimeType ?? \"\",\n          },\n        );\n  return {\n    kind: \"blob\",\n    blob,\n    fileName: metadata.fileName,\n    mimeType: metadata.mimeType ?? blob.type,\n    downloadUrl: metadata.downloadUrl,\n    identityKey: metadata.identityKey,\n  };\n}\n\nfunction createUrlResource(\n  source: UrlViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const content = internUrlResourceContent(source, keys);\n  const originalDownload = createHrefDownloadAction({\n    id: \"download-original\",\n    label: \"Download\",\n    href: source.downloadUrl ?? source.url,\n    fileName: descriptor.fileName,\n  });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internUrlResourceContent(\n  source: UrlViewerSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const cached = urlViewerResourceContentRegistry.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: source.url,\n    payload: { kind: \"url\", url: source.url },\n    readBlob: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      return readResponseBlob(response);\n    },\n    readBytes: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      return readResponseArrayBuffer(response);\n    },\n    readText: async ({ cache, signal, maxBytes, maxLines } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      return readBoundedResponseText(response, { maxBytes, maxLines });\n    },\n    readStream: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      if (!response.body) {\n        if (response.status === 204 || response.status === 205) {\n          return emptyByteStream();\n        }\n        throw new ResourceError({\n          kind: \"unsupported_capability\",\n          message: \"This response cannot be streamed.\",\n        });\n      }\n      return response.body;\n    },\n    readRange: async (range, { cache, signal } = {}) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      const init = {\n        signal,\n        headers: { Range: `bytes=${start}-${end}` },\n      };\n      const response = await fetchResource(\n        source.url,\n        cache ? { ...init, cache } : init,\n      );\n      const buffer = await readResponseArrayBuffer(response);\n      const contentRange = parseContentRange(\n        response.headers.get(\"content-range\"),\n      );\n      validateUrlRangeResponse({\n        bufferLength: buffer.byteLength,\n        contentRange,\n        range,\n        status: response.status,\n      });\n      return {\n        buffer,\n        contentRange,\n        isComplete: isByteRangeComplete({\n          bufferLength: buffer.byteLength,\n          contentRange,\n          requestedLength: end - start + 1,\n          status: response.status,\n        }),\n      };\n    },\n  });\n  urlViewerResourceContentRegistry.set(keys.load, content);\n  pruneUrlResourceContentRegistry();\n  return content;\n}\n\nfunction emptyByteStream() {\n  return new ReadableStream<Uint8Array>({\n    start(controller) {\n      controller.close();\n    },\n  });\n}\n\nfunction createBlobResource(\n  source: BlobViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const blob = source.blob;\n  const content = internBlobResourceContent(source, keys);\n  const originalDownload = source.downloadUrl\n    ? createHrefDownloadAction({\n        id: \"download-original\",\n        label: \"Download\",\n        href: source.downloadUrl,\n        fileName: descriptor.fileName,\n      })\n    : createBlobDownloadAction({\n        id: \"download-original\",\n        label: \"Download\",\n        blob,\n        fileName: descriptor.fileName,\n      });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internBlobResourceContent(\n  source: BlobViewerSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const blob = source.blob;\n  let contents = blobViewerResourceContentRegistry.get(blob);\n  if (!contents) {\n    contents = new Map();\n    blobViewerResourceContentRegistry.set(blob, contents);\n  }\n\n  const cached = contents.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: null,\n    payload: { kind: \"blob\", blob },\n    readBlob: async () => blob,\n    readBytes: async () => blob.arrayBuffer(),\n    readText: async ({ maxBytes, maxLines } = {}) =>\n      readBoundedBlobText(blob, { maxBytes, maxLines }),\n    readStream: async () => blob.stream(),\n    readRange: async (range) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      validateKnownByteRangeStart(start, blob.size);\n      const rangeBlob = blob.slice(start, end + 1);\n      return {\n        buffer: await rangeBlob.arrayBuffer(),\n        contentRange: {\n          start,\n          end: Math.min(end, blob.size - 1),\n          total: blob.size,\n        },\n        isComplete: end >= blob.size - 1,\n      };\n    },\n  });\n  contents.set(keys.load, content);\n  return content;\n}\n\nfunction createTextResource(\n  source: TextSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const content = internTextResourceContent(source, keys);\n  const originalDownload = createTextDownloadAction({\n    id: \"download-original\",\n    label: \"Download\",\n    text: source.text,\n    fileName: descriptor.fileName,\n    mimeType: descriptor.mimeType,\n  });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internTextResourceContent(\n  source: TextSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const cached = textViewerResourceContentRegistry.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: null,\n    payload: { kind: \"text\", text: source.text },\n    readBlob: async () =>\n      new Blob([source.text], {\n        type: \"text/plain;charset=utf-8\",\n      }),\n    readBytes: async () =>\n      typedArrayBuffer(new TextEncoder().encode(source.text)),\n    readText: async ({ maxBytes, maxLines } = {}) =>\n      readBoundedInlineText(source.text, { maxBytes, maxLines }),\n    readStream: async () => new Blob([source.text]).stream(),\n    readRange: async (range) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      const buffer = new TextEncoder().encode(source.text);\n      validateKnownByteRangeStart(start, buffer.byteLength);\n      const slice = buffer.slice(start, end + 1);\n      return {\n        buffer: typedArrayBuffer(slice),\n        contentRange: {\n          start,\n          end: Math.min(end, buffer.byteLength - 1),\n          total: buffer.byteLength,\n        },\n        isComplete: end >= buffer.byteLength - 1,\n      };\n    },\n  });\n  textViewerResourceContentRegistry.set(keys.load, content);\n  pruneTextResourceContentRegistry();\n  return content;\n}\n\nfunction resourceBase(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n  options: {\n    content: ViewerResourceContent;\n    originalDownload: ViewerDownloadAction;\n  },\n): ViewerResource {\n  const { content, originalDownload } = options;\n  return Object.freeze({\n    descriptor,\n    sourceKind: source.kind,\n    keys,\n    identityKey: descriptor.identityKey,\n    fileName: descriptor.fileName,\n    mimeType: descriptor.mimeType,\n    content,\n    originalDownload,\n  });\n}\n\nfunction resourceContentBase(\n  source: ViewerSource,\n  keys: ViewerResourceKeys,\n  methods: Omit<ViewerResourceContent, \"key\" | \"sourceKind\" | \"mimeType\">,\n): ViewerResourceContent {\n  return Object.freeze({\n    key: keys.load,\n    sourceKind: source.kind,\n    mimeType: sourceMimeType(source),\n    ...methods,\n  });\n}\n\nfunction typedArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n  return bytes.buffer.slice(\n    bytes.byteOffset,\n    bytes.byteOffset + bytes.byteLength,\n  ) as ArrayBuffer;\n}\n\nasync function fetchResource(\n  input: RequestInfo | URL,\n  init?: RequestInit,\n): Promise<Response> {\n  let response: Response;\n  try {\n    response = await fetch(input, init);\n  } catch (error) {\n    if (isAbortError(error)) {\n      throw new ResourceError({\n        kind: \"aborted\",\n        message: \"Loading was cancelled.\",\n        cause: error,\n      });\n    }\n    throw new ResourceError({\n      kind: \"fetch_failed\",\n      message: \"Could not fetch this resource.\",\n      cause: error,\n    });\n  }\n\n  if (!response.ok && response.status !== 206) {\n    throw new ResourceError({\n      kind: \"http_error\",\n      message: `Failed to load resource: ${response.status}`,\n      status: response.status,\n    });\n  }\n\n  return response;\n}\n\nasync function readBoundedResponseText(\n  response: Response,\n  bounds: { maxBytes?: number; maxLines?: number },\n) {\n  validateFullContentResponse(response);\n\n  const maxBytes = bounds.maxBytes;\n  if (\n    isContentLengthOverLimit(response.headers.get(\"content-length\"), maxBytes)\n  ) {\n    throw tooLarge(\"bytes\");\n  }\n\n  const body = response.body;\n  if (!body) {\n    const buffer = await readResponseArrayBuffer(response);\n    if (maxBytes != null && buffer.byteLength > maxBytes) {\n      throw tooLarge(\"bytes\");\n    }\n    const text = new TextDecoder().decode(buffer);\n    assertLineLimit(text, bounds.maxLines);\n    return text;\n  }\n\n  const reader = body.getReader();\n  const decoder = new TextDecoder();\n  const lineLimitTracker = createLineLimitTracker(bounds.maxLines);\n  let receivedBytes = 0;\n  let text = \"\";\n\n  while (true) {\n    const { done, value } = await readResponseStreamChunk(reader);\n    if (done) break;\n    receivedBytes += value.byteLength;\n    if (maxBytes != null && receivedBytes > maxBytes) {\n      await cancelReaderSilently(reader);\n      throw tooLarge(\"bytes\");\n    }\n    const chunkText = decoder.decode(value, { stream: true });\n    try {\n      lineLimitTracker.push(chunkText);\n    } catch (error) {\n      await cancelReaderSilently(reader);\n      throw error;\n    }\n    text += chunkText;\n  }\n\n  const finalText = decoder.decode();\n  lineLimitTracker.push(finalText);\n  text += finalText;\n  return text;\n}\n\nfunction isContentLengthOverLimit(\n  contentLength: string | null,\n  maxBytes: number | undefined,\n) {\n  if (maxBytes == null || contentLength == null) return false;\n\n  const normalizedLength = contentLength.trim().replace(/^0+(?=\\d)/, \"\");\n  if (!/^\\d+$/.test(normalizedLength)) return false;\n\n  const maxLength = String(maxBytes);\n  return (\n    normalizedLength.length > maxLength.length ||\n    (normalizedLength.length === maxLength.length &&\n      normalizedLength > maxLength)\n  );\n}\n\nfunction validateFullContentResponse(response: Response) {\n  if (response.status !== 206) return;\n\n  const contentRange = parseContentRange(response.headers.get(\"content-range\"));\n  if (\n    contentRange?.total != null &&\n    contentRange.start === 0 &&\n    contentRange.end === contentRange.total - 1\n  ) {\n    return;\n  }\n\n  throw new ResourceError({\n    kind: \"partial_content\",\n    message: \"Full response returned partial content.\",\n    status: response.status,\n  });\n}\n\nasync function readResponseStreamChunk(\n  reader: ReadableStreamDefaultReader<Uint8Array>,\n) {\n  try {\n    return await reader.read();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nasync function readResponseArrayBuffer(response: Response) {\n  try {\n    return await response.arrayBuffer();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nasync function readResponseBlob(response: Response) {\n  try {\n    return await response.blob();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nfunction resourceReadError(error: unknown) {\n  if (isAbortError(error)) {\n    return new ResourceError({\n      kind: \"aborted\",\n      message: \"Loading was cancelled.\",\n      cause: error,\n    });\n  }\n  return new ResourceError({\n    kind: \"fetch_failed\",\n    message: \"Could not read this resource.\",\n    cause: error,\n  });\n}\n\nasync function readBoundedBlobText(\n  blob: Blob,\n  bounds: { maxBytes?: number; maxLines?: number },\n) {\n  if (bounds.maxBytes != null && blob.size > bounds.maxBytes) {\n    throw tooLarge(\"bytes\");\n  }\n  const text = await blob.text();\n  assertLineLimit(text, bounds.maxLines);\n  return text;\n}\n\nfunction readBoundedInlineText(\n  text: string,\n  { maxBytes, maxLines }: { maxBytes?: number; maxLines?: number },\n) {\n  // For inline sources the string *is* the resource, so its UTF-8 byte length\n  // is the authoritative size to measure against maxBytes.\n  if (\n    maxBytes != null &&\n    new TextEncoder().encode(text).byteLength > maxBytes\n  ) {\n    throw tooLarge(\"bytes\");\n  }\n  assertLineLimit(text, maxLines);\n  return text;\n}\n\n// Used after a transferred-byte check has already enforced maxBytes (URL/blob).\n// Re-encoding the decoded text here would double-count: invalid UTF-8 decodes to\n// U+FFFD (3 bytes each), inflating the measured size past the real wire bytes\n// and falsely rejecting small resources as \"too large\".\nfunction assertLineLimit(text: string, maxLines: number | undefined) {\n  if (\n    maxLines != null &&\n    text.split(TEXT_LINE_BREAK_PATTERN).length > maxLines\n  ) {\n    throw tooLarge(\"lines\");\n  }\n}\n\nfunction tooLarge(reason: ResourceTooLargeReason) {\n  return new ResourceError({\n    kind: \"too_large\",\n    tooLargeReason: reason,\n    message: `Resource exceeds ${reason} limit.`,\n  });\n}\n\nasync function cancelReaderSilently(\n  reader: ReadableStreamDefaultReader<Uint8Array>,\n) {\n  try {\n    await reader.cancel();\n  } catch {\n    // Preserve the user-facing load failure; cancellation is best-effort cleanup.\n  }\n}\n\nfunction validateByteRange({ start, end }: ByteRange) {\n  if (\n    !Number.isSafeInteger(start) ||\n    !Number.isSafeInteger(end) ||\n    start < 0 ||\n    end < start\n  ) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Byte range must use non-negative integer bounds.\",\n    });\n  }\n}\n\nfunction validateKnownByteRangeStart(start: number, total: number) {\n  if (start > 0 && start >= total) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Byte range starts past the available resource.\",\n    });\n  }\n}\n\nfunction validateUrlRangeResponse({\n  bufferLength,\n  contentRange,\n  range,\n  status,\n}: {\n  bufferLength: number;\n  contentRange: ByteRangeResult[\"contentRange\"];\n  range: ByteRange;\n  status: number;\n}) {\n  if (status === 200) {\n    if (range.start !== 0 || bufferLength > range.end - range.start + 1) {\n      throw new ResourceError({\n        kind: \"invalid_range\",\n        message: \"Full response does not match the requested byte range.\",\n      });\n    }\n    return;\n  }\n  if (status !== 206) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Range response must return full or partial content.\",\n    });\n  }\n  if (!contentRange) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Partial content response is missing a valid byte range.\",\n    });\n  }\n  const declaredLength = contentRange.end - contentRange.start + 1;\n  if (\n    contentRange.start !== range.start ||\n    contentRange.end < contentRange.start ||\n    contentRange.end > range.end ||\n    (contentRange.total != null && contentRange.end >= contentRange.total) ||\n    declaredLength !== bufferLength\n  ) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Response byte range does not match the requested range.\",\n    });\n  }\n}\n\nfunction isByteRangeComplete({\n  bufferLength,\n  contentRange,\n  requestedLength,\n  status,\n}: {\n  bufferLength: number;\n  contentRange: ByteRangeResult[\"contentRange\"];\n  requestedLength: number;\n  status: number;\n}) {\n  if (status === 200) return true;\n  if (contentRange?.total != null) {\n    if (contentRange.total <= 0) return true;\n    return contentRange.end >= contentRange.total - 1;\n  }\n  if (contentRange) return false;\n  return bufferLength < requestedLength;\n}\n\nfunction isStandaloneLineBreak(character: string) {\n  const code = character.charCodeAt(0);\n  return code === 0x0a || code === 0x2028 || code === 0x2029;\n}\n\nfunction createLineLimitTracker(maxLines: number | undefined) {\n  let lineCount = 1;\n  let previousWasCR = false;\n\n  return {\n    push(text: string) {\n      if (maxLines == null || text.length === 0) return;\n\n      for (const character of text) {\n        if (previousWasCR) {\n          previousWasCR = false;\n          if (character === \"\\n\") continue;\n        }\n\n        if (character === \"\\r\") {\n          lineCount += 1;\n          previousWasCR = true;\n        } else if (isStandaloneLineBreak(character)) {\n          // LF, plus LINE/PARAGRAPH SEPARATOR (U+2028/U+2029); none pair with CR.\n          lineCount += 1;\n        }\n\n        if (lineCount > maxLines) {\n          throw tooLarge(\"lines\");\n        }\n      }\n    },\n  };\n}\n\nfunction parseContentRange(value: string | null) {\n  if (!value) return undefined;\n  const match = value.match(/^bytes\\s+(\\d+)-(\\d+)\\/(\\d+|\\*)\\s*$/i);\n  if (!match) return undefined;\n  const start = parseContentRangeNumber(match[1]);\n  const end = parseContentRangeNumber(match[2]);\n  const total =\n    match[3] === \"*\" ? null : parseContentRangeNumber(match[3] ?? \"\");\n  if (start == null || end == null || total === undefined) return undefined;\n  return {\n    start,\n    end,\n    total,\n  };\n}\n\nfunction parseContentRangeNumber(value: string) {\n  const number = Number(value);\n  return Number.isSafeInteger(number) ? number : undefined;\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-resource.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-source.ts",
      "content": "export type FileCategory =\n  | \"pdf\"\n  | \"docx\"\n  | \"xlsx\"\n  | \"pptx\"\n  | \"csv\"\n  | \"image\"\n  | \"markdown\"\n  | \"html\"\n  | \"email\"\n  | \"text\"\n  | \"unsupported\";\n\nexport type ViewerSource = UrlViewerSource | TextSource | BlobViewerSource;\n\nexport interface UrlViewerSource {\n  kind: \"url\";\n  url: string;\n  fileName?: string;\n  mimeType?: string;\n  downloadUrl?: string;\n  identityKey?: string;\n}\n\nexport interface TextSource {\n  kind: \"text\";\n  text: string;\n  fileName?: string;\n  mimeType?: string;\n  identityKey?: string;\n}\n\nexport interface BlobViewerSource {\n  kind: \"blob\";\n  blob: Blob;\n  identityKey: string;\n  fileName?: string;\n  mimeType?: string;\n  downloadUrl?: string;\n}\n\nexport interface ViewerDescriptor {\n  source: ViewerSource;\n  category: FileCategory;\n  identityKey: string;\n  displayName: string;\n  fileName: string;\n  mimeType?: string;\n}\n\nconst EXTENSION_CATEGORY: Record<string, FileCategory> = {\n  pdf: \"pdf\",\n  docx: \"docx\",\n  xlsx: \"xlsx\",\n  xls: \"xlsx\",\n  xlsm: \"xlsx\",\n  pptx: \"pptx\",\n  csv: \"csv\",\n  tsv: \"csv\",\n  png: \"image\",\n  jpg: \"image\",\n  jpeg: \"image\",\n  gif: \"image\",\n  webp: \"image\",\n  avif: \"image\",\n  bmp: \"image\",\n  svg: \"image\",\n  ico: \"image\",\n  tif: \"image\",\n  tiff: \"image\",\n  md: \"markdown\",\n  markdown: \"markdown\",\n  mdx: \"text\",\n  html: \"html\",\n  htm: \"html\",\n  eml: \"email\",\n  txt: \"text\",\n  text: \"text\",\n  log: \"text\",\n  json: \"text\",\n  jsonl: \"text\",\n  json5: \"text\",\n  ndjson: \"text\",\n  xml: \"text\",\n  yaml: \"text\",\n  yml: \"text\",\n  toml: \"text\",\n  ini: \"text\",\n  env: \"text\",\n  js: \"text\",\n  mjs: \"text\",\n  cjs: \"text\",\n  jsx: \"text\",\n  ts: \"text\",\n  tsx: \"text\",\n  css: \"text\",\n  scss: \"text\",\n  less: \"text\",\n  py: \"text\",\n  rb: \"text\",\n  go: \"text\",\n  rs: \"text\",\n  java: \"text\",\n  kt: \"text\",\n  c: \"text\",\n  h: \"text\",\n  cpp: \"text\",\n  cc: \"text\",\n  cs: \"text\",\n  php: \"text\",\n  sh: \"text\",\n  bash: \"text\",\n  zsh: \"text\",\n  sql: \"text\",\n  graphql: \"text\",\n  proto: \"text\",\n  lua: \"text\",\n  r: \"text\",\n  swift: \"text\",\n  scala: \"text\",\n  pl: \"text\",\n  vue: \"text\",\n  svelte: \"text\",\n};\n\nexport function extensionOf(name: string): string | null {\n  const clean = name.split(/[?#]/)[0];\n  const base = clean.split(\"/\").pop() ?? clean;\n  const dot = base.lastIndexOf(\".\");\n  return dot > 0 ? base.slice(dot + 1).toLowerCase() : null;\n}\n\nexport function extractName(url: string): string {\n  const clean = url.split(/[?#]/)[0];\n  return clean.split(\"/\").pop() || \"file\";\n}\n\nexport function detectCategory(\n  fileName: string,\n  mimeType?: string,\n): FileCategory {\n  const ext = extensionOf(fileName);\n  if (ext && EXTENSION_CATEGORY[ext]) return EXTENSION_CATEGORY[ext];\n  if (mimeType) {\n    const fromMime = categoryFromMime(mimeType);\n    if (fromMime) return fromMime;\n  }\n  return \"unsupported\";\n}\n\nexport function resolveViewerDescriptor({\n  source,\n  category,\n}: {\n  source: ViewerSource;\n  category?: FileCategory;\n}): ViewerDescriptor {\n  const resolvedMimeType =\n    source.mimeType ??\n    (source.kind === \"blob\" && source.blob.type ? source.blob.type : undefined);\n  const displayName = source.fileName ?? defaultDisplayName(source);\n  const fileName = source.fileName ?? defaultFileName(source);\n  const resolvedCategory =\n    category ?? detectCategory(displayName, resolvedMimeType);\n\n  return {\n    source,\n    category: resolvedCategory,\n    identityKey: source.identityKey ?? defaultIdentityKey(source),\n    displayName,\n    fileName,\n    mimeType: resolvedMimeType,\n  };\n}\n\nfunction categoryFromMime(mimeType: string): FileCategory | null {\n  const mime = mimeType.toLowerCase().split(\";\")[0].trim();\n  if (mime === \"application/pdf\") return \"pdf\";\n  if (mime.includes(\"wordprocessingml\")) return \"docx\";\n  if (mime.includes(\"spreadsheet\") || mime.includes(\"ms-excel\")) return \"xlsx\";\n  if (mime.includes(\"presentation\") || mime.includes(\"ms-powerpoint\")) {\n    return \"pptx\";\n  }\n  if (mime === \"text/csv\" || mime === \"text/tab-separated-values\") return \"csv\";\n  if (mime === \"text/markdown\") return \"markdown\";\n  if (mime === \"text/html\") return \"html\";\n  if (mime === \"message/rfc822\" || mime === \"message/global\") {\n    return \"email\";\n  }\n  if (mime.startsWith(\"image/\")) return \"image\";\n  if (mime === \"application/json\" || mime === \"application/xml\") return \"text\";\n  if (mime.startsWith(\"text/\")) return \"text\";\n  return null;\n}\n\nfunction defaultDisplayName(source: ViewerSource) {\n  if (source.kind === \"url\") return source.url;\n  if (source.kind === \"text\") return \"text.txt\";\n  return \"file\";\n}\n\nfunction defaultFileName(source: ViewerSource) {\n  if (source.kind === \"url\") return extractName(source.url);\n  if (source.kind === \"text\") return \"text.txt\";\n  return \"file\";\n}\n\nfunction defaultIdentityKey(source: ViewerSource) {\n  if (source.kind === \"url\") return `url:${source.url}`;\n  if (source.kind === \"text\") return textPayloadIdentityKey(source.text);\n  return source.identityKey;\n}\n\nexport function textPayloadIdentityKey(text: string) {\n  return textPayloadKey(text);\n}\n\nexport function textPayloadKey(text: string) {\n  return `text:${text.length}:${hashString(text)}`;\n}\n\nfunction hashString(text: string) {\n  let hash = 0x811c9dc5;\n  for (let index = 0; index < text.length; index += 1) {\n    hash ^= text.charCodeAt(index);\n    hash = Math.imul(hash, 0x01000193);\n  }\n  return (hash >>> 0).toString(36);\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-source.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-is-client.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nconst emptySubscribe = () => () => {};\nconst getClientSnapshot = () => true;\nconst getServerSnapshot = () => false;\n\n/**\n * SSR gate: `false` on the server (and during hydration's first pass),\n * `true` on the client.\n *\n * This must stay a synchronous external-store read, NOT the\n * `useState(false)` + mount-effect flip. The flip pattern makes every\n * viewer mount its Suspense boundary in a later update; when two such\n * boundaries suspend on pending resources in the same flush as other\n * commit-phase updates (viewer sidebar/geometry registration), React 19's\n * retry lanes desynchronize and re-attempt each other's boundary on every\n * commit — an unbounded synchronous suspend/retry loop that starves the\n * event loop (jsdom tests OOM; browsers busy-spin until the resource\n * resolves). With the store read, client renders suspend on mount, which\n * never enters that loop. Regression-guarded in\n * tests/pdf-viewer-thumbnails.test.tsx (\"shares one document resource…\").\n */\nexport function useIsClient(): boolean {\n  return React.useSyncExternalStore(\n    emptySubscribe,\n    getClientSnapshot,\n    getServerSnapshot,\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-is-client.ts"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-fit-width-motion.ts",
      "content": "\"use client\";\n\nimport type { FileViewerDocumentSurfaceMotionResolver } from \"./file-viewer-motion-kernel\";\nimport type { FileViewerMotionFrame } from \"./file-viewer-motion-plan\";\nimport type {\n  FileViewerDocumentAlign,\n  FileViewerInlineDirection,\n} from \"./file-viewer-renderer-contract\";\n\nexport const FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY =\n  \"--file-viewer-fit-width-anchor-block\";\n\n// Commit-then-relax: the renderer lays out at the motion's TARGET width from\n// the first commit, and this resolver reprojects that settled layout to the\n// in-flight visual width with one uniform transform. The transform terminates\n// on identity, so settle removes a no-op style instead of committing layout.\n//\n// The anchor custom property is the reading line's block offset in the settled\n// stage's own coordinates (post-rebase scrollTop + marker offset). It is read\n// live via var(), so the renderer writes it once per motion (in a layout\n// effect after the slide-start scroll rebase) without re-entering the kernel.\nexport function createFileViewerFitWidthSurfaceMotionResolver({\n  align,\n  anchorBlockProperty = FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY,\n  direction = \"ltr\",\n  isFitWidth,\n  stageInlineSize,\n  stageOuterInlinePadding = 0,\n  stageInlinePadding = 0,\n  stageInlineSlope = 1,\n  stageBlockSlope = stageInlineSlope,\n}: {\n  align: FileViewerDocumentAlign;\n  anchorBlockProperty?: string;\n  direction?: FileViewerInlineDirection;\n  isFitWidth: boolean;\n  stageInlineSize: number;\n  /** Constant symmetric padding around the transformed stage. */\n  stageOuterInlinePadding?: number;\n  /** Constant symmetric inline padding inside the transformed stage. */\n  stageInlinePadding?: number;\n  stageInlineSlope?: number;\n  /**\n   * Slope for the BLOCK axis when it differs from the inline one. A stage\n   * whose inline box carries constant padding while its block stack scales\n   * with the content (the image viewer: fit subtracts the horizontal\n   * padding, vertical gaps/padding scale) has two different affine models —\n   * X tracks the pane 1:1 while Y scales by the content ratio — and a\n   * uniform scale cannot land both axes exactly. Defaults to the inline\n   * slope (uniform scale) for fully proportional stages like the PDF.\n   */\n  stageBlockSlope?: number;\n}): FileViewerDocumentSurfaceMotionResolver {\n  return (frame) => {\n    if (!isFitWidth || frame.phase !== \"sliding\") {\n      return {\n        transform: \"\",\n        transformOrigin: \"\",\n        willChange: \"\",\n      };\n    }\n\n    return {\n      transform: getFileViewerFitWidthSurfaceMotionTransform({\n        align,\n        anchorBlockProperty,\n        direction,\n        frame,\n        stageInlineSize,\n        stageOuterInlinePadding,\n        stageInlinePadding,\n        stageInlineSlope,\n        stageBlockSlope,\n      }),\n      transformOrigin: \"0px 0px\",\n      willChange: \"transform\",\n    };\n  };\n}\n\n// Commit-then-relax for a CLAMPED reading column rather than a fit-width\n// stage: the stage's inline size is min(canvas, maxStageInlineSize), so it\n// does not scale with the pane — the only thing a width change moves is the\n// align margin. The canvas commits the motion's TARGET width from the first\n// sliding frame (minWidth under layoutPolicy \"target\"), which means a\n// widening pane's chunks land at the settled margin synchronously with the\n// click; this resolver reprojects them back to the live width's margin with\n// a translate that terminates on identity. A narrowing pane never engages it\n// (the canvas tracks the live width above its minWidth, so live and settled\n// margins agree) — exactly the leg that already glides on layout.\nexport function createFileViewerAlignTranslateSurfaceMotionResolver({\n  align,\n  direction = \"ltr\",\n  maxStageInlineSize,\n}: {\n  align: FileViewerDocumentAlign;\n  direction?: FileViewerInlineDirection;\n  /** The column's max inline size (the chunk's max-width, in px). */\n  maxStageInlineSize: number;\n}): FileViewerDocumentSurfaceMotionResolver {\n  return (frame) => {\n    if (frame.phase !== \"sliding\") {\n      return {\n        transform: \"\",\n        transformOrigin: \"\",\n        willChange: \"\",\n      };\n    }\n\n    return {\n      transform: getFileViewerAlignTranslateSurfaceMotionTransform({\n        align,\n        direction,\n        frame,\n        maxStageInlineSize,\n      }),\n      transformOrigin: \"0px 0px\",\n      willChange: \"transform\",\n    };\n  };\n}\n\nfunction getFileViewerAlignTranslateSurfaceMotionTransform({\n  align,\n  direction,\n  frame,\n  maxStageInlineSize,\n}: {\n  align: FileViewerDocumentAlign;\n  direction: FileViewerInlineDirection;\n  frame: FileViewerMotionFrame;\n  maxStageInlineSize: number;\n}) {\n  if (\n    !Number.isFinite(maxStageInlineSize) ||\n    maxStageInlineSize <= 0 ||\n    frame.layoutInlineSize <= 0 ||\n    frame.toInlineSize <= 0\n  ) {\n    return \"\";\n  }\n\n  // The canvas lays out at max(live, target): minWidth holds the committed\n  // target under a still-narrow pane, and a pane wider than the target just\n  // fills. The stage (reading column) centers/aligns INSIDE the canvas, and\n  // an overflowing canvas itself pins to the pane's start edge — left in\n  // LTR, right in RTL — so the stage's pane-space position carries the\n  // canvas offset too.\n  const canvasInlineSize = Math.max(frame.layoutInlineSize, frame.toInlineSize);\n  const stageInlineSize = Math.min(canvasInlineSize, maxStageInlineSize);\n  const canvasInlineOffset =\n    direction === \"rtl\"\n      ? Math.min(0, frame.layoutInlineSize - canvasInlineSize)\n      : 0;\n  const settledStageLeft =\n    canvasInlineOffset +\n    getFileViewerStageInlineMargin({\n      align,\n      availableInlineSize: canvasInlineSize,\n      direction,\n      stageInlineSize,\n    });\n  const liveStageLeft = getFileViewerStageInlineMargin({\n    align,\n    availableInlineSize: frame.layoutInlineSize,\n    direction,\n    stageInlineSize,\n  });\n  const translateX = liveStageLeft - settledStageLeft;\n\n  if (Math.abs(translateX) <= 0.001) return \"\";\n\n  return `translate3d(${formatFileViewerMotionPixel(translateX)}px, 0px, 0)`;\n}\n\nexport function getFileViewerFitWidthScale({\n  availableInlineSize,\n  contentInlineSize,\n  stageInlinePadding = 0,\n}: {\n  availableInlineSize: number;\n  contentInlineSize: number;\n  stageInlinePadding?: number;\n}) {\n  if (availableInlineSize <= 0 || contentInlineSize <= 0) return 1;\n\n  const contentAvailableInlineSize = Math.max(\n    1,\n    availableInlineSize - stageInlinePadding,\n  );\n  return contentAvailableInlineSize / contentInlineSize;\n}\n\n// The visual scale the resolver renders for a given live width — the same\n// affine reprojection as the transform itself. Renderers use it to reason\n// about the on-screen state (anchor capture/solve) without duplicating the\n// formula.\n//\n// stageInlineSlope is how many stage pixels the settled stage grows per pane\n// pixel. It is 1 whenever the stage IS the fit-width content (image, docx,\n// pptx, uniform-width PDFs: stage = pane − constant padding), but a stage\n// that is WIDER than its fit basis grows faster than the pane — a PDF fits\n// its FIRST page while the stage spans its WIDEST page, so a mixed-width\n// document has slope maxBase/fitBase > 1. A unit-slope assumption there\n// under-scales the first frame by (slope − 1)·delta/stage — measured as a\n// ~7px content step at the anchor-hold frame of a 355-page prospectus.\nexport function getFileViewerFitWidthVisualScale({\n  liveInlineSize,\n  stageInlineSize,\n  stageInlineSlope = 1,\n  targetInlineSize,\n}: {\n  liveInlineSize: number;\n  stageInlineSize: number;\n  stageInlineSlope?: number;\n  targetInlineSize: number;\n}) {\n  if (\n    stageInlineSize <= 0 ||\n    !Number.isFinite(liveInlineSize) ||\n    !Number.isFinite(targetInlineSize)\n  ) {\n    return 1;\n  }\n  const slope =\n    Number.isFinite(stageInlineSlope) && stageInlineSlope > 0\n      ? stageInlineSlope\n      : 1;\n  return (\n    Math.max(1, stageInlineSize + slope * (liveInlineSize - targetInlineSize)) /\n    stageInlineSize\n  );\n}\n\n// Capture side of the motion anchor: the probe content line's on-screen block\n// offset relative to the scroll box, taken just before a motion (or retarget)\n// commits. When a motion is already in flight the DOM is the settled layout\n// PLUS the live transform, so the capture applies that transform — otherwise\n// a retarget would solve continuity against a picture the reader never saw.\nexport function captureFileViewerFitWidthAnchorScreenOffset({\n  lastAnchorBlock,\n  liveFrame,\n  probeStageOffset,\n  scrollTop,\n  stageInlineSize,\n  stageInlinePadding = 0,\n  stageBlockSlope = 1,\n}: {\n  lastAnchorBlock: number | null;\n  liveFrame: FileViewerMotionFrame | null;\n  probeStageOffset: number;\n  scrollTop: number;\n  stageInlineSize: number;\n  stageInlinePadding?: number;\n  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */\n  stageBlockSlope?: number;\n}) {\n  const untransformed = probeStageOffset - scrollTop;\n  if (!liveFrame || liveFrame.phase !== \"sliding\") return untransformed;\n\n  const liveScale = getFileViewerFitWidthVisualScale({\n    liveInlineSize: liveFrame.layoutInlineSize,\n    stageInlineSize: getFileViewerFitWidthContentInlineSize({\n      stageInlinePadding,\n      stageInlineSize,\n    }),\n    stageInlineSlope: stageBlockSlope,\n    targetInlineSize: liveFrame.toInlineSize,\n  });\n  if (Math.abs(1 - liveScale) <= 0.001) return untransformed;\n\n  return (\n    liveScale * probeStageOffset +\n    (1 - liveScale) * (lastAnchorBlock ?? 0) -\n    scrollTop\n  );\n}\n\n// Solve side: the anchor block offset that puts the probe content line back on\n// its captured screen position under the NEW layout model at the motion's\n// first-frame scale. Exact regardless of how the rebase clamped or how the\n// old/new layout models relate (measured page sizes, constant gaps/padding).\n// Returns null when the motion is degenerate (caller falls back to the live\n// reading marker).\nexport function resolveFileViewerFitWidthMotionAnchorBlock({\n  fromInlineSize,\n  probeScreenOffset,\n  probeStageOffset,\n  scrollTop,\n  stageInlineSize,\n  stageInlinePadding = 0,\n  stageBlockSlope = 1,\n  toInlineSize,\n}: {\n  fromInlineSize: number | null;\n  probeScreenOffset: number;\n  probeStageOffset: number;\n  scrollTop: number;\n  stageInlineSize: number;\n  stageInlinePadding?: number;\n  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */\n  stageBlockSlope?: number;\n  toInlineSize: number | null;\n}) {\n  if (fromInlineSize == null || toInlineSize == null) return null;\n\n  const startScale = getFileViewerFitWidthVisualScale({\n    liveInlineSize: fromInlineSize,\n    stageInlineSize: getFileViewerFitWidthContentInlineSize({\n      stageInlinePadding,\n      stageInlineSize,\n    }),\n    stageInlineSlope: stageBlockSlope,\n    targetInlineSize: toInlineSize,\n  });\n  if (!Number.isFinite(startScale) || Math.abs(1 - startScale) <= 0.001) {\n    return null;\n  }\n\n  return (\n    (probeScreenOffset + scrollTop - startScale * probeStageOffset) /\n    (1 - startScale)\n  );\n}\n\nfunction getFileViewerFitWidthSurfaceMotionTransform({\n  align,\n  anchorBlockProperty,\n  direction,\n  frame,\n  stageInlineSize,\n  stageOuterInlinePadding,\n  stageInlinePadding,\n  stageInlineSlope,\n  stageBlockSlope,\n}: {\n  align: FileViewerDocumentAlign;\n  anchorBlockProperty: string;\n  direction: FileViewerInlineDirection;\n  frame: FileViewerMotionFrame;\n  stageInlineSize: number;\n  stageOuterInlinePadding: number;\n  stageInlinePadding: number;\n  stageInlineSlope: number;\n  stageBlockSlope: number;\n}) {\n  if (\n    stageInlineSize <= 0 ||\n    frame.layoutInlineSize <= 0 ||\n    frame.toInlineSize <= 0\n  ) {\n    return \"\";\n  }\n\n  // Fit-width renderers size their stage as an affine function of the\n  // available width (stage = slope × width − constant padding), so the\n  // in-flight visual stage is the settled stage plus the scaled live width\n  // delta. At the first frame this resolves to exactly the pre-toggle stage\n  // size, and at the last frame to the settled stage — identity. Each axis\n  // carries its own slope: they differ when the stage's inline box holds\n  // constant padding while its block stack scales with the content.\n  const contentInlineSize = getFileViewerFitWidthContentInlineSize({\n    stageInlinePadding,\n    stageInlineSize,\n  });\n  const inlineScale = getFileViewerFitWidthVisualScale({\n    liveInlineSize: frame.layoutInlineSize,\n    stageInlineSize: contentInlineSize,\n    stageInlineSlope,\n    targetInlineSize: frame.toInlineSize,\n  });\n  const blockScale = getFileViewerFitWidthVisualScale({\n    liveInlineSize: frame.layoutInlineSize,\n    stageInlineSize: contentInlineSize,\n    stageInlineSlope: stageBlockSlope,\n    targetInlineSize: frame.toInlineSize,\n  });\n  const visualStageInlineSize =\n    inlineScale * contentInlineSize + stageInlinePadding;\n  const availableStageInlineSize = Math.max(\n    1,\n    frame.layoutInlineSize - Math.max(0, stageOuterInlinePadding),\n  );\n  const settledMargin = getFileViewerStageInlineMargin({\n    align,\n    availableInlineSize: availableStageInlineSize,\n    direction,\n    stageInlineSize,\n  });\n  const visualMargin = getFileViewerStageInlineMargin({\n    align,\n    availableInlineSize: availableStageInlineSize,\n    direction,\n    stageInlineSize: visualStageInlineSize,\n  });\n  // The padding is constant in both endpoint layouts. Scaling the outer stage\n  // would scale that inset too, making the visible page briefly too wide or\n  // narrow on the first frame. Rebase the symmetric start inset so the inner\n  // content edge, not the transparent wrapper edge, is pixel-continuous.\n  const inlinePaddingStart = stageInlinePadding / 2;\n  const translateX =\n    visualMargin - settledMargin + (1 - inlineScale) * inlinePaddingStart;\n\n  if (Math.abs(frame.layoutInlineSize - frame.toInlineSize) <= 0.001) {\n    return \"\";\n  }\n\n  const formattedInlineScale = formatFileViewerMotionScale(inlineScale);\n  const formattedBlockScale = formatFileViewerMotionScale(blockScale);\n  const formattedTranslateX = formatFileViewerMotionPixel(translateX);\n  // Scale about the stage origin; the anchor term keeps the reading line\n  // fixed on the block axis: y' = s·y + (1 − s)·anchor equals y at\n  // y = anchor.\n  const translateY = `calc((1 - ${formattedBlockScale}) * var(${anchorBlockProperty}, 0px))`;\n  const formattedScale =\n    formattedInlineScale === formattedBlockScale\n      ? formattedInlineScale\n      : `${formattedInlineScale}, ${formattedBlockScale}`;\n\n  return `translate3d(${formattedTranslateX}px, ${translateY}, 0) scale(${formattedScale})`;\n}\n\nfunction getFileViewerFitWidthContentInlineSize({\n  stageInlinePadding,\n  stageInlineSize,\n}: {\n  stageInlinePadding: number;\n  stageInlineSize: number;\n}) {\n  const padding = Number.isFinite(stageInlinePadding)\n    ? Math.max(0, stageInlinePadding)\n    : 0;\n  return Math.max(1, stageInlineSize - padding);\n}\n\n// Physical LEFT offset of the stage box inside the available inline size —\n// translateX shifts along the physical X axis, so the model must speak\n// physical-left in both directions. Stages align with physical auto margins\n// (mx-auto for center, ml-auto for end, plain flow for start), so:\n// - free space ≥ 0: center splits it; end pins right in both directions\n//   (ml-auto is physical); start follows flow (left in LTR, right in RTL).\n// - free space < 0 (the settled stage overflows the live container — the\n//   close leg's early frames): auto margins collapse to 0 and CSS resolves\n//   the over-constraint against the direction's end edge, pinning the box to\n//   the start edge — left edge at 0 in LTR, at the negative free space in\n//   RTL. The old unconditional max(0, …) clamp encoded only the LTR half and\n//   made the RTL close leg overshoot by the overflow amount.\nfunction getFileViewerStageInlineMargin({\n  align,\n  availableInlineSize,\n  direction,\n  stageInlineSize,\n}: {\n  align: FileViewerDocumentAlign;\n  availableInlineSize: number;\n  direction: FileViewerInlineDirection;\n  stageInlineSize: number;\n}) {\n  const freeInlineSize = availableInlineSize - stageInlineSize;\n  if (freeInlineSize < 0) return direction === \"rtl\" ? freeInlineSize : 0;\n\n  switch (align) {\n    case \"start\":\n      return direction === \"rtl\" ? freeInlineSize : 0;\n    case \"end\":\n      return freeInlineSize;\n    case \"center\":\n      return freeInlineSize / 2;\n  }\n}\n\nfunction formatFileViewerMotionPixel(value: number) {\n  return Number.isFinite(value) ? Number(value.toFixed(3)) : 0;\n}\n\nfunction formatFileViewerMotionScale(value: number) {\n  return Number.isFinite(value) ? String(Number(value.toFixed(6))) : \"1\";\n}\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-fit-width-motion.ts"
    }
  ],
  "type": "registry:ui"
}