shadimage
retab
uifile-viewersource: ok · refreshed

File Viewer

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.

npx shadcn add "https://ui.retab.com/r/file-viewer.json"
free

Visual style

terminal-mono

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
iconslucide-react
ui-primitives@radix-ui/react-slot@^1.1.1

Registry dependencies

@retab/utilsbutton@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/skeletonseparator@retab/spinner@retab/email-viewer

Files49 · source cached 10/8/2026, 10:21:13 AM

registry/new-york-v4/ui/file-viewer.tsx
"use client";

export type { ViewerSource } from "./file-viewer-core";
export {
  FileViewerContent,
  FileViewerInset,
  FileViewerLegend,
  FileViewerViewport,
  type FileViewerContentProps,
  type FileViewerInsetProps,
  type FileViewerLegendProps,
  type FileViewerViewportProps,
} from "./file-viewer-layout";
export {
  FileViewerSidebar,
  FileViewerSidebarContent,
  FileViewerSidebarFooter,
  FileViewerSidebarHeader,
  FileViewerSidebarRail,
  FileViewerSidebarSection,
  FileViewerSidebarSectionAction,
  FileViewerSidebarSectionContent,
  FileViewerSidebarSectionHeader,
  FileViewerSidebarSectionTitle,
  FileViewerSidebarSeparator,
  type FileViewerSidebarProps,
  type FileViewerSidebarContentProps,
  type FileViewerSidebarFooterProps,
  type FileViewerSidebarHeaderProps,
  type FileViewerSidebarRailProps,
  type FileViewerSidebarSectionActionProps,
  type FileViewerSidebarSectionContentProps,
  type FileViewerSidebarSectionHeaderProps,
  type FileViewerSidebarSectionProps,
  type FileViewerSidebarSectionTitleProps,
  type FileViewerSidebarSeparatorProps,
} from "./file-viewer-sidebar";
export {
  FileViewerDocument,
  type FileViewerDocumentProps,
} from "./file-viewer-document";
export {
  FileViewerControls,
  FileViewerHeader,
  FileViewerMeta,
  FileViewerSidebarTrigger,
  FileViewerTitle,
  type FileViewerControlsProps,
  type FileViewerHeaderProps,
  type FileViewerMetaProps,
  type FileViewerSidebarTriggerProps,
  type FileViewerTitleProps,
} from "./file-viewer-header";
export {
  FileViewerEmptyState,
  FileViewerErrorState,
  FileViewerLoadingState,
  FileViewerUnavailableState,
  FileViewerUnsupportedState,
  type FileViewerErrorStateProps,
  type FileViewerStateProps,
  type FileViewerUnsupportedStateProps,
} from "./file-viewer-state";
export { useFileViewerResource } from "./file-viewer-resource-state";
export {
  FileViewerPreview,
  type FileViewerPreviewProps,
} from "./file-viewer-preview";
export {
  useFileViewerSidebar,
  type FileViewerHeaderMode,
  type FileViewerSidebarValue,
} from "./file-viewer-context";
export {
  FileViewer,
  type FileViewerLayout,
  type FileViewerProps,
} from "./file-viewer-shell";
export {
  FileViewerProvider,
  type FileViewerProviderProps,
} from "./file-viewer-provider";
registry/new-york-v4/ui/file-viewer-accessibility.ts
"use client";

import type { FileViewerSidebarCollapsible } from "./file-viewer-context";
import type { FileViewerElements } from "./file-viewer-elements";

export type FileViewerSidebarAccessibilityProps = {
  "aria-hidden"?: true;
  inert?: true;
};

export type FileViewerSidebarTriggerAccessibilityProps = {
  "aria-controls"?: string;
  "aria-disabled"?: true;
  "aria-expanded"?: boolean;
  "aria-label": string;
};

const visibleSidebarAccessibilityProps = {};
const hiddenSidebarAccessibilityProps = {
  "aria-hidden": true,
  inert: true,
} as const;

export function resolveFileViewerSidebarAccessibilityProps({
  collapsible,
  deferInert,
  isSidebarInteractive,
}: {
  collapsible: FileViewerSidebarCollapsible;
  deferInert?: boolean;
  isSidebarInteractive: boolean;
}): FileViewerSidebarAccessibilityProps {
  if (collapsible === "none" || isSidebarInteractive) {
    return visibleSidebarAccessibilityProps;
  }

  if (deferInert) {
    return {
      "aria-hidden": true,
    };
  }

  return hiddenSidebarAccessibilityProps;
}

export function resolveFileViewerSidebarTriggerAccessibilityProps({
  ariaLabel,
  canToggleSidebar,
  isDisabled,
  isSidebarInteractive,
  sidebarId,
}: {
  ariaLabel: string;
  canToggleSidebar: boolean;
  isDisabled: boolean;
  isSidebarInteractive: boolean;
  sidebarId: string;
}): FileViewerSidebarTriggerAccessibilityProps {
  return {
    "aria-controls": canToggleSidebar ? sidebarId : undefined,
    "aria-disabled": isDisabled ? true : undefined,
    // Deliberately tracks interactivity, not the requested state: while the
    // panel slides open it is still inert, and announcing "expanded" would
    // point assistive tech at a region that cannot yet be used.
    "aria-expanded": canToggleSidebar ? isSidebarInteractive : undefined,
    "aria-label": ariaLabel,
  };
}

export function restoreFileViewerSidebarFocusOnClose({
  elements,
  isSidebarInteractive,
  previousIsSidebarInteractive,
}: {
  elements: FileViewerElements;
  isSidebarInteractive: boolean;
  previousIsSidebarInteractive: boolean;
}) {
  if (isSidebarInteractive || !previousIsSidebarInteractive) return;
  if (!elements.sidebarElement || !elements.sidebarTriggerElement) return;

  const ownerWindow = elements.sidebarElement.ownerDocument.defaultView;
  const activeElement = elements.sidebarElement.ownerDocument.activeElement;
  if (
    ownerWindow &&
    activeElement instanceof ownerWindow.HTMLElement &&
    elements.sidebarElement.contains(activeElement)
  ) {
    elements.sidebarTriggerElement.focus({ preventScroll: true });
  }
}
registry/new-york-v4/ui/file-viewer-layout.tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";

import {
  useOptionalFileViewerShellStatic,
  useFileViewerShellStatic,
} from "./file-viewer-context";
import {
  FileViewerDocumentFrameProvider,
  type FileViewerDocumentFrameState,
} from "./file-viewer-renderer-frame";
import { type FileViewerDocumentAlign } from "./file-viewer-renderer-contract";
import { useStableElementSize } from "./viewer-measurement";

const FileViewerContentContext = React.createContext(false);
const FileViewerInsetContext = React.createContext(false);
const FileViewerViewportContext = React.createContext(false);

export function useIsInsideFileViewerContent(): boolean {
  return React.useContext(FileViewerContentContext);
}

export function useIsInsideFileViewerInset(): boolean {
  return React.useContext(FileViewerInsetContext);
}

export function useIsInsideFileViewerViewport(): boolean {
  return React.useContext(FileViewerViewportContext);
}

export type FileViewerContentProps = React.ComponentProps<"div">;
export type FileViewerInsetProps = React.ComponentProps<"div"> & {
  align?: FileViewerDocumentAlign;
  documentFrameClassName?: string;
  documentFrameStyle?: React.CSSProperties;
  maxInlineSize?: React.CSSProperties["maxInlineSize"];
};
export type FileViewerLegendProps = React.ComponentProps<"div">;
export type FileViewerViewportProps = React.ComponentProps<"div">;

export function FileViewerContent({
  className,
  ...props
}: FileViewerContentProps) {
  const shell = useFileViewerShellStatic("FileViewerContent");

  return (
    <FileViewerContentContext.Provider value={true}>
      <div
        data-file-viewer-sidebar-mode={shell.mode}
        data-file-viewer-sidebar-side={shell.side}
        data-slot="file-viewer-content"
        className={cn(
          "relative flex min-h-0 min-w-0 flex-1 overflow-hidden",
          className,
        )}
        {...props}
      />
    </FileViewerContentContext.Provider>
  );
}

export function FileViewerLegend({
  className,
  ...props
}: FileViewerLegendProps) {
  return (
    <div
      data-slot="file-viewer-legend"
      className={cn("min-w-0 flex-shrink-0 border-b", className)}
      {...props}
    />
  );
}

export function FileViewerInset({
  align = "start",
  children,
  className,
  documentFrameClassName,
  documentFrameStyle,
  maxInlineSize,
  ...props
}: FileViewerInsetProps) {
  const shell = useOptionalFileViewerShellStatic();
  const hasAnimatedFileViewerWidth =
    shell?.mode === "inline" && shell.canToggleSidebar;
  const documentFrame = useMeasuredFileViewerDocumentFrame({
    enabled: !hasAnimatedFileViewerWidth,
  });
  const frame = React.useMemo<FileViewerDocumentFrameState>(
    () => ({
      align,
      element: documentFrame.element,
      inlineSize: documentFrame.inlineSize,
    }),
    [align, documentFrame.element, documentFrame.inlineSize],
  );

  return (
    <FileViewerInsetContext.Provider value={true}>
      <FileViewerDocumentFrameProvider value={frame}>
        <div
          data-slot="file-viewer-inset"
          className={cn(
            "relative flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
            className,
          )}
          {...props}
        >
          <div
            ref={documentFrame.setElement}
            data-slot="file-viewer-document-frame"
            className={cn(
              "[container-type:inline-size] relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col",
              getDocumentFrameAlignClass(align),
              documentFrameClassName,
            )}
            style={
              {
                maxInlineSize,
                ...documentFrameStyle,
              } as React.CSSProperties
            }
          >
            {children}
          </div>
        </div>
      </FileViewerDocumentFrameProvider>
    </FileViewerInsetContext.Provider>
  );
}

export const FileViewerViewport = React.forwardRef<
  HTMLDivElement,
  FileViewerViewportProps
>(function FileViewerViewport({ className, style, ...props }, ref) {
  const isInsideInset = useIsInsideFileViewerInset();
  const setElement = React.useCallback(
    (element: HTMLDivElement | null) => {
      if (typeof ref === "function") {
        ref(element);
      } else if (ref) {
        ref.current = element;
      }
    },
    [ref],
  );

  if (process.env.NODE_ENV !== "production" && !isInsideInset) {
    throw new Error(
      "FileViewerViewport must be rendered inside FileViewerInset.",
    );
  }

  return (
    <FileViewerViewportContext.Provider value={true}>
      <div
        ref={setElement}
        data-slot="file-viewer-viewport"
        className={cn(
          "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
          className,
        )}
        style={
          {
            ...style,
          } as React.CSSProperties
        }
        {...props}
      />
    </FileViewerViewportContext.Provider>
  );
});

function useMeasuredFileViewerDocumentFrame({ enabled }: { enabled: boolean }) {
  const size = useStableElementSize<HTMLDivElement>({
    enabled,
  });

  return React.useMemo(
    () => ({
      element: size.element,
      inlineSize: size.width,
      setElement: size.setElement,
    }),
    [size.element, size.setElement, size.width],
  );
}

function getDocumentFrameAlignClass(align: FileViewerDocumentAlign) {
  switch (align) {
    case "center":
      return "mx-auto";
    case "end":
      return "ml-auto";
    case "start":
      return "mr-auto";
  }
}
registry/new-york-v4/ui/file-viewer-context.tsx
"use client";

import * as React from "react";

import type { FileCategory } from "@/lib/viewer-source";
import type { FileViewerElementRegistry } from "./file-viewer-elements";
import type { FileViewerMotionKernel } from "./file-viewer-motion-kernel";

export type FileViewerSidebarMode = "inline" | "overlay";
export type FileViewerSidebarRequestedMode = "auto" | FileViewerSidebarMode;
export type FileViewerSidebarState = "expanded" | "collapsed";
export type FileViewerSidebarSide = "left" | "right";
export type FileViewerSidebarCollapsible = "offcanvas" | "none";

export type FileViewerHeaderMode = "inline" | "outlets";

export const DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH = "10rem";

export type FileViewerSetSidebarOpen = (
  value: boolean | ((isSidebarOpen: boolean) => boolean),
) => void;

export type FileViewerSidebarOpenProps = {
  defaultOpen?: boolean;
  onOpenChange?: (open: boolean) => void;
  open?: boolean;
};

export type FileViewerContextValue = {
  headerMode: FileViewerHeaderMode;
  hasHeaderOutlets: boolean;
  isInsideFileViewer: boolean;
  resourceCategory: FileCategory;
  sidebarOpenProps: FileViewerSidebarOpenProps;
};

export type FileViewerSidebarRegistration = {
  collapsible: FileViewerSidebarCollapsible;
  id: string;
  side: FileViewerSidebarSide;
  width: string;
  widthPixels: number;
};

export type FileViewerSidebarValue = {
  canToggleSidebar: boolean;
  isSidebarInteractive: boolean;
  isSidebarOpen: boolean;
  mode: FileViewerSidebarMode;
  side: FileViewerSidebarSide;
  sidebarId: string;
  sidebarState: FileViewerSidebarState;
  setSidebarOpen: FileViewerSetSidebarOpen;
  toggleSidebar: () => void;
};

export type FileViewerShellStaticContextValue = {
  canToggleSidebar: boolean;
  collapsible: FileViewerSidebarCollapsible;
  elementRegistry: FileViewerElementRegistry;
  mode: FileViewerSidebarMode;
  motionDurationMs: number;
  motionKernel: FileViewerMotionKernel;
  registerSidebar: (registration: FileViewerSidebarRegistration) => () => void;
  rootId: string;
  setSidebarOpen: FileViewerSetSidebarOpen;
  side: FileViewerSidebarSide;
  sidebarId: string;
  sidebarWidth: string;
  toggleSidebar: () => void;
};

export type FileViewerSidebarDynamicContextValue = {
  isSidebarInteractive: boolean;
  isSidebarOpen: boolean;
  isSidebarTransitioning: boolean;
  sidebarState: FileViewerSidebarState;
};

export const FileViewerContext = React.createContext<FileViewerContextValue>({
  headerMode: "inline",
  hasHeaderOutlets: false,
  isInsideFileViewer: false,
  resourceCategory: "unsupported",
  sidebarOpenProps: {},
});

export const FileViewerShellStaticContext =
  React.createContext<FileViewerShellStaticContextValue | null>(null);

export const FileViewerSidebarDynamicContext =
  React.createContext<FileViewerSidebarDynamicContextValue | null>(null);

export function useFileViewerContext() {
  return React.useContext(FileViewerContext);
}

export function useOptionalFileViewerShellStatic() {
  return React.useContext(FileViewerShellStaticContext);
}

export function useFileViewerShellStatic(consumer: string) {
  const context = React.useContext(FileViewerShellStaticContext);
  if (!context) {
    throw new Error(`${consumer} must be rendered inside FileViewer.`);
  }
  return context;
}

export function useOptionalFileViewerShell() {
  const staticContext = React.useContext(FileViewerShellStaticContext);
  const sidebarContext = React.useContext(FileViewerSidebarDynamicContext);

  return React.useMemo(
    () =>
      staticContext && sidebarContext
        ? { ...staticContext, ...sidebarContext }
        : null,
    [sidebarContext, staticContext],
  );
}

export function useFileViewerShell(consumer: string) {
  const context = useOptionalFileViewerShell();
  if (!context) {
    throw new Error(`${consumer} must be rendered inside FileViewer.`);
  }
  return context;
}

export function useFileViewerSidebar(): FileViewerSidebarValue {
  const fileViewerContext = React.useContext(FileViewerContext);
  const shellContext = useOptionalFileViewerShell();

  if (!fileViewerContext.isInsideFileViewer || !shellContext) {
    throw new Error("useFileViewerSidebar must be used within FileViewer.");
  }

  return shellContext;
}
registry/new-york-v4/ui/file-viewer-core.ts
import {
  detectCategory,
  extensionOf,
  extractName,
  resolveViewerDescriptor,
  textPayloadKey,
  type FileCategory,
  type ViewerDescriptor,
  type ViewerSource,
} from "@/lib/viewer-source";

export type { FileCategory, ViewerSource };
export type FileViewerFallbackSize = { width: number; height: number };

export interface FileViewerCoreProps {
  source: ViewerSource;
  category?: FileCategory;
  className?: string;
  /** Intrinsic first-frame size for image/TIFF loading skeletons. */
  fallbackFrameSize?: FileViewerFallbackSize;
  /** Intrinsic first-slide size for PPTX loading skeletons. */
  fallbackSlideSize?: FileViewerFallbackSize;
  isolateStyles?: boolean;
}

export type FileDescriptor = ViewerDescriptor;

export function resolveFileDescriptor({
  source,
  category,
}: FileViewerCoreProps): FileDescriptor {
  return resolveViewerDescriptor({
    source,
    category,
  });
}

export function descriptorResetKey(descriptor: FileDescriptor): string {
  return [
    descriptorIdentityResetKey(descriptor),
    descriptor.displayName,
    descriptor.mimeType ?? "",
    descriptor.category,
  ].join("\u0000");
}

function descriptorIdentityResetKey(descriptor: FileDescriptor): string {
  if (
    descriptor.source.kind === "text" &&
    descriptor.source.identityKey == null
  ) {
    return textPayloadKey(descriptor.source.text);
  }
  return descriptor.identityKey;
}

export function isProseTextDescriptor(descriptor: FileDescriptor): boolean {
  if (descriptor.category !== "text") return false;

  const extension = extensionOf(descriptor.fileName);
  if (extension === "txt" || extension === "text") return true;
  return !extension && descriptor.mimeType?.toLowerCase() === "text/plain";
}

export { detectCategory, extensionOf, extractName };
registry/new-york-v4/ui/file-viewer-document.tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";

import { FileErrorBoundary, ViewerFallback } from "./file-viewer-fallback";
import { useFileViewerRequiredResourceState } from "./file-viewer-resource-state";
import { FileViewerRoute } from "./file-viewer-route";
import { useIsInsideFileViewerViewport } from "./file-viewer-layout";

export type FileViewerDocumentProps = {
  className?: string;
  controls?: boolean;
};

export function FileViewerDocument({
  className,
  controls = false,
}: FileViewerDocumentProps) {
  const isInsideViewport = useIsInsideFileViewerViewport();

  if (process.env.NODE_ENV !== "production" && !isInsideViewport) {
    throw new Error(
      "FileViewerDocument must be rendered inside FileViewerViewport.",
    );
  }

  return (
    <FileViewerDocumentContent
      className={cn("h-full", className)}
      controls={controls}
    />
  );
}

function FileViewerDocumentContent({
  className,
  controls,
}: Required<Pick<FileViewerDocumentProps, "controls">> &
  Pick<FileViewerDocumentProps, "className">) {
  const {
    descriptor,
    descriptorKey,
    descriptorSignal,
    fallbackFrameSize,
    fallbackSlideSize,
    isClient,
    isolateStyles,
    resource,
  } = useFileViewerRequiredResourceState();
  const fallback = (
    <ViewerFallback
      resource={resource}
      className={className}
      bare
      controls={controls}
      fallbackFrameSize={fallbackFrameSize}
      fallbackSlideSize={fallbackSlideSize}
    />
  );

  if (!isClient) return fallback;

  return (
    <FileErrorBoundary
      key={descriptorKey}
      descriptor={descriptor}
      resource={resource}
      className={className}
      resetKey={descriptorKey}
      showDownload={controls}
    >
      <React.Suspense fallback={fallback}>
        <FileViewerRoute
          className={className}
          controls={controls}
          descriptor={descriptor}
          descriptorSignal={descriptorSignal}
          fallbackFrameSize={fallbackFrameSize}
          fallbackSlideSize={fallbackSlideSize}
          frame="none"
          isolateStyles={isolateStyles}
          resource={resource}
        />
      </React.Suspense>
    </FileErrorBoundary>
  );
}
registry/new-york-v4/ui/file-viewer-elements.ts
"use client";

import type {
  FileViewerDocumentSurface,
  FileViewerMotionKernel,
} from "./file-viewer-motion-kernel";
import type { FileViewerMotionFrame } from "./file-viewer-motion-plan";

export const FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT =
  "file-viewer:before-layout-motion";

// The kernel dispatches the before-layout-motion event as a CustomEvent whose
// detail is its LIVE interactive frame, so renderers can capture pre-commit
// anchors against what is actually on screen (a mid-flight retarget sees the
// settled layout plus the in-flight transform).
export function readFileViewerBeforeLayoutMotionFrame(
  event: Event,
): FileViewerMotionFrame | null {
  if (!(event instanceof CustomEvent)) return null;
  const detail: unknown = event.detail;
  return detail != null && typeof detail === "object"
    ? (detail as FileViewerMotionFrame)
    : null;
}

export type FileViewerElements = {
  documentSurfaceElement: HTMLElement | null;
  getDocumentSurfaceMotionProbeElement: (() => HTMLElement | null) | null;
  sidebarElement: HTMLElement | null;
  sidebarGapElement: HTMLDivElement | null;
  sidebarTriggerElement: HTMLElement | null;
  viewerShellElement: HTMLDivElement | null;
};

export type FileViewerElementRegistry = {
  getElements: () => FileViewerElements;
  registerDocumentSurface: (surface: FileViewerDocumentSurface) => () => void;
  registerSidebarElement: (element: HTMLElement | null) => void;
  registerSidebarGapElement: (element: HTMLDivElement | null) => void;
  registerSidebarTriggerElement: (element: HTMLElement | null) => void;
  registerViewerShellElement: (element: HTMLDivElement | null) => void;
};

export function createFileViewerElementRegistry({
  motionKernel,
  onViewerShellElementChange,
}: {
  motionKernel: FileViewerMotionKernel;
  onViewerShellElementChange: (element: HTMLDivElement | null) => void;
}): FileViewerElementRegistry {
  const elements: FileViewerElements = {
    documentSurfaceElement: null,
    getDocumentSurfaceMotionProbeElement: null,
    sidebarElement: null,
    sidebarGapElement: null,
    sidebarTriggerElement: null,
    viewerShellElement: null,
  };
  let documentSurfaceRegistration = 0;

  return {
    getElements: () => elements,
    registerDocumentSurface: (surface) => {
      documentSurfaceRegistration += 1;
      const registration = documentSurfaceRegistration;
      elements.documentSurfaceElement = surface.element;
      elements.getDocumentSurfaceMotionProbeElement =
        surface.getMotionProbeElement ?? null;
      motionKernel.setDocumentSurface(surface);
      return () => {
        if (documentSurfaceRegistration !== registration) return;
        elements.documentSurfaceElement = null;
        elements.getDocumentSurfaceMotionProbeElement = null;
        motionKernel.setDocumentSurface(null);
      };
    },
    registerSidebarElement: (element) => {
      if (elements.sidebarElement === element) return;
      elements.sidebarElement = element;
    },
    registerSidebarGapElement: (element) => {
      if (elements.sidebarGapElement === element) return;
      elements.sidebarGapElement = element;
      motionKernel.setSidebarGapElement(element);
    },
    registerSidebarTriggerElement: (element) => {
      elements.sidebarTriggerElement = element;
    },
    registerViewerShellElement: (element) => {
      if (elements.viewerShellElement === element) return;
      elements.viewerShellElement = element;
      onViewerShellElementChange(element);
    },
  };
}
registry/new-york-v4/ui/file-viewer-fallback.tsx
"use client";

import * as React from "react";

import {
  fileViewerDiagnostic,
  summarizeViewerDescriptor,
  summarizeViewerError,
  summarizeViewerResource,
} from "@/lib/pdf-viewer-diagnostics";
import { cn } from "@/lib/utils";
import type { ViewerResource } from "@/lib/viewer-resource";
import { Skeleton } from "@/components/ui/skeleton";

import { CodeViewerFallback } from "./code-viewer-chrome";
import { DocxViewerFallback } from "./docx-viewer-chrome";
import {
  isProseTextDescriptor,
  type FileDescriptor,
  type FileViewerFallbackSize,
} from "./file-viewer-core";
import {
  FileViewerErrorState,
  FileViewerLoadingState,
  FileViewerUnsupportedState,
} from "./file-viewer-state";
import { ImageViewerFallback } from "./image-viewer-chrome";
import { PdfViewerFallback } from "./pdf-viewer-states";
import { PptxViewerFallback } from "./pptx-viewer-fallback";
import { TextViewerFallback } from "./text-viewer-chrome";
import { ViewerControlsSkeleton } from "./viewer-controls";
import { XlsxViewerFallback } from "./xlsx-viewer-chrome";

export function UnsupportedCard({
  resource,
  className,
  bare,
  message,
  showDownload = true,
}: {
  resource: ViewerResource;
  className?: string;
  bare?: boolean;
  message?: string;
  showDownload?: boolean;
}) {
  return (
    <FileViewerUnsupportedState
      className={cn(
        bare ? "bg-muted/20 h-full" : "bg-muted/30 min-h-64 rounded-xl border",
        className,
      )}
      description={
        message ?? `No preview is available for ${resource.fileName}.`
      }
      download={resource.originalDownload}
      fileName={resource.fileName}
      showDownload={showDownload}
    />
  );
}

export function ViewerFallback({
  resource,
  className,
  bare = false,
  controls = true,
  fallbackFrameSize,
  fallbackSlideSize,
}: {
  resource: ViewerResource;
  className?: string;
  bare?: boolean;
  controls?: boolean;
  fallbackFrameSize?: FileViewerFallbackSize;
  fallbackSlideSize?: FileViewerFallbackSize;
}) {
  const descriptor = resource.descriptor;
  const category = descriptor.category;

  // Render the exact per-type skeleton each viewer shows while it parses, so the
  // SSR + chunk-loading paint is identical to the in-viewer loading state (same
  // controls row, same body) — no controls popping in and no geometry shift as one
  // skeleton hands off to the next.
  switch (category) {
    case "pdf":
      return (
        <PdfViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      );
    case "docx":
      return (
        <DocxViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      );
    case "pptx":
      return (
        <PptxViewerFallback
          bare={bare}
          className={className}
          controls={controls}
          fallbackSlideSize={fallbackSlideSize}
        />
      );
    case "image":
      return (
        <ImageViewerFallback
          bare={bare}
          className={className}
          controls={controls}
          fallbackFrameSize={fallbackFrameSize}
        />
      );
    case "xlsx":
      return (
        <XlsxViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      );
    case "markdown":
      return (
        <TextViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      );
    case "text":
      // The "text" category fans out to a prose viewer or a code viewer; match
      // whichever the route will pick so the body skeleton (gutter vs none) lines up.
      return isProseTextDescriptor(descriptor) ? (
        <TextViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      ) : (
        <CodeViewerFallback
          bare={bare}
          className={className}
          controls={controls}
        />
      );
    case "csv":
      return (
        <TextFamilyFallbackFrame bare={bare} className={className}>
          {controls ? (
            <ViewerControlsSkeleton title subtitle zoom download />
          ) : null}
          <TableBodySkeleton />
        </TextFamilyFallbackFrame>
      );
    case "html":
      return (
        <TextFamilyFallbackFrame bare={bare} className={className}>
          {controls ? <ViewerControlsSkeleton zoom /> : null}
          <div className="bg-card min-h-0 flex-1 p-4">
            <Skeleton className="size-full rounded-md" />
          </div>
        </TextFamilyFallbackFrame>
      );
  }

  // Unsupported / unknown categories: a neutral page-sheet placeholder.
  return (
    <FileViewerLoadingState
      description={null}
      icon={null}
      title={null}
      className={cn(
        "flex min-h-0 flex-col items-stretch justify-start gap-0 overflow-hidden p-0 text-left",
        bare ? "bg-muted/20 h-full" : "bg-muted/20 min-h-64",
        className,
      )}
    >
      <div
        data-slot="file-viewer-document-fallback"
        className="min-h-0 flex-1 overflow-hidden"
      >
        <div className="flex flex-col items-center p-4">
          <Skeleton
            aria-hidden
            className="ring-border w-full rounded-none shadow-sm ring-1"
            style={{ aspectRatio: "8.5 / 11" }}
          />
        </div>
      </div>
    </FileViewerLoadingState>
  );
}

function TextFamilyFallbackFrame({
  bare,
  className,
  children,
}: {
  bare?: boolean;
  className?: string;
  children: React.ReactNode;
}) {
  return (
    <div
      data-slot="file-viewer-document-fallback"
      className={cn(
        "bg-card flex min-h-0 flex-1 flex-col overflow-hidden",
        bare ? "h-full" : "min-h-64",
        className,
      )}
    >
      {children}
    </div>
  );
}

function TableBodySkeleton() {
  const gutter = 52;
  const colWidth = 150;
  const cols = 6;
  const rows = 14;
  const widths = [70, 45, 88, 56, 62, 78];
  return (
    <div
      aria-hidden
      className="bg-card flex min-h-0 flex-1 flex-col overflow-hidden text-sm"
    >
      <div className="bg-muted/60 flex border-b">
        <div
          className="shrink-0 border-r"
          style={{ width: gutter, height: 33 }}
        />
        {Array.from({ length: cols }, (_, c) => (
          <div
            key={c}
            className="flex shrink-0 items-center border-r px-3"
            style={{ width: colWidth, height: 33 }}
          >
            <Skeleton className="h-3 w-16" />
          </div>
        ))}
      </div>
      <div className="min-h-0 flex-1 overflow-hidden">
        {Array.from({ length: rows }, (_, r) => (
          <div key={r} className="flex border-b" style={{ height: 33 }}>
            <div
              className="flex shrink-0 items-center justify-end border-r px-2"
              style={{ width: gutter }}
            >
              <Skeleton className="h-3 w-4" />
            </div>
            {Array.from({ length: cols }, (_, c) => (
              <div
                key={c}
                className="flex shrink-0 items-center border-r px-3"
                style={{ width: colWidth }}
              >
                <Skeleton
                  className="h-3"
                  style={{ width: `${widths[(r + c) % widths.length]}%` }}
                />
              </div>
            ))}
          </div>
        ))}
      </div>
    </div>
  );
}

export class FileErrorBoundary extends React.Component<
  {
    children: React.ReactNode;
    descriptor: FileDescriptor;
    resource: ViewerResource;
    className?: string;
    resetKey?: unknown;
    showDownload?: boolean;
  },
  { error: unknown | null }
> {
  state: Readonly<{ error: unknown | null }> = { error: null };

  componentDidUpdate(prev: { resetKey?: unknown }) {
    if (prev.resetKey !== this.props.resetKey && this.state.error) {
      fileViewerDiagnostic("debug", "file_viewer_error_boundary_reset", {
        descriptor: summarizeViewerDescriptor(this.props.descriptor),
        resource: summarizeViewerResource(this.props.resource),
      });
      this.setState({ error: null });
    }
  }

  static getDerivedStateFromError(error: unknown) {
    return { error };
  }

  componentDidCatch(error: unknown) {
    fileViewerDiagnostic("error", "file_viewer_error_boundary_caught", {
      descriptor: summarizeViewerDescriptor(this.props.descriptor),
      resource: summarizeViewerResource(this.props.resource),
      error: summarizeViewerError(error),
    });
  }

  render() {
    if (this.state.error != null) {
      return (
        <FileViewerErrorState
          error={this.state.error}
          format="file"
          sourceKind={this.props.resource.sourceKind}
          download={
            this.props.showDownload === false
              ? null
              : this.props.resource.originalDownload
          }
          className={this.props.className}
        />
      );
    }
    return this.props.children;
  }
}
registry/new-york-v4/ui/file-viewer-shell.tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";

import {
  FileViewerShellStaticContext,
  FileViewerSidebarDynamicContext,
  type FileViewerSidebarRequestedMode,
} from "./file-viewer-context";
import {
  FILE_VIEWER_INLINE_BREAKPOINT,
  useFileViewerShellController,
} from "./file-viewer-shell-controller";

export type FileViewerLayout = "fill" | "intrinsic";

export type FileViewerProps = React.ComponentProps<"div"> & {
  inlineBreakpoint?: number;
  layout?: FileViewerLayout;
  sidebarMode?: FileViewerSidebarRequestedMode;
};

export function FileViewer({
  children,
  className,
  inlineBreakpoint = FILE_VIEWER_INLINE_BREAKPOINT,
  layout = "fill",
  sidebarMode = "auto",
  style,
  ...props
}: FileViewerProps) {
  const controller = useFileViewerShellController({
    inlineBreakpoint,
    sidebarMode,
  });

  return (
    <FileViewerShellStaticContext.Provider
      value={controller.shellStaticContext}
    >
      <FileViewerSidebarDynamicContext.Provider
        value={controller.sidebarDynamicContext}
      >
        <div
          ref={controller.setRootElement}
          data-file-viewer="root"
          data-file-viewer-header-mode={controller.headerMode}
          data-file-viewer-layout={layout}
          data-file-viewer-resource-category={controller.resourceCategory}
          data-file-viewer-sidebar-collapsible={controller.effectiveCollapsible}
          data-file-viewer-sidebar-mode={controller.mode}
          data-file-viewer-sidebar-open={
            controller.isSidebarOpen ? "true" : "false"
          }
          data-file-viewer-sidebar-side={controller.side}
          data-file-viewer-sidebar-state={controller.sidebarState}
          data-slot="file-viewer-root"
          id={controller.rootId}
          className={cn(
            "relative flex min-h-0 min-w-0 flex-col overflow-hidden",
            layout === "fill" && "h-full w-full flex-1",
            className,
          )}
          style={
            {
              overflowAnchor: "none",
              ...style,
            } as React.CSSProperties
          }
          {...props}
        >
          {children}
        </div>
      </FileViewerSidebarDynamicContext.Provider>
    </FileViewerShellStaticContext.Provider>
  );
}
registry/new-york-v4/ui/file-viewer-shell-controller.ts
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

import { restoreFileViewerSidebarFocusOnClose } from "./file-viewer-accessibility";
import {
  DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,
  useFileViewerContext,
  type FileViewerContextValue,
  type FileViewerSetSidebarOpen,
  type FileViewerShellStaticContextValue,
  type FileViewerSidebarCollapsible,
  type FileViewerSidebarDynamicContextValue,
  type FileViewerSidebarMode,
  type FileViewerSidebarOpenProps,
  type FileViewerSidebarRegistration,
  type FileViewerSidebarRequestedMode,
  type FileViewerSidebarSide,
  type FileViewerSidebarState,
} from "./file-viewer-context";
import { createFileViewerElementRegistry } from "./file-viewer-elements";
import { useFileViewerShellKeyboard } from "./file-viewer-shell-keyboard";
import {
  createFileViewerMotionKernel,
  useFileViewerMotionFrame,
} from "./file-viewer-motion-kernel";
import { useFileViewerMotionTelemetry } from "./viewer-motion-telemetry";
import {
  FILE_VIEWER_MOTION_DURATION_MS,
  type FileViewerMotionTarget,
} from "./file-viewer-motion-plan";
import {
  readElementRectSnapshot,
  useStableElementSize,
} from "./viewer-measurement";

export const FILE_VIEWER_INLINE_BREAKPOINT = 768;

type FileViewerSidebarOpenController = {
  getSidebarOpen: () => boolean;
  isSidebarOpen: boolean;
  setSidebarOpen: (isSidebarOpen: boolean) => void;
};

// Bridges the controlled/uncontrolled `open` prop pair to real React state, so
// every toggle re-renders the tree and the declarative writers (data
// attributes, inert/aria, overlay translate classes) stay current.
function useFileViewerSidebarOpenController({
  collapsible,
  defaultOpen,
  onOpenChange,
  open,
}: FileViewerSidebarOpenProps & {
  collapsible: FileViewerSidebarCollapsible;
}): FileViewerSidebarOpenController {
  const isControlled = open !== undefined;
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
    defaultOpen ?? false,
  );
  const isSidebarOpen =
    collapsible === "none" ? true : (open ?? uncontrolledOpen);
  const openRef = React.useRef(isSidebarOpen);
  // Not an inline render-time mirror: setSidebarOpen writes this ref eagerly
  // before a controlled parent applies the prop, and the kernel's flushSync
  // re-renders mid-click with the stale prop value. Assign only when the
  // committed value actually changes so the eager write survives that window.
  useKeyedLayoutEffect(
    joinEffectKey(["file-viewer-open-mirror", isSidebarOpen]),
    () => {
      openRef.current = isSidebarOpen;
    },
  );

  const getSidebarOpen = React.useCallback(() => openRef.current, []);

  const setSidebarOpen = React.useCallback(
    (nextIsSidebarOpen: boolean) => {
      openRef.current = nextIsSidebarOpen;
      if (!isControlled) {
        setUncontrolledOpen(nextIsSidebarOpen);
      }
      onOpenChange?.(nextIsSidebarOpen);
    },
    [isControlled, onOpenChange],
  );

  return React.useMemo(
    () => ({
      getSidebarOpen,
      isSidebarOpen,
      setSidebarOpen,
    }),
    [getSidebarOpen, isSidebarOpen, setSidebarOpen],
  );
}

type FileViewerSidebarRegistrationController = {
  canToggleSidebar: boolean;
  effectiveCollapsible: FileViewerSidebarCollapsible;
  registerSidebar: (registration: FileViewerSidebarRegistration) => () => void;
  side: FileViewerSidebarSide;
  sidebarId: string;
  sidebarWidth: string;
  sidebarWidthPixels: number;
};

// The mounted FileViewerSidebar is the single source of sidebar configuration
// (collapsible, side, width); the shell only holds neutral defaults until one
// registers. No sidebar registered means nothing to toggle and no motion, so
// no fallback width measurement is needed.
function useFileViewerSidebarRegistration({
  fallbackSidebarId,
}: {
  fallbackSidebarId: string;
}): FileViewerSidebarRegistrationController {
  const [sidebarRegistration, setSidebarRegistration] =
    React.useState<FileViewerSidebarRegistration | null>(null);
  const effectiveCollapsible = sidebarRegistration?.collapsible ?? "offcanvas";
  const side = sidebarRegistration?.side ?? "left";
  const sidebarId = sidebarRegistration?.id ?? fallbackSidebarId;
  const sidebarWidth =
    sidebarRegistration?.width ?? DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH;
  const sidebarWidthPixels = sidebarRegistration?.widthPixels ?? 0;
  const canToggleSidebar =
    sidebarRegistration !== null && effectiveCollapsible !== "none";

  const registerSidebar = React.useCallback(
    (registration: FileViewerSidebarRegistration) => {
      setSidebarRegistration(registration);
      return () => {
        setSidebarRegistration((current) =>
          current?.id === registration.id ? null : current,
        );
      };
    },
    [],
  );

  return React.useMemo(
    () => ({
      canToggleSidebar,
      effectiveCollapsible,
      registerSidebar,
      side,
      sidebarId,
      sidebarWidth,
      sidebarWidthPixels,
    }),
    [
      canToggleSidebar,
      effectiveCollapsible,
      registerSidebar,
      side,
      sidebarId,
      sidebarWidth,
      sidebarWidthPixels,
    ],
  );
}

type FileViewerShellControllerOptions = {
  inlineBreakpoint: number;
  sidebarMode: FileViewerSidebarRequestedMode;
};

export type FileViewerShellController = {
  effectiveCollapsible: FileViewerSidebarCollapsible;
  headerMode: FileViewerContextValue["headerMode"];
  isSidebarOpen: boolean;
  mode: FileViewerSidebarMode;
  resourceCategory: FileViewerContextValue["resourceCategory"];
  rootId: string;
  setRootElement: (element: HTMLDivElement | null) => void;
  shellStaticContext: FileViewerShellStaticContextValue;
  side: FileViewerSidebarSide;
  sidebarDynamicContext: FileViewerSidebarDynamicContextValue;
  sidebarState: FileViewerSidebarState;
};

export function useFileViewerShellController({
  inlineBreakpoint,
  sidebarMode,
}: FileViewerShellControllerOptions): FileViewerShellController {
  const context = useFileViewerContext();

  if (!context.isInsideFileViewer) {
    throw new Error("FileViewer must be rendered within FileViewerProvider.");
  }

  const rootId = React.useId();
  const fallbackSidebarId = `${rootId}-sidebar`;
  // The kernel owns time + style writes only; its settle-hold layout reads are
  // injected from the quarantined measurement module here at the controller
  // layer.
  const motionKernel = React.useMemo(
    () => createFileViewerMotionKernel({ readElementRectSnapshot }),
    [],
  );
  const size = useStableElementSize<HTMLDivElement>({
    retainLastNonZero: true,
  });
  const elementRegistry = React.useMemo(
    () =>
      createFileViewerElementRegistry({
        motionKernel,
        onViewerShellElementChange: size.setElement,
      }),
    [motionKernel, size.setElement],
  );
  const {
    canToggleSidebar,
    effectiveCollapsible,
    registerSidebar,
    side,
    sidebarId,
    sidebarWidth,
    sidebarWidthPixels,
  } = useFileViewerSidebarRegistration({
    fallbackSidebarId,
  });
  const openController = useFileViewerSidebarOpenController({
    ...context.sidebarOpenProps,
    collapsible: effectiveCollapsible,
  });
  const isSidebarOpen = openController.isSidebarOpen;
  const motionFrame = useFileViewerMotionFrame(motionKernel);
  const isSidebarTransitioning = motionFrame.phase === "sliding";
  const mode = resolveFileViewerSidebarMode({
    inlineBreakpoint,
    requestedMode: sidebarMode,
    width: size.width,
  });
  const isSidebarInteractive = resolveFileViewerSidebarInteractive({
    canToggleSidebar,
    collapsible: effectiveCollapsible,
    isSidebarOpen,
    isSidebarTransitioning,
    mode,
  });
  const sidebarState: FileViewerSidebarState = isSidebarOpen
    ? "expanded"
    : "collapsed";
  const motionTarget = React.useMemo<FileViewerMotionTarget>(
    () => ({
      shellInlineSize: size.width ?? 0,
      durationMs: FILE_VIEWER_MOTION_DURATION_MS,
      mode,
      open: isSidebarOpen,
      side,
      sidebarWidth: sidebarWidthPixels,
    }),
    [isSidebarOpen, mode, side, sidebarWidthPixels, size.width],
  );
  const motionTargetRef = React.useRef(motionTarget);
  // Same two-writer shape as openRef above: setSidebarOpen stores the eager
  // nextTarget before React commits, so only adopt the rendered target when
  // it is a genuinely new object.
  useKeyedLayoutEffect(
    joinEffectKey(["file-viewer-motion-target-mirror", motionTarget]),
    () => {
      motionTargetRef.current = motionTarget;
    },
  );

  const setRootElement = React.useCallback(
    (element: HTMLDivElement | null) => {
      elementRegistry.registerViewerShellElement(element);
    },
    [elementRegistry],
  );

  // Adopt idle geometry (mount, resizes, mode flips) without animating. While
  // a motion is in flight the kernel owns the target — EXCEPT on a mode flip:
  // React renders the new mode immediately, so the kernel must be told (it
  // snaps the motion) or an inline slide keeps writing gap widths against
  // overlay DOM until settle.
  useKeyedLayoutEffect(
    joinEffectKey([
      "file-viewer-sync-target",
      motionFrame.mode,
      motionFrame.phase,
      motionKernel,
      motionTarget,
    ]),
    () => {
      if (
        motionFrame.phase !== "idle" &&
        motionFrame.mode === motionTarget.mode
      ) {
        return;
      }
      motionKernel.syncTarget(motionTarget);
    },
  );

  // Single owner of focus restoration: when the sidebar stops being
  // interactive while focus is inside it, hand focus back to the trigger.
  const previousIsSidebarInteractiveRef = React.useRef(isSidebarInteractive);
  useKeyedLayoutEffect(
    joinEffectKey([
      "file-viewer-sidebar-focus-restore",
      elementRegistry,
      isSidebarInteractive,
    ]),
    () => {
      restoreFileViewerSidebarFocusOnClose({
        elements: elementRegistry.getElements(),
        isSidebarInteractive,
        previousIsSidebarInteractive: previousIsSidebarInteractiveRef.current,
      });
      previousIsSidebarInteractiveRef.current = isSidebarInteractive;
    },
  );

  const setSidebarOpen = React.useCallback<FileViewerSetSidebarOpen>(
    (value) => {
      // Toggling requires a registered, collapsible sidebar; a non-collapsible
      // sidebar is pinned open and an unregistered one has nothing to move.
      if (!canToggleSidebar) return;

      const currentOpen = openController.getSidebarOpen();
      const nextOpen = typeof value === "function" ? value(currentOpen) : value;
      if (nextOpen === currentOpen) return;

      openController.setSidebarOpen(nextOpen);
      const nextTarget: FileViewerMotionTarget = {
        ...motionTargetRef.current,
        open: nextOpen,
      };
      motionTargetRef.current = nextTarget;
      motionKernel.startMotion(nextTarget);
    },
    [canToggleSidebar, motionKernel, openController],
  );
  const toggleSidebar = React.useCallback(
    () => setSidebarOpen((open) => !open),
    [setSidebarOpen],
  );
  const closeSidebar = React.useCallback(
    () => setSidebarOpen(false),
    [setSidebarOpen],
  );

  useFileViewerShellKeyboard({
    canToggleSidebar,
    closeSidebar,
    elementRegistry,
    isSidebarInteractive,
    isSidebarOverlayDismissible:
      mode === "overlay" && canToggleSidebar && isSidebarOpen,
    viewerShellElement: size.element,
  });

  useFileViewerMotionTelemetry({
    getElements: elementRegistry.getElements,
    getIsSidebarOpen: openController.getSidebarOpen,
    motionDurationMs: FILE_VIEWER_MOTION_DURATION_MS,
    motionKernel,
    toggleSidebar,
  });

  const shellStaticContext = React.useMemo<FileViewerShellStaticContextValue>(
    () => ({
      canToggleSidebar,
      collapsible: effectiveCollapsible,
      elementRegistry,
      mode,
      motionDurationMs: FILE_VIEWER_MOTION_DURATION_MS,
      motionKernel,
      registerSidebar,
      rootId,
      setSidebarOpen,
      side,
      sidebarId,
      sidebarWidth,
      toggleSidebar,
    }),
    [
      canToggleSidebar,
      effectiveCollapsible,
      elementRegistry,
      mode,
      motionKernel,
      registerSidebar,
      rootId,
      setSidebarOpen,
      side,
      sidebarId,
      sidebarWidth,
      toggleSidebar,
    ],
  );
  const sidebarDynamicContext =
    React.useMemo<FileViewerSidebarDynamicContextValue>(
      () => ({
        isSidebarInteractive,
        isSidebarOpen,
        isSidebarTransitioning,
        sidebarState,
      }),
      [
        isSidebarInteractive,
        isSidebarOpen,
        isSidebarTransitioning,
        sidebarState,
      ],
    );

  return {
    effectiveCollapsible,
    headerMode: context.headerMode,
    isSidebarOpen,
    mode,
    resourceCategory: context.resourceCategory,
    rootId,
    setRootElement,
    shellStaticContext,
    side,
    sidebarDynamicContext,
    sidebarState,
  };
}

// An inline sidebar stays non-interactive while its geometry is in motion:
// closing makes it inert immediately, opening keeps it inert until the
// settle publish leaves the "sliding" phase.
function resolveFileViewerSidebarInteractive({
  canToggleSidebar,
  collapsible,
  isSidebarOpen,
  isSidebarTransitioning,
  mode,
}: {
  canToggleSidebar: boolean;
  collapsible: FileViewerSidebarCollapsible;
  isSidebarOpen: boolean;
  isSidebarTransitioning: boolean;
  mode: FileViewerSidebarMode;
}): boolean {
  if (collapsible === "none") return true;
  if (!isSidebarOpen) return false;
  return !(mode === "inline" && canToggleSidebar && isSidebarTransitioning);
}

function resolveFileViewerSidebarMode({
  inlineBreakpoint,
  requestedMode,
  width,
}: {
  inlineBreakpoint: number;
  requestedMode: FileViewerSidebarRequestedMode;
  width: number | null;
}): FileViewerSidebarMode {
  if (requestedMode !== "auto") return requestedMode;
  if (width === null) return "overlay";
  return width >= inlineBreakpoint ? "inline" : "overlay";
}
registry/new-york-v4/ui/file-viewer-shell-keyboard.ts
"use client";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

import type { FileViewerElementRegistry } from "./file-viewer-elements";

export function shouldCloseFileViewerSidebarOnEscape({
  canToggleSidebar,
  event,
  isSidebarInteractive,
}: {
  canToggleSidebar: boolean;
  event: KeyboardEvent;
  isSidebarInteractive: boolean;
}) {
  return (
    canToggleSidebar &&
    isSidebarInteractive &&
    event.key === "Escape" &&
    !event.defaultPrevented &&
    !event.repeat
  );
}

export function isFileViewerActiveElementInsideShell({
  activeElement,
  viewerShellElement,
}: {
  activeElement: Element | null;
  viewerShellElement: HTMLElement | null;
}) {
  return Boolean(
    activeElement &&
      viewerShellElement &&
      viewerShellElement.contains(activeElement),
  );
}

export function useFileViewerShellKeyboard({
  canToggleSidebar,
  closeSidebar,
  elementRegistry,
  isSidebarInteractive,
  isSidebarOverlayDismissible,
  viewerShellElement,
}: {
  canToggleSidebar: boolean;
  closeSidebar: () => void;
  elementRegistry: FileViewerElementRegistry;
  isSidebarInteractive: boolean;
  isSidebarOverlayDismissible: boolean;
  viewerShellElement: HTMLDivElement | null;
}) {
  useKeyedLayoutEffect(
    joinEffectKey([
      "file-viewer-escape-close",
      canToggleSidebar,
      closeSidebar,
      elementRegistry,
      isSidebarInteractive,
      viewerShellElement,
    ]),
    () => {
      const ownerDocument = viewerShellElement?.ownerDocument;
      if (!ownerDocument) return;

      const handleKeyDown = (event: KeyboardEvent) => {
        if (
          !shouldCloseFileViewerSidebarOnEscape({
            canToggleSidebar,
            event,
            isSidebarInteractive,
          })
        ) {
          return;
        }

        if (
          !isFileViewerActiveElementInsideShell({
            activeElement: ownerDocument.activeElement,
            viewerShellElement:
              elementRegistry.getElements().viewerShellElement,
          })
        ) {
          return;
        }

        event.preventDefault();
        closeSidebar();
      };

      ownerDocument.addEventListener("keydown", handleKeyDown);
      return () => ownerDocument.removeEventListener("keydown", handleKeyDown);
    },
  );

  // An overlay sidebar floats above the document, so a pointer press anywhere
  // outside the panel (and its trigger, whose own click handles the toggle)
  // dismisses it — the same light-dismiss contract as a popover.
  useKeyedLayoutEffect(
    joinEffectKey([
      "file-viewer-overlay-dismiss",
      closeSidebar,
      elementRegistry,
      isSidebarOverlayDismissible,
      viewerShellElement,
    ]),
    () => {
      const ownerDocument = viewerShellElement?.ownerDocument;
      if (!ownerDocument || !isSidebarOverlayDismissible) return;

      const handlePointerDown = (event: PointerEvent) => {
        const target = event.target;
        if (!(target instanceof Node)) return;
        const elements = elementRegistry.getElements();
        if (elements.sidebarElement?.contains(target)) return;
        if (elements.sidebarTriggerElement?.contains(target)) return;
        closeSidebar();
      };

      ownerDocument.addEventListener("pointerdown", handlePointerDown, {
        capture: true,
      });
      return () =>
        ownerDocument.removeEventListener("pointerdown", handlePointerDown, {
          capture: true,
        });
    },
  );
}
registry/new-york-v4/ui/file-viewer-sidebar-controller.ts
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

import { resolveFileViewerSidebarAccessibilityProps } from "./file-viewer-accessibility";
import {
  type FileViewerSidebarCollapsible,
  type FileViewerSidebarMode,
  type FileViewerSidebarSide,
  type FileViewerSidebarState,
  useFileViewerShell,
} from "./file-viewer-context";
import { useIsInsideFileViewerContent } from "./file-viewer-layout";
import { useStableCssLength, useStableElementSize } from "./viewer-measurement";

export type FileViewerSidebarController = {
  accessibilityProps: ReturnType<
    typeof resolveFileViewerSidebarAccessibilityProps
  >;
  customProperties: React.CSSProperties;
  isInline: boolean;
  isSidebarOpen: boolean;
  mode: FileViewerSidebarMode;
  panelStyle: React.CSSProperties;
  resolvedCollapsible: FileViewerSidebarCollapsible;
  resolvedSide: FileViewerSidebarSide;
  setSidebarGapElement: (element: HTMLDivElement | null) => void;
  setSidebarPanelElement: (element: HTMLElement | null) => void;
  sidebarId: string;
  sidebarState: FileViewerSidebarState;
};

export function useFileViewerSidebarController({
  collapsible,
  id,
  side,
  style,
  width,
}: {
  collapsible: FileViewerSidebarCollapsible | undefined;
  id: string | undefined;
  side: FileViewerSidebarSide | undefined;
  style: React.CSSProperties | undefined;
  width: string;
}): FileViewerSidebarController {
  const isInsideContent = useIsInsideFileViewerContent();
  const shell = useFileViewerShell("FileViewerSidebar");
  const registerSidebar = shell.registerSidebar;
  // Observed, not measured-once: the panel's resolved pixel width is the same
  // number the motion kernel animates the gap to, so it must stay fresh across
  // font-size and container changes or the panel edge and the layout width
  // drift apart.
  const sidebarSize = useStableElementSize<HTMLElement>({
    retainLastNonZero: true,
  });
  const reactId = React.useId();
  const sidebarId = id ?? `${reactId}-file-viewer-sidebar`;
  const resolvedCollapsible = collapsible ?? shell.collapsible;
  const resolvedSide = side ?? shell.side;
  const isSidebarOpen =
    resolvedCollapsible === "none" ? true : shell.isSidebarOpen;
  const isSidebarInteractive =
    resolvedCollapsible === "none" ? true : shell.isSidebarInteractive;
  const sidebarState = isSidebarOpen ? "expanded" : "collapsed";
  const isInline = shell.mode === "inline";
  const declaredWidthPixels = useStableCssLength({
    element: sidebarSize.element,
    value: width,
  });
  const widthPixels =
    declaredWidthPixels > 0 ? declaredWidthPixels : (sidebarSize.width ?? 0);
  const setSidebarGapElement = React.useCallback(
    (element: HTMLDivElement | null) => {
      shell.elementRegistry.registerSidebarGapElement(element);
    },
    [shell.elementRegistry],
  );
  const setSidebarSizeElement = sidebarSize.setElement;
  const setSidebarPanelElement = React.useCallback(
    (element: HTMLElement | null) => {
      setSidebarSizeElement(element);
      shell.elementRegistry.registerSidebarElement(element);
    },
    [setSidebarSizeElement, shell.elementRegistry],
  );

  if (process.env.NODE_ENV !== "production" && !isInsideContent) {
    throw new Error(
      "FileViewerSidebar must be rendered inside FileViewerContent.",
    );
  }

  useKeyedLayoutEffect(
    joinEffectKey([
      "file-viewer-sidebar-registration",
      registerSidebar,
      resolvedCollapsible,
      resolvedSide,
      sidebarId,
      width,
      widthPixels,
    ]),
    () =>
      registerSidebar({
        collapsible: resolvedCollapsible,
        id: sidebarId,
        side: resolvedSide,
        width,
        widthPixels,
      }),
  );

  const accessibilityProps = resolveFileViewerSidebarAccessibilityProps({
    collapsible: resolvedCollapsible,
    deferInert:
      !isSidebarOpen && !isSidebarInteractive && shell.isSidebarTransitioning,
    isSidebarInteractive,
  });
  const customProperties = pickCssCustomProperties(style);
  const panelStyle: React.CSSProperties = {
    width,
    // Overlay slides on a CSS transition; its duration is the same constant
    // that drives the kernel clock, so the two timelines cannot drift.
    ...(isInline
      ? null
      : { transitionDuration: `${shell.motionDurationMs}ms` }),
    ...style,
    ...(isSidebarInteractive ? null : { pointerEvents: "none" as const }),
  };

  // No memo: `panelStyle` and `customProperties` are fresh objects every
  // render, so the controller value can never be referentially stable anyway.
  return {
    accessibilityProps,
    customProperties,
    isInline,
    isSidebarOpen,
    mode: shell.mode,
    panelStyle,
    resolvedCollapsible,
    resolvedSide,
    setSidebarGapElement,
    setSidebarPanelElement,
    sidebarId,
    sidebarState,
  };
}

function pickCssCustomProperties(style: React.CSSProperties | undefined) {
  if (!style) return {};

  return Object.fromEntries(
    Object.entries(style).filter(([name]) => name.startsWith("--")),
  ) as React.CSSProperties;
}
registry/new-york-v4/ui/file-viewer-motion-kernel.ts
"use client";

import * as React from "react";
import { flushSync } from "react-dom";

import { FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT } from "./file-viewer-elements";
import {
  easeFileViewerMotion,
  FILE_VIEWER_MOTION_DURATION_MS,
  areFileViewerMotionFramesEqual,
  areFileViewerMotionRestFramesEqual,
  createFileViewerIdleMotionFrame,
  createFileViewerMotionPlan,
  createFileViewerMotionRestFrame,
  type FileViewerMotionFrame,
  type FileViewerMotionPlan,
  type FileViewerMotionRestFrame,
  type FileViewerMotionTarget,
} from "./file-viewer-motion-plan";

// The kernel is the single owner of sidebar motion. It holds the clock (one
// rAF loop from slide start through settle), writes the continuous inline
// styles, and publishes to React subscribers only at phase edges
// (idle → sliding → settling → idle). Everything discrete — data attributes,
// inert/aria, the overlay translate classes — is owned by React renders.
//
// Commit-then-relax ordering: the first sliding frame is flushed
// synchronously inside the toggle's own task, so renderers commit their
// TARGET layout and rebase scroll before anything paints; the per-tick style
// writes then only relax the counter-transform to identity. Settle removes a
// no-op transform — it never commits layout, so no flushSync runs inside rAF.
export type FileViewerMotionKernel = {
  getFlightRecords: () => readonly FileViewerMotionFlightRecord[];
  getInteractiveSnapshot: () => FileViewerMotionFrame;
  getSnapshot: () => FileViewerMotionFrame;
  setDocumentSurface: (surface: FileViewerDocumentSurface | null) => void;
  setSidebarGapElement: (element: HTMLElement | null) => void;
  startMotion: (target: FileViewerMotionTarget) => void;
  subscribe: (listener: () => void) => () => void;
  syncTarget: (target: FileViewerMotionTarget) => void;
};

// Always-on flight recorder: every motion leaves a bounded trace (per-tick
// widths, phase edges, settle holds, inter-frame gaps) so a blink report is
// diagnosable after the fact without re-instrumenting.
export type FileViewerMotionFlightRecord = {
  fromInlineSize: number;
  id: number;
  interrupted: boolean;
  maxTickGapMs: number;
  open: boolean;
  settleHoldFrameCount: number;
  startedAt: number;
  ticks: FileViewerMotionFlightTick[];
  toInlineSize: number;
};

export type FileViewerMotionFlightTick = {
  elapsedMs: number;
  phase: FileViewerMotionFrame["phase"];
  sidebarInlineSize: number;
};

const FILE_VIEWER_FLIGHT_RECORD_LIMIT = 8;
const FILE_VIEWER_FLIGHT_TICK_LIMIT = 240;

export type FileViewerDocumentSurface = {
  element: HTMLElement;
  getMotionProbeElement?: (() => HTMLElement | null) | null;
  readSettleSnapshot?: FileViewerDocumentSurfaceSettleSnapshotReader | null;
  resolveMotionStyle?: FileViewerDocumentSurfaceMotionResolver | null;
};

export type FileViewerDocumentSurfaceSettleSnapshotReader = () =>
  | readonly number[]
  | null
  | undefined;

// Layout reads are quarantined outside the kernel (viewer-measurement / the
// frame controller layer). The kernel owns time and style writes only, so the
// settle-hold rect reader is injected by its creator rather than imported.
export type FileViewerElementRectSnapshotReader = (
  element: HTMLElement | null,
) => readonly number[];

export type FileViewerMotionKernelOptions = {
  readElementRectSnapshot?: FileViewerElementRectSnapshotReader | null;
};

export type FileViewerDocumentSurfaceMotionStyle = {
  customProperties?: Readonly<Record<string, string | null>>;
  transform: string;
  transformOrigin: string;
  willChange: string;
};

export type FileViewerDocumentSurfaceMotionResolver = (
  frame: FileViewerMotionFrame,
) => FileViewerDocumentSurfaceMotionStyle | null;

type FileViewerActiveMotion = {
  durationMs: number;
  from: FileViewerMotionRestFrame;
  // The clock re-anchors to the first tick's vsync frame time: startedAt is
  // stamped inside the toggle's task, but the synchronous slide-start commit
  // can burn 10ms+ before anything paints, and an ease anchored at the click
  // lands its first painted frame that deep into the curve.
  hasFrameClockAnchor: boolean;
  id: number;
  startedAt: number;
  to: FileViewerMotionRestFrame;
};

type FileViewerSettleRelease = {
  idleFrame: FileViewerMotionFrame;
  lastSnapshot: readonly number[];
  remainingFrameCount: number;
  settlingFrame: FileViewerMotionFrame;
  stableFrameCount: number;
};

const FILE_VIEWER_SETTLE_SCROLL_EPSILON_PX = 0.25;
const FILE_VIEWER_SETTLE_STABLE_FRAME_COUNT = 2;
const FILE_VIEWER_SETTLE_MAX_HOLD_FRAMES = 6;
const FILE_VIEWER_SUBPIXEL_ENDPOINT_EPSILON_PX = 1;

export const DEFAULT_FILE_VIEWER_MOTION_FRAME: FileViewerMotionFrame = {
  shellInlineSize: 0,
  durationMs: FILE_VIEWER_MOTION_DURATION_MS,
  fromInlineSize: 0,
  layoutInlineSize: 0,
  mode: "overlay",
  motionId: null,
  motionProgress: 1,
  open: false,
  phase: "idle",
  side: "left",
  sidebarInlineSize: 0,
  sidebarWidth: 0,
  toInlineSize: 0,
};

export function createFileViewerMotionKernel({
  readElementRectSnapshot = null,
}: FileViewerMotionKernelOptions = {}): FileViewerMotionKernel {
  const listeners = new Set<() => void>();
  let contractFrame = DEFAULT_FILE_VIEWER_MOTION_FRAME;
  let interactiveFrame = DEFAULT_FILE_VIEWER_MOTION_FRAME;
  let target: FileViewerMotionTarget = {
    shellInlineSize: 0,
    durationMs: DEFAULT_FILE_VIEWER_MOTION_FRAME.durationMs,
    mode: DEFAULT_FILE_VIEWER_MOTION_FRAME.mode,
    open: DEFAULT_FILE_VIEWER_MOTION_FRAME.open,
    side: DEFAULT_FILE_VIEWER_MOTION_FRAME.side,
    sidebarWidth: 0,
  };
  let documentSurface: FileViewerDocumentSurface | null = null;
  let documentSurfaceCustomProperties = new Set<string>();
  let sidebarGapElement: HTMLElement | null = null;
  let activeMotion: FileViewerActiveMotion | null = null;
  let settleRelease: FileViewerSettleRelease | null = null;
  let rafHandle = 0;
  let settleReleaseHandle = 0;
  let motionSequence = 0;
  const flightRecords: FileViewerMotionFlightRecord[] = [];
  let activeFlightRecord: FileViewerMotionFlightRecord | null = null;
  let lastFlightTickAt = 0;

  const beginFlightRecord = (motion: FileViewerActiveMotion) => {
    if (activeFlightRecord && activeFlightRecord.id !== motion.id) {
      activeFlightRecord.interrupted = true;
    }
    activeFlightRecord = {
      fromInlineSize: motion.from.layoutInlineSize,
      id: motion.id,
      interrupted: false,
      maxTickGapMs: 0,
      open: motion.to.open,
      settleHoldFrameCount: 0,
      startedAt: motion.startedAt,
      ticks: [],
      toInlineSize: motion.to.layoutInlineSize,
    };
    lastFlightTickAt = motion.startedAt;
    flightRecords.push(activeFlightRecord);
    if (flightRecords.length > FILE_VIEWER_FLIGHT_RECORD_LIMIT) {
      flightRecords.splice(
        0,
        flightRecords.length - FILE_VIEWER_FLIGHT_RECORD_LIMIT,
      );
    }
  };

  const recordFlightTick = (frame: FileViewerMotionFrame, now = readNow()) => {
    const record = activeFlightRecord;
    if (!record || frame.motionId !== record.id) return;
    record.maxTickGapMs = Math.max(record.maxTickGapMs, now - lastFlightTickAt);
    lastFlightTickAt = now;
    if (frame.phase === "settling") record.settleHoldFrameCount += 1;
    if (record.ticks.length >= FILE_VIEWER_FLIGHT_TICK_LIMIT) return;
    record.ticks.push({
      elapsedMs: Math.max(0, now - record.startedAt),
      phase: frame.phase,
      sidebarInlineSize: frame.sidebarInlineSize,
    });
  };

  const notify = () => {
    for (const listener of listeners) listener();
  };

  const publishContractFrame = (
    nextFrame: FileViewerMotionFrame,
    { flushSubscribers = false }: { flushSubscribers?: boolean } = {},
  ) => {
    if (areFileViewerMotionFramesEqual(contractFrame, nextFrame)) return;
    contractFrame = nextFrame;

    if (flushSubscribers) {
      flushSync(notify);
      return;
    }

    notify();
  };

  // The gap's inline size and the document surface's counter-scale must land
  // in the same frame: two independent CSS transitions (width on the gap,
  // transform on the surface) can desync under main-thread jank, letting the
  // document edge drift off the sidebar edge mid-slide. The kernel therefore
  // writes both here, once per tick.
  const writeElementStyles = (nextFrame: FileViewerMotionFrame) => {
    writeSidebarGapStyle(nextFrame);
    writeDocumentSurfaceStyle(nextFrame);
  };

  const commit = (
    nextFrame: FileViewerMotionFrame,
    { publish = true }: { publish?: boolean } = {},
  ) => {
    writeElementStyles(nextFrame);
    interactiveFrame = nextFrame;
    if (publish) publishContractFrame(nextFrame);
  };

  const cancelTick = () => {
    if (rafHandle === 0) return;
    getCancelAnimationFrame()(rafHandle);
    rafHandle = 0;
  };

  const cancelSettleRelease = () => {
    settleRelease = null;
    if (settleReleaseHandle === 0) return;
    getCancelAnimationFrame()(settleReleaseHandle);
    settleReleaseHandle = 0;
  };

  const readMotionSample = (
    motion: FileViewerActiveMotion,
    now = readNow(),
  ): FileViewerMotionFrame => {
    const rawTimeProgress =
      motion.durationMs <= 0
        ? 1
        : clamp((now - motion.startedAt) / motion.durationMs, 0, 1);
    const rawMotionProgress = easeFileViewerMotion(rawTimeProgress);
    const rawSidebarInlineSize = lerp(
      motion.from.sidebarInlineSize,
      motion.to.sidebarInlineSize,
      rawMotionProgress,
    );
    const isSubpixelEndpoint =
      rawMotionProgress > 0.98 &&
      Math.abs(rawSidebarInlineSize - motion.to.sidebarInlineSize) <=
        FILE_VIEWER_SUBPIXEL_ENDPOINT_EPSILON_PX;
    const motionProgress = isSubpixelEndpoint ? 1 : rawMotionProgress;
    const sidebarInlineSize = isSubpixelEndpoint
      ? motion.to.sidebarInlineSize
      : rawSidebarInlineSize;
    const layoutInlineSize = Math.max(
      0,
      motion.to.shellInlineSize - sidebarInlineSize,
    );
    const fromInlineSize = motion.from.layoutInlineSize;

    return {
      shellInlineSize: motion.to.shellInlineSize,
      durationMs: motion.durationMs,
      fromInlineSize,
      layoutInlineSize,
      mode: motion.to.mode,
      motionId: motion.id,
      motionProgress,
      open: motion.to.open,
      phase: motionProgress < 1 ? "sliding" : "settling",
      side: motion.to.side,
      sidebarInlineSize,
      sidebarWidth: motion.to.sidebarWidth,
      toInlineSize: motion.to.layoutInlineSize,
    };
  };

  const settle = () => {
    if (!activeMotion) return;
    const finishedMotion = activeMotion;
    activeMotion = null;
    cancelTick();

    const idleFrame = createFileViewerIdleMotionFrame(finishedMotion.to);
    const settlingFrame: FileViewerMotionFrame = {
      ...idleFrame,
      fromInlineSize: finishedMotion.from.layoutInlineSize,
      motionId: finishedMotion.id,
      phase: "settling",
    };

    // Layout and scroll were committed at slide start; settling only clears
    // the (now identity) counter-transform and holds until shell geometry
    // stops moving. Nothing here re-renders geometry, so no flushSync in rAF.
    commit(settlingFrame, { publish: false });
    recordFlightTick(settlingFrame);
    publishContractFrame(settlingFrame);
    scheduleSettleRelease(settlingFrame, idleFrame);
  };

  // Ticks sample the clock at the rAF FRAME timestamp, never the callback's
  // execution time: the frame time is the vsync the paint belongs to, and a
  // callback running late in a janky frame would otherwise write a position
  // ahead of the frame's own time axis — a real paint-side velocity excess
  // (the probes' rule 11, applied to the writer). The first tick also
  // re-anchors startedAt to its frame time, so the ease starts at the first
  // paintable frame rather than at the click that precedes the slide-start
  // commit.
  const tick = (frameTime: number) => {
    rafHandle = 0;
    if (!activeMotion) return;
    const now = Number.isFinite(frameTime) ? frameTime : readNow();
    if (!activeMotion.hasFrameClockAnchor) {
      activeMotion.hasFrameClockAnchor = true;
      activeMotion.startedAt = now;
    }
    const sample = readMotionSample(activeMotion, now);
    if (sample.motionProgress >= 1) {
      settle();
      return;
    }
    commit(sample, { publish: false });
    recordFlightTick(sample, now);
    scheduleTick();
  };

  const scheduleTick = () => {
    if (rafHandle !== 0) return;
    rafHandle = getRequestAnimationFrame()(tick);
  };

  const scheduleSettleRelease = (
    settlingFrame: FileViewerMotionFrame,
    idleFrame: FileViewerMotionFrame,
  ) => {
    cancelSettleRelease();
    settleRelease = {
      idleFrame,
      lastSnapshot: readSettleSnapshot(),
      remainingFrameCount: FILE_VIEWER_SETTLE_MAX_HOLD_FRAMES,
      settlingFrame,
      stableFrameCount: 0,
    };
    scheduleSettleReleaseFrame();
  };

  const scheduleSettleReleaseFrame = () => {
    if (settleReleaseHandle !== 0) return;
    settleReleaseHandle = getRequestAnimationFrame()(holdSettleRelease);
  };

  const holdSettleRelease = () => {
    settleReleaseHandle = 0;
    if (!settleRelease) return;

    commit(settleRelease.settlingFrame, { publish: false });
    recordFlightTick(settleRelease.settlingFrame);

    const snapshot = readSettleSnapshot();
    const stableFrameCount = areSettleSnapshotsEqual(
      settleRelease.lastSnapshot,
      snapshot,
    )
      ? settleRelease.stableFrameCount + 1
      : 0;
    const remainingFrameCount = settleRelease.remainingFrameCount - 1;

    if (
      stableFrameCount >= FILE_VIEWER_SETTLE_STABLE_FRAME_COUNT ||
      remainingFrameCount <= 0
    ) {
      const idleFrame = settleRelease.idleFrame;
      settleRelease = null;
      // Natural completion: close the flight record so the next motion does
      // not mark this one interrupted.
      activeFlightRecord = null;
      commit(idleFrame);
      return;
    }

    settleRelease = {
      ...settleRelease,
      lastSnapshot: snapshot,
      remainingFrameCount,
      stableFrameCount,
    };
    scheduleSettleReleaseFrame();
  };

  // The event carries the kernel's LIVE frame so renderers can capture their
  // pre-commit anchor against what is actually on screen — during a mid-flight
  // retarget that is the settled layout PLUS the in-flight transform, not the
  // settled layout alone.
  const dispatchBeforeLayoutMotion = (currentFrame: FileViewerMotionFrame) => {
    documentSurface?.element.dispatchEvent(
      new CustomEvent<FileViewerMotionFrame>(
        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,
        { detail: currentFrame },
      ),
    );
  };

  const interruptActiveFlightRecord = () => {
    if (!activeFlightRecord) return;
    activeFlightRecord.interrupted = true;
    activeFlightRecord = null;
  };

  const retarget = (nextTarget: FileViewerMotionTarget, animate: boolean) => {
    cancelSettleRelease();
    // Continuity is with what is PAINTED, not with the clock: mid-flight the
    // screen shows the last tick's commit (`interactiveFrame`), which can be
    // a frame behind a fresh clock sample. Planning (and the before-motion
    // capture renderers do off the event detail) from the painted frame keeps
    // the retarget hand-off pixel-continuous; the new motion simply re-lerps
    // from the painted geometry.
    const currentFrame =
      interactiveFrame.shellInlineSize > 0
        ? interactiveFrame
        : createFileViewerIdleMotionFrame(
            createFileViewerMotionRestFrame(target),
          );
    const plan = createFileViewerMotionPlan({
      animate: animate && !prefersReducedMotion(),
      currentFrame,
      nextTarget,
    });
    if (shouldDispatchBeforeLayoutMotion(plan)) {
      dispatchBeforeLayoutMotion(currentFrame);
    }
    target = plan.resolvedTarget;

    if (!plan.shouldAnimate) {
      if (activeMotion) interruptActiveFlightRecord();
      activeMotion = null;
      cancelTick();
      commit(createFileViewerIdleMotionFrame(plan.nextRestFrame));
      return;
    }

    motionSequence += 1;
    activeMotion = {
      durationMs: plan.resolvedTarget.durationMs,
      from: { ...plan.currentRestFrame, layoutInlineSize: plan.fromInlineSize },
      hasFrameClockAnchor: false,
      id: motionSequence,
      startedAt: readNow(),
      to: plan.nextRestFrame,
    };
    beginFlightRecord(activeMotion);
    const startFrame = readMotionSample(activeMotion, activeMotion.startedAt);
    // Commit the discontinuity while it cannot be seen: flush the first
    // sliding frame synchronously (inside the toggle's own task) so renderers
    // lay out at the target width and rebase scroll before first paint, hidden
    // behind the counter-transform written above in the same task.
    writeElementStyles(startFrame);
    interactiveFrame = startFrame;
    recordFlightTick(startFrame, activeMotion.startedAt);
    publishContractFrame(startFrame, { flushSubscribers: true });
    scheduleTick();
  };

  const syncTarget = (nextTarget: FileViewerMotionTarget) => {
    const nextRestFrame = createFileViewerMotionRestFrame(nextTarget);

    if (activeMotion) {
      target = nextTarget;
      if (areFileViewerMotionRestFramesEqual(activeMotion.to, nextRestFrame)) {
        return;
      }
      // A mode flip mid-motion (breakpoint crossing during the slide) cannot
      // be animated: React re-renders the new mode immediately, so an inline
      // slide continuing against overlay DOM (or vice versa) double-moves the
      // surface. Snap to the new rest geometry instead.
      if (nextRestFrame.mode !== activeMotion.to.mode) {
        interruptActiveFlightRecord();
        activeMotion = null;
        cancelTick();
        cancelSettleRelease();
        commit(createFileViewerIdleMotionFrame(nextRestFrame));
        return;
      }
      retarget(nextTarget, true);
      return;
    }

    cancelSettleRelease();
    target = nextTarget;
    commit(createFileViewerIdleMotionFrame(nextRestFrame));
  };

  return {
    getFlightRecords: () => flightRecords.slice(),
    getInteractiveSnapshot: () =>
      activeMotion ? readMotionSample(activeMotion) : interactiveFrame,
    getSnapshot: () => contractFrame,
    setDocumentSurface: (surface) => {
      const previousSurface = documentSurface;
      if (
        previousSurface &&
        (!surface || previousSurface.element !== surface.element)
      ) {
        clearDocumentSurfaceStyle(previousSurface.element);
      }
      documentSurface = surface;
      writeDocumentSurfaceStyle(interactiveFrame);
    },
    setSidebarGapElement: (element) => {
      sidebarGapElement = element;
      writeSidebarGapStyle(interactiveFrame);
    },
    startMotion: (nextTarget) => retarget(nextTarget, true),
    subscribe: (listener) => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
    syncTarget,
  };

  function writeSidebarGapStyle(nextFrame: FileViewerMotionFrame) {
    if (!sidebarGapElement) return;

    // Overlay motion is CSS-owned; relinquish the gap so its `w-0` class is
    // the only writer outside inline mode.
    if (nextFrame.mode !== "inline") {
      sidebarGapElement.style.width = "";
      sidebarGapElement.style.flexBasis = "";
      return;
    }

    sidebarGapElement.style.width = `${nextFrame.sidebarInlineSize}px`;
    sidebarGapElement.style.flexBasis = `${nextFrame.sidebarInlineSize}px`;
  }

  function writeDocumentSurfaceStyle(nextFrame: FileViewerMotionFrame) {
    if (!documentSurface) return;

    const { element, resolveMotionStyle } = documentSurface;
    const resolvedStyle = resolveMotionStyle?.(nextFrame);
    if (resolvedStyle) {
      writeDocumentSurfaceCustomProperties(
        element,
        resolvedStyle.customProperties,
      );
      element.style.transform = resolvedStyle.transform;
      element.style.transformOrigin = resolvedStyle.transformOrigin;
      element.style.willChange = resolvedStyle.willChange;
      return;
    }

    // Default (no motion resolver): identity. Fit-width renderers register
    // the shared commit-then-relax resolver (file-viewer-fit-width-motion);
    // a surface without one either tracks the live DOM width on its own or
    // opts out of shell motion entirely, and must not be transformed here.
    writeDocumentSurfaceCustomProperties(element, null);
    element.style.transform = "";
    element.style.transformOrigin = "";
    element.style.willChange = "";
  }

  function writeDocumentSurfaceCustomProperties(
    element: HTMLElement,
    customProperties:
      | Readonly<Record<string, string | null>>
      | null
      | undefined,
  ) {
    const nextNames = new Set(Object.keys(customProperties ?? {}));
    for (const name of documentSurfaceCustomProperties) {
      if (!nextNames.has(name)) {
        element.style.removeProperty(name);
      }
    }

    for (const [name, value] of Object.entries(customProperties ?? {})) {
      if (value == null) {
        element.style.removeProperty(name);
      } else {
        element.style.setProperty(name, value);
      }
    }

    documentSurfaceCustomProperties = nextNames;
  }

  function clearDocumentSurfaceStyle(element: HTMLElement) {
    element.style.transform = "";
    element.style.transformOrigin = "";
    element.style.willChange = "";
    for (const name of documentSurfaceCustomProperties) {
      element.style.removeProperty(name);
    }
    documentSurfaceCustomProperties = new Set();
  }

  function readSettleSnapshot(): readonly number[] {
    const values: number[] = [];

    appendElementRectSnapshot(values, sidebarGapElement);
    appendElementRectSnapshot(values, documentSurface?.element ?? null);

    try {
      const surfaceSnapshot = documentSurface?.readSettleSnapshot?.();
      if (surfaceSnapshot) {
        values.push(...surfaceSnapshot.map(toSettleSnapshotNumber));
      }
    } catch {
      // A renderer snapshot is diagnostic, not correctness-critical. If a
      // renderer unmounts while settling, fall back to shell geometry.
    }

    return values.length > 0 ? values : [0];
  }

  function appendElementRectSnapshot(
    values: number[],
    element: HTMLElement | null,
  ) {
    if (!readElementRectSnapshot || !element) return;
    for (const value of readElementRectSnapshot(element)) {
      values.push(toSettleSnapshotNumber(value));
    }
  }
}

function areSettleSnapshotsEqual(
  previous: readonly number[],
  next: readonly number[],
) {
  if (previous.length !== next.length) return false;
  return previous.every(
    (value, index) =>
      Math.abs(value - next[index]) <= FILE_VIEWER_SETTLE_SCROLL_EPSILON_PX,
  );
}

function toSettleSnapshotNumber(value: number) {
  return Number.isFinite(value) ? value : 0;
}

function shouldDispatchBeforeLayoutMotion({
  currentRestFrame,
  nextRestFrame,
}: FileViewerMotionPlan) {
  return (
    currentRestFrame.mode === "inline" &&
    nextRestFrame.mode === "inline" &&
    Math.abs(
      currentRestFrame.layoutInlineSize - nextRestFrame.layoutInlineSize,
    ) > 0.001
  );
}

export function useFileViewerMotionFrame(
  kernel: FileViewerMotionKernel | null | undefined,
): FileViewerMotionFrame {
  const subscribe = React.useCallback(
    (listener: () => void) => kernel?.subscribe(listener) ?? (() => {}),
    [kernel],
  );
  const getSnapshot = React.useCallback(
    () => kernel?.getSnapshot() ?? DEFAULT_FILE_VIEWER_MOTION_FRAME,
    [kernel],
  );

  return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
}

function prefersReducedMotion() {
  return (
    typeof matchMedia === "function" &&
    matchMedia("(prefers-reduced-motion: reduce)").matches
  );
}

function readNow() {
  return typeof performance !== "undefined" &&
    typeof performance.now === "function"
    ? performance.now()
    : Date.now();
}

function getRequestAnimationFrame() {
  return (
    globalThis.requestAnimationFrame ??
    ((callback: FrameRequestCallback) =>
      window.setTimeout(() => callback(readNow()), 16))
  );
}

function getCancelAnimationFrame() {
  return (
    globalThis.cancelAnimationFrame ??
    ((id: number) => {
      window.clearTimeout(id);
    })
  );
}

function lerp(from: number, to: number, progress: number) {
  return from + (to - from) * progress;
}

function clamp(value: number, min: number, max: number) {
  return Math.min(max, Math.max(min, value));
}
registry/new-york-v4/ui/viewer-motion-telemetry.ts
"use client";

import * as React from "react";

import { joinEffectKey } from "@/lib/effect-key";
import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";

import type { FileViewerElementRegistry } from "./file-viewer-elements";
import type {
  FileViewerMotionFlightRecord,
  FileViewerMotionKernel,
} from "./file-viewer-motion-kernel";

// Shell-level motion telemetry, shipped with the component. It measures the
// FileViewer's own contract — gap monotonicity, surface continuity across the
// sliding → settling edge, scroll identity, DOM churn, layout shift, main
// thread — for ANY renderer hosted in the shell, complementing the renderer
// probes (e.g. __pdfViewerTelemetry's canvas continuity).
type FileViewerMotionTelemetryMetricId =
  | "blink"
  | "gap-stability"
  | "overshoot"
  | "content-start-snap"
  | "content-overshoot"
  | "settle-snap"
  | "scroll-identity"
  | "dom-mutations"
  | "layout-shift"
  | "main-thread"
  | "cycle-invariance";

export type FileViewerMotionTelemetryMetric = {
  budget: string;
  detail: string;
  id: FileViewerMotionTelemetryMetricId;
  label: string;
  passed: boolean;
  value: string;
};

export type FileViewerMotionTelemetrySample = {
  contentHeight: number;
  contentLeft: number;
  contentTop: number;
  contentWidth: number;
  elapsedMs: number;
  gapWidth: number;
  motionPhase: string;
  scrollHeight: number;
  scrollTop: number;
  scrollWidth: number;
  sidebarState: string | null;
  sidebarWidth: number;
  surfaceHeight: number;
  surfaceLeft: number;
  surfaceTop: number;
  surfaceTransform: string;
  surfaceWidth: number;
  timestamp: number;
  visibleCanvasCount: number;
};

export type FileViewerMotionTelemetryRun = {
  action: "close" | "open";
  addedNodeCount: number;
  after: FileViewerMotionTelemetrySample;
  before: FileViewerMotionTelemetrySample;
  canvasResizeMutationCount: number;
  layoutShiftCount: number;
  layoutShiftScore: number;
  longTaskCount: number;
  longTaskDurationMs: number;
  mutationCount: number;
  removedNodeCount: number;
  samples: FileViewerMotionTelemetrySample[];
  scrollEventCount: number;
};

export type FileViewerMotionTelemetryResult = {
  durationMs: number;
  metrics: FileViewerMotionTelemetryMetric[];
  runs: FileViewerMotionTelemetryRun[];
  sampledFrameCount: number;
  status: "failed" | "passed";
};

export type FileViewerMotionTelemetryRuntime = {
  getFlightRecords: () => readonly FileViewerMotionFlightRecord[];
  getLastResult: () => FileViewerMotionTelemetryResult | null;
  readSample: () => FileViewerMotionTelemetrySample | null;
  run: (options?: {
    settleFrameCount?: number;
  }) => Promise<FileViewerMotionTelemetryResult | null>;
};

declare global {
  interface Window {
    __fileViewerMotionTelemetry?: FileViewerMotionTelemetryRuntime;
  }
}

const FILE_VIEWER_TELEMETRY_DEFAULT_SETTLE_FRAMES = 16;
const FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX = 1;
const FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX = 1;
const FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX = 1.5;
const FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX = 1;
const FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX = 1.5;
const FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX = 1;
const FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX = 2;
const FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS = 34;
const FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS = 250;

type FileViewerMotionTelemetryHost = {
  getElements: FileViewerElementRegistry["getElements"];
  getIsSidebarOpen: () => boolean;
  motionDurationMs: number;
  motionKernel: FileViewerMotionKernel;
  toggleSidebar: () => void;
};

export function useFileViewerMotionTelemetry(
  host: FileViewerMotionTelemetryHost,
) {
  const hostRef = React.useRef(host);
  const lastResultRef = React.useRef<FileViewerMotionTelemetryResult | null>(
    null,
  );
  const isRunningRef = React.useRef(false);
  hostRef.current = host;

  useKeyedMountEffect(joinEffectKey(["file-viewer-motion-telemetry"]), () => {
    if (typeof window === "undefined") return;

    const previousRuntime = window.__fileViewerMotionTelemetry;
    const runtime: FileViewerMotionTelemetryRuntime = {
      getFlightRecords: () => hostRef.current.motionKernel.getFlightRecords(),
      getLastResult: () => lastResultRef.current,
      readSample: () =>
        readFileViewerMotionTelemetrySample(hostRef.current, performance.now()),
      run: async (options) => {
        // A telemetry run drives real toggles; a concurrent run would
        // interleave motions and score garbage. One at a time.
        if (isRunningRef.current) return null;
        isRunningRef.current = true;
        try {
          const result = await runFileViewerMotionTelemetry(hostRef, options);
          lastResultRef.current = result;
          if (result) logFileViewerMotionTelemetryResult(result);
          return result;
        } finally {
          isRunningRef.current = false;
        }
      },
    };

    window.__fileViewerMotionTelemetry = runtime;

    return () => {
      if (window.__fileViewerMotionTelemetry === runtime) {
        window.__fileViewerMotionTelemetry = previousRuntime;
      }
    };
  });
}

async function runFileViewerMotionTelemetry(
  hostRef: React.MutableRefObject<FileViewerMotionTelemetryHost>,
  options?: { settleFrameCount?: number },
): Promise<FileViewerMotionTelemetryResult | null> {
  const host = hostRef.current;
  const elements = host.getElements();
  if (!elements.viewerShellElement || !elements.documentSurfaceElement) {
    return null;
  }

  const startedAt = performance.now();
  const settleFrameCount =
    options?.settleFrameCount ?? FILE_VIEWER_TELEMETRY_DEFAULT_SETTLE_FRAMES;
  const firstAction = host.getIsSidebarOpen() ? "close" : "open";
  const secondAction = firstAction === "close" ? "open" : "close";
  const runs: FileViewerMotionTelemetryRun[] = [];

  runs.push(
    await sampleFileViewerMotionRun({
      action: firstAction,
      hostRef,
      settleFrameCount,
    }),
  );
  await waitForFileViewerTelemetryFrames(6);
  runs.push(
    await sampleFileViewerMotionRun({
      action: secondAction,
      hostRef,
      settleFrameCount,
    }),
  );

  const metrics = collectFileViewerMotionTelemetryMetrics(runs);

  return {
    durationMs: Math.max(0, performance.now() - startedAt),
    metrics,
    runs,
    sampledFrameCount: runs.reduce(
      (count, run) => count + run.samples.length,
      0,
    ),
    status: metrics.every((metric) => metric.passed) ? "passed" : "failed",
  };
}

async function sampleFileViewerMotionRun({
  action,
  hostRef,
  settleFrameCount,
}: {
  action: "close" | "open";
  hostRef: React.MutableRefObject<FileViewerMotionTelemetryHost>;
  settleFrameCount: number;
}): Promise<FileViewerMotionTelemetryRun> {
  const host = hostRef.current;
  const startedAt = performance.now();
  const before =
    readFileViewerMotionTelemetrySample(host, startedAt) ??
    createEmptyFileViewerMotionTelemetrySample();
  const trackers = createFileViewerMotionTelemetryTrackers(host);
  const samples: FileViewerMotionTelemetrySample[] = [];

  host.toggleSidebar();
  samples.push(readFileViewerMotionTelemetrySample(host, startedAt) ?? before);

  const sampleUntil = host.motionDurationMs + settleFrameCount * (1000 / 60);
  while (performance.now() - startedAt < sampleUntil) {
    await waitForFileViewerTelemetryFrames(1);
    const sample = readFileViewerMotionTelemetrySample(
      hostRef.current,
      startedAt,
    );
    if (sample) samples.push(sample);
  }

  await waitForFileViewerTelemetryFrames(2);
  const after =
    readFileViewerMotionTelemetrySample(hostRef.current, startedAt) ?? before;
  trackers.disconnect();

  return {
    action,
    addedNodeCount: trackers.addedNodeCount,
    after,
    before,
    canvasResizeMutationCount: trackers.canvasResizeMutationCount,
    layoutShiftCount: trackers.layoutShiftCount,
    layoutShiftScore: trackers.layoutShiftScore,
    longTaskCount: trackers.longTaskCount,
    longTaskDurationMs: trackers.longTaskDurationMs,
    mutationCount: trackers.mutationCount,
    removedNodeCount: trackers.removedNodeCount,
    samples,
    scrollEventCount: trackers.scrollEventCount,
  };
}

function createEmptyFileViewerMotionTelemetrySample(): FileViewerMotionTelemetrySample {
  return {
    contentHeight: 0,
    contentLeft: 0,
    contentTop: 0,
    contentWidth: 0,
    elapsedMs: 0,
    gapWidth: 0,
    motionPhase: "idle",
    scrollHeight: 0,
    scrollTop: 0,
    scrollWidth: 0,
    sidebarState: null,
    sidebarWidth: 0,
    surfaceHeight: 0,
    surfaceLeft: 0,
    surfaceTop: 0,
    surfaceTransform: "",
    surfaceWidth: 0,
    timestamp: 0,
    visibleCanvasCount: 0,
  };
}

function readFileViewerMotionTelemetrySample(
  host: FileViewerMotionTelemetryHost,
  startedAt: number,
): FileViewerMotionTelemetrySample | null {
  const elements = host.getElements();
  const surfaceElement = elements.documentSurfaceElement;
  if (!surfaceElement) return null;

  const gapRect = elements.sidebarGapElement?.getBoundingClientRect();
  const sidebarRect = elements.sidebarElement?.getBoundingClientRect();
  const surfaceRect = surfaceElement.getBoundingClientRect();
  const contentElement = readFileViewerMotionProbeElement(elements);
  const contentRect = contentElement
    ? readFileViewerMotionProbeRect(contentElement, elements.viewerShellElement)
    : null;
  const scroller = findFileViewerTelemetryScroller(surfaceElement);
  const frame = host.motionKernel.getInteractiveSnapshot();
  const visibleCanvasCount = countFileViewerVisibleCanvases(surfaceElement);

  return {
    contentHeight: contentRect?.height ?? surfaceRect.height,
    contentLeft: contentRect?.left ?? surfaceRect.left,
    contentTop: contentRect?.top ?? surfaceRect.top,
    contentWidth: contentRect?.width ?? surfaceRect.width,
    elapsedMs: Math.max(0, performance.now() - startedAt),
    gapWidth: gapRect?.width ?? 0,
    motionPhase: frame.phase,
    scrollHeight: scroller?.scrollHeight ?? 0,
    scrollTop: scroller?.scrollTop ?? 0,
    scrollWidth: scroller?.scrollWidth ?? 0,
    sidebarState:
      elements.sidebarElement?.getAttribute("data-file-viewer-sidebar-state") ??
      null,
    sidebarWidth: sidebarRect?.width ?? 0,
    surfaceHeight: surfaceRect.height,
    surfaceLeft: surfaceRect.left,
    surfaceTop: surfaceRect.top,
    surfaceTransform: getComputedStyle(surfaceElement).transform,
    surfaceWidth: surfaceRect.width,
    timestamp: performance.now(),
    visibleCanvasCount,
  };
}

function readFileViewerMotionProbeElement(
  elements: ReturnType<FileViewerElementRegistry["getElements"]>,
) {
  try {
    return elements.getDocumentSurfaceMotionProbeElement?.() ?? null;
  } catch {
    return null;
  }
}

function readFileViewerMotionProbeRect(
  element: HTMLElement,
  boundaryElement: HTMLElement | null,
) {
  const rect = element.getBoundingClientRect();
  let left = rect.left;
  let right = rect.right;

  // getBoundingClientRect reports the transformed content box even when an
  // ancestor removes part of it at paint. Intersect horizontal clipping
  // ancestors so telemetry measures the pixels a sidebar resize can actually
  // show, while leaving vertical viewport clipping (ordinary document
  // scrolling) out of this horizontal motion signal.
  let ancestor = element.parentElement;
  while (ancestor) {
    const style = getComputedStyle(ancestor);
    if (clipsFileViewerMotionProbeInline(style)) {
      const ancestorRect = ancestor.getBoundingClientRect();
      left = Math.max(left, ancestorRect.left);
      right = Math.min(right, ancestorRect.right);
    }
    if (ancestor === boundaryElement) break;
    ancestor = ancestor.parentElement;
  }

  return {
    height: rect.height,
    left,
    top: rect.top,
    width: Math.max(0, right - left),
  };
}

function clipsFileViewerMotionProbeInline(style: CSSStyleDeclaration) {
  const contain = style.contain.split(/\s+/);
  return (
    style.overflowX === "auto" ||
    style.overflowX === "clip" ||
    style.overflowX === "hidden" ||
    style.overflowX === "scroll" ||
    contain.includes("content") ||
    contain.includes("paint") ||
    contain.includes("strict")
  );
}

function findFileViewerTelemetryScroller(element: HTMLElement) {
  let candidate: HTMLElement | null = element.parentElement;
  while (candidate) {
    const overflowY = getComputedStyle(candidate).overflowY;
    if (overflowY === "auto" || overflowY === "scroll") return candidate;
    candidate = candidate.parentElement;
  }
  return null;
}

function countFileViewerVisibleCanvases(surfaceElement: HTMLElement) {
  let count = 0;
  for (const canvas of surfaceElement.querySelectorAll("canvas")) {
    if (!(canvas instanceof HTMLCanvasElement)) continue;
    if (canvas.width <= 0 || canvas.height <= 0) continue;
    const rect = canvas.getBoundingClientRect();
    if (rect.width <= 0 || rect.height <= 0) continue;
    count += 1;
  }
  return count;
}

function createFileViewerMotionTelemetryTrackers(
  host: FileViewerMotionTelemetryHost,
) {
  const elements = host.getElements();
  const rootElement = elements.viewerShellElement;
  const scroller = elements.documentSurfaceElement
    ? findFileViewerTelemetryScroller(elements.documentSurfaceElement)
    : null;

  const trackers = {
    addedNodeCount: 0,
    canvasResizeMutationCount: 0,
    layoutShiftCount: 0,
    layoutShiftScore: 0,
    longTaskCount: 0,
    longTaskDurationMs: 0,
    mutationCount: 0,
    removedNodeCount: 0,
    scrollEventCount: 0,
    disconnect: () => {},
  };

  const cleanups: (() => void)[] = [];

  if (rootElement && typeof MutationObserver === "function") {
    const mutationObserver = new MutationObserver((records) => {
      for (const record of records) {
        trackers.mutationCount += 1;
        trackers.addedNodeCount += record.addedNodes.length;
        trackers.removedNodeCount += record.removedNodes.length;
        if (
          record.type === "attributes" &&
          record.target instanceof HTMLCanvasElement &&
          (record.attributeName === "width" ||
            record.attributeName === "height")
        ) {
          trackers.canvasResizeMutationCount += 1;
        }
      }
    });
    mutationObserver.observe(rootElement, {
      attributeFilter: ["width", "height", "style", "data-pdf-render-status"],
      attributes: true,
      childList: true,
      subtree: true,
    });
    cleanups.push(() => mutationObserver.disconnect());
  }

  const layoutShiftObserver = observeFileViewerPerformanceEntries(
    "layout-shift",
    (entry) => {
      const layoutShift = entry as PerformanceEntry & {
        hadRecentInput?: boolean;
        value?: number;
      };
      if (layoutShift.hadRecentInput) return;
      trackers.layoutShiftCount += 1;
      trackers.layoutShiftScore += layoutShift.value ?? 0;
    },
  );
  if (layoutShiftObserver) cleanups.push(layoutShiftObserver);

  const longTaskObserver = observeFileViewerPerformanceEntries(
    "longtask",
    (entry) => {
      trackers.longTaskCount += 1;
      trackers.longTaskDurationMs += entry.duration;
    },
  );
  if (longTaskObserver) cleanups.push(longTaskObserver);

  if (scroller) {
    const handleScroll = () => {
      trackers.scrollEventCount += 1;
    };
    scroller.addEventListener("scroll", handleScroll, { passive: true });
    cleanups.push(() => scroller.removeEventListener("scroll", handleScroll));
  }

  trackers.disconnect = () => {
    for (const cleanup of cleanups) cleanup();
  };

  return trackers;
}

function observeFileViewerPerformanceEntries(
  type: string,
  onEntry: (entry: PerformanceEntry) => void,
) {
  if (typeof PerformanceObserver !== "function") return null;
  const supported = PerformanceObserver.supportedEntryTypes;
  if (Array.isArray(supported) && !supported.includes(type)) return null;

  try {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) onEntry(entry);
    });
    observer.observe({ type, buffered: false });
    return () => observer.disconnect();
  } catch {
    return null;
  }
}

function waitForFileViewerTelemetryFrames(frameCount: number) {
  return new Promise<void>((resolve) => {
    let remaining = Math.max(1, frameCount);
    const step = () => {
      remaining -= 1;
      if (remaining <= 0) {
        resolve();
        return;
      }
      requestAnimationFrame(step);
    };
    requestAnimationFrame(step);
  });
}

function collectFileViewerMotionTelemetryMetrics(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric[] {
  return [
    collectFileViewerBlinkMetric(runs),
    collectFileViewerGapStabilityMetric(runs),
    collectFileViewerOvershootMetric(runs),
    collectFileViewerContentStartSnapMetric(runs),
    collectFileViewerContentOvershootMetric(runs),
    collectFileViewerSettleSnapMetric(runs),
    collectFileViewerScrollIdentityMetric(runs),
    collectFileViewerDomMutationsMetric(runs),
    collectFileViewerLayoutShiftMetric(runs),
    collectFileViewerMainThreadMetric(runs),
    collectFileViewerCycleInvarianceMetric(runs),
  ];
}

function collectFileViewerContentStartSnapMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let snapPx = 0;

  for (const run of runs) {
    const before = getFileViewerTelemetryContentEdges(run.before);
    const first = run.samples[0]
      ? getFileViewerTelemetryContentEdges(run.samples[0])
      : null;
    if (!before || !first) continue;
    for (let index = 0; index < before.length; index += 1) {
      snapPx = Math.max(snapPx, Math.abs(first[index]! - before[index]!));
    }
  }

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX}px`,
    detail:
      "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.",
    id: "content-start-snap",
    label: "Content start snap",
    passed: snapPx <= FILE_VIEWER_TELEMETRY_CONTENT_START_SNAP_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(snapPx, 2)}px`,
  };
}

function collectFileViewerContentOvershootMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let overshootPx = 0;

  for (const run of runs) {
    const before = getFileViewerTelemetryContentEdges(run.before);
    const after = getFileViewerTelemetryContentEdges(run.after);
    if (!before || !after) continue;

    for (const sample of run.samples) {
      const edges = getFileViewerTelemetryContentEdges(sample);
      if (!edges) continue;
      for (let index = 0; index < edges.length; index += 1) {
        const low = Math.min(before[index]!, after[index]!);
        const high = Math.max(before[index]!, after[index]!);
        overshootPx = Math.max(
          overshootPx,
          low - edges[index]!,
          edges[index]! - high,
        );
      }
    }
  }

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX}px`,
    detail:
      "The visible content edges must stay inside their endpoint corridor; an excursion outside it is the first-frame back-and-forth wobble.",
    id: "content-overshoot",
    label: "Content overshoot",
    passed: overshootPx <= FILE_VIEWER_TELEMETRY_CONTENT_OVERSHOOT_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(Math.max(0, overshootPx), 2)}px`,
  };
}

function getFileViewerTelemetryContentEdges(
  sample: FileViewerMotionTelemetrySample,
): readonly [number, number, number, number] | null {
  if (sample.contentWidth <= 0 || sample.contentHeight <= 0) return null;
  return [
    sample.contentLeft,
    sample.contentTop,
    sample.contentLeft + sample.contentWidth,
    sample.contentTop + sample.contentHeight,
  ];
}

function collectFileViewerBlinkMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let blinkFrameCount = 0;
  for (const run of runs) {
    // Canvas-backed renderers must never lose all visible pixels mid-motion;
    // renderers without canvases (text-like) hold both endpoints at 0 and the
    // check degrades to surface-box presence.
    const endpointCanvasFloor = Math.min(
      run.before.visibleCanvasCount,
      run.after.visibleCanvasCount,
    );
    for (const sample of run.samples) {
      const surfaceMissing =
        sample.surfaceWidth <= 0 || sample.surfaceHeight <= 0;
      const canvasDropout =
        endpointCanvasFloor > 0 && sample.visibleCanvasCount === 0;
      if (surfaceMissing || canvasDropout) blinkFrameCount += 1;
    }
  }

  return {
    budget: "0 blink frames",
    detail:
      "A blink frame has a collapsed document surface or loses every visible canvas while both endpoints have rendered content.",
    id: "blink",
    label: "Blink",
    passed: blinkFrameCount === 0,
    value: `${blinkFrameCount} frames`,
  };
}

function collectFileViewerGapStabilityMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let reversalCount = 0;
  for (const run of runs) {
    const widths = run.samples.map((sample) => sample.gapWidth);
    const direction = Math.sign((widths.at(-1) ?? 0) - (widths.at(0) ?? 0));
    if (direction === 0) continue;
    for (let index = 1; index < widths.length; index += 1) {
      const step = widths[index] - widths[index - 1];
      if (
        Math.sign(step) === -direction &&
        Math.abs(step) > FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX
      ) {
        reversalCount += 1;
      }
    }
  }

  return {
    budget: `0 reversals over ${FILE_VIEWER_TELEMETRY_GAP_REVERSAL_EPSILON_PX}px`,
    detail:
      "The sidebar gap width must progress monotonically toward its target.",
    id: "gap-stability",
    label: "Gap stability",
    passed: reversalCount === 0,
    value: `${reversalCount} reversals`,
  };
}

function collectFileViewerOvershootMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let overshootPx = 0;
  for (const run of runs) {
    const endpoints = [
      run.samples.at(0)?.gapWidth ?? 0,
      run.samples.at(-1)?.gapWidth ?? 0,
    ];
    const low = Math.min(...endpoints);
    const high = Math.max(...endpoints);
    for (const sample of run.samples) {
      overshootPx = Math.max(
        overshootPx,
        low - sample.gapWidth,
        sample.gapWidth - high,
      );
    }
  }

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX}px`,
    detail:
      "The gap width must stay inside the interval spanned by its endpoints.",
    id: "overshoot",
    label: "Overshoot",
    passed: overshootPx <= FILE_VIEWER_TELEMETRY_OVERSHOOT_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(Math.max(0, overshootPx), 2)}px`,
  };
}

// The settle-boundary blink: once the transform leaves the sliding phase it
// is identity over the already-settled layout, so every post-sliding sample's
// surface box must already equal the final rest box. Deviation here is the
// "reach the target, then move again" wobble. (Comparing against the final
// box — not the previous frame — keeps dropped frames, whose mid-flight box
// coordinates legitimately sweep with the transform's anchor term, out of
// this metric; main-thread covers those.)
function collectFileViewerSettleSnapMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let snapPx = 0;
  for (const run of runs) {
    let sawSliding = false;
    // Content position, not box position: late page-size measurements can
    // resize the stage after the motion — vertically the scroll layer
    // compensates scrollTop in the same commit, horizontally the pages stay
    // centered — so the box moves while the pixels do not. The
    // content-invariant observables are surfaceTop + scrollTop (vertical)
    // and the stage center (horizontal).
    const afterContentTop = run.after.surfaceTop + run.after.scrollTop;
    const afterContentCenter =
      run.after.surfaceLeft + run.after.surfaceWidth / 2;
    for (const sample of run.samples) {
      if (sample.motionPhase === "sliding") {
        sawSliding = true;
        continue;
      }
      if (!sawSliding) continue;
      // A sample can catch the frame where the kernel has advanced to
      // settling but the last sliding tick's transform is still applied; its
      // box sweeps with the anchor term while the viewport pixels hold. The
      // metric asserts rest AFTER the transform clears.
      if (
        sample.surfaceTransform !== "none" &&
        sample.surfaceTransform !== ""
      ) {
        continue;
      }
      snapPx = Math.max(
        snapPx,
        Math.abs(
          sample.surfaceLeft + sample.surfaceWidth / 2 - afterContentCenter,
        ),
        Math.abs(sample.surfaceTop + sample.scrollTop - afterContentTop),
      );
    }
  }

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX}px settle deviation`,
    detail:
      "After the sliding phase ends the surface box must already sit at its final rest position; deviation is the settle-boundary wobble.",
    id: "settle-snap",
    label: "Settle snap",
    passed: snapPx <= FILE_VIEWER_TELEMETRY_SETTLE_SNAP_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(snapPx, 2)}px`,
  };
}

function collectFileViewerScrollIdentityMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  let scrollRangePx = 0;
  for (const run of runs) {
    // Layout and scroll commit in the toggle's own task, so every sampled
    // frame (the first sample runs after the toggle) sees the final scrollTop.
    const tops = run.samples.map((sample) => sample.scrollTop);
    if (tops.length === 0) continue;
    scrollRangePx = Math.max(
      scrollRangePx,
      Math.max(...tops) - Math.min(...tops),
    );
  }

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX}px scroll range`,
    detail:
      "scrollTop is rebased once at slide start and must hold for the rest of the motion.",
    id: "scroll-identity",
    label: "Scroll identity",
    passed: scrollRangePx <= FILE_VIEWER_TELEMETRY_SCROLL_IDENTITY_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(scrollRangePx, 2)}px`,
  };
}

function collectFileViewerDomMutationsMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  const canvasResizeMutationCount = runs.reduce(
    (count, run) => count + run.canvasResizeMutationCount,
    0,
  );
  const mutationCount = runs.reduce(
    (count, run) => count + run.mutationCount,
    0,
  );

  return {
    budget: "0 canvas resizes",
    detail:
      "Raster headroom is prepared before motion, so no canvas backing store may resize (and wipe) during it. Total mutations are reported for diagnosis.",
    id: "dom-mutations",
    label: "DOM mutations",
    passed: canvasResizeMutationCount === 0,
    value: `${canvasResizeMutationCount} canvas resizes / ${mutationCount} total`,
  };
}

function collectFileViewerLayoutShiftMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  const score = runs.reduce((total, run) => total + run.layoutShiftScore, 0);
  const count = runs.reduce((total, run) => total + run.layoutShiftCount, 0);

  return {
    budget: "diagnostic",
    detail:
      "Cumulative layout-shift entries during motion (the intentional sidebar reflow is included; read alongside settle-snap).",
    id: "layout-shift",
    label: "Layout shift",
    passed: true,
    value: `${count} shifts / ${formatFileViewerTelemetryNumber(score, 4)}`,
  };
}

function collectFileViewerMainThreadMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  const gaps: number[] = [];
  for (const run of runs) {
    for (let index = 1; index < run.samples.length; index += 1) {
      gaps.push(
        run.samples[index].timestamp - run.samples[index - 1].timestamp,
      );
    }
  }
  const sortedGaps = gaps.slice().sort((left, right) => left - right);
  const p95 =
    sortedGaps.length > 0
      ? sortedGaps[
          Math.min(sortedGaps.length - 1, Math.floor(sortedGaps.length * 0.95))
        ]
      : 0;
  const max = sortedGaps.at(-1) ?? 0;
  const longTaskDurationMs = runs.reduce(
    (total, run) => total + run.longTaskDurationMs,
    0,
  );

  const passed =
    p95 <= FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS &&
    max <= FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS;

  return {
    budget: `p95 <= ${FILE_VIEWER_TELEMETRY_MAIN_THREAD_P95_BUDGET_MS}ms, max <= ${FILE_VIEWER_TELEMETRY_MAIN_THREAD_MAX_BUDGET_MS}ms`,
    detail:
      "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.",
    id: "main-thread",
    label: "Main thread",
    passed,
    value: `p95 ${formatFileViewerTelemetryNumber(p95, 1)}ms / max ${formatFileViewerTelemetryNumber(max, 1)}ms / ${formatFileViewerTelemetryNumber(longTaskDurationMs, 1)}ms long tasks`,
  };
}

function collectFileViewerCycleInvarianceMetric(
  runs: readonly FileViewerMotionTelemetryRun[],
): FileViewerMotionTelemetryMetric {
  if (runs.length < 2) {
    return {
      budget: `<= ${FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX}px`,
      detail: "Needs a full toggle cycle.",
      id: "cycle-invariance",
      label: "Cycle invariance",
      passed: true,
      value: "n/a",
    };
  }

  const first = runs[0].before;
  const last = runs[runs.length - 1].after;
  const drift = Math.max(
    Math.abs(first.gapWidth - last.gapWidth),
    Math.abs(first.surfaceLeft - last.surfaceLeft),
    Math.abs(first.surfaceWidth - last.surfaceWidth),
    Math.abs(first.scrollTop - last.scrollTop),
  );

  return {
    budget: `<= ${FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX}px`,
    detail:
      "A full open/close cycle must return the gap, surface box, and scroll position to their starting values.",
    id: "cycle-invariance",
    label: "Cycle invariance",
    passed: drift <= FILE_VIEWER_TELEMETRY_CYCLE_BUDGET_PX,
    value: `${formatFileViewerTelemetryNumber(drift, 2)}px`,
  };
}

function logFileViewerMotionTelemetryResult(
  result: FileViewerMotionTelemetryResult,
) {
  const summary = {
    durationMs: Number(result.durationMs.toFixed(1)),
    failedMetricIds: result.metrics
      .filter((metric) => !metric.passed)
      .map((metric) => metric.id),
    metrics: result.metrics,
    sampledFrameCount: result.sampledFrameCount,
    status: result.status,
  };
  console.info(
    `[file-viewer:motion-telemetry] result ${JSON.stringify(summary)}`,
  );
}

function formatFileViewerTelemetryNumber(
  value: number,
  fractionDigits: number,
) {
  return Number.isFinite(value) ? Number(value.toFixed(fractionDigits)) : 0;
}
registry/new-york-v4/ui/file-viewer-header.tsx
"use client";

import * as React from "react";
import { PanelLeft, PanelRight } from "lucide-react";

import { cn } from "@/lib/utils";

import { Button } from "./button";
import { Spinner } from "./spinner";
import { useFileViewerControlsState } from "./file-viewer-provider";
import { useFileViewerRequiredResourceState } from "./file-viewer-resource-state";
import {
  useFileViewerContext,
  useFileViewerShell,
} from "./file-viewer-context";
import { resolveFileViewerSidebarTriggerAccessibilityProps } from "./file-viewer-accessibility";
import { ViewerControls } from "./viewer-controls";
import { ViewerHeaderOutlet } from "./viewer-header-outlet";

type ButtonProps = React.ComponentProps<typeof Button> & {
  loading?: boolean;
};

export type FileViewerHeaderProps = React.ComponentProps<"div">;
export type FileViewerTitleProps = React.ComponentProps<"span">;
export type FileViewerMetaProps = React.ComponentProps<"span"> & {
  visibility?: "responsive" | "visible";
};
export type FileViewerControlsProps = Omit<
  React.ComponentProps<typeof ViewerControls>,
  | "downloads"
  | "loading"
  | "position"
  | "rotate"
  | "subtitle"
  | "title"
  | "zoom"
>;
export type FileViewerSidebarTriggerProps = ButtonProps;

export function FileViewerHeader({
  children,
  className,
  ...props
}: FileViewerHeaderProps) {
  const { hasHeaderOutlets, headerMode } = useFileViewerContext();
  const content = children ?? <FileViewerDefaultHeaderContent />;

  if (headerMode === "outlets" && hasHeaderOutlets) {
    return <>{content}</>;
  }

  return (
    <div
      data-slot="file-viewer-header"
      className={cn(
        "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",
        className,
      )}
      {...props}
    >
      {content}
    </div>
  );
}

function FileViewerDefaultHeaderContent() {
  const { hasHeaderOutlets, headerMode } = useFileViewerContext();

  if (headerMode === "outlets" && hasHeaderOutlets) {
    return (
      <>
        <FileViewerTitle />
        <FileViewerMeta />
        <FileViewerControls />
      </>
    );
  }

  return (
    <>
      <div
        data-slot="file-viewer-header-title-group"
        className="flex min-w-0 flex-1 items-baseline gap-2"
      >
        <FileViewerTitle />
        <FileViewerMeta />
      </div>
      <FileViewerControls />
    </>
  );
}

export function FileViewerTitle({ className, ...props }: FileViewerTitleProps) {
  const { hasHeaderOutlets, headerMode } = useFileViewerContext();
  const { descriptor } = useFileViewerRequiredResourceState();
  const title = (
    <span
      data-slot="file-viewer-title"
      className={cn(
        "text-foreground min-w-0 truncate text-[13px] leading-5 font-medium",
        className,
      )}
      title={descriptor.displayName}
      {...props}
    >
      {descriptor.displayName}
    </span>
  );

  if (headerMode === "outlets" && hasHeaderOutlets) {
    return <ViewerHeaderOutlet name="titleGroup">{title}</ViewerHeaderOutlet>;
  }

  return title;
}

export function FileViewerMeta({
  className,
  visibility = "responsive",
  ...props
}: FileViewerMetaProps) {
  const { hasHeaderOutlets, headerMode } = useFileViewerContext();
  const { descriptor, resource } = useFileViewerRequiredResourceState();
  const metaText =
    resource.mimeType || descriptor.mimeType || descriptor.category;

  if (!metaText) return null;

  const meta = (
    <span
      data-slot="file-viewer-meta"
      className={cn(
        "text-muted-foreground min-w-0 shrink truncate text-xs",
        visibility === "responsive" && "hidden sm:inline",
        className,
      )}
      {...props}
    >
      {metaText}
    </span>
  );

  if (headerMode === "outlets" && hasHeaderOutlets) {
    return <ViewerHeaderOutlet name="titleGroup">{meta}</ViewerHeaderOutlet>;
  }

  return meta;
}

export function FileViewerControls({
  className,
  extra,
  ...props
}: FileViewerControlsProps) {
  const { resource } = useFileViewerRequiredResourceState();
  const controlsState = useFileViewerControlsState();
  const downloads = controlsState?.downloads ?? [resource.originalDownload];
  const { hasHeaderOutlets, headerMode } = useFileViewerContext();

  const controls = (
    <ViewerControls
      {...props}
      data-file-viewer-controls={controlsState ? "ready" : "idle"}
      data-slot="file-viewer-controls"
      className={cn(
        "ms-auto h-8 min-w-0 border-b-0 bg-transparent px-0",
        className,
      )}
      downloads={downloads}
      extra={extra ?? controlsState?.extra}
      loading={controlsState?.loading ?? false}
      position={controlsState?.position ?? null}
      rotate={controlsState?.rotate ?? null}
      zoom={controlsState?.zoom ?? null}
    />
  );

  if (headerMode === "outlets" && hasHeaderOutlets) {
    return <ViewerHeaderOutlet name="controls">{controls}</ViewerHeaderOutlet>;
  }

  return controls;
}

export function FileViewerSidebarTrigger({
  className,
  disabled,
  loading,
  onClick,
  children,
  size = "icon",
  variant = "ghost",
  "aria-label": ariaLabel = "Toggle sidebar",
  ...props
}: FileViewerSidebarTriggerProps) {
  const {
    canToggleSidebar,
    elementRegistry,
    mode,
    side,
    isSidebarInteractive,
    sidebarId,
    sidebarState,
    toggleSidebar,
  } = useFileViewerShell("FileViewerSidebarTrigger");
  const isDisabled = Boolean(disabled || loading || !canToggleSidebar);
  const Icon = side === "right" ? PanelRight : PanelLeft;
  const setTriggerElement = React.useCallback(
    (element: HTMLButtonElement | null) => {
      elementRegistry.registerSidebarTriggerElement(element);
    },
    [elementRegistry],
  );
  const accessibilityProps = resolveFileViewerSidebarTriggerAccessibilityProps({
    ariaLabel,
    canToggleSidebar,
    isDisabled,
    isSidebarInteractive,
    sidebarId,
  });

  return (
    <Button
      ref={setTriggerElement}
      {...accessibilityProps}
      className={cn("size-8", className)}
      data-file-viewer-sidebar-mode={mode}
      data-file-viewer-sidebar-side={side}
      data-file-viewer-sidebar-state={sidebarState}
      data-side={side}
      data-slot="file-viewer-sidebar-trigger"
      disabled={isDisabled}
      onClick={(event) => {
        if (isDisabled) {
          event.preventDefault();
          return;
        }

        onClick?.(event);
        if (!event.defaultPrevented) {
          toggleSidebar();
        }
      }}
      size={size}
      variant={variant}
      {...props}
    >
      {loading ? <Spinner className="size-4 animate-spin" /> : null}
      {children ?? (
        <>
          {loading ? null : <Icon />}
          <span className="sr-only">Toggle sidebar</span>
        </>
      )}
    </Button>
  );
}
registry/new-york-v4/ui/file-viewer-html-viewer.tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import {
  viewerContentRenderKey,
  type ViewerResource,
} from "@/lib/viewer-resource";

import { ViewerFallback } from "./file-viewer-fallback";
import { getHtmlViewerSrcDoc } from "./file-viewer-html-srcdoc-cache";
import { loadTextResource } from "./file-viewer-text-resource";
import { isAbortError } from "./viewer-abortable-request";
import { useViewerControlsRegistration } from "./viewer-controls";
import { useZoom, ZoomActions } from "./viewer-zoom";
import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { joinEffectKey } from "@/lib/effect-key";

type HtmlLoadState =
  | { status: "loading"; key: unknown }
  | { status: "loaded"; key: unknown; html: string }
  | { status: "error"; key: unknown; error: unknown };

export function FileViewerHtmlContent({
  resource,
  className,
  bare,
  controls = true,
  descriptorSignal,
}: {
  resource: ViewerResource;
  className?: string;
  bare?: boolean;
  controls?: boolean;
  descriptorSignal: AbortSignal;
}) {
  if (resource.content.payload.kind === "text") {
    return (
      <FileViewerHtmlContentFrame
        key={viewerContentRenderKey(resource.content)}
        resource={resource}
        html={resource.content.payload.text}
        className={className}
        bare={bare}
        controls={controls}
      />
    );
  }
  return (
    <HtmlFileResource
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      descriptorSignal={descriptorSignal}
    />
  );
}

function HtmlFileResource({
  resource,
  className,
  bare,
  controls,
  descriptorSignal,
}: {
  resource: ViewerResource;
  className?: string;
  bare?: boolean;
  controls: boolean;
  descriptorSignal: AbortSignal;
}) {
  const content = resource.content;
  const contentKey = content.key;
  const [state, setState] = React.useState<HtmlLoadState>({
    status: "loading",
    key: contentKey,
  });

  useKeyedMountEffect(
    joinEffectKey([content, contentKey, descriptorSignal, resource.fileName]),
    () => {
      let active = true;
      const controller = new AbortController();
      const abortLocal = () => controller.abort();
      setState({ status: "loading", key: contentKey });

      if (descriptorSignal.aborted) {
        abortLocal();
      } else {
        descriptorSignal.addEventListener("abort", abortLocal, { once: true });
      }

      loadTextResource({
        content,
        fileName: resource.fileName,
        signal: controller.signal,
      }).then(
        (html) => {
          if (active && !controller.signal.aborted) {
            setState({ status: "loaded", key: contentKey, html });
          }
        },
        (error: unknown) => {
          if (!active || isAbortError(error)) return;
          setState({ status: "error", key: contentKey, error });
        },
      );

      return () => {
        active = false;
        descriptorSignal.removeEventListener("abort", abortLocal);
        abortLocal();
      };
    },
  );

  if (state.key !== contentKey) {
    return (
      <ViewerFallback resource={resource} className={className} bare={bare} />
    );
  }
  if (state.status === "error") {
    throw state.error;
  }
  if (state.status === "loading") {
    return (
      <ViewerFallback resource={resource} className={className} bare={bare} />
    );
  }

  const { html } = state;
  return (
    <FileViewerHtmlContentFrame
      key={contentKey}
      resource={resource}
      html={html}
      className={className}
      bare={bare}
      controls={controls}
    />
  );
}

function FileViewerHtmlContentFrame({
  resource,
  html,
  className,
  bare,
  controls,
}: {
  resource: ViewerResource;
  html: string;
  className?: string;
  bare?: boolean;
  controls: boolean;
}) {
  const fileName = resource.fileName;
  const { scale, zoom, reset } = useZoom();
  useHtmlControlsRegistration({ reset, scale, zoom });

  return (
    <div
      data-slot="html-file-viewer-content"
      className={cn(
        "bg-card flex min-h-0 flex-1 flex-col overflow-hidden",
        bare ? "h-full" : "min-h-64",
        className,
      )}
    >
      {controls ? (
        <HtmlContentToolbar scale={scale} zoom={zoom} reset={reset} />
      ) : null}
      <SandboxedDoc
        contentKey={resource.content.key}
        html={html}
        title={fileName}
        scale={scale}
      />
    </div>
  );
}

function useHtmlControlsRegistration({
  reset,
  scale,
  zoom,
}: {
  reset: () => void;
  scale: number;
  zoom: (factor: number) => void;
}) {
  const onControlsChange = useViewerControlsRegistration();

  useKeyedMountEffect(
    joinEffectKey([onControlsChange, reset, scale, zoom]),
    () => {
      if (!onControlsChange) return;

      onControlsChange({
        zoom: {
          scale,
          onZoomOut: () => zoom(1 / 1.2),
          onZoomIn: () => zoom(1.2),
          onReset: reset,
        },
      });

      return () => onControlsChange(null);
    },
  );
}

function HtmlContentToolbar({
  scale,
  zoom,
  reset,
}: {
  scale: number;
  zoom: (factor: number) => void;
  reset: () => void;
}) {
  return (
    <div className="bg-card flex h-10 shrink-0 items-center justify-end gap-1 border-b px-2">
      <ZoomActions scale={scale} zoom={zoom} reset={reset} />
    </div>
  );
}

function SandboxedDoc({
  contentKey,
  html,
  title,
  scale = 1,
}: {
  contentKey: string;
  html: string;
  title: string;
  scale?: number;
}) {
  const { containerRef, shouldMountIframe } =
    useLazySandboxedIframeMount(contentKey);
  const srcDoc = React.useMemo(
    () =>
      shouldMountIframe
        ? getHtmlViewerSrcDoc({
            contentKey,
            html,
          })
        : "",
    [contentKey, html, shouldMountIframe],
  );

  return (
    <div
      ref={containerRef}
      className="bg-document flex min-h-0 flex-1 flex-col overflow-auto"
    >
      {shouldMountIframe ? (
        <iframe
          sandbox=""
          srcDoc={srcDoc}
          title={title}
          className="bg-document h-full min-h-0 w-full flex-1 border-0"
          style={{ zoom: scale }}
        />
      ) : null}
    </div>
  );
}

function useLazySandboxedIframeMount(resetKey: string) {
  const containerRef = React.useRef<HTMLDivElement>(null);
  const [shouldMountIframe, setShouldMountIframe] = React.useState(false);
  const mountEffectKey = shouldMountIframe
    ? null
    : joinEffectKey(["html-iframe-visible", resetKey]);

  useKeyedMountEffect(mountEffectKey, () => {
    if (typeof IntersectionObserver === "undefined") {
      setShouldMountIframe(true);
      return;
    }

    const node = containerRef.current;
    if (!node) {
      setShouldMountIframe(true);
      return;
    }

    let observer: IntersectionObserver | null = null;
    try {
      observer = new IntersectionObserver(
        (entries) => {
          if (
            entries.some(
              (entry) => entry.isIntersecting || entry.intersectionRatio > 0,
            )
          ) {
            setShouldMountIframe(true);
            observer?.disconnect();
          }
        },
        { rootMargin: "512px" },
      );
    } catch {
      setShouldMountIframe(true);
      return;
    }

    observer.observe(node);

    return () => observer.disconnect();
  });

  return { containerRef, shouldMountIframe };
}
registry/new-york-v4/ui/file-viewer-html-srcdoc-cache.ts
import { lruGet, lruSet } from "./viewer-lru-cache";

const HTML_VIEWER_SRC_DOC_IDENTITIES_PER_CONTENT = 2;

const HTML_VIEWER_DOCUMENT_STYLE = `<style data-retab-html-viewer-padding>
html {
  background: white;
}
body {
  box-sizing: border-box;
  margin: 0;
  min-width: 0;
  padding: 1rem;
}
body > :first-child {
  margin-block-start: 0;
}
body > :last-child {
  margin-block-end: 0;
}
</style>`;

interface HtmlViewerSrcDocCacheEntry {
  readonly html: string;
  readonly srcDoc: string;
}

interface HtmlViewerSrcDocCacheBucket {
  readonly entries: HtmlViewerSrcDocCacheEntry[];
}

const htmlViewerSrcDocCache = new Map<string, HtmlViewerSrcDocCacheBucket>();
let htmlViewerSrcDocCacheHitsForTests = 0;
let htmlViewerSrcDocCacheMissesForTests = 0;

export function getHtmlViewerSrcDoc({
  contentKey,
  html,
}: {
  contentKey: string;
  html: string;
}) {
  const bucket = lruGet(htmlViewerSrcDocCache, contentKey);
  const cached = bucket?.entries.find((entry) => entry.html === html);

  if (cached) {
    htmlViewerSrcDocCacheHitsForTests += 1;
    return cached.srcDoc;
  }

  htmlViewerSrcDocCacheMissesForTests += 1;
  const srcDoc = createHtmlViewerSrcDoc(html);
  const entries = [...(bucket?.entries ?? []), { html, srcDoc }].slice(
    -HTML_VIEWER_SRC_DOC_IDENTITIES_PER_CONTENT,
  );

  lruSet(htmlViewerSrcDocCache, contentKey, { entries });
  return srcDoc;
}

export function clearHtmlViewerSrcDocCacheForTests() {
  htmlViewerSrcDocCache.clear();
  htmlViewerSrcDocCacheHitsForTests = 0;
  htmlViewerSrcDocCacheMissesForTests = 0;
}

export function htmlViewerSrcDocCacheStatsForTests() {
  let identities = 0;
  for (const bucket of htmlViewerSrcDocCache.values()) {
    identities += bucket.entries.length;
  }

  return {
    contentKeys: htmlViewerSrcDocCache.size,
    hits: htmlViewerSrcDocCacheHitsForTests,
    identities,
    misses: htmlViewerSrcDocCacheMissesForTests,
  };
}

function createHtmlViewerSrcDoc(html: string) {
  const headOpenTag = /<head(?:\s[^>]*)?>/i;
  const htmlOpenTag = /<html(?:\s[^>]*)?>/i;

  if (headOpenTag.test(html)) {
    return html.replace(
      headOpenTag,
      (tag) => `${tag}${HTML_VIEWER_DOCUMENT_STYLE}`,
    );
  }
  if (htmlOpenTag.test(html)) {
    return html.replace(
      htmlOpenTag,
      (tag) => `${tag}<head>${HTML_VIEWER_DOCUMENT_STYLE}</head>`,
    );
  }
  return `${HTML_VIEWER_DOCUMENT_STYLE}${html}`;
}
registry/new-york-v4/ui/file-viewer-preview.tsx
"use client";

import { cn } from "@/lib/utils";

import {
  FileViewerContent,
  FileViewerInset,
  FileViewerViewport,
} from "./file-viewer-layout";
import { FileViewerDocument } from "./file-viewer-document";
import { FileViewer } from "./file-viewer-shell";
import { FileViewerProvider } from "./file-viewer-provider";
import { type FileViewerResourceOptions } from "./file-viewer-resource-state";

export type FileViewerPreviewProps = FileViewerResourceOptions & {
  className?: string;
};

export function FileViewerPreview({
  category,
  className,
  fallbackFrameSize,
  fallbackSlideSize,
  isolateStyles,
  source,
}: FileViewerPreviewProps) {
  return (
    <FileViewerProvider
      category={category}
      fallbackFrameSize={fallbackFrameSize}
      fallbackSlideSize={fallbackSlideSize}
      isolateStyles={isolateStyles}
      source={source}
    >
      <FileViewer
        data-file-viewer-mode="preview"
        className={cn("h-full min-h-0", className)}
      >
        <FileViewerContent>
          <FileViewerInset>
            <FileViewerViewport>
              <FileViewerDocument controls />
            </FileViewerViewport>
          </FileViewerInset>
        </FileViewerContent>
      </FileViewer>
    </FileViewerProvider>
  );
}
registry/new-york-v4/ui/file-viewer-provider.tsx
"use client";

import * as React from "react";

import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { joinEffectKey } from "@/lib/effect-key";
import type { FileCategory, ViewerSource } from "@/lib/viewer-source";

import {
  FileViewerContext,
  type FileViewerContextValue,
  type FileViewerHeaderMode,
} from "./file-viewer-context";
import type { FileViewerFallbackSize } from "./file-viewer-core";
import {
  FileViewerResourceProvider,
  useFileViewerResourceState,
} from "./file-viewer-resource-state";
import {
  ViewerControlsRegistrationProvider,
  type ViewerControlsState,
} from "./viewer-controls";
import { warnViewerDevelopmentOnce } from "./viewer-diagnostics";
import {
  useViewerHeaderOutletAvailable,
  useViewerHeaderOutletsAvailable,
} from "./viewer-header-outlet";

export type FileViewerControlsState = ViewerControlsState;

type FileViewerControlsRegistration = {
  descriptorKey: string;
  state: FileViewerControlsState | null;
};

type FileViewerControlsValue = {
  controlsState: FileViewerControlsState | null;
};

const FileViewerControlsContext =
  React.createContext<FileViewerControlsValue | null>(null);

function useFileViewerControlsController(descriptorKey: string): {
  controlsValue: FileViewerControlsValue;
  handleControlsChange: (state: FileViewerControlsState | null) => void;
} {
  const [controlsRegistration, setControlsRegistration] =
    React.useState<FileViewerControlsRegistration>(() => ({
      descriptorKey,
      state: null,
    }));
  const controlsState =
    controlsRegistration.descriptorKey === descriptorKey
      ? controlsRegistration.state
      : null;
  const handleControlsChange = React.useCallback(
    (state: FileViewerControlsState | null) => {
      setControlsRegistration({ descriptorKey, state });
    },
    [descriptorKey],
  );
  const controlsValue = React.useMemo<FileViewerControlsValue>(
    () => ({
      controlsState,
    }),
    [controlsState],
  );

  return {
    controlsValue,
    handleControlsChange,
  };
}

export function useFileViewerControlsState() {
  const context = React.useContext(FileViewerControlsContext);
  if (!context) {
    throw new Error("File viewer controls must be used within FileViewer.");
  }
  return context.controlsState;
}

export type FileViewerProviderProps = {
  children: React.ReactNode;
  source: ViewerSource;
  category?: FileCategory;
  fallbackFrameSize?: FileViewerFallbackSize;
  fallbackSlideSize?: FileViewerFallbackSize;
  isolateStyles?: boolean;
  headerMode?: FileViewerHeaderMode;
  defaultSidebarOpen?: boolean;
  sidebarOpen?: boolean;
  onSidebarOpenChange?: (open: boolean) => void;
};

export function FileViewerProvider({
  children,
  source,
  category,
  fallbackFrameSize,
  fallbackSlideSize,
  isolateStyles,
  headerMode,
  defaultSidebarOpen,
  sidebarOpen,
  onSidebarOpenChange,
}: FileViewerProviderProps) {
  const diagnosticViewerId = React.useId();
  const resourceState = useFileViewerResourceState({
    category,
    fallbackFrameSize,
    fallbackSlideSize,
    isolateStyles,
    source,
  });
  const controlsController = useFileViewerControlsController(
    resourceState.descriptorKey,
  );
  const hasTitleGroupOutlet = useViewerHeaderOutletAvailable("titleGroup");
  const hasControlsOutlet = useViewerHeaderOutletAvailable("controls");
  const hasHeaderOutlets = useViewerHeaderOutletsAvailable();
  const resolvedHeaderMode = headerMode ?? "inline";
  const contextValue = React.useMemo<FileViewerContextValue>(
    () => ({
      headerMode: resolvedHeaderMode,
      hasHeaderOutlets: resolvedHeaderMode === "outlets" && hasHeaderOutlets,
      isInsideFileViewer: true,
      resourceCategory: resourceState.descriptor.category,
      sidebarOpenProps: {
        defaultOpen: defaultSidebarOpen,
        onOpenChange: onSidebarOpenChange,
        open: sidebarOpen,
      },
    }),
    [
      defaultSidebarOpen,
      hasHeaderOutlets,
      onSidebarOpenChange,
      resolvedHeaderMode,
      resourceState.descriptor.category,
      sidebarOpen,
    ],
  );
  const hasExactlyOneHeaderOutlet =
    resolvedHeaderMode === "outlets" &&
    hasTitleGroupOutlet !== hasControlsOutlet;

  useKeyedMountEffect(
    hasExactlyOneHeaderOutlet
      ? joinEffectKey([
          "file-viewer-header-outlet-single-mount",
          diagnosticViewerId,
          hasTitleGroupOutlet,
          hasControlsOutlet,
        ])
      : null,
    () => {
      warnViewerDevelopmentOnce({
        code: "file_viewer_header_outlet_single_mount",
        message: "file viewer has exactly one header outlet mounted.",
        rootId: diagnosticViewerId,
        details: {
          controlsOutletAvailable: hasControlsOutlet,
          titleGroupOutletAvailable: hasTitleGroupOutlet,
        },
      });
    },
  );

  return (
    <FileViewerResourceProvider value={resourceState}>
      <FileViewerControlsContext.Provider
        value={controlsController.controlsValue}
      >
        <ViewerControlsRegistrationProvider
          onControlsChange={controlsController.handleControlsChange}
        >
          <FileViewerContext.Provider value={contextValue}>
            {children}
          </FileViewerContext.Provider>
        </ViewerControlsRegistrationProvider>
      </FileViewerControlsContext.Provider>
    </FileViewerResourceProvider>
  );
}
registry/new-york-v4/ui/file-viewer-renderer-contract.ts
"use client";

import {
  getFileViewerMotionRasterInlineSize,
  type FileViewerMotionFrame,
  type FileViewerMotionPhase,
} from "./file-viewer-motion-plan";
import type { ViewerDocumentTransition } from "./viewer-types";

export type FileViewerDocumentAlign = "start" | "center" | "end";

// Physical inline direction of the document frame (computed CSS `direction`).
// The fit-width motion transform works on the physical X axis, so it needs
// the direction to model where auto-margin alignment actually puts the stage.
export type FileViewerInlineDirection = "ltr" | "rtl";

// `phase` is the motion clock's state; `documentTransition` is the single
// spelling of the policies derived from it. Renderers read layout/scroll/
// visual decisions from the transition, never from duplicated top-level
// fields. `isTransitioning` is shorthand for `phase === "sliding"`.
export type FileViewerRendererFrame = {
  align: FileViewerDocumentAlign;
  canToggleSidebar: boolean;
  direction: FileViewerInlineDirection;
  documentTransition: ViewerDocumentTransition;
  element: HTMLDivElement | null;
  fromInlineSize: number | null;
  isTransitioning: boolean;
  layoutInlineSize: number | null;
  motionDurationMs: number;
  phase: FileViewerMotionPhase;
  rasterInlineSize: number | null;
  settledInlineSize: number | null;
  shellInlineSize: number | null;
  toInlineSize: number | null;
  usesShellGeometry: boolean;
};

export function resolveFileViewerRendererLayoutInlineSize({
  fallbackInlineSize,
  rendererFrame,
}: {
  fallbackInlineSize: number | null;
  rendererFrame: FileViewerRendererFrame;
}) {
  const fallbackSize = resolveMeasuredInlineSize(fallbackInlineSize);

  // Commit-then-relax: the renderer lays out at the motion's TARGET width for
  // the entire motion (layoutPolicy "target" from the first sliding frame).
  // The in-flight visual is the surface motion transform reprojecting that
  // settled layout, so settle never commits layout.
  if (
    rendererFrame.documentTransition.layoutPolicy === "target" &&
    rendererFrame.toInlineSize != null
  ) {
    return rendererFrame.toInlineSize;
  }

  return rendererFrame.layoutInlineSize ?? fallbackSize;
}

export function createFileViewerRendererFrame({
  align,
  canToggleSidebar,
  direction = "ltr",
  element,
  fallbackInlineSize,
  motionFrame,
  motionDurationMs,
  usesShellGeometry,
}: {
  align: FileViewerDocumentAlign;
  canToggleSidebar: boolean;
  direction?: FileViewerInlineDirection;
  element: HTMLDivElement | null;
  fallbackInlineSize: number | null;
  motionFrame: FileViewerMotionFrame;
  motionDurationMs: number;
  usesShellGeometry: boolean;
}): FileViewerRendererFrame {
  const measuredInlineSize = resolveMeasuredInlineSize(fallbackInlineSize);
  const shellInlineSize = usesShellGeometry
    ? motionFrame.shellInlineSize
    : null;
  const layoutInlineSize = usesShellGeometry
    ? motionFrame.layoutInlineSize
    : measuredInlineSize;
  const settledInlineSize = usesShellGeometry
    ? motionFrame.toInlineSize
    : measuredInlineSize;
  const rasterInlineSize = usesShellGeometry
    ? getFileViewerMotionRasterInlineSize(motionFrame)
    : layoutInlineSize;
  const fromInlineSize = usesShellGeometry
    ? motionFrame.fromInlineSize
    : settledInlineSize;
  const toInlineSize = usesShellGeometry
    ? motionFrame.toInlineSize
    : settledInlineSize;
  const documentTransition = createFileViewerRendererTransition({
    motionFrame,
    usesShellGeometry,
  });

  const phase = usesShellGeometry ? motionFrame.phase : "idle";

  return {
    align,
    canToggleSidebar,
    direction,
    documentTransition,
    element,
    fromInlineSize,
    isTransitioning: phase === "sliding",
    layoutInlineSize,
    motionDurationMs,
    phase,
    rasterInlineSize,
    settledInlineSize: settledInlineSize ?? layoutInlineSize,
    shellInlineSize,
    toInlineSize: toInlineSize ?? layoutInlineSize,
    usesShellGeometry,
  };
}

function resolveMeasuredInlineSize(inlineSize: number | null | undefined) {
  return inlineSize != null && Number.isFinite(inlineSize) && inlineSize > 0
    ? inlineSize
    : null;
}

function createFileViewerRendererTransition({
  motionFrame,
  usesShellGeometry,
}: {
  motionFrame: FileViewerMotionFrame;
  usesShellGeometry: boolean;
}): ViewerDocumentTransition {
  if (!usesShellGeometry) {
    return {
      layoutPolicy: "live",
      scrollPolicy: "preserve",
      source: "none",
      transitionId: null,
      visualPolicy: "none",
    };
  }

  switch (motionFrame.phase) {
    // Sliding commits the TARGET layout immediately (inside the toggle's own
    // task, before first paint) and rebases scroll to the reading anchor in
    // the same commit; the shell transform hides the jump. Settling then has
    // no layout or scroll work left — it only clears the identity transform.
    case "sliding":
      return {
        layoutPolicy: "target",
        scrollPolicy: "rebase",
        source: "viewer-shell",
        transitionId: motionFrame.motionId,
        visualPolicy: "shell-transform",
      };
    case "settling":
      return {
        layoutPolicy: "target",
        scrollPolicy: "rebase",
        source: "viewer-shell",
        transitionId: motionFrame.motionId,
        visualPolicy: "shell-transform",
      };
    case "idle":
      return {
        layoutPolicy: "live",
        scrollPolicy: "preserve",
        source: "none",
        transitionId: null,
        visualPolicy: "none",
      };
  }
}
registry/new-york-v4/ui/file-viewer-renderer-frame.tsx
"use client";

import * as React from "react";

import {
  useOptionalFileViewerShell,
  useOptionalFileViewerShellStatic,
  useFileViewerShellStatic,
} from "./file-viewer-context";
import type { FileViewerDocumentSurface } from "./file-viewer-motion-kernel";
import {
  createFileViewerRendererFrame,
  type FileViewerDocumentAlign,
  type FileViewerRendererFrame,
} from "./file-viewer-renderer-contract";
import { useFileViewerMotionFrame } from "./file-viewer-motion-kernel";
import { useViewerInlineDirection } from "./viewer-measurement";

export type FileViewerRendererEnvironment = {
  registerDocumentSurface: (surface: FileViewerDocumentSurface) => () => void;
  usesShellGeometry: boolean;
};

export type FileViewerDocumentFrameState = {
  align: FileViewerDocumentAlign;
  element: HTMLDivElement | null;
  inlineSize: number | null;
};

const FileViewerDocumentFrameContext =
  React.createContext<FileViewerDocumentFrameState | null>(null);

export function FileViewerDocumentFrameProvider({
  children,
  value,
}: {
  children: React.ReactNode;
  value: FileViewerDocumentFrameState;
}) {
  return (
    <FileViewerDocumentFrameContext.Provider value={value}>
      {children}
    </FileViewerDocumentFrameContext.Provider>
  );
}

export function useOptionalFileViewerDocumentFrame(): FileViewerDocumentFrameState | null {
  return React.useContext(FileViewerDocumentFrameContext);
}

export function useOptionalFileViewerRendererEnvironment(): FileViewerRendererEnvironment {
  const { elementRegistry, usesShellGeometry } =
    useFileViewerRendererEnvironmentState();
  const registerDocumentSurface = React.useCallback(
    (surface: FileViewerDocumentSurface) =>
      elementRegistry?.registerDocumentSurface(surface) ?? (() => {}),
    [elementRegistry],
  );

  return React.useMemo(
    () => ({
      registerDocumentSurface,
      usesShellGeometry,
    }),
    [registerDocumentSurface, usesShellGeometry],
  );
}

export type FileViewerSidebarMotion = {
  /** True when the shell animates the sidebar (inline mode with a toggle). */
  isMotionManaged: boolean;
  isSidebarInteractive: boolean;
  isSidebarOpen: boolean;
  isSidebarTransitioning: boolean;
};

export function useOptionalFileViewerSidebarMotion(): FileViewerSidebarMotion | null {
  const shell = useOptionalFileViewerShell();

  return React.useMemo(
    () =>
      shell
        ? {
            isMotionManaged: shell.mode === "inline" && shell.canToggleSidebar,
            isSidebarInteractive: shell.isSidebarInteractive,
            isSidebarOpen: shell.isSidebarOpen,
            isSidebarTransitioning: shell.isSidebarTransitioning,
          }
        : null,
    [shell],
  );
}

export function useFileViewerRendererFrame({
  fallbackInlineSize,
}: {
  fallbackInlineSize?: number | null;
} = {}): FileViewerRendererFrame {
  useFileViewerShellStatic("useFileViewerRendererFrame");
  return useResolvedFileViewerRendererFrame({
    fallbackInlineSize,
    required: true,
  });
}

export function useOptionalFileViewerRendererFrame({
  fallbackInlineSize,
}: {
  fallbackInlineSize?: number | null;
} = {}): FileViewerRendererFrame {
  return useResolvedFileViewerRendererFrame({
    fallbackInlineSize,
    required: false,
  });
}

function useResolvedFileViewerRendererFrame({
  fallbackInlineSize,
  required,
}: {
  fallbackInlineSize?: number | null;
  required: boolean;
}): FileViewerRendererFrame {
  const { motionFrame, shell, usesShellGeometry } =
    useFileViewerRendererEnvironmentState();
  const documentFrame = useOptionalFileViewerDocumentFrame();

  if (required && !documentFrame) {
    throw new Error(
      "useFileViewerRendererFrame must be used within FileViewerInset.",
    );
  }

  const fallbackSize =
    fallbackInlineSize != null && Number.isFinite(fallbackInlineSize)
      ? fallbackInlineSize
      : null;

  // The fit-width motion transform is a physical-X computation, so renderers
  // need the frame's computed CSS `direction` alongside its logical align.
  const direction = useViewerInlineDirection(documentFrame?.element ?? null);

  return React.useMemo(
    () =>
      createFileViewerRendererFrame({
        align: documentFrame?.align ?? "center",
        canToggleSidebar: shell?.canToggleSidebar ?? false,
        direction,
        element: documentFrame?.element ?? null,
        fallbackInlineSize: documentFrame?.inlineSize ?? fallbackSize,
        motionFrame,
        motionDurationMs: shell?.motionDurationMs ?? 0,
        usesShellGeometry,
      }),
    [
      direction,
      documentFrame?.align,
      documentFrame?.element,
      documentFrame?.inlineSize,
      fallbackSize,
      shell?.canToggleSidebar,
      motionFrame,
      shell?.motionDurationMs,
      usesShellGeometry,
    ],
  );
}

function useFileViewerRendererEnvironmentState() {
  const shell = useOptionalFileViewerShellStatic();
  const motionFrame = useFileViewerMotionFrame(shell?.motionKernel);
  const usesShellGeometry = Boolean(
    shell &&
      motionFrame.shellInlineSize > 0 &&
      shell.mode === "inline" &&
      (shell.canToggleSidebar || shell.collapsible === "none"),
  );

  return React.useMemo(
    () => ({
      elementRegistry: shell?.elementRegistry,
      motionFrame,
      shell,
      usesShellGeometry,
    }),
    [motionFrame, shell, usesShellGeometry],
  );
}
registry/new-york-v4/ui/file-viewer-resource-state.ts
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import {
  fileViewerDiagnostic,
  nextFileViewerSessionId,
  summarizeDiagnosticId,
  summarizeViewerDescriptor,
  summarizeViewerResource,
} from "@/lib/pdf-viewer-diagnostics";
import {
  createViewerResource,
  type ViewerResource,
} from "@/lib/viewer-resource";

import {
  descriptorResetKey,
  type FileDescriptor,
  type FileViewerFallbackSize,
  type FileViewerCoreProps,
} from "./file-viewer-core";
import { useIsClient } from "./use-is-client";

export type FileViewerResourceOptions = Pick<
  FileViewerCoreProps,
  | "category"
  | "fallbackFrameSize"
  | "fallbackSlideSize"
  | "isolateStyles"
  | "source"
>;

export type FileViewerResourceState = {
  descriptor: FileDescriptor;
  descriptorKey: string;
  descriptorSignal: AbortSignal;
  fallbackFrameSize?: FileViewerFallbackSize;
  fallbackSlideSize?: FileViewerFallbackSize;
  isClient: boolean;
  isolateStyles: boolean;
  resource: ViewerResource;
};

const FileViewerResourceContext =
  React.createContext<FileViewerResourceState | null>(null);

// Per-descriptor cancellation signal. Aborting is deferred a macrotask so a
// keyed remount can keep reusing a shared resource request through React
// StrictMode's mount/unmount/mount sequence.
function useDescriptorSignal(descriptorKey: string): AbortSignal {
  const controller = React.useMemo(() => {
    void descriptorKey;
    return new AbortController();
  }, [descriptorKey]);
  const abortTimerRef = React.useRef<{
    controller: AbortController;
    timer: ReturnType<typeof setTimeout>;
  } | null>(null);

  useKeyedLayoutEffect(joinEffectKey([controller]), () => {
    const descriptorKeyHash = summarizeDiagnosticId(descriptorKey);
    fileViewerDiagnostic("debug", "file_viewer_descriptor_signal_active", {
      descriptorKeyHash,
    });
    if (abortTimerRef.current?.controller === controller) {
      clearTimeout(abortTimerRef.current.timer);
      abortTimerRef.current = null;
      fileViewerDiagnostic(
        "debug",
        "file_viewer_descriptor_signal_abort_cancelled",
        { descriptorKeyHash },
      );
    }

    return () => {
      fileViewerDiagnostic("debug", "file_viewer_descriptor_abort_scheduled", {
        descriptorKeyHash,
        isSignalAborted: controller.signal.aborted,
      });
      const abortTimer = {
        controller,
        timer: setTimeout(() => {
          fileViewerDiagnostic("debug", "file_viewer_descriptor_abort_fired", {
            descriptorKeyHash,
            isSignalAborted: controller.signal.aborted,
          });
          controller.abort();
          if (abortTimerRef.current === abortTimer) {
            abortTimerRef.current = null;
          }
        }, 0),
      };
      abortTimerRef.current = abortTimer;
    };
  });

  return controller.signal;
}

export function useFileViewerResourceState({
  category,
  fallbackFrameSize,
  fallbackSlideSize,
  isolateStyles = false,
  source,
}: FileViewerResourceOptions): FileViewerResourceState {
  const isClient = useIsClient();
  const resource = React.useMemo(
    () => createViewerResource(source, category),
    [source, category],
  );
  const descriptor = resource.descriptor;
  const descriptorKey = descriptorResetKey(descriptor);
  const viewerSessionId = React.useMemo(() => {
    void descriptorKey;
    return nextFileViewerSessionId("fv");
  }, [descriptorKey]);
  const descriptorSignal = useDescriptorSignal(descriptorKey);

  useKeyedLayoutEffect(joinEffectKey([descriptorKey, viewerSessionId]), () => {
    fileViewerDiagnostic("info", "file_viewer_resource_mount", {
      descriptor: summarizeViewerDescriptor(descriptor),
      descriptorKeyHash: summarizeDiagnosticId(descriptorKey),
      isClient,
      isolateStyles,
      resource: summarizeViewerResource(resource),
      viewerSessionId,
    });
    return () => {
      fileViewerDiagnostic("debug", "file_viewer_resource_unmount", {
        descriptor: summarizeViewerDescriptor(descriptor),
        descriptorKeyHash: summarizeDiagnosticId(descriptorKey),
        resource: summarizeViewerResource(resource),
        viewerSessionId,
      });
    };
  });

  return React.useMemo<FileViewerResourceState>(
    () => ({
      descriptor,
      descriptorKey,
      descriptorSignal,
      fallbackFrameSize,
      fallbackSlideSize,
      isClient,
      isolateStyles,
      resource,
    }),
    [
      descriptor,
      descriptorKey,
      descriptorSignal,
      fallbackFrameSize,
      fallbackSlideSize,
      isClient,
      isolateStyles,
      resource,
    ],
  );
}

export function FileViewerResourceProvider({
  children,
  value,
}: {
  children: React.ReactNode;
  value: FileViewerResourceState;
}) {
  return React.createElement(
    FileViewerResourceContext.Provider,
    { value },
    children,
  );
}

export function useFileViewerRequiredResourceState(): FileViewerResourceState {
  const context = React.useContext(FileViewerResourceContext);
  if (!context) {
    throw new Error("File viewer parts must be used within FileViewer.");
  }
  return context;
}

export function useOptionalFileViewerResource(): ViewerResource | null {
  return React.useContext(FileViewerResourceContext)?.resource ?? null;
}

export function useFileViewerResource(): ViewerResource {
  const resource = useOptionalFileViewerResource();
  if (!resource) {
    throw new Error("useFileViewerResource must be used within FileViewer.");
  }
  return resource;
}
registry/new-york-v4/ui/file-viewer-route.tsx
"use client";

import * as React from "react";

import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { inferCsvDialect } from "@/lib/csv";
import { joinEffectKey } from "@/lib/effect-key";
import { cn } from "@/lib/utils";
import { type ViewerResource } from "@/lib/viewer-resource";
import {
  fileViewerDiagnostic,
  summarizeViewerDescriptor,
  summarizeViewerSource,
} from "@/lib/pdf-viewer-diagnostics";

import {
  isProseTextDescriptor,
  type FileCategory,
  type FileDescriptor,
  type FileViewerFallbackSize,
} from "./file-viewer-core";
import { UnsupportedCard } from "./file-viewer-fallback";
import { FileViewerHtmlContent } from "./file-viewer-html-viewer";
import {
  getFileViewerPrewarmTarget,
  scheduleFileViewerRendererPrewarm,
} from "./file-viewer-prewarm";

const PdfResourceContent = React.lazy(() =>
  import("@/components/ui/pdf-viewer").then((m) => ({
    default: m.PdfResourceContent,
  })),
);
const DocxResourceContent = React.lazy(() =>
  import("@/components/ui/docx-viewer").then((m) => ({
    default: m.DocxResourceContent,
  })),
);
const ImageResourceContent = React.lazy(() =>
  import("@/components/ui/image-viewer").then((m) => ({
    default: m.ImageResourceContent,
  })),
);
const PptxResourceContent = React.lazy(() =>
  import("@/components/ui/pptx-viewer").then((m) => ({
    default: m.PptxResourceContent,
  })),
);
const XlsxResourceContent = React.lazy(() =>
  import("@/components/ui/xlsx-viewer").then((m) => ({
    default: m.XlsxResourceContent,
  })),
);
const ProseTextViewer = React.lazy(() =>
  import("@/components/ui/text-viewer").then((m) => ({
    default: m.TextResourceContent,
  })),
);
const MarkdownViewer = React.lazy(() =>
  import("@/components/ui/markdown-viewer").then((m) => ({
    default: m.MarkdownResourceContent,
  })),
);
const CodeTextViewer = React.lazy(() =>
  import("@/components/ui/code-viewer").then((m) => ({
    default: m.CodeResourceContent,
  })),
);
const EmailResourceContent = React.lazy(() =>
  import("@/components/ui/email-viewer").then((m) => ({
    default: m.EmailResourceContent,
  })),
);
const CsvResourceContent = React.lazy(() =>
  import("@/components/ui/csv-viewer").then((m) => ({
    default: m.CsvResourceContent,
  })),
);

function FileViewerCsvContent({
  resource,
  className,
  bare,
  controls = false,
  isolateStyles,
}: {
  resource: ViewerResource;
  className?: string;
  bare?: boolean;
  controls?: boolean;
  isolateStyles?: boolean;
}) {
  const dialect = inferCsvDialect({
    src: resource.content.directUrl ?? undefined,
    fileName: resource.fileName,
    mimeType: resource.mimeType,
  });
  return (
    <div
      data-slot="csv-file-viewer-content"
      className={cn(
        "bg-card flex min-h-0 flex-1 flex-col overflow-hidden",
        bare ? "h-full" : "min-h-64",
        className,
      )}
    >
      <CsvResourceContent
        resource={resource}
        dialect={dialect}
        fillHeight
        frame={!bare}
        controls={controls}
        isolateStyles={isolateStyles}
      />
    </div>
  );
}

export type FileViewerRouteProps = {
  className?: string;
  descriptor: FileDescriptor;
  descriptorSignal: AbortSignal;
  fallbackFrameSize?: FileViewerFallbackSize;
  fallbackSlideSize?: FileViewerFallbackSize;
  frame?: "contained" | "none";
  isolateStyles: boolean;
  resource: ViewerResource;
  controls: boolean;
};

type RenderContext = {
  descriptor: FileDescriptor;
  resource: ViewerResource;
  className?: string;
  bare: boolean;
  controls: boolean;
  fallbackFrameSize?: FileViewerFallbackSize;
  fallbackSlideSize?: FileViewerFallbackSize;
  isolateStyles: boolean;
  descriptorSignal: AbortSignal;
};

// Categories a text-kind source (raw text, no blob/url backing) can render.
const TEXT_SOURCE_CATEGORIES = new Set<FileCategory>([
  "csv",
  "markdown",
  "html",
  "email",
  "text",
]);

const RENDERERS: Partial<
  Record<FileCategory, (ctx: RenderContext) => React.ReactNode>
> = {
  pdf: ({ resource, className, bare, controls }) => (
    <PdfResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
    />
  ),
  docx: ({ resource, className, bare, controls }) => (
    <DocxResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
    />
  ),
  image: ({ resource, className, bare, controls, fallbackFrameSize }) => (
    <ImageResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
      fallbackFrameSize={fallbackFrameSize}
    />
  ),
  pptx: ({ resource, className, bare, controls, fallbackSlideSize }) => (
    <PptxResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
      fallbackSlideSize={fallbackSlideSize}
    />
  ),
  xlsx: ({ resource, className, bare, controls, isolateStyles }) => (
    <XlsxResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
      isolateStyles={isolateStyles}
    />
  ),
  csv: ({ resource, className, bare, controls, isolateStyles }) => (
    <FileViewerCsvContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      isolateStyles={isolateStyles}
    />
  ),
  html: ({ resource, className, bare, controls, descriptorSignal }) => (
    <FileViewerHtmlContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      descriptorSignal={descriptorSignal}
    />
  ),
  email: ({ resource, className, bare, controls, descriptorSignal }) => (
    <EmailResourceContent
      resource={resource}
      className={className}
      bare={bare}
      controls={controls}
      download
      descriptorSignal={descriptorSignal}
    />
  ),
  markdown: ({ resource, className, bare, controls }) => (
    <MarkdownViewer
      resource={resource}
      className={className}
      controls={controls}
      download
      bare={bare}
      urlFragmentNavigation={false}
    />
  ),
  text: renderTextViewer,
};

export function FileViewerRoute({
  descriptor,
  className,
  frame = "contained",
  isolateStyles,
  descriptorSignal,
  fallbackFrameSize,
  fallbackSlideSize,
  resource,
  controls,
}: FileViewerRouteProps) {
  const { category } = descriptor;
  const bare = frame === "none";
  const isTextSource = descriptor.source.kind === "text";

  const renderer = RENDERERS[category];
  const textSourceAllowed =
    !isTextSource || TEXT_SOURCE_CATEGORIES.has(category);
  const isRouteRenderable = Boolean(renderer) && textSourceAllowed;
  const prewarmTarget = getFileViewerPrewarmTarget({
    descriptor,
    isRouteRenderable,
  });
  useKeyedMountEffect(
    prewarmTarget
      ? joinEffectKey(["file-viewer-renderer-prewarm", prewarmTarget])
      : null,
    () => {
      if (!prewarmTarget) return;
      return scheduleFileViewerRendererPrewarm(prewarmTarget);
    },
  );

  const routeDetails = {
    bare,
    category,
    hasRenderer: Boolean(renderer),
    isTextSource,
    isolateStyles,
    source: summarizeViewerSource(descriptor.source),
    descriptor: summarizeViewerDescriptor(descriptor),
    controls,
  };
  if (renderer && textSourceAllowed) {
    fileViewerDiagnostic("debug", "file_viewer_route_renderer_selected", {
      ...routeDetails,
      textSourceAllowed,
    });
    return renderer({
      descriptor,
      resource,
      className,
      bare,
      controls,
      fallbackFrameSize,
      fallbackSlideSize,
      isolateStyles,
      descriptorSignal,
    });
  }

  fileViewerDiagnostic("warn", "file_viewer_route_unsupported", {
    ...routeDetails,
    textSourceAllowed,
  });
  return (
    <UnsupportedCard
      resource={resource}
      className={className}
      bare={bare}
      showDownload={controls}
    />
  );
}

function renderTextViewer({
  descriptor,
  resource,
  className,
  bare,
  controls,
}: RenderContext): React.ReactNode {
  if (isProseTextDescriptor(descriptor)) {
    return (
      <ProseTextViewer
        resource={resource}
        className={className}
        controls={controls}
        download
        bare={bare}
        mode="text"
      />
    );
  }
  return (
    <CodeTextViewer
      resource={resource}
      className={className}
      controls={controls}
      download
      bare={bare}
    />
  );
}
registry/new-york-v4/ui/file-viewer-prewarm.ts
import {
  isProseTextDescriptor,
  type FileDescriptor,
  type FileCategory,
} from "./file-viewer-core";

export type FileViewerPrewarmTarget =
  | "pdf"
  | "docx"
  | "image"
  | "pptx"
  | "xlsx"
  | "markdown"
  | "email"
  | "text"
  | "code";

type FileViewerPrewarmStatus = "scheduled" | "running" | "complete";

type FileViewerIdleWindow = Window &
  typeof globalThis & {
    cancelIdleCallback?: Window["cancelIdleCallback"];
    requestIdleCallback?: Window["requestIdleCallback"];
  };

type FileViewerIdleTask =
  | { kind: "idle"; id: number }
  | { kind: "timeout"; id: ReturnType<typeof setTimeout> };

type FileViewerPrewarmRecord = {
  browserWindow: FileViewerIdleWindow;
  isCancelled: boolean;
  status: FileViewerPrewarmStatus;
  subscribers: number;
  task: FileViewerIdleTask | null;
};

const CATEGORY_PREWARM_TARGETS: Partial<
  Record<FileCategory, FileViewerPrewarmTarget>
> = {
  pdf: "pdf",
  docx: "docx",
  image: "image",
  pptx: "pptx",
  xlsx: "xlsx",
  markdown: "markdown",
  email: "email",
};

const PREWARM_LOADERS: Record<FileViewerPrewarmTarget, () => Promise<void>> = {
  pdf: () => import("@/components/ui/pdf-viewer").then(() => undefined),
  docx: () => import("@/components/ui/docx-viewer").then(() => undefined),
  image: () => import("@/components/ui/image-viewer").then(() => undefined),
  pptx: () =>
    import("@/components/ui/pptx-viewer").then((module) => {
      const preloadViewer = (module as { preloadPptxViewer?: () => void })
        .preloadPptxViewer;
      preloadViewer?.();
    }),
  xlsx: () => import("@/components/ui/xlsx-viewer").then(() => undefined),
  markdown: () =>
    import("@/components/ui/markdown-viewer").then(() => undefined),
  email: () => import("@/components/ui/email-viewer").then(() => undefined),
  text: () =>
    import("@/components/ui/text-viewer-chenglou").then(() => undefined),
  code: () => import("@/components/ui/code-viewer").then(() => undefined),
};

const prewarmRecords = new Map<
  FileViewerPrewarmTarget,
  FileViewerPrewarmRecord
>();

export function getFileViewerPrewarmTarget({
  descriptor,
  isRouteRenderable,
}: {
  descriptor: FileDescriptor;
  isRouteRenderable: boolean;
}): FileViewerPrewarmTarget | null {
  if (!isRouteRenderable) return null;
  if (descriptor.category === "text") {
    return isProseTextDescriptor(descriptor) ? "text" : "code";
  }
  return CATEGORY_PREWARM_TARGETS[descriptor.category] ?? null;
}

export function scheduleFileViewerRendererPrewarm(
  target: FileViewerPrewarmTarget,
): () => void {
  if (typeof window === "undefined") return noop;

  const existingRecord = prewarmRecords.get(target);
  if (existingRecord) {
    if (existingRecord.status !== "scheduled") return noop;
    existingRecord.subscribers += 1;
    return () => releaseScheduledPrewarm(target, existingRecord);
  }

  const browserWindow = window as FileViewerIdleWindow;
  const record: FileViewerPrewarmRecord = {
    browserWindow,
    isCancelled: false,
    status: "scheduled",
    subscribers: 1,
    task: null,
  };
  record.task = scheduleIdleTask(browserWindow, () => {
    if (record.isCancelled) return;
    record.status = "running";
    record.task = null;
    void PREWARM_LOADERS[target]()
      .then(() => {
        if (prewarmRecords.get(target) === record) {
          record.status = "complete";
        }
      })
      .catch(() => {
        if (prewarmRecords.get(target) === record) {
          prewarmRecords.delete(target);
        }
      });
  });
  prewarmRecords.set(target, record);

  return () => releaseScheduledPrewarm(target, record);
}

export function resetFileViewerRendererPrewarmForTests() {
  prewarmRecords.clear();
}

function releaseScheduledPrewarm(
  target: FileViewerPrewarmTarget,
  record: FileViewerPrewarmRecord,
) {
  if (prewarmRecords.get(target) !== record || record.status !== "scheduled") {
    return;
  }
  record.subscribers -= 1;
  if (record.subscribers > 0) return;

  record.isCancelled = true;
  if (record.task) cancelIdleTask(record.browserWindow, record.task);
  prewarmRecords.delete(target);
}

function scheduleIdleTask(
  browserWindow: FileViewerIdleWindow,
  callback: () => void,
): FileViewerIdleTask {
  if (browserWindow.requestIdleCallback) {
    return {
      kind: "idle",
      id: browserWindow.requestIdleCallback(callback, { timeout: 600 }),
    };
  }
  return { kind: "timeout", id: setTimeout(callback, 120) };
}

function cancelIdleTask(
  browserWindow: FileViewerIdleWindow,
  task: FileViewerIdleTask,
) {
  if (task.kind === "idle") {
    browserWindow.cancelIdleCallback?.(task.id);
    return;
  }
  clearTimeout(task.id);
}

function noop() {}
registry/new-york-v4/ui/file-viewer-state.tsx
"use client";

import * as React from "react";
import {
  AlertCircle,
  FileQuestion,
  Inbox,
  Loader2,
  WifiOff,
} from "lucide-react";

import { cn } from "@/lib/utils";
import type { ViewerDownloadAction } from "@/lib/viewer-download-actions";
import {
  toViewerErrorInfo,
  type ViewerErrorContext,
} from "@/lib/viewer-errors";

import { Button } from "./button";
import { Skeleton } from "./skeleton";
import { ViewerDownloadButton } from "./viewer-download";

type FileViewerStateKind =
  | "empty"
  | "error"
  | "loading"
  | "unavailable"
  | "unsupported";

export type FileViewerStateProps = Omit<
  React.ComponentProps<"div">,
  "title"
> & {
  action?: React.ReactNode;
  description?: React.ReactNode;
  icon?: React.ReactNode;
  title?: React.ReactNode;
};

export type FileViewerErrorStateProps = FileViewerStateProps &
  ViewerErrorContext & {
    canDownload?: boolean;
    download?: ViewerDownloadAction | null;
    error?: unknown;
    onRetry?: () => void;
  };

export type FileViewerUnsupportedStateProps = FileViewerStateProps & {
  download?: ViewerDownloadAction | null;
  fileName?: string;
  showDownload?: boolean;
};

function stateSlot(kind: FileViewerStateKind) {
  return `file-viewer-${kind}-state`;
}

function renderErrorDescription(error: unknown): React.ReactNode {
  if (error instanceof Error && error.message) return error.message;
  if (typeof error === "string" && error) return error;
  return undefined;
}

function FileViewerStateFrame({
  action,
  children,
  className,
  description,
  icon,
  kind,
  role,
  title,
  ...props
}: FileViewerStateProps & {
  kind: FileViewerStateKind;
  role?: React.AriaRole;
}) {
  return (
    <div
      data-slot={stateSlot(kind)}
      role={role}
      className={cn(
        "text-muted-foreground flex min-h-48 min-w-0 flex-col items-center justify-center gap-3 p-6 text-center text-sm",
        className,
      )}
      {...props}
    >
      {icon ? (
        <div
          aria-hidden
          data-slot="file-viewer-state-icon"
          className="text-muted-foreground flex size-9 items-center justify-center"
        >
          {icon}
        </div>
      ) : null}
      <div className="flex max-w-md min-w-0 flex-col items-center gap-1">
        {title ? (
          <p
            data-slot="file-viewer-state-title"
            className="text-foreground text-sm font-medium"
          >
            {title}
          </p>
        ) : null}
        {description ? (
          <p
            data-slot="file-viewer-state-description"
            className="text-muted-foreground text-sm"
          >
            {description}
          </p>
        ) : null}
      </div>
      {children}
      {action ? (
        <div
          data-slot="file-viewer-state-action"
          className="flex items-center justify-center gap-2"
        >
          {action}
        </div>
      ) : null}
    </div>
  );
}

export function FileViewerLoadingState({
  children,
  description = "Preparing the preview.",
  icon = <Loader2 className="size-5 animate-spin" />,
  title = "Loading file",
  ...props
}: FileViewerStateProps) {
  return (
    <FileViewerStateFrame
      aria-busy="true"
      description={description}
      icon={icon}
      kind="loading"
      title={title}
      {...props}
    >
      {children ?? (
        <div
          aria-hidden
          data-slot="file-viewer-state-skeleton"
          className="flex w-full max-w-xs flex-col gap-2"
        >
          <Skeleton className="h-2 w-full" />
          <Skeleton className="h-2 w-3/4 self-center" />
        </div>
      )}
    </FileViewerStateFrame>
  );
}

export function FileViewerUnavailableState({
  description = "The file is not available right now.",
  icon = <WifiOff className="size-5" />,
  title = "File unavailable",
  ...props
}: FileViewerStateProps) {
  return (
    <FileViewerStateFrame
      description={description}
      icon={icon}
      kind="unavailable"
      role="status"
      title={title}
      {...props}
    />
  );
}

export function FileViewerEmptyState({
  description = "Choose a file to preview.",
  icon = <Inbox className="size-5" />,
  title = "No file selected",
  ...props
}: FileViewerStateProps) {
  return (
    <FileViewerStateFrame
      description={description}
      icon={icon}
      kind="empty"
      role="status"
      title={title}
      {...props}
    />
  );
}

export function FileViewerErrorState({
  action,
  canDownload,
  description,
  download,
  error,
  format = "file",
  icon = <AlertCircle className="size-5" />,
  onRetry,
  retry,
  sourceKind,
  title,
  ...props
}: FileViewerErrorStateProps) {
  const info =
    error === undefined
      ? null
      : toViewerErrorInfo(error, {
          canDownload: canDownload ?? Boolean(download && !download.isDisabled),
          format,
          retry,
          sourceKind,
        });
  const showRetry = Boolean(info?.isRetryable && onRetry);
  const showDownload = Boolean(
    info?.isDownloadUseful && download && !download.isDisabled,
  );
  const resolvedAction =
    action ??
    (showRetry || showDownload ? (
      <>
        {showRetry ? (
          <Button variant="outline" size="sm" onClick={onRetry}>
            Retry
          </Button>
        ) : null}
        {showDownload && download ? (
          <ViewerDownloadButton
            action={download}
            variant={showRetry ? "ghost" : "outline"}
            size="sm"
            className=""
            showLabel
          />
        ) : null}
      </>
    ) : null);

  return (
    <FileViewerStateFrame
      action={resolvedAction}
      data-error-domain={info?.domain}
      data-error-format={info?.format}
      data-error-kind={info?.kind}
      data-error-message={info?.message}
      description={
        description ?? info?.userMessage ?? renderErrorDescription(error)
      }
      icon={icon}
      kind="error"
      role="alert"
      title={title ?? "Could not preview file"}
      {...props}
    />
  );
}

export function FileViewerUnsupportedState({
  description,
  download,
  fileName,
  icon = <FileQuestion className="size-5" />,
  showDownload = true,
  title = "Preview unsupported",
  ...props
}: FileViewerUnsupportedStateProps) {
  const resolvedDescription =
    description ??
    (fileName
      ? `No preview is available for ${fileName}.`
      : "No preview is available for this file.");

  return (
    <FileViewerStateFrame
      action={
        showDownload && download && !download.isDisabled ? (
          <ViewerDownloadButton
            action={download}
            variant="outline"
            size="sm"
            className=""
            showLabel
          />
        ) : undefined
      }
      description={resolvedDescription}
      icon={icon}
      kind="unsupported"
      role="status"
      title={title}
      {...props}
    />
  );
}
registry/new-york-v4/ui/file-viewer-sidebar.tsx
"use client";

import * as React from "react";
import { Slot } from "radix-ui";

import { cn } from "@/lib/utils";

import {
  DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,
  type FileViewerSidebarCollapsible,
  type FileViewerSidebarSide,
  useFileViewerShell,
} from "./file-viewer-context";
import { useFileViewerSidebarController } from "./file-viewer-sidebar-controller";
import { Separator } from "./separator";

export type FileViewerSidebarProps = React.ComponentProps<"aside"> & {
  collapsible?: FileViewerSidebarCollapsible;
  innerClassName?: string;
  side?: FileViewerSidebarSide;
  width?: string;
};
export type FileViewerSidebarContentProps = React.ComponentProps<"div">;
export type FileViewerSidebarFooterProps = React.ComponentProps<"div">;
export type FileViewerSidebarHeaderProps = React.ComponentProps<"div">;
export type FileViewerSidebarRailProps = React.ComponentProps<"button"> & {
  asChild?: boolean;
};
export type FileViewerSidebarSectionProps = React.ComponentProps<"section">;
export type FileViewerSidebarSectionActionProps =
  React.ComponentProps<"button"> & {
    asChild?: boolean;
  };
export type FileViewerSidebarSectionContentProps = React.ComponentProps<"div">;
export type FileViewerSidebarSectionHeaderProps = React.ComponentProps<"div">;
export type FileViewerSidebarSectionTitleProps = React.ComponentProps<"h3">;
export type FileViewerSidebarSeparatorProps = React.ComponentProps<
  typeof Separator
>;

export function FileViewerSidebar({
  children,
  className,
  collapsible,
  id,
  innerClassName,
  side,
  style,
  width = DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,
  ...props
}: FileViewerSidebarProps) {
  const sidebar = useFileViewerSidebarController({
    collapsible,
    id,
    side,
    style,
    width,
  });

  return (
    <div
      ref={sidebar.setSidebarGapElement}
      data-file-viewer-sidebar-collapsible={sidebar.resolvedCollapsible}
      data-file-viewer-sidebar-mode={sidebar.mode}
      data-file-viewer-sidebar-open={sidebar.isSidebarOpen ? "true" : "false"}
      data-file-viewer-sidebar-side={sidebar.resolvedSide}
      data-file-viewer-sidebar-state={sidebar.sidebarState}
      data-slot="file-viewer-sidebar-gap"
      className={cn(
        "group/file-viewer-sidebar z-30 min-h-0 min-w-0 shrink-0 overflow-hidden",
        sidebar.isInline
          ? "relative h-full"
          : "absolute inset-y-0 w-0 overflow-visible",
        sidebar.resolvedSide === "left" ? "order-first" : "order-last",
        !sidebar.isInline && sidebar.resolvedSide === "left" && "left-0",
        !sidebar.isInline && sidebar.resolvedSide === "right" && "right-0",
      )}
      style={
        {
          ...sidebar.customProperties,
        } as React.CSSProperties
      }
    >
      <aside
        ref={sidebar.setSidebarPanelElement}
        id={sidebar.sidebarId}
        data-file-viewer-sidebar-collapsible={sidebar.resolvedCollapsible}
        data-file-viewer-sidebar-mode={sidebar.mode}
        data-file-viewer-sidebar-open={sidebar.isSidebarOpen ? "true" : "false"}
        data-file-viewer-sidebar-side={sidebar.resolvedSide}
        data-file-viewer-sidebar-state={sidebar.sidebarState}
        data-slot="file-viewer-sidebar"
        className={cn(
          "bg-background absolute inset-y-0 flex min-w-0 flex-col overflow-hidden",
          sidebar.resolvedSide === "left"
            ? "right-0 border-r"
            : "left-0 border-l",
          !sidebar.isInline && "shadow-lg transition-transform ease-out",
          !sidebar.isInline &&
            !sidebar.isSidebarOpen &&
            sidebar.resolvedSide === "left" &&
            "-translate-x-full",
          !sidebar.isInline &&
            !sidebar.isSidebarOpen &&
            sidebar.resolvedSide === "right" &&
            "translate-x-full",
          className,
        )}
        style={sidebar.panelStyle}
        {...props}
        {...sidebar.accessibilityProps}
      >
        <FileViewerSidebarInner className={innerClassName}>
          {children}
        </FileViewerSidebarInner>
      </aside>
    </div>
  );
}

const FileViewerSidebarInner = React.memo(function FileViewerSidebarInner({
  children,
  className,
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="file-viewer-sidebar-inner"
      className={cn(
        "flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden",
        className,
      )}
    >
      {children}
    </div>
  );
});

export function FileViewerSidebarContent({
  className,
  ...props
}: FileViewerSidebarContentProps) {
  return (
    <div
      data-slot="file-viewer-sidebar-content"
      className={cn(
        "flex min-h-0 min-w-0 flex-1 flex-col overflow-auto",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarHeader({
  className,
  ...props
}: FileViewerSidebarHeaderProps) {
  return (
    <div
      data-slot="file-viewer-sidebar-header"
      className={cn(
        "flex min-h-10 min-w-0 shrink-0 items-center gap-2 border-b px-3",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarFooter({
  className,
  ...props
}: FileViewerSidebarFooterProps) {
  return (
    <div
      data-slot="file-viewer-sidebar-footer"
      className={cn(
        "flex min-h-10 min-w-0 shrink-0 items-center gap-2 border-t px-3",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarRail({
  asChild = false,
  className,
  onClick,
  type = "button",
  ...props
}: FileViewerSidebarRailProps) {
  const Comp = asChild ? Slot.Root : "button";
  const sidebar = useFileViewerShell("FileViewerSidebarRail");

  return (
    <Comp
      aria-disabled={sidebar.canToggleSidebar ? undefined : true}
      aria-label="Toggle sidebar"
      data-file-viewer-sidebar-mode={sidebar.mode}
      data-file-viewer-sidebar-side={sidebar.side}
      data-file-viewer-sidebar-state={sidebar.sidebarState}
      data-side={sidebar.side}
      data-slot="file-viewer-sidebar-rail"
      type={asChild ? undefined : type}
      onClick={(event) => {
        if (!sidebar.canToggleSidebar) {
          event.preventDefault();
          return;
        }

        onClick?.(event);
        if (!event.defaultPrevented) {
          sidebar.toggleSidebar();
        }
      }}
      className={cn(
        "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",
        sidebar.side === "left" ? "right-0" : "left-0",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarSection({
  className,
  ...props
}: FileViewerSidebarSectionProps) {
  return (
    <section
      data-slot="file-viewer-sidebar-section"
      className={cn("relative flex w-full min-w-0 flex-col p-3", className)}
      {...props}
    />
  );
}

export function FileViewerSidebarSectionHeader({
  className,
  ...props
}: FileViewerSidebarSectionHeaderProps) {
  return (
    <div
      data-slot="file-viewer-sidebar-section-header"
      className={cn(
        "flex min-h-8 min-w-0 shrink-0 items-center gap-2",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarSectionTitle({
  className,
  ...props
}: FileViewerSidebarSectionTitleProps) {
  return (
    <h3
      data-slot="file-viewer-sidebar-section-title"
      className={cn(
        "text-muted-foreground min-w-0 flex-1 truncate px-1 text-xs font-medium",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarSectionAction({
  asChild = false,
  className,
  type = "button",
  ...props
}: FileViewerSidebarSectionActionProps) {
  const Comp = asChild ? Slot.Root : "button";

  return (
    <Comp
      data-slot="file-viewer-sidebar-section-action"
      type={asChild ? undefined : type}
      className={cn(
        "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",
        className,
      )}
      {...props}
    />
  );
}

export function FileViewerSidebarSectionContent({
  className,
  ...props
}: FileViewerSidebarSectionContentProps) {
  return (
    <div
      data-slot="file-viewer-sidebar-section-content"
      className={cn("flex min-w-0 flex-col gap-2", className)}
      {...props}
    />
  );
}

export function FileViewerSidebarSeparator({
  className,
  ...props
}: FileViewerSidebarSeparatorProps) {
  return (
    <Separator
      data-slot="file-viewer-sidebar-separator"
      className={cn("mx-3 w-auto", className)}
      {...props}
    />
  );
}
registry/new-york-v4/ui/file-viewer-motion-plan.ts
"use client";

import type {
  FileViewerSidebarMode,
  FileViewerSidebarSide,
} from "./file-viewer-context";

export const FILE_VIEWER_MOTION_EPSILON = 0.5;

// The one duration for every sidebar motion timeline — the kernel clock, the
// motion targets, and the overlay panel's CSS transition all read this value.
export const FILE_VIEWER_MOTION_DURATION_MS = 150;

// The one easing for every sidebar motion timeline. Cubic ease-out: the slide
// decelerates into rest. Linear progress ends at full velocity, and content
// far from the reading anchor (the bottom of a tall fit-width document)
// travels several pixels per millisecond straight into a hard stop — a
// visible jolt the anchor line never shows.
export function easeFileViewerMotion(timeProgress: number) {
  return 1 - (1 - timeProgress) ** 3;
}

export type FileViewerMotionPhase = "idle" | "sliding" | "settling";

export type FileViewerMotionTarget = {
  shellInlineSize: number;
  durationMs: number;
  mode: FileViewerSidebarMode;
  open: boolean;
  side: FileViewerSidebarSide;
  sidebarWidth: number;
};

export type FileViewerMotionFrame = {
  shellInlineSize: number;
  durationMs: number;
  fromInlineSize: number;
  layoutInlineSize: number;
  mode: FileViewerSidebarMode;
  motionId: number | null;
  motionProgress: number;
  open: boolean;
  phase: FileViewerMotionPhase;
  side: FileViewerSidebarSide;
  sidebarInlineSize: number;
  sidebarWidth: number;
  toInlineSize: number;
};

export type FileViewerMotionRestFrame = Pick<
  FileViewerMotionFrame,
  | "shellInlineSize"
  | "durationMs"
  | "layoutInlineSize"
  | "mode"
  | "open"
  | "side"
  | "sidebarInlineSize"
  | "sidebarWidth"
>;

export type FileViewerMotionPlan = {
  currentRestFrame: FileViewerMotionRestFrame;
  fromInlineSize: number;
  nextRestFrame: FileViewerMotionRestFrame;
  resolvedTarget: FileViewerMotionTarget;
  shouldAnimate: boolean;
};

export function createFileViewerMotionRestFrame(
  target: FileViewerMotionTarget,
): FileViewerMotionRestFrame {
  const shellInlineSize = target.shellInlineSize;
  const sidebarInlineSize =
    target.mode === "inline" && target.open
      ? Math.min(target.sidebarWidth, shellInlineSize)
      : 0;

  return {
    shellInlineSize,
    durationMs: target.durationMs,
    layoutInlineSize: Math.max(0, shellInlineSize - sidebarInlineSize),
    mode: target.mode,
    open: target.open,
    side: target.side,
    sidebarInlineSize,
    sidebarWidth: target.sidebarWidth,
  };
}

export function createFileViewerIdleMotionFrame(
  restFrame: FileViewerMotionRestFrame,
): FileViewerMotionFrame {
  return {
    ...restFrame,
    fromInlineSize: restFrame.layoutInlineSize,
    motionId: null,
    motionProgress: 1,
    phase: "idle",
    toInlineSize: restFrame.layoutInlineSize,
  };
}

export function getFileViewerMotionRasterInlineSize(
  frame: Pick<
    FileViewerMotionFrame,
    "fromInlineSize" | "layoutInlineSize" | "toInlineSize"
  >,
): number {
  return Math.max(
    frame.fromInlineSize,
    frame.toInlineSize,
    frame.layoutInlineSize,
  );
}

export function createFileViewerMotionPlan({
  animate,
  currentFrame,
  nextTarget,
}: {
  animate: boolean;
  currentFrame: FileViewerMotionFrame;
  nextTarget: FileViewerMotionTarget;
}): FileViewerMotionPlan {
  const resolvedTarget = resolveFileViewerMotionTarget({
    currentFrame,
    nextTarget,
  });
  const currentRestFrame = pickFileViewerMotionRestFrame(currentFrame);
  const nextRestFrame = createFileViewerMotionRestFrame(resolvedTarget);
  // The motion's visual origin is what is on screen RIGHT NOW: for a fresh
  // motion that is the rest layout; for a mid-flight retarget it is the live
  // interpolated width, so the new motion's first frame (and every renderer's
  // anchor solve) continues from the picture the reader is looking at rather
  // than the interrupted motion's origin.
  const fromInlineSize =
    currentFrame.phase === "sliding"
      ? currentFrame.layoutInlineSize
      : currentRestFrame.layoutInlineSize;
  const shouldAnimate =
    animate &&
    resolvedTarget.mode === "inline" &&
    currentFrame.shellInlineSize > 0 &&
    Math.abs(
      currentRestFrame.sidebarInlineSize - nextRestFrame.sidebarInlineSize,
    ) > FILE_VIEWER_MOTION_EPSILON &&
    resolvedTarget.durationMs > 0;

  return {
    currentRestFrame,
    fromInlineSize,
    nextRestFrame,
    resolvedTarget,
    shouldAnimate,
  };
}

export function areFileViewerMotionRestFramesEqual(
  previous: FileViewerMotionRestFrame,
  next: FileViewerMotionRestFrame,
) {
  return (
    areFileViewerMotionNumbersEqual(
      previous.shellInlineSize,
      next.shellInlineSize,
    ) &&
    previous.durationMs === next.durationMs &&
    areFileViewerMotionNumbersEqual(
      previous.layoutInlineSize,
      next.layoutInlineSize,
    ) &&
    previous.mode === next.mode &&
    previous.open === next.open &&
    previous.side === next.side &&
    areFileViewerMotionNumbersEqual(
      previous.sidebarInlineSize,
      next.sidebarInlineSize,
    ) &&
    areFileViewerMotionNumbersEqual(previous.sidebarWidth, next.sidebarWidth)
  );
}

export function areFileViewerMotionFramesEqual(
  previous: FileViewerMotionFrame,
  next: FileViewerMotionFrame,
) {
  return (
    areFileViewerMotionRestFramesEqual(previous, next) &&
    previous.motionId === next.motionId &&
    areFileViewerMotionNumbersEqual(
      previous.motionProgress,
      next.motionProgress,
    ) &&
    previous.phase === next.phase &&
    areFileViewerMotionNumbersEqual(
      previous.fromInlineSize,
      next.fromInlineSize,
    ) &&
    areFileViewerMotionNumbersEqual(previous.toInlineSize, next.toInlineSize)
  );
}

function pickFileViewerMotionRestFrame(
  frame: FileViewerMotionFrame,
): FileViewerMotionRestFrame {
  return {
    shellInlineSize: frame.shellInlineSize,
    durationMs: frame.durationMs,
    layoutInlineSize: frame.layoutInlineSize,
    mode: frame.mode,
    open: frame.open,
    side: frame.side,
    sidebarInlineSize: frame.sidebarInlineSize,
    sidebarWidth: frame.sidebarWidth,
  };
}

function resolveFileViewerMotionTarget({
  currentFrame,
  nextTarget,
}: {
  currentFrame: FileViewerMotionFrame;
  nextTarget: FileViewerMotionTarget;
}): FileViewerMotionTarget {
  if (
    nextTarget.mode !== "overlay" ||
    currentFrame.mode !== "inline" ||
    currentFrame.shellInlineSize <= 0
  ) {
    return nextTarget;
  }

  return {
    ...nextTarget,
    shellInlineSize:
      nextTarget.shellInlineSize > 0
        ? nextTarget.shellInlineSize
        : currentFrame.shellInlineSize,
    mode: "inline",
  };
}

function areFileViewerMotionNumbersEqual(previous: number, next: number) {
  return Math.abs(previous - next) <= 0.001;
}
registry/new-york-v4/ui/file-viewer-text-resource.ts
import type {
  ViewerContentIdentity,
  ViewerContentText,
} from "@/lib/viewer-resource";

import {
  abortError,
  isAbortError,
  subscribeToAbortableRequest,
  type SharedAbortableRequest,
} from "./viewer-abortable-request";
import { lruGet } from "./viewer-lru-cache";

async function timed<T>(label: string, fn: () => Promise<T>): Promise<T> {
  const on =
    typeof globalThis !== "undefined" &&
    (globalThis as { __FILE_VIEWER_PROFILE__?: boolean })
      .__FILE_VIEWER_PROFILE__;
  if (!on) return fn();
  const t0 = performance.now();
  try {
    return await fn();
  } finally {
    console.log(
      `[file-viewer] ${label} ${(performance.now() - t0).toFixed(1)}ms`,
    );
  }
}

export interface TextResourceSubscription {
  content: ViewerContentIdentity & ViewerContentText;
  fileName: string;
  signal: AbortSignal;
}

export interface TextResourceCache {
  load(sub: TextResourceSubscription): Promise<string>;
  clear(): void;
  size(): number;
}

export function createTextResourceCache(maxEntries = 12): TextResourceCache {
  const requests = new Map<string, SharedAbortableRequest<string>>();

  function remove(contentKey: string) {
    requests.delete(contentKey);
  }

  function pruneInactiveEntries() {
    while (requests.size > maxEntries) {
      let pruned = false;
      for (const [contentKey, entry] of requests) {
        if (!entry.settled && entry.subscribers.size > 0) continue;
        requests.delete(contentKey);
        if (!entry.settled) entry.controller.abort();
        pruned = true;
        break;
      }
      if (!pruned) break;
    }
  }

  function entryFor({
    content,
    fileName,
  }: TextResourceSubscription): SharedAbortableRequest<string> {
    const contentKey = content.key;
    const existingEntry = lruGet(requests, contentKey);
    if (existingEntry) return existingEntry;

    const controller = new AbortController();
    let entry: SharedAbortableRequest<string> | null = null;
    const promise = timed(`text:fetch ${fileName}`, () =>
      content.readText({ signal: controller.signal }),
    )
      .catch((error: unknown) => {
        if (!isAbortError(error)) requests.delete(contentKey);
        throw error;
      })
      .finally(() => {
        if (entry) {
          entry.settled = true;
          pruneInactiveEntries();
        }
      });

    entry = {
      controller,
      promise,
      subscriberPromises: new WeakMap(),
      subscribers: new Set(),
      settled: false,
    };

    requests.set(contentKey, entry);
    return entry;
  }

  return {
    load(sub) {
      if (sub.signal.aborted) return Promise.reject(abortError());

      const contentKey = sub.content.key;
      const entry = entryFor(sub);
      const promise = subscribeToAbortableRequest(entry, sub.signal, () =>
        remove(contentKey),
      );
      pruneInactiveEntries();
      return promise;
    },
    clear() {
      for (const entry of requests.values()) entry.controller.abort();
      requests.clear();
    },
    size() {
      return requests.size;
    },
  };
}

export const textResource = createTextResourceCache();

export function loadTextResource(
  sub: TextResourceSubscription,
): Promise<string> {
  return textResource.load(sub);
}
registry/new-york-v4/ui/viewer.tsx
"use client";

export { ViewerBody } from "./viewer-body";
export type { ViewerSidebarTriggerProps } from "./viewer-chrome";
export {
  ViewerFrame,
  ViewerHeader,
  ViewerSidebarTrigger,
} from "./viewer-chrome";
export {
  ViewerRoot,
  useOptionalViewerSidebar,
  useViewerSidebar,
} from "./viewer-root";
export { ViewerSidebar } from "./viewer-sidebar";
export { ViewerSurface, ViewerViewport } from "./viewer-surface";
export type {
  ViewerBodyProps,
  ViewerDataAttributes,
  ViewerFrameProps,
  ViewerHeaderProps,
  ViewerRootProps,
  ViewerSidebarCollapsible,
  ViewerSidebarStateValue,
  ViewerSidebarMode,
  ViewerSidebarProps,
  ViewerSidebarRequestedMode,
  ViewerSidebarSide,
  ViewerSidebarSlotNames,
  ViewerSidebarState,
  ViewerStateAttributeNamespace,
  ViewerStateAttributeSlot,
  ViewerStateAttributeValues,
  ViewerSurfaceProps,
  ViewerViewportProps,
} from "./viewer-types";
registry/new-york-v4/ui/viewer-body.tsx
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import { cn } from "@/lib/utils";

import {
  createViewerStateAttributes,
  readViewerElementSize,
  useOptionalViewerSidebarRegistration,
  useViewerDevelopmentLayoutWarning,
} from "./viewer-internals";
import { useOptionalViewerSidebar } from "./viewer-root";
import { warnViewerDevelopmentOnce } from "./viewer-diagnostics";
import type { ViewerBodyProps } from "./viewer-types";

export function ViewerBody({ className, ...props }: ViewerBodyProps) {
  const bodyRef = React.useRef<HTMLDivElement | null>(null);
  const [bodyElement, setBodyElement] = React.useState<HTMLDivElement | null>(
    null,
  );
  const sidebarState = useOptionalViewerSidebar();
  const sidebarRegistration = useOptionalViewerSidebarRegistration();
  const namespacedBodyStateAttributes = createViewerStateAttributes(
    sidebarRegistration?.stateNamespace,
    "body",
    {
      hasSidebar: sidebarRegistration?.hasSidebar === true,
      sidebarMode: sidebarState?.mode,
      sidebarOpen: sidebarState?.open,
      sidebarSide: sidebarRegistration?.sidebarSide,
      sidebarState: sidebarState?.state,
    },
  );

  useViewerDevelopmentLayoutWarning({
    enabled: Boolean(sidebarRegistration),
    elements: () => [
      sidebarRegistration?.getRootElement() ?? null,
      bodyRef.current,
    ],
    inspect: () => {
      const rootElement = sidebarRegistration?.getRootElement() ?? null;
      const bodyElement = bodyRef.current;
      const rootSize = readViewerElementSize(rootElement);
      const bodySize = readViewerElementSize(bodyElement);

      if (rootSize.width > 0 && bodySize.width === 0) {
        warnViewerDevelopmentOnce({
          code: "viewer_body_zero_width",
          message:
            "viewer root has nonzero width but viewer body width is zero.",
          rootId: sidebarRegistration?.rootId ?? "unknown-root",
          details: {
            bodyHeight: bodySize.height,
            bodySlot: bodyElement?.dataset.slot,
            bodyWidth: bodySize.width,
            rootHeight: rootSize.height,
            rootSlot: rootElement?.dataset.slot,
            rootWidth: rootSize.width,
          },
        });
      }
    },
    keyParts: [
      "viewer-body-zero-width",
      sidebarRegistration?.rootId,
      sidebarRegistration?.getRootElement(),
      bodyRef.current,
    ],
  });
  const setBodyRef = React.useCallback((element: HTMLDivElement | null) => {
    bodyRef.current = element;
    setBodyElement(element);
  }, []);

  useKeyedLayoutEffect(
    bodyElement && sidebarRegistration
      ? joinEffectKey([
          "viewer-body:register-geometry",
          bodyElement,
          sidebarRegistration.registerBody,
        ])
      : null,
    () => {
      if (!bodyElement || !sidebarRegistration) return;
      return sidebarRegistration.registerBody(bodyElement);
    },
  );

  return (
    <div
      ref={setBodyRef}
      data-slot="viewer-body"
      data-viewer-has-sidebar={
        sidebarRegistration?.hasSidebar === true ? "true" : "false"
      }
      data-viewer-sidebar-mode={sidebarState?.mode}
      data-viewer-sidebar-open={
        sidebarState ? (sidebarState.open ? "true" : "false") : undefined
      }
      data-viewer-sidebar-side={sidebarRegistration?.sidebarSide}
      data-viewer-sidebar-state={sidebarState?.state}
      data-viewer-slot="body"
      {...namespacedBodyStateAttributes}
      className={cn(
        "relative flex min-h-0 min-w-0 flex-1 overflow-hidden",
        className,
      )}
      {...props}
    />
  );
}
registry/new-york-v4/ui/viewer-chrome.tsx
"use client";

import * as React from "react";
import { PanelLeft, PanelRight } from "lucide-react";

import { cn } from "@/lib/utils";

import { Button } from "./button";
import { Spinner } from "./spinner";
import {
  isAriaDisabled,
  useViewerSidebarState,
  useViewerSidebarRegistrationContext,
} from "./viewer-internals";
import type { ViewerFrameProps, ViewerHeaderProps } from "./viewer-types";

// Stock Button no longer exports ButtonProps or a `loading` prop. ViewerSidebarTrigger
// keeps its own `loading` API, so we define ButtonProps locally as the stock Button's
// props plus the retab `loading` flag (translated to disabled + Spinner at the leaf).
type ButtonProps = React.ComponentProps<typeof Button> & {
  loading?: boolean;
};

export type ViewerSidebarTriggerProps = ButtonProps;

export function ViewerFrame({ className, ...props }: ViewerFrameProps) {
  return (
    <div
      data-slot="viewer-frame"
      className={cn(
        "bg-background relative min-h-0 overflow-hidden rounded-xl border",
        className,
      )}
      {...props}
    />
  );
}

export function ViewerHeader({ className, ...props }: ViewerHeaderProps) {
  return (
    <div
      data-slot="viewer-header"
      className={cn("bg-card flex-shrink-0 border-b", className)}
      {...props}
    />
  );
}

export function ViewerSidebarTrigger({
  className,
  disabled,
  loading,
  onClick,
  onPointerDown,
  children,
  size = "icon",
  variant = "ghost",
  "aria-label": ariaLabel = "Toggle sidebar",
  "aria-disabled": ariaDisabled,
  ...props
}: ViewerSidebarTriggerProps) {
  const {
    hasSidebar,
    rootId,
    sidebarId,
    sidebarSide,
    setLastTriggerElement,
  } = useViewerSidebarRegistrationContext("ViewerSidebarTrigger");
  const sidebarState = useViewerSidebarState("ViewerSidebarTrigger");
  const { canToggleSidebar, open, state, toggleSidebar } = sidebarState;
  const isDisabled = Boolean(
    disabled || loading || isAriaDisabled(ariaDisabled) || !canToggleSidebar,
  );
  const Icon = sidebarSide === "right" ? PanelRight : PanelLeft;

  return (
    <Button
      aria-controls={hasSidebar ? sidebarId : undefined}
      aria-disabled={isDisabled ? true : ariaDisabled}
      aria-expanded={hasSidebar ? open : undefined}
      aria-label={ariaLabel}
      className={cn("size-8", className)}
      data-side={sidebarSide}
      data-slot="viewer-sidebar-trigger"
      data-state={state}
      data-viewer-root-id={rootId}
      data-viewer-sidebar-trigger=""
      disabled={isDisabled}
      onClick={(event) => {
        setLastTriggerElement(event.currentTarget);
        if (isDisabled) {
          event.preventDefault();
          return;
        }

        onClick?.(event);
        if (!event.defaultPrevented) {
          toggleSidebar();
        }
      }}
      onPointerDown={(event) => {
        setLastTriggerElement(event.currentTarget);
        onPointerDown?.(event);
      }}
      size={size}
      variant={variant}
      {...props}
    >
      {loading ? <Spinner className="size-4 animate-spin" /> : null}
      {children ?? (
        <>
          {loading ? null : <Icon />}
          <span className="sr-only">Toggle sidebar</span>
        </>
      )}
    </Button>
  );
}
registry/new-york-v4/ui/viewer-diagnostics.ts
"use client";

const emittedViewerDevelopmentWarnings = new Set<string>();

export type ViewerDevelopmentWarningDetails = Record<string, unknown>;

export function warnViewerDevelopmentOnce({
  code,
  details,
  message,
  rootId,
}: {
  code: string;
  details?: ViewerDevelopmentWarningDetails;
  message: string;
  rootId: string;
}) {
  if (process.env.NODE_ENV === "production") return;

  const warningKey = `${rootId}:${code}`;
  if (emittedViewerDevelopmentWarnings.has(warningKey)) return;
  emittedViewerDevelopmentWarnings.add(warningKey);

  console.warn(`[file-viewer] ${message}`, {
    code,
    rootId,
    ...details,
  });
}

export function resetViewerDevelopmentWarningsForTests() {
  emittedViewerDevelopmentWarnings.clear();
}
registry/new-york-v4/ui/viewer-header-outlet.tsx
"use client";

import { createContext, useContext, useMemo, type ReactNode } from "react";
import { createPortal } from "react-dom";

import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";

export type ViewerHeaderOutletName = "titleGroup" | "controls";

type ViewerHeaderOutletContextValue = {
  outlets: Partial<Record<ViewerHeaderOutletName, HTMLElement | null>>;
  registerContent?: (name: ViewerHeaderOutletName) => () => void;
};

const ViewerHeaderOutletContext =
  createContext<ViewerHeaderOutletContextValue | null>(null);

export function useViewerHeaderOutletAvailable(name: ViewerHeaderOutletName) {
  const context = useContext(ViewerHeaderOutletContext);
  return Boolean(context?.outlets[name]);
}

export function useViewerHeaderOutletsAvailable(
  names: ViewerHeaderOutletName[] = ["titleGroup", "controls"],
) {
  const context = useContext(ViewerHeaderOutletContext);
  if (!context) return false;
  return names.every((name) => Boolean(context.outlets[name]));
}

export function useViewerHeaderOutlets() {
  return useViewerHeaderOutletsAvailable();
}

export function ViewerHeaderOutlet({
  children,
  name,
}: {
  children: ReactNode;
  name: ViewerHeaderOutletName;
}) {
  const context = useContext(ViewerHeaderOutletContext);
  const target = context?.outlets[name] ?? null;
  const outletMountKey = target ? `viewer-header-outlet:${name}` : null;

  useKeyedMountEffect(outletMountKey, () =>
    target ? context?.registerContent?.(name) : undefined,
  );

  if (!target) return null;

  return createPortal(children, target);
}

export function ViewerHeaderOutletStart({ children }: { children: ReactNode }) {
  const hasHeaderOutlets = useViewerHeaderOutlets();
  if (!hasHeaderOutlets) return <>{children}</>;
  return <ViewerHeaderOutlet name="titleGroup">{children}</ViewerHeaderOutlet>;
}

export function ViewerHeaderOutletEnd({ children }: { children: ReactNode }) {
  const hasHeaderOutlets = useViewerHeaderOutlets();
  if (!hasHeaderOutlets) return <>{children}</>;
  return <ViewerHeaderOutlet name="controls">{children}</ViewerHeaderOutlet>;
}

export function ViewerHeaderOutletProvider({
  children,
  outlets,
  registerContent,
}: {
  children: ReactNode;
  outlets: Partial<Record<ViewerHeaderOutletName, HTMLElement | null>>;
  registerContent?: (name: ViewerHeaderOutletName) => () => void;
}) {
  const value = useMemo(
    () => ({ outlets, registerContent }),
    [outlets, registerContent],
  );

  return (
    <ViewerHeaderOutletContext.Provider value={value}>
      {children}
    </ViewerHeaderOutletContext.Provider>
  );
}
registry/new-york-v4/ui/viewer-internals.tsx
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

import type {
  ViewerDataAttributes,
  ViewerPortalContainmentAttributes,
  ViewerSidebarStateValue,
  ViewerGeometrySnapshot,
  ViewerGeometryStore,
  ViewerGeometryTarget,
  ViewerSidebarRegistrationState,
  ViewerSidebarRequestedMode,
  ViewerSidebarMode,
  ViewerStateAttributeNamespace,
  ViewerStateAttributeSlot,
  ViewerStateAttributeValues,
  ViewerSurfaceMeasurement,
} from "./viewer-types";

export const VIEWER_SIDEBAR_INLINE_BREAKPOINT = 768;
export const VIEWER_SIDEBAR_WIDTH = "10rem";
export const VIEWER_ROOT_ID_ATTRIBUTE = "data-viewer-root-id";
export const VIEWER_PORTAL_ROOT_ID_ATTRIBUTE = "data-viewer-portal-root-id";

const VIEWER_SIDEBAR_MODE_HYSTERESIS = 16;

const DEFAULT_VIEWER_GEOMETRY_SNAPSHOT: ViewerGeometrySnapshot = {
  bodyInlineSize: 0,
  documentInlineSize: 0,
  hasMeasuredBody: false,
  isTransitioning: false,
  mode: "overlay",
  open: false,
  progress: 1,
  sidebarGapTransition: "width",
  sidebarInlineSize: 0,
  sidebarWidth: 0,
  side: "left",
  state: "collapsed",
  transitionPhase: "idle",
};

const ViewerSidebarStateContext =
  React.createContext<ViewerSidebarStateValue | null>(null);
const ViewerSidebarRegistrationContext =
  React.createContext<ViewerSidebarRegistrationState | null>(null);
const ViewerSurfaceMeasurementContext =
  React.createContext<ViewerSurfaceMeasurement | null>(null);
const ViewerViewportPresenceContext = React.createContext(false);
const VIEWER_GEOMETRY_TRANSITION_MS = 150;

type ViewerGeometryBaseSnapshot = Pick<
  ViewerGeometrySnapshot,
  | "bodyInlineSize"
  | "documentInlineSize"
  | "hasMeasuredBody"
  | "mode"
  | "open"
  | "sidebarGapTransition"
  | "sidebarInlineSize"
  | "sidebarWidth"
  | "side"
  | "state"
>;

export function ViewerSidebarStateProvider({
  children,
  value,
}: {
  children: React.ReactNode;
  value: ViewerSidebarStateValue;
}) {
  return React.createElement(
    ViewerSidebarStateContext.Provider,
    { value },
    children,
  );
}

export function ViewerSidebarRegistrationProvider({
  children,
  value,
}: {
  children: React.ReactNode;
  value: ViewerSidebarRegistrationState;
}) {
  return React.createElement(
    ViewerSidebarRegistrationContext.Provider,
    { value },
    children,
  );
}

export function ViewerSurfaceMeasurementProvider({
  children,
  value,
}: {
  children: React.ReactNode;
  value: ViewerSurfaceMeasurement;
}) {
  return React.createElement(
    ViewerSurfaceMeasurementContext.Provider,
    { value },
    children,
  );
}

export function ViewerViewportPresenceProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return React.createElement(
    ViewerViewportPresenceContext.Provider,
    { value: true },
    children,
  );
}

export function useOptionalViewerSidebarState(): ViewerSidebarStateValue | null {
  return React.useContext(ViewerSidebarStateContext);
}

export function useViewerSidebarState(
  consumer: string,
): ViewerSidebarStateValue {
  const context = useOptionalViewerSidebarState();
  if (!context) {
    throw new Error(`${consumer} must be used within a ViewerRoot.`);
  }
  return context;
}

export function useOptionalViewerSidebarRegistration(): ViewerSidebarRegistrationState | null {
  return React.useContext(ViewerSidebarRegistrationContext);
}

export function useOptionalViewerSurfaceMeasurementContext(): ViewerSurfaceMeasurement | null {
  return React.useContext(ViewerSurfaceMeasurementContext);
}

export function useViewerViewportPresence(): boolean {
  return React.useContext(ViewerViewportPresenceContext);
}

export function useViewerGeometrySnapshot(
  geometryStore: ViewerGeometryStore,
): ViewerGeometrySnapshot {
  return React.useSyncExternalStore(
    geometryStore.subscribe,
    geometryStore.getSnapshot,
    geometryStore.getSnapshot,
  );
}

export function createViewerGeometryStore(): ViewerGeometryStore {
  const listeners = new Set<() => void>();
  let snapshot = DEFAULT_VIEWER_GEOMETRY_SNAPSHOT;
  let transitionTargetSnapshot: ViewerGeometryBaseSnapshot | null = null;
  let transitionFrame = 0;
  let transitionStartTime: number | null = null;
  let transitionStartSidebarInlineSize =
    DEFAULT_VIEWER_GEOMETRY_SNAPSHOT.sidebarInlineSize;

  const notify = () => {
    for (const listener of listeners) listener();
  };

  const commitSnapshot = (nextSnapshot: ViewerGeometrySnapshot) => {
    if (areViewerGeometrySnapshotsEqual(snapshot, nextSnapshot)) return;

    snapshot = nextSnapshot;
    notify();
  };

  const cancelTransitionFrame = () => {
    if (transitionFrame === 0) return;
    const cancelAnimationFrame =
      globalThis.cancelAnimationFrame ??
      (typeof window === "undefined" ? undefined : window.cancelAnimationFrame);
    cancelAnimationFrame?.(transitionFrame);
    transitionFrame = 0;
  };

  const commitGeometryProgress = (progress: number) => {
    if (!transitionTargetSnapshot) return;

    const sidebarInlineSize = lerp(
      transitionStartSidebarInlineSize,
      transitionTargetSnapshot.sidebarInlineSize,
      progress,
    );

    commitSnapshot({
      ...transitionTargetSnapshot,
      documentInlineSize: getViewerGeometryDocumentInlineSize({
        bodyInlineSize: transitionTargetSnapshot.bodyInlineSize,
        mode: transitionTargetSnapshot.mode,
        sidebarGapTransition: transitionTargetSnapshot.sidebarGapTransition,
        sidebarInlineSize,
      }),
      isTransitioning: progress < 1,
      progress,
      sidebarInlineSize,
      transitionPhase: progress < 1 ? "sliding" : "idle",
    });

    if (progress >= 1) {
      transitionTargetSnapshot = null;
    }
  };

  const scheduleTransitionFrame = () => {
    const requestAnimationFrame =
      globalThis.requestAnimationFrame ??
      (typeof window === "undefined" ? undefined : window.requestAnimationFrame);

    if (typeof requestAnimationFrame !== "function") {
      commitGeometryProgress(1);
      return;
    }

    transitionFrame = requestAnimationFrame((timestamp) => {
      transitionFrame = 0;
      if (!transitionTargetSnapshot) return;
      if (transitionStartTime === null) transitionStartTime = timestamp;

      const progress = clamp01(
        (timestamp - transitionStartTime) / VIEWER_GEOMETRY_TRANSITION_MS,
      );
      commitGeometryProgress(progress);

      if (progress < 1) scheduleTransitionFrame();
    });
  };

  const startTransition = (targetSnapshot: ViewerGeometryBaseSnapshot) => {
    cancelTransitionFrame();

    transitionTargetSnapshot = targetSnapshot;
    transitionStartSidebarInlineSize = snapshot.sidebarInlineSize;
    transitionStartTime = readViewerGeometryNow();

    commitGeometryProgress(0);
    scheduleTransitionFrame();
  };

  return {
    getSnapshot: () => snapshot,
    setTarget: (target) => {
      const resolvedSidebarWidth =
        Number.isFinite(target.sidebarWidth) && target.sidebarWidth > 0
          ? target.sidebarWidth
          : readViewerGeometrySidebarWidth(target);
      const sidebarWidth =
        resolvedSidebarWidth > 0 ? resolvedSidebarWidth : snapshot.sidebarWidth;
      const measuredBodyInlineSize = readViewerGeometryBodyInlineSize(target);
      const bodyInlineSize =
        measuredBodyInlineSize > 0
          ? measuredBodyInlineSize
          : snapshot.bodyInlineSize;
      const hasMeasuredBody =
        measuredBodyInlineSize > 0 || snapshot.hasMeasuredBody;
      const sidebarInlineSize = getViewerGeometryTargetSidebarInlineSize({
        mode: target.mode,
        open: target.open,
        sidebarGapTransition: target.sidebarGapTransition,
        sidebarWidth,
      });
      const nextBaseSnapshot: ViewerGeometryBaseSnapshot = {
        bodyInlineSize,
        documentInlineSize: getViewerGeometryDocumentInlineSize({
          bodyInlineSize,
          mode: target.mode,
          sidebarGapTransition: target.sidebarGapTransition,
          sidebarInlineSize,
        }),
        hasMeasuredBody,
        mode: target.mode,
        open: target.open,
        sidebarGapTransition: target.sidebarGapTransition,
        sidebarInlineSize,
        sidebarWidth,
        side: target.side,
        state: target.state,
      };

      if (
        snapshot.isTransitioning &&
        transitionTargetSnapshot &&
        areViewerGeometryTargetsEqual(
          transitionTargetSnapshot,
          nextBaseSnapshot,
        )
      ) {
        return;
      }

      const shouldAnimate = shouldAnimateViewerGeometry({
        previousSnapshot: snapshot,
        targetSnapshot: nextBaseSnapshot,
      });

      if (!shouldAnimate) {
        cancelTransitionFrame();
        transitionTargetSnapshot = null;
        commitSnapshot({
          ...nextBaseSnapshot,
          isTransitioning: false,
          progress: 1,
          transitionPhase: "idle",
        });
        return;
      }

      startTransition(nextBaseSnapshot);
    },
    subscribe: (listener) => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
  };
}

export function resolveSidebarMode({
  requestedMode,
  width,
  inlineBreakpoint,
}: {
  requestedMode: ViewerSidebarRequestedMode;
  width: number | null;
  inlineBreakpoint: number;
}): ViewerSidebarMode {
  if (requestedMode !== "auto") return requestedMode;
  if (width === null) return "overlay";
  return width >= inlineBreakpoint ? "inline" : "overlay";
}

export function resolveMeasuredSidebarMode({
  currentMode,
  hasMeasured,
  inlineBreakpoint,
  requestedMode,
  width,
}: {
  currentMode: ViewerSidebarMode;
  hasMeasured: boolean;
  inlineBreakpoint: number;
  requestedMode: ViewerSidebarRequestedMode;
  width: number;
}): ViewerSidebarMode {
  if (requestedMode !== "auto") return requestedMode;
  if (!hasMeasured) {
    return width >= inlineBreakpoint ? "inline" : "overlay";
  }

  if (currentMode === "inline") {
    return width < inlineBreakpoint - VIEWER_SIDEBAR_MODE_HYSTERESIS
      ? "overlay"
      : "inline";
  }

  return width > inlineBreakpoint + VIEWER_SIDEBAR_MODE_HYSTERESIS
    ? "inline"
    : "overlay";
}

export function isAriaDisabled(value: unknown): boolean {
  return value === true || value === "true";
}

export function pickCssCustomProperties(
  style: React.CSSProperties | undefined,
): React.CSSProperties {
  if (!style) return {};

  return Object.fromEntries(
    Object.entries(style).filter(([name]) => name.startsWith("--")),
  ) as React.CSSProperties;
}

function isViewerStateNamespaceEnabled(
  namespace: ViewerStateAttributeNamespace | undefined,
  slot: ViewerStateAttributeSlot,
): namespace is ViewerStateAttributeNamespace {
  if (!namespace) return false;
  return namespace.slots?.[slot] ?? true;
}

export function createViewerStateAttributes(
  namespace: ViewerStateAttributeNamespace | undefined,
  slot: ViewerStateAttributeSlot,
  values: ViewerStateAttributeValues,
): ViewerDataAttributes {
  if (!isViewerStateNamespaceEnabled(namespace, slot)) return {};

  const attributes: ViewerDataAttributes = {};
  const prefix = namespace.prefix;

  if (values.hasSidebar !== undefined) {
    attributes[`data-${prefix}-has-sidebar`] = values.hasSidebar
      ? "true"
      : "false";
  }
  if (values.sidebarCollapsible !== undefined) {
    attributes[`data-${prefix}-sidebar-collapsible`] =
      values.sidebarCollapsible;
  }
  if (values.sidebarMode !== undefined) {
    attributes[`data-${prefix}-sidebar-mode`] = values.sidebarMode;
  }
  if (values.sidebarOpen !== undefined) {
    attributes[`data-${prefix}-sidebar-open`] = values.sidebarOpen
      ? "true"
      : "false";
  }
  if (values.sidebarSide !== undefined) {
    attributes[`data-${prefix}-sidebar-side`] = values.sidebarSide;
  }
  if (values.sidebarState !== undefined) {
    attributes[`data-${prefix}-sidebar-state`] = values.sidebarState;
  }

  return attributes;
}

export function createViewerSlotAttributes(
  namespace: ViewerStateAttributeNamespace | undefined,
  slot: ViewerStateAttributeSlot,
  value: string | undefined,
): ViewerDataAttributes {
  if (!value || !isViewerStateNamespaceEnabled(namespace, slot)) return {};
  return {
    [`data-${namespace.prefix}-slot`]: value,
  };
}

export function createViewerPortalContainmentAttributes(
  rootId: string,
): ViewerPortalContainmentAttributes {
  return {
    [VIEWER_PORTAL_ROOT_ID_ATTRIBUTE]: rootId,
  };
}

export function readViewerElementSize(element: HTMLElement | null) {
  if (!element) return { height: 0, width: 0 };

  const rect =
    typeof element.getBoundingClientRect === "function"
      ? element.getBoundingClientRect()
      : null;
  return {
    height: rect?.height || element.clientHeight || 0,
    width: rect?.width || element.clientWidth || 0,
  };
}

function readViewerGeometrySidebarWidth({
  rootElement,
  sidebarElement,
}: ViewerGeometryTarget) {
  const elementSize = readViewerElementSize(sidebarElement);
  if (Number.isFinite(elementSize.width) && elementSize.width > 0) {
    return elementSize.width;
  }

  return readViewerSidebarDeclaredWidth(rootElement, sidebarElement);
}

function readViewerGeometryBodyInlineSize({
  bodyElement,
  rootElement,
}: ViewerGeometryTarget) {
  const bodySize = readViewerElementSize(bodyElement);
  if (Number.isFinite(bodySize.width) && bodySize.width > 0) {
    return bodySize.width;
  }

  const rootSize = readViewerElementSize(rootElement);
  return Number.isFinite(rootSize.width) && rootSize.width > 0
    ? rootSize.width
    : 0;
}

function readViewerSidebarDeclaredWidth(
  rootElement: HTMLElement | null,
  sidebarElement: HTMLElement | null,
) {
  return (
    readViewerCssLength(rootElement, "--viewer-sidebar-width") ??
    readViewerCssLength(sidebarElement, "--viewer-sidebar-width") ??
    0
  );
}

function readViewerCssLength(
  element: HTMLElement | null,
  propertyName: string,
) {
  if (!element || typeof window === "undefined") return null;

  const styleValue = element.style.getPropertyValue(propertyName);
  const computedValue =
    window.getComputedStyle(element).getPropertyValue(propertyName);
  return parseViewerCssLength(styleValue) ?? parseViewerCssLength(computedValue);
}

function parseViewerCssLength(value: string) {
  const trimmedValue = value.trim();
  if (!trimmedValue.endsWith("px")) return null;

  const length = Number.parseFloat(trimmedValue);
  return Number.isFinite(length) && length > 0 ? length : null;
}

function areViewerGeometrySnapshotsEqual(
  previous: ViewerGeometrySnapshot,
  next: ViewerGeometrySnapshot,
) {
  return (
    previous.bodyInlineSize === next.bodyInlineSize &&
    previous.documentInlineSize === next.documentInlineSize &&
    previous.hasMeasuredBody === next.hasMeasuredBody &&
    previous.isTransitioning === next.isTransitioning &&
    previous.mode === next.mode &&
    previous.open === next.open &&
    previous.progress === next.progress &&
    previous.sidebarGapTransition === next.sidebarGapTransition &&
    previous.sidebarInlineSize === next.sidebarInlineSize &&
    previous.sidebarWidth === next.sidebarWidth &&
    previous.side === next.side &&
    previous.state === next.state &&
    previous.transitionPhase === next.transitionPhase
  );
}

function areViewerGeometryTargetsEqual(
  previous: ViewerGeometryBaseSnapshot,
  next: ViewerGeometryBaseSnapshot,
) {
  return (
    previous.bodyInlineSize === next.bodyInlineSize &&
    previous.documentInlineSize === next.documentInlineSize &&
    previous.hasMeasuredBody === next.hasMeasuredBody &&
    previous.mode === next.mode &&
    previous.open === next.open &&
    previous.sidebarGapTransition === next.sidebarGapTransition &&
    previous.sidebarInlineSize === next.sidebarInlineSize &&
    previous.sidebarWidth === next.sidebarWidth &&
    previous.side === next.side &&
    previous.state === next.state
  );
}

function shouldAnimateViewerGeometry({
  previousSnapshot,
  targetSnapshot,
}: {
  previousSnapshot: ViewerGeometrySnapshot;
  targetSnapshot: ViewerGeometryBaseSnapshot;
}) {
  return (
    previousSnapshot.sidebarWidth > 0 &&
    targetSnapshot.mode === "inline" &&
    targetSnapshot.sidebarGapTransition === "width" &&
    targetSnapshot.sidebarWidth > 0 &&
    hasViewerGeometryAnimationSupport() &&
    !prefersReducedViewerGeometryMotion() &&
    !areViewerGeometryInlineSizesEqual(
      previousSnapshot.sidebarInlineSize,
      targetSnapshot.sidebarInlineSize,
    )
  );
}

function getViewerGeometryTargetSidebarInlineSize(
  snapshot: Pick<
    ViewerGeometryBaseSnapshot,
    "mode" | "open" | "sidebarGapTransition" | "sidebarWidth"
  >,
) {
  return snapshot.mode === "inline" && snapshot.open && snapshot.sidebarWidth > 0
    ? snapshot.sidebarWidth
    : 0;
}

function getViewerGeometryDocumentInlineSize({
  bodyInlineSize,
  mode,
  sidebarGapTransition,
  sidebarInlineSize,
}: {
  bodyInlineSize: number;
  mode: ViewerSidebarMode;
  sidebarGapTransition: string;
  sidebarInlineSize: number;
}) {
  if (bodyInlineSize <= 0) return 0;
  if (mode !== "inline" || sidebarGapTransition !== "width") {
    return bodyInlineSize;
  }
  return Math.max(1, bodyInlineSize - sidebarInlineSize);
}

function areViewerGeometryInlineSizesEqual(
  previousInlineSize: number,
  nextInlineSize: number,
) {
  return Math.abs(previousInlineSize - nextInlineSize) < 0.5;
}

function hasViewerGeometryAnimationSupport() {
  const requestAnimationFrame =
    globalThis.requestAnimationFrame ??
    (typeof window === "undefined" ? undefined : window.requestAnimationFrame);
  return typeof requestAnimationFrame === "function";
}

function prefersReducedViewerGeometryMotion() {
  return (
    typeof window !== "undefined" &&
    typeof window.matchMedia === "function" &&
    window.matchMedia("(prefers-reduced-motion: reduce)").matches
  );
}

function readViewerGeometryNow() {
  const performanceNow =
    globalThis.performance?.now ??
    (typeof window === "undefined" ? undefined : window.performance?.now);

  return typeof performanceNow === "function"
    ? performanceNow.call(globalThis.performance ?? window.performance)
    : Date.now();
}

function lerp(start: number, end: number, progress: number) {
  return start + (end - start) * progress;
}

function clamp01(value: number) {
  if (!Number.isFinite(value)) return 1;
  return Math.min(1, Math.max(0, value));
}

export function elementContainsTarget(
  element: HTMLElement | null | undefined,
  target: unknown,
): boolean {
  if (!element || target === null || target === undefined) return false;
  if (element === target) return true;

  if (typeof element.contains === "function") {
    try {
      if (element.contains(target as Node)) return true;
    } catch {
      // Some test and embedded DOM hosts do not share a Node prototype chain.
    }
  }

  if (typeof target !== "object") return false;

  let current: unknown = (target as { parentNode?: unknown }).parentNode;
  while (current && typeof current === "object") {
    if (current === element) return true;
    current = (current as { parentNode?: unknown }).parentNode;
  }

  return false;
}

export function targetHasViewerPortalContainment({
  rootId,
  target,
}: {
  rootId: string;
  target: unknown;
}): boolean {
  if (!target || typeof target !== "object") return false;

  if (typeof (target as { closest?: unknown }).closest === "function") {
    try {
      const ownerElement = (
        target as {
          closest: (selector: string) => {
            getAttribute?: (attributeName: string) => string | null;
          } | null;
        }
      ).closest(`[${VIEWER_PORTAL_ROOT_ID_ATTRIBUTE}]`);

      if (
        ownerElement?.getAttribute?.(VIEWER_PORTAL_ROOT_ID_ATTRIBUTE) === rootId
      ) {
        return true;
      }
    } catch {
      // Some test and embedded DOM hosts do not support Element.closest.
    }
  }

  let current: unknown = target;
  while (current && typeof current === "object") {
    const getAttribute = (current as { getAttribute?: unknown }).getAttribute;
    if (
      typeof getAttribute === "function" &&
      getAttribute.call(current, VIEWER_PORTAL_ROOT_ID_ATTRIBUTE) === rootId
    ) {
      return true;
    }
    current = (current as { parentNode?: unknown }).parentNode;
  }

  return false;
}

export function targetViewerRootId(target: unknown): string | null {
  if (!target || typeof target !== "object") return null;

  if (typeof (target as { closest?: unknown }).closest === "function") {
    try {
      const rootElement = (
        target as {
          closest: (selector: string) => {
            getAttribute?: (attributeName: string) => string | null;
          } | null;
        }
      ).closest(`[${VIEWER_ROOT_ID_ATTRIBUTE}]`);
      const rootId = rootElement?.getAttribute?.(VIEWER_ROOT_ID_ATTRIBUTE);
      if (rootId) return rootId;
    } catch {
      // Some test and embedded DOM hosts do not support Element.closest.
    }
  }

  let current: unknown = target;
  while (current && typeof current === "object") {
    const getAttribute = (current as { getAttribute?: unknown }).getAttribute;
    if (typeof getAttribute === "function") {
      const rootId = getAttribute.call(current, VIEWER_ROOT_ID_ATTRIBUTE);
      if (typeof rootId === "string" && rootId) {
        return rootId;
      }
    }
    current = (current as { parentNode?: unknown }).parentNode;
  }

  return null;
}

export function findClosestViewerBody(
  element: HTMLDivElement | null,
): HTMLElement | null {
  if (!element || typeof element.closest !== "function") return null;
  return element.closest<HTMLElement>('[data-viewer-slot="body"]');
}

export function useViewerDevelopmentLayoutWarning({
  enabled = true,
  elements,
  inspect,
  keyParts,
}: {
  enabled?: boolean;
  elements: () => Array<HTMLElement | null>;
  inspect: () => void;
  keyParts: unknown[];
}) {
  useKeyedLayoutEffect(enabled ? joinEffectKey(keyParts) : null, () => {
    if (process.env.NODE_ENV === "production" || !enabled) return;

    const observedElements = elements().filter(
      (element): element is HTMLElement => Boolean(element),
    );

    let frame: number | null = null;
    const run = () => {
      frame = null;
      inspect();
    };
    const schedule = () => {
      if (frame !== null) return;

      if (typeof requestAnimationFrame === "function") {
        frame = requestAnimationFrame(run);
        return;
      }

      run();
    };

    schedule();

    const ResizeObserverConstructor = globalThis.ResizeObserver;
    if (
      observedElements.length === 0 ||
      typeof ResizeObserverConstructor === "undefined"
    ) {
      return () => {
        if (frame !== null && typeof cancelAnimationFrame === "function") {
          cancelAnimationFrame(frame);
        }
      };
    }

    const observer = new ResizeObserverConstructor(schedule);
    for (const element of observedElements) {
      observer.observe(element);
    }

    return () => {
      if (frame !== null && typeof cancelAnimationFrame === "function") {
        cancelAnimationFrame(frame);
      }
      observer.disconnect();
    };
  });
}

export function useViewerSidebarRegistrationContext(
  consumer: string,
): ViewerSidebarRegistrationState {
  const context = useOptionalViewerSidebarRegistration();
  if (!context) {
    throw new Error(`${consumer} must be used within a ViewerRoot.`);
  }
  return context;
}
registry/new-york-v4/ui/viewer-measurement.ts
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

export type StableElementSize<Element extends HTMLElement = HTMLElement> = {
  element: Element | null;
  hasMeasured: boolean;
  height: number | null;
  setElement: React.RefCallback<Element>;
  width: number | null;
};

export type StableElementSizeOptions = {
  enabled?: boolean;
  observe?: boolean;
  retainLastNonZero?: boolean;
};

export type StableCssLengthOptions = {
  element: HTMLElement | null;
  retainLastNonZero?: boolean;
  value: string;
};

type MeasuredSize = {
  height: number | null;
  width: number | null;
};

type RawMeasuredSize = {
  height: number;
  width: number;
};

// DOM layout reads for viewer chrome are quarantined in this module. The
// file-viewer motion kernel (time + style writes only) receives this reader by
// injection from the frame controller instead of touching layout APIs itself.
export function readElementRectSnapshot(
  element: HTMLElement | null,
): readonly number[] {
  if (!element) return [];
  const rect = element.getBoundingClientRect();
  return [rect.left, rect.top, rect.width, rect.height];
}

// Computed CSS inline direction of an element, sampled when it attaches (a
// runtime `dir` flip is picked up on the next mount). The fit-width motion
// transform works on the physical X axis, so the renderer frame needs to
// know which edge auto-margin alignment pins the stage to.
export function useViewerInlineDirection(
  element: HTMLElement | null,
): "ltr" | "rtl" {
  const [direction, setDirection] = React.useState<"ltr" | "rtl">("ltr");

  useKeyedLayoutEffect(element ? joinEffectKey([element]) : null, () => {
    if (!element) return;
    setDirection(getComputedStyle(element).direction === "rtl" ? "rtl" : "ltr");
  });

  return direction;
}

function readElementSize(element: HTMLElement): RawMeasuredSize {
  const rect =
    typeof element.getBoundingClientRect === "function"
      ? element.getBoundingClientRect()
      : null;

  return {
    height: rect?.height || element.clientHeight,
    width: rect?.width || element.clientWidth,
  };
}

function resolveMeasuredElementSize({
  currentSize,
  nextSize,
  retainLastNonZero,
}: {
  currentSize: MeasuredSize;
  nextSize: RawMeasuredSize;
  retainLastNonZero: boolean;
}): MeasuredSize {
  const width =
    Number.isFinite(nextSize.width) &&
    (!retainLastNonZero || nextSize.width > 0)
      ? nextSize.width
      : currentSize.width;
  const height =
    Number.isFinite(nextSize.height) &&
    (!retainLastNonZero || nextSize.height > 0)
      ? nextSize.height
      : currentSize.height;

  if (currentSize.width === width && currentSize.height === height) {
    return currentSize;
  }

  return { height, width };
}

export function useStableElementSize<Element extends HTMLElement = HTMLElement>(
  options: StableElementSizeOptions = {},
): StableElementSize<Element> {
  const enabled = options.enabled ?? true;
  const observe = options.observe ?? true;
  const retainLastNonZero = options.retainLastNonZero ?? false;
  const [element, setElementState] = React.useState<Element | null>(null);
  const [size, setSize] = React.useState<MeasuredSize>({
    height: null,
    width: null,
  });
  const hasMeasured = size.height !== null || size.width !== null;

  const setElement = React.useCallback((nextElement: Element | null) => {
    setElementState(nextElement);
  }, []);

  useKeyedLayoutEffect(enabled ? null : "reset", () => {
    setSize({ height: null, width: null });
  });

  useKeyedLayoutEffect(
    enabled && element
      ? joinEffectKey([element, observe, retainLastNonZero])
      : null,
    () => {
      if (!element) return;

      setSize((currentSize) =>
        resolveMeasuredElementSize({
          currentSize,
          nextSize: readElementSize(element),
          retainLastNonZero,
        }),
      );

      const ResizeObserverConstructor = observe
        ? globalThis.ResizeObserver
        : undefined;
      if (typeof ResizeObserverConstructor === "undefined") return;

      let frame = 0;
      let latestSize = readElementSize(element);
      const observer = new ResizeObserverConstructor((entries) => {
        for (const entry of entries) {
          latestSize = readElementSize(entry.target as HTMLElement);
        }

        if (frame) return;
        frame = requestAnimationFrame(() => {
          frame = 0;
          setSize((currentSize) =>
            resolveMeasuredElementSize({
              currentSize,
              nextSize: latestSize,
              retainLastNonZero,
            }),
          );
        });
      });

      observer.observe(element);

      return () => {
        if (frame) cancelAnimationFrame(frame);
        observer.disconnect();
      };
    },
  );

  return React.useMemo(
    () => ({
      element,
      hasMeasured,
      height: size.height,
      setElement,
      width: size.width,
    }),
    [element, hasMeasured, setElement, size.height, size.width],
  );
}

export function useStableCssLength({
  element,
  retainLastNonZero = true,
  value,
}: StableCssLengthOptions) {
  const [resolvedLength, setResolvedLength] = React.useState(0);

  useKeyedLayoutEffect(
    value ? joinEffectKey([element, retainLastNonZero, value]) : null,
    () => {
      const nextLength = resolveCssLength(value, element);

      setResolvedLength((currentLength) => {
        if (retainLastNonZero && nextLength <= 0) return currentLength;
        return areCssLengthsEqual(currentLength, nextLength)
          ? currentLength
          : nextLength;
      });
    },
  );

  return resolvedLength;
}

function resolveCssLength(value: string, element: HTMLElement | null) {
  const trimmedValue = value.trim();
  const pixelMatch = trimmedValue.match(/^(-?\d+(?:\.\d+)?)px$/);
  if (pixelMatch) return Math.max(0, Number(pixelMatch[1]));

  if (typeof window === "undefined") return 0;

  const remMatch = trimmedValue.match(/^(-?\d+(?:\.\d+)?)rem$/);
  if (remMatch) {
    return (
      Math.max(0, Number(remMatch[1])) *
      readComputedFontSize(window.document.documentElement)
    );
  }

  const emMatch = trimmedValue.match(/^(-?\d+(?:\.\d+)?)em$/);
  if (emMatch) {
    return Math.max(0, Number(emMatch[1])) * readComputedFontSize(element);
  }

  const measuringElement = window.document.createElement("div");
  measuringElement.style.contain = "strict";
  measuringElement.style.inlineSize = trimmedValue;
  measuringElement.style.position = "absolute";
  measuringElement.style.visibility = "hidden";
  (element ?? window.document.body).appendChild(measuringElement);
  const width = readElementInlineSize(measuringElement);
  measuringElement.remove();
  return width;
}

function readElementInlineSize(element: HTMLElement) {
  const rect =
    typeof element.getBoundingClientRect === "function"
      ? element.getBoundingClientRect()
      : null;
  const width = rect?.width || element.clientWidth || 0;
  return Number.isFinite(width) && width > 0 ? width : 0;
}

function readComputedFontSize(element: Element | null) {
  if (typeof window === "undefined") return 16;
  const fontSize = element ? window.getComputedStyle(element).fontSize : "16px";
  const value = Number.parseFloat(fontSize);
  return Number.isFinite(value) && value > 0 ? value : 16;
}

function areCssLengthsEqual(previous: number, next: number) {
  return Math.abs(previous - next) <= 0.001;
}
registry/new-york-v4/ui/viewer-root.tsx
"use client";

import * as React from "react";

import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import { cn } from "@/lib/utils";

import {
  createViewerStateAttributes,
  createViewerPortalContainmentAttributes,
  createViewerGeometryStore,
  elementContainsTarget,
  readViewerElementSize,
  resolveMeasuredSidebarMode,
  resolveSidebarMode,
  targetHasViewerPortalContainment,
  targetViewerRootId,
  useOptionalViewerSidebarRegistration,
  useOptionalViewerSidebarState,
  useViewerSidebarState,
  VIEWER_SIDEBAR_INLINE_BREAKPOINT,
  VIEWER_SIDEBAR_WIDTH,
  ViewerSidebarRegistrationProvider,
  ViewerSidebarStateProvider,
} from "./viewer-internals";
import type {
  ViewerRootDiagnostics,
  ViewerPortalContainmentAttributes,
  ViewerRootProps,
  ViewerSidebarStateValue,
  ViewerSidebarMode,
  ViewerSidebarRegistration,
  ViewerSidebarRegistrationState,
  ViewerSidebarState,
} from "./viewer-types";

export function useViewerSidebar(): ViewerSidebarStateValue {
  return useViewerSidebarState("useViewerSidebar");
}

export function useOptionalViewerSidebar(): ViewerSidebarStateValue | null {
  return useOptionalViewerSidebarState();
}

export function useViewerPortalContainmentAttributes(): ViewerPortalContainmentAttributes {
  const context = useOptionalViewerSidebarRegistration();
  if (!context) {
    throw new Error(
      "useViewerPortalContainmentAttributes must be used within a ViewerRoot.",
    );
  }

  return React.useMemo(
    () => createViewerPortalContainmentAttributes(context.rootId),
    [context.rootId],
  );
}

export function useOptionalViewerRootDiagnostics(): ViewerRootDiagnostics | null {
  const context = useOptionalViewerSidebarRegistration();
  if (!context) return null;

  const geometry = context.geometryStore.getSnapshot();
  return {
    getRootElement: context.getRootElement,
    layoutSignature: [
      context.hasSidebar ? "sidebar" : "no-sidebar",
      context.sidebarSide,
      geometry.mode,
      geometry.open ? "open" : "closed",
      geometry.state,
    ].join(":"),
    rootId: context.rootId,
  };
}

export function ViewerRoot({
  className,
  style,
  defaultOpen = false,
  open: openProp,
  onOpenChange,
  mode = "auto",
  inlineBreakpoint = VIEWER_SIDEBAR_INLINE_BREAKPOINT,
  sidebarSide = "left",
  sidebarCollapsible = "offcanvas",
  sidebarGapTransition = "width",
  stateNamespace,
  ...props
}: ViewerRootProps) {
  const rootRef = React.useRef<HTMLDivElement | null>(null);
  const reactId = React.useId();
  const rootId = `${reactId}-viewer-root`;
  const fallbackSidebarId = `${reactId}-viewer-sidebar`;
  const isControlled = openProp !== undefined;
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
  const open = openProp ?? internalOpen;
  const openRef = React.useRef(open);
  openRef.current = open;
  const hasMeasuredSidebarWidthRef = React.useRef(false);
  const lastTriggerElementRef = React.useRef<HTMLElement | null>(null);
  const registeredSidebarRef = React.useRef<ViewerSidebarRegistration | null>(
    null,
  );
  const bodyElementRef = React.useRef<HTMLElement | null>(null);
  const geometryStore = React.useMemo(() => createViewerGeometryStore(), []);
  const [registeredSidebar, setRegisteredSidebar] =
    React.useState<ViewerSidebarRegistration | null>(null);
  const [bodyElement, setBodyElement] = React.useState<HTMLElement | null>(
    null,
  );
  const [resolvedSidebarMode, setResolvedSidebarMode] =
    React.useState<ViewerSidebarMode>(() =>
      resolveSidebarMode({
        requestedMode: mode,
        width: null,
        inlineBreakpoint,
      }),
    );

  const commitViewerGeometryTarget = React.useCallback(
    (nextOpen: boolean) => {
      const sidebarRegistration = registeredSidebarRef.current;
      const nextEffectiveOpen =
        sidebarRegistration?.collapsible === "none" ? true : nextOpen;
      const nextState: ViewerSidebarState = nextEffectiveOpen
        ? "expanded"
        : "collapsed";

      geometryStore.setTarget({
        bodyElement: bodyElementRef.current,
        mode: resolvedSidebarMode,
        open: nextEffectiveOpen,
        rootElement: rootRef.current,
        sidebarElement: sidebarRegistration?.element ?? null,
        sidebarGapTransition,
        sidebarWidth: sidebarRegistration?.widthPixels ?? 0,
        side: sidebarRegistration?.side ?? sidebarSide,
        state: nextState,
      });
    },
    [
      geometryStore,
      resolvedSidebarMode,
      sidebarGapTransition,
      sidebarSide,
    ],
  );

  useKeyedLayoutEffect(joinEffectKey([inlineBreakpoint, mode]), () => {
    if (mode !== "auto") {
      setResolvedSidebarMode((currentMode) =>
        currentMode === mode ? currentMode : mode,
      );
      return;
    }

    const element = rootRef.current;
    const ResizeObserverConstructor = globalThis.ResizeObserver;
    if (!element || typeof ResizeObserverConstructor === "undefined") return;

    const updateMode = () => {
      const nextWidth = readViewerElementSize(element).width;
      if (nextWidth === 0) return;

      setResolvedSidebarMode((currentMode) => {
        const nextMode = resolveMeasuredSidebarMode({
          currentMode,
          hasMeasured: hasMeasuredSidebarWidthRef.current,
          requestedMode: mode,
          width: nextWidth,
          inlineBreakpoint,
        });

        hasMeasuredSidebarWidthRef.current = true;
        return currentMode === nextMode ? currentMode : nextMode;
      });
    };

    updateMode();

    const observer = new ResizeObserverConstructor(updateMode);
    observer.observe(element);

    return () => observer.disconnect();
  });

  const setOpen = React.useCallback(
    (value: boolean | ((open: boolean) => boolean)) => {
      const previousOpen = isControlled ? open : openRef.current;
      const nextOpen =
        typeof value === "function" ? value(previousOpen) : value;

      if (nextOpen === previousOpen) {
        return;
      }

      commitViewerGeometryTarget(nextOpen);

      if (!isControlled) {
        openRef.current = nextOpen;
        setInternalOpen(nextOpen);
      }
      onOpenChange?.(nextOpen);
    },
    [commitViewerGeometryTarget, isControlled, onOpenChange, open],
  );

  const registerSidebar = React.useCallback(
    (registration: ViewerSidebarRegistration) => {
      const currentRegistration = registeredSidebarRef.current;

      if (
        currentRegistration &&
        currentRegistration.instanceId !== registration.instanceId
      ) {
        throw new Error(
          "ViewerRoot supports one primary ViewerSidebar. Use a nested ViewerRoot for a complete nested viewer, or put secondary content inside ViewerSurface.",
        );
      }

      registeredSidebarRef.current = registration;
      setRegisteredSidebar(registration);

      return () => {
        if (
          registeredSidebarRef.current?.instanceId !== registration.instanceId
        ) {
          return;
        }

        registeredSidebarRef.current = null;
        setRegisteredSidebar(null);
      };
    },
    [],
  );
  const registerBody = React.useCallback((element: HTMLElement) => {
    bodyElementRef.current = element;
    setBodyElement(element);

    return () => {
      if (bodyElementRef.current !== element) return;
      bodyElementRef.current = null;
      setBodyElement(null);
    };
  }, []);

  const toggleSidebar = React.useCallback(() => {
    setOpen((currentOpen) => !currentOpen);
  }, [setOpen]);

  const hasSidebar = registeredSidebar !== null;
  const effectiveOpen = registeredSidebar?.collapsible === "none" ? true : open;
  const state: ViewerSidebarState = effectiveOpen ? "expanded" : "collapsed";
  const canToggleSidebar =
    hasSidebar && registeredSidebar.collapsible !== "none";
  const sidebarId = registeredSidebar?.id ?? fallbackSidebarId;
  const resolvedSidebarSide = registeredSidebar?.side ?? sidebarSide;
  const sidebarWidth = registeredSidebar?.width ?? VIEWER_SIDEBAR_WIDTH;

  useKeyedLayoutEffect(
    joinEffectKey([
      effectiveOpen,
      registeredSidebar?.element,
      registeredSidebar?.widthPixels,
      bodyElement,
      resolvedSidebarMode,
      resolvedSidebarSide,
      rootRef.current,
      sidebarGapTransition,
      geometryStore,
      state,
    ]),
    () => {
      geometryStore.setTarget({
        bodyElement,
        mode: resolvedSidebarMode,
        open: effectiveOpen,
        rootElement: rootRef.current,
        sidebarElement: registeredSidebar?.element ?? null,
        sidebarGapTransition,
        sidebarWidth: registeredSidebar?.widthPixels ?? 0,
        side: resolvedSidebarSide,
        state,
      });
    },
  );
  useKeyedLayoutEffect(
    bodyElement
      ? joinEffectKey([bodyElement, commitViewerGeometryTarget, effectiveOpen])
      : null,
    () => {
      if (!bodyElement) return;
      const ResizeObserverConstructor = globalThis.ResizeObserver;
      if (typeof ResizeObserverConstructor === "undefined") {
        commitViewerGeometryTarget(effectiveOpen);
        return;
      }

      const observer = new ResizeObserverConstructor(() => {
        commitViewerGeometryTarget(effectiveOpen);
      });
      observer.observe(bodyElement);
      commitViewerGeometryTarget(effectiveOpen);

      return () => observer.disconnect();
    },
  );
  const namespacedRootStateAttributes = createViewerStateAttributes(
    stateNamespace,
    "root",
    {
      hasSidebar,
      sidebarCollapsible: registeredSidebar?.collapsible ?? sidebarCollapsible,
      sidebarMode: resolvedSidebarMode,
      sidebarOpen: effectiveOpen,
      sidebarSide: resolvedSidebarSide,
      sidebarState: state,
    },
  );
  const setLastTriggerElement = React.useCallback(
    (element: HTMLElement | null) => {
      lastTriggerElementRef.current = element;
    },
    [],
  );
  const getRootElement = React.useCallback(() => rootRef.current, []);

  useKeyedMountEffect(
    joinEffectKey([
      canToggleSidebar,
      open,
      registeredSidebar,
      resolvedSidebarMode,
      rootId,
      setOpen,
    ]),
    () => {
      if (
        !open ||
        !canToggleSidebar ||
        resolvedSidebarMode !== "overlay" ||
        typeof document === "undefined"
      ) {
        return;
      }

      const handleKeyDown = (event: KeyboardEvent) => {
        if (event.key === "Escape") {
          if (event.defaultPrevented) {
            return;
          }

          const activeElement = document.activeElement;
          if (
            activeElement &&
            rootRef.current &&
            !elementContainsTarget(rootRef.current, activeElement) &&
            !elementContainsTarget(registeredSidebar?.element, activeElement) &&
            !targetHasViewerPortalContainment({
              rootId,
              target: activeElement,
            })
          ) {
            return;
          }

          setOpen(false);
          const triggerElement =
            lastTriggerElementRef.current?.isConnected === true
              ? lastTriggerElementRef.current
              : rootRef.current?.querySelector<HTMLElement>(
                  "[data-viewer-sidebar-trigger]",
                );
          if (typeof triggerElement?.focus === "function") {
            triggerElement.focus();
          }
        }
      };
      const handlePointerDown = (event: PointerEvent) => {
        const target = event.target;

        if (elementContainsTarget(registeredSidebar?.element, target)) {
          return;
        }

        if (targetHasViewerPortalContainment({ rootId, target })) {
          return;
        }

        const targetRootId = targetViewerRootId(target);
        if (targetRootId && targetRootId !== rootId) {
          return;
        }

        if (
          target &&
          typeof (target as { closest?: unknown }).closest === "function"
        ) {
          const triggerElement = (
            target as unknown as {
              closest: (selector: string) => HTMLElement | null;
            }
          ).closest("[data-viewer-sidebar-trigger]");
          if (triggerElement?.dataset.viewerRootId === rootId) {
            return;
          }
        }

        setOpen(false);
      };

      document.addEventListener("keydown", handleKeyDown);
      document.addEventListener("pointerdown", handlePointerDown);

      return () => {
        document.removeEventListener("keydown", handleKeyDown);
        document.removeEventListener("pointerdown", handlePointerDown);
      };
    },
  );

  const sidebarStateContext = React.useMemo<ViewerSidebarStateValue>(
    () => ({
      state,
      open: effectiveOpen,
      setOpen,
      toggleSidebar,
      canToggleSidebar,
      mode: resolvedSidebarMode,
      side: resolvedSidebarSide,
    }),
    [
      state,
      effectiveOpen,
      setOpen,
      toggleSidebar,
      canToggleSidebar,
      resolvedSidebarMode,
      resolvedSidebarSide,
    ],
  );

  const sidebarRegistrationContext =
    React.useMemo<ViewerSidebarRegistrationState>(
      () => ({
        defaultSidebarCollapsible: sidebarCollapsible,
        defaultSidebarSide: sidebarSide,
        geometryStore,
        getRootElement,
        hasSidebar,
        registerBody,
        registerSidebar,
        rootId,
        sidebarId,
        sidebarGapTransition,
        sidebarSide: resolvedSidebarSide,
        setLastTriggerElement,
        stateNamespace,
      }),
      [
        geometryStore,
        hasSidebar,
        sidebarCollapsible,
        resolvedSidebarSide,
        getRootElement,
        registerBody,
        registerSidebar,
        rootId,
        sidebarId,
        sidebarGapTransition,
        sidebarSide,
        setLastTriggerElement,
        stateNamespace,
      ],
    );

  return (
    <ViewerSidebarStateProvider value={sidebarStateContext}>
      <ViewerSidebarRegistrationProvider value={sidebarRegistrationContext}>
        <div
          ref={rootRef}
          data-slot="viewer-root"
          data-viewer-root-id={rootId}
          data-viewer-has-sidebar={hasSidebar ? "true" : "false"}
          data-viewer-sidebar-collapsible={
            registeredSidebar?.collapsible ?? sidebarCollapsible
          }
          data-viewer-sidebar-mode={resolvedSidebarMode}
          data-viewer-sidebar-open={effectiveOpen ? "true" : "false"}
          data-viewer-sidebar-side={resolvedSidebarSide}
          data-viewer-sidebar-state={state}
          data-viewer-slot="root"
          {...namespacedRootStateAttributes}
          className={cn(
            "relative flex min-h-0 min-w-0 flex-col overflow-hidden",
            className,
          )}
          style={
            {
              overflowAnchor: "none",
              ...style,
              "--viewer-sidebar-width": sidebarWidth,
            } as React.CSSProperties
          }
          {...props}
        />
      </ViewerSidebarRegistrationProvider>
    </ViewerSidebarStateProvider>
  );
}
registry/new-york-v4/ui/viewer-sidebar.tsx
"use client";

import * as React from "react";

import { useMountEffect } from "@/hooks/use-mount-effect";
import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import { cn } from "@/lib/utils";

import {
  createViewerSlotAttributes,
  createViewerStateAttributes,
  pickCssCustomProperties,
  readViewerElementSize,
  useViewerDevelopmentLayoutWarning,
  useViewerSidebarState,
  useViewerGeometrySnapshot,
  useViewerSidebarRegistrationContext,
  VIEWER_SIDEBAR_WIDTH,
} from "./viewer-internals";
import { warnViewerDevelopmentOnce } from "./viewer-diagnostics";
import type { ViewerSidebarProps, ViewerSidebarState } from "./viewer-types";

export function ViewerSidebar({
  children,
  className,
  innerClassName,
  namespacedSlot,
  namespacedSlotNames,
  side: sideProp,
  slotNames,
  collapsible: collapsibleProp,
  width = VIEWER_SIDEBAR_WIDTH,
  style,
  id: idProp,
  ...props
}: ViewerSidebarProps) {
  const registrationContext =
    useViewerSidebarRegistrationContext("ViewerSidebar");
  const sidebarState = useViewerSidebarState("ViewerSidebar");
  const {
    defaultSidebarCollapsible,
    defaultSidebarSide,
    getRootElement,
    registerSidebar,
    rootId,
    sidebarGapTransition,
    geometryStore,
  } = registrationContext;
  const geometry = useViewerGeometrySnapshot(geometryStore);
  const reactId = React.useId();
  const instanceId = `${reactId}-viewer-sidebar-instance`;
  const generatedSidebarId = `${reactId}-viewer-sidebar`;
  const sidebarId = idProp ?? generatedSidebarId;
  const sidebarRef = React.useRef<HTMLElement | null>(null);
  const [transitionsReady, setTransitionsReady] = React.useState(false);
  const collapsible = collapsibleProp ?? defaultSidebarCollapsible;
  const side = sideProp ?? defaultSidebarSide;
  const open = collapsible === "none" ? true : sidebarState.open;
  const state: ViewerSidebarState = open ? "expanded" : "collapsed";
  const mode = sidebarState.mode;
  const visualOpen = mode === "inline" ? geometry.open : open;
  const visualState: ViewerSidebarState = visualOpen
    ? "expanded"
    : "collapsed";
  const isCollapsed = collapsible !== "none" && !open;
  const isOpening =
    collapsible !== "none" &&
    open &&
    geometry.isTransitioning;
  const frameSlot = props["data-slot"] ?? "viewer-sidebar";
  const sidebarProps = { ...props };
  delete sidebarProps["data-slot"];
  const styleWithoutWidth: React.CSSProperties = { ...style };
  delete styleWithoutWidth.width;
  const customProperties = pickCssCustomProperties(style);
  const frameStyle = {
    ...customProperties,
    "--viewer-sidebar-width": width,
  } as React.CSSProperties;
  const inlineFrameStyle = {
    ...frameStyle,
    flexBasis: `${geometry.sidebarInlineSize}px`,
    maxWidth: `${geometry.sidebarInlineSize}px`,
    width: `${geometry.sidebarInlineSize}px`,
  } as React.CSSProperties;
  const ariaLabel = sidebarProps["aria-label"];
  const ariaLabelledBy = sidebarProps["aria-labelledby"];
  const hasAccessibleName =
    (typeof ariaLabel === "string"
      ? ariaLabel.trim().length > 0
      : Boolean(ariaLabel)) ||
    (typeof ariaLabelledBy === "string"
      ? ariaLabelledBy.trim().length > 0
      : Boolean(ariaLabelledBy));

  useViewerDevelopmentLayoutWarning({
    enabled: mode === "inline" && open,
    elements: () => [getRootElement(), sidebarRef.current],
    inspect: () => {
      const rootElement = getRootElement();
      const sidebarElement = sidebarRef.current;
      const rootSize = readViewerElementSize(rootElement);
      const sidebarSize = readViewerElementSize(sidebarElement);

      if (rootSize.width > 0 && sidebarSize.width > rootSize.width) {
        warnViewerDevelopmentOnce({
          code: "viewer_sidebar_wider_than_root",
          message: "inline viewer sidebar is wider than its root.",
          rootId,
          details: {
            rootSlot: rootElement?.dataset.slot,
            rootWidth: rootSize.width,
            sidebarMode: mode,
            sidebarOpen: open,
            sidebarSide: side,
            sidebarSlot: sidebarElement?.dataset.slot,
            sidebarWidth: sidebarSize.width,
          },
        });
      }
    },
    keyParts: [
      "viewer-sidebar-wider-than-root",
      rootId,
      mode,
      open,
      side,
      width,
      getRootElement(),
      sidebarRef.current,
    ],
  });

  useMountEffect(() => {
    if (
      typeof window === "undefined" ||
      typeof window.requestAnimationFrame !== "function"
    ) {
      setTransitionsReady(true);
      return;
    }
    let secondFrame = 0;
    const firstFrame = window.requestAnimationFrame(() => {
      secondFrame = window.requestAnimationFrame(() => {
        setTransitionsReady(true);
      });
    });

    return () => {
      window.cancelAnimationFrame(firstFrame);
      window.cancelAnimationFrame(secondFrame);
    };
  });

  useKeyedLayoutEffect(
    joinEffectKey([
      collapsible,
      instanceId,
      registerSidebar,
      side,
      sidebarId,
      width,
    ]),
    () => {
      const element = sidebarRef.current;

      if (!element) {
        return;
      }

      return registerSidebar({
        collapsible,
        element,
        id: sidebarId,
        instanceId,
        side,
        width,
        widthPixels: resolveViewerSidebarWidthPixels(element, width),
      });
    },
  );

  useKeyedLayoutEffect(
    mode === "overlay" && open && !hasAccessibleName
      ? joinEffectKey([
          "viewer-overlay-sidebar-missing-accessible-name",
          rootId,
          sidebarId,
          side,
        ])
      : null,
    () => {
      warnViewerDevelopmentOnce({
        code: "viewer_overlay_sidebar_missing_accessible_name",
        message: "overlay viewer sidebar is missing an accessible name.",
        rootId,
        details: {
          sidebarId,
          sidebarMode: mode,
          sidebarSide: side,
        },
      });
    },
  );

  useKeyedLayoutEffect(
    isCollapsed
      ? joinEffectKey([
          "viewer-sidebar:move-focus-on-close",
          getRootElement,
          sidebarId,
        ])
      : null,
    () => {
      moveFocusOutOfViewerSidebar(
        sidebarRef.current,
        getRootElement(),
        sidebarId,
      );
    },
  );

  const hiddenProps = isCollapsed || isOpening
    ? ({
        "aria-hidden": true,
        inert: true,
      } as React.HTMLAttributes<HTMLElement>)
    : {};
  const namespacedSidebarStateAttributes = createViewerStateAttributes(
    registrationContext.stateNamespace,
    "sidebar",
    {
      sidebarCollapsible: collapsible,
      sidebarMode: mode,
      sidebarOpen: visualOpen,
      sidebarSide: side,
      sidebarState: visualState,
    },
  );
  const frameAttributes = {
    "data-slot": frameSlot,
    "data-collapsible": collapsible,
    "data-mode": mode,
    "data-side": side,
    "data-state": visualState,
    "data-viewer-sidebar-transitions": transitionsReady ? "ready" : undefined,
    "data-viewer-sidebar-mode": mode,
    "data-viewer-sidebar-open": visualOpen ? "true" : "false",
    "data-viewer-sidebar-state": visualState,
    "data-viewer-slot": "sidebar",
    ...createViewerSlotAttributes(
      registrationContext.stateNamespace,
      "sidebar",
      namespacedSlot,
    ),
    ...namespacedSidebarStateAttributes,
  };
  const sidebarElement = (
    <aside
      ref={sidebarRef}
      id={sidebarId}
      data-slot={slotNames?.container ?? "viewer-sidebar-container"}
      data-collapsible={collapsible}
      data-mode={mode}
      data-side={side}
      data-state={visualState}
      data-viewer-sidebar-transitions={transitionsReady ? "ready" : undefined}
      data-viewer-sidebar-mode={mode}
      data-viewer-sidebar-open={visualOpen ? "true" : "false"}
      data-viewer-sidebar-state={visualState}
      data-viewer-slot="sidebar-container"
      {...createViewerSlotAttributes(
        registrationContext.stateNamespace,
        "sidebar",
        namespacedSlotNames?.container,
      )}
      {...namespacedSidebarStateAttributes}
        className={cn(
          className,
          "z-30 flex w-(--viewer-sidebar-width) min-w-0 overflow-hidden",
          mode === "inline" &&
            "absolute inset-y-0 h-full min-w-(--viewer-sidebar-width) flex-shrink-0 transition-none",
        mode === "inline" && side === "left" && "right-0",
        mode === "inline" && side === "right" && "left-0",
        mode === "overlay" &&
          "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",
        mode === "overlay" && side === "left" && "left-0",
        mode === "overlay" && side === "right" && "right-0",
        collapsible === "offcanvas" && mode === "overlay" && "shadow-lg",
        isCollapsed && "pointer-events-none",
        collapsible === "offcanvas" &&
          mode === "overlay" &&
          !visualOpen &&
          side === "left" &&
          "pointer-events-none -translate-x-full border-transparent",
        collapsible === "offcanvas" &&
          mode === "overlay" &&
          !visualOpen &&
          side === "right" &&
          "pointer-events-none translate-x-full border-transparent",
      )}
      style={
        {
          ...styleWithoutWidth,
          "--viewer-sidebar-width": width,
        } as React.CSSProperties
      }
      {...sidebarProps}
      {...hiddenProps}
    >
      <div
        data-slot={slotNames?.inner ?? "viewer-sidebar-inner"}
        data-viewer-slot="sidebar-inner"
        {...createViewerSlotAttributes(
          registrationContext.stateNamespace,
          "sidebar",
          namespacedSlotNames?.inner,
        )}
        className={cn(
          "flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden",
          innerClassName,
        )}
      >
        {children}
      </div>
    </aside>
  );

  if (mode === "inline") {
    return (
      <div
        {...frameAttributes}
        className={cn(
          "group/viewer-sidebar relative z-30 min-h-0 flex-shrink-0 overflow-hidden transition-none",
          side === "right" && "order-last",
        )}
        style={inlineFrameStyle}
      >
        {sidebarElement}
      </div>
    );
  }

  return (
    <div
      {...frameAttributes}
      className={cn(
        "group/viewer-sidebar relative z-30 min-h-0 flex-shrink-0",
        side === "right" && "order-last",
      )}
      style={frameStyle}
    >
      <div
        data-slot={slotNames?.gap ?? "viewer-sidebar-gap"}
        data-viewer-slot="sidebar-gap"
        {...createViewerSlotAttributes(
          registrationContext.stateNamespace,
          "sidebar",
          namespacedSlotNames?.gap,
        )}
        className={cn(
          "relative h-full w-(--viewer-sidebar-width) flex-shrink-0",
          sidebarGapTransition === "width" &&
            "transition-[width] duration-150 ease-out",
          collapsible === "offcanvas" && mode === "overlay" && "w-0",
        )}
      />
      {sidebarElement}
    </div>
  );
}

function moveFocusOutOfViewerSidebar(
  sidebarElement: HTMLElement | null,
  rootElement: HTMLElement | null,
  sidebarId: string,
) {
  if (typeof document === "undefined" || !sidebarElement) return;

  const activeElement = document.activeElement;
  if (
    !(activeElement instanceof HTMLElement) ||
    !sidebarElement.contains(activeElement)
  ) {
    return;
  }

  const trigger = findViewerSidebarTrigger(rootElement, sidebarId);
  if (trigger) {
    trigger.focus({ preventScroll: true });
    return;
  }

  activeElement.blur();
}

function findViewerSidebarTrigger(
  rootElement: HTMLElement | null,
  sidebarId: string,
) {
  if (!rootElement) return null;

  for (const element of rootElement.querySelectorAll<HTMLElement>(
    "[aria-controls]",
  )) {
    if (element.getAttribute("aria-controls") === sidebarId) {
      return element;
    }
  }

  return null;
}

function resolveViewerSidebarWidthPixels(element: HTMLElement, width: string) {
  const declaredWidth = parseViewerSidebarCssLength(width, element);
  if (declaredWidth !== null) return declaredWidth;

  if (typeof window !== "undefined") {
    const computedWidth = parseViewerSidebarCssLength(
      window.getComputedStyle(element).width,
      element,
    );
    if (computedWidth !== null) return computedWidth;
  }

  const measuredWidth = readViewerElementSize(element).width;
  return Number.isFinite(measuredWidth) && measuredWidth > 0
    ? measuredWidth
    : 0;
}

function parseViewerSidebarCssLength(value: string, element: HTMLElement) {
  const trimmedValue = value.trim();
  const length = Number.parseFloat(trimmedValue);
  if (!Number.isFinite(length) || length <= 0) return null;

  if (trimmedValue.endsWith("px")) return length;
  if (trimmedValue.endsWith("rem")) {
    return length * readRootFontSize();
  }
  if (trimmedValue.endsWith("em")) {
    return length * readElementFontSize(element);
  }

  return null;
}

function readRootFontSize() {
  if (typeof window === "undefined") return 16;

  const rootFontSize = Number.parseFloat(
    window.getComputedStyle(document.documentElement).fontSize,
  );
  return Number.isFinite(rootFontSize) && rootFontSize > 0
    ? rootFontSize
    : 16;
}

function readElementFontSize(element: HTMLElement) {
  if (typeof window === "undefined") return 16;

  const elementFontSize = Number.parseFloat(
    window.getComputedStyle(element).fontSize,
  );
  return Number.isFinite(elementFontSize) && elementFontSize > 0
    ? elementFontSize
    : readRootFontSize();
}
registry/new-york-v4/ui/viewer-surface.tsx
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import { cn } from "@/lib/utils";

import {
  findClosestViewerBody,
  readViewerElementSize,
  useOptionalViewerSidebarRegistration,
  useOptionalViewerSurfaceMeasurementContext,
  useViewerDevelopmentLayoutWarning,
  useViewerViewportPresence,
  ViewerSurfaceMeasurementProvider,
  ViewerViewportPresenceProvider,
} from "./viewer-internals";
import { useOptionalViewerRootDiagnostics } from "./viewer-root";
import { warnViewerDevelopmentOnce } from "./viewer-diagnostics";
import { useStableElementSize } from "./viewer-measurement";
import type {
  ViewerDocumentFrameAlign,
  ViewerDocumentFrameProps,
  ViewerGeometryStore,
  ViewerSurfaceMeasurement,
  ViewerSurfaceProps,
  ViewerViewportProps,
  ViewerGeometrySnapshot,
} from "./viewer-types";

export type ViewerDocumentFrameLayout = {
  activeInlineSize: number | null;
  isTransitioning: boolean;
  maxInlineSize: number | null;
  settledInlineSize: number | null;
};

export type ViewerDocumentFrameState = {
  align: ViewerDocumentFrameAlign;
  element: HTMLDivElement | null;
  geometryStore: ViewerGeometryStore | null;
  inlineSize: number | null;
};

const ViewerDocumentFrameContext =
  React.createContext<ViewerDocumentFrameState | null>(null);

function getDocumentFrameAlignClass(align: ViewerDocumentFrameAlign) {
  switch (align) {
    case "center":
      return "mx-auto";
    case "end":
      return "ml-auto";
    case "start":
      return "mr-auto";
  }
}

export function useOptionalViewerDocumentFrame(): ViewerDocumentFrameState | null {
  return React.useContext(ViewerDocumentFrameContext);
}

export function useViewerDocumentFrameLayout({
  documentFrame,
  fallbackInlineSize,
}: {
  documentFrame: ViewerDocumentFrameState | null;
  fallbackInlineSize: number | null;
}): ViewerDocumentFrameLayout {
  const element = documentFrame?.element ?? null;
  const geometryStore = documentFrame?.geometryStore ?? null;
  const snapshotCacheRef = React.useRef<ViewerDocumentFrameLayout | null>(null);
  const subscribe = React.useCallback(
    (listener: () => void) =>
      geometryStore ? geometryStore.subscribe(listener) : () => {},
    [geometryStore],
  );
  const getSnapshot = React.useCallback(() => {
    const geometry = geometryStore?.getSnapshot() ?? null;
    const previousLayout = snapshotCacheRef.current;
    const nextLayout =
      geometry && geometry.hasMeasuredBody
        ? createViewerDocumentFrameLayoutFromGeometry({
            element,
            fallbackInlineSize,
            geometry,
          })
        : createViewerDocumentFrameFallbackLayout(fallbackInlineSize);

    if (
      previousLayout &&
      areViewerDocumentFrameLayoutsEqual(previousLayout, nextLayout)
    ) {
      return previousLayout;
    }

    snapshotCacheRef.current = nextLayout;
    return nextLayout;
  }, [element, fallbackInlineSize, geometryStore]);
  const activeLayout = React.useSyncExternalStore(
    subscribe,
    getSnapshot,
    getSnapshot,
  );
  const [settledInlineSize, setSettledInlineSize] = React.useState<
    number | null
  >(() => activeLayout.activeInlineSize);

  useKeyedLayoutEffect(
    joinEffectKey([
      "viewer-document-frame-layout:settled-inline-size",
      activeLayout.activeInlineSize,
      activeLayout.isTransitioning,
      geometryStore,
    ]),
    () => {
      if (activeLayout.isTransitioning) return;

      setSettledInlineSize((currentInlineSize) =>
        currentInlineSize === activeLayout.activeInlineSize
          ? currentInlineSize
          : activeLayout.activeInlineSize,
      );
    },
  );
  return React.useMemo(
    () => ({
      ...activeLayout,
      settledInlineSize:
        settledInlineSize ?? activeLayout.activeInlineSize ?? null,
    }),
    [activeLayout, settledInlineSize],
  );
}

export function ViewerDocumentFrame({
  align = "start",
  children,
  className,
  maxInlineSize,
  style,
  ...props
}: ViewerDocumentFrameProps) {
  const size = useStableElementSize<HTMLDivElement>({
    retainLastNonZero: true,
  });
  const sidebarRegistration = useOptionalViewerSidebarRegistration();
  const value = React.useMemo<ViewerDocumentFrameState>(
    () => ({
      align,
      element: size.element,
      geometryStore: sidebarRegistration?.geometryStore ?? null,
      inlineSize: size.width,
    }),
    [align, sidebarRegistration?.geometryStore, size.element, size.width],
  );

  return (
    <ViewerDocumentFrameContext.Provider value={value}>
      <div
        ref={size.setElement}
        data-slot="viewer-document-frame"
        className={cn(
          "[container-type:inline-size] relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col",
          getDocumentFrameAlignClass(align),
          className,
        )}
        style={
          {
            maxInlineSize,
            ...style,
          } as React.CSSProperties
        }
        {...props}
      >
        {children}
      </div>
    </ViewerDocumentFrameContext.Provider>
  );
}

function createViewerDocumentFrameLayoutFromGeometry({
  element,
  fallbackInlineSize,
  geometry,
}: {
  element: HTMLDivElement | null;
  fallbackInlineSize: number | null;
  geometry: ViewerGeometrySnapshot;
}): ViewerDocumentFrameLayout {
  const maxCssInlineSize = element
    ? readViewerDocumentFrameMaxInlineSize(element)
    : null;
  const measuredFrameInlineSize =
    fallbackInlineSize != null &&
    Number.isFinite(fallbackInlineSize) &&
    fallbackInlineSize > 0
      ? fallbackInlineSize
      : null;
  const usesSidebarGeometry =
    geometry.sidebarWidth > 0 ||
    geometry.sidebarInlineSize > 0 ||
    geometry.isTransitioning;
  const activeSourceInlineSize = usesSidebarGeometry
    ? geometry.documentInlineSize
    : (measuredFrameInlineSize ?? geometry.documentInlineSize);
  const maxSourceInlineSize = usesSidebarGeometry
    ? Math.max(geometry.bodyInlineSize, activeSourceInlineSize)
    : activeSourceInlineSize;
  const activeInlineSize = constrainViewerDocumentFrameInlineSize(
    activeSourceInlineSize,
    maxCssInlineSize,
  );
  const maxInlineSize = constrainViewerDocumentFrameInlineSize(
    maxSourceInlineSize,
    maxCssInlineSize,
  );

  return {
    activeInlineSize,
    isTransitioning: geometry.isTransitioning,
    maxInlineSize,
    settledInlineSize: null,
  };
}

function createViewerDocumentFrameFallbackLayout(
  fallbackInlineSize: number | null,
): ViewerDocumentFrameLayout {
  const inlineSize =
    fallbackInlineSize != null &&
    Number.isFinite(fallbackInlineSize) &&
    fallbackInlineSize > 0
      ? fallbackInlineSize
      : null;

  return {
    activeInlineSize: inlineSize,
    isTransitioning: false,
    maxInlineSize: inlineSize,
    settledInlineSize: inlineSize,
  };
}

function areViewerDocumentFrameLayoutsEqual(
  previousLayout: ViewerDocumentFrameLayout,
  nextLayout: ViewerDocumentFrameLayout,
) {
  return (
    previousLayout.activeInlineSize === nextLayout.activeInlineSize &&
    previousLayout.isTransitioning === nextLayout.isTransitioning &&
    previousLayout.maxInlineSize === nextLayout.maxInlineSize
  );
}

function readViewerDocumentFrameMaxInlineSize(element: HTMLElement) {
  if (typeof window === "undefined") return null;

  const value = window.getComputedStyle(element).maxInlineSize;
  if (!value || value === "none") return null;

  const size = Number.parseFloat(value);
  return Number.isFinite(size) && size > 0 ? size : null;
}

function constrainViewerDocumentFrameInlineSize(
  inlineSize: number,
  maxInlineSize: number | null,
) {
  const safeInlineSize =
    Number.isFinite(inlineSize) && inlineSize > 0 ? inlineSize : 0;
  const constrainedInlineSize =
    maxInlineSize == null
      ? safeInlineSize
      : Math.min(safeInlineSize, maxInlineSize);
  return constrainedInlineSize > 0 ? constrainedInlineSize : null;
}

function useViewerSurfaceMeasurementValue(): ViewerSurfaceMeasurement {
  const size = useStableElementSize<HTMLDivElement>({
    retainLastNonZero: true,
  });

  return React.useMemo(
    () => ({
      hasMeasured: size.hasMeasured,
      setViewportElement: size.setElement,
      viewportElement: size.element,
      viewportHeight: size.height,
      viewportWidth: size.width,
    }),
    [size.element, size.hasMeasured, size.height, size.setElement, size.width],
  );
}

export function useOptionalViewerSurfaceMeasurement(): ViewerSurfaceMeasurement | null {
  return useOptionalViewerSurfaceMeasurementContext();
}

export function useViewerSurfaceMeasurement(): ViewerSurfaceMeasurement {
  const context = useOptionalViewerSurfaceMeasurement();
  if (!context) {
    throw new Error(
      "useViewerSurfaceMeasurement must be used within a ViewerSurface.",
    );
  }
  return context;
}

export function useIsInsideViewerViewport(): boolean {
  return useViewerViewportPresence();
}

export function ViewerSurface({ className, ...props }: ViewerSurfaceProps) {
  const measurement = useViewerSurfaceMeasurementValue();

  return (
    <ViewerSurfaceMeasurementProvider value={measurement}>
      <div
        data-slot="viewer-surface"
        data-viewer-surface="inset"
        className={cn(
          "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
          className,
        )}
        {...props}
      />
    </ViewerSurfaceMeasurementProvider>
  );
}

export const ViewerViewport = React.forwardRef<
  HTMLDivElement,
  ViewerViewportProps
>(function ViewerViewport({ children, className, ...props }, forwardedRef) {
  const measurement = useOptionalViewerSurfaceMeasurement();
  const rootDiagnostics = useOptionalViewerRootDiagnostics();
  const viewportRef = React.useRef<HTMLDivElement | null>(null);
  const setViewportElement = React.useCallback(
    (element: HTMLDivElement | null) => {
      viewportRef.current = element;
      measurement?.setViewportElement(element);

      if (typeof forwardedRef === "function") {
        forwardedRef(element);
        return;
      }

      if (forwardedRef) {
        forwardedRef.current = element;
      }
    },
    [forwardedRef, measurement],
  );

  useViewerDevelopmentLayoutWarning({
    enabled: Boolean(rootDiagnostics),
    elements: () => [
      findClosestViewerBody(viewportRef.current),
      viewportRef.current,
    ],
    inspect: () => {
      const viewportElement = viewportRef.current;
      const bodyElement = findClosestViewerBody(viewportElement);
      const bodySize = readViewerElementSize(bodyElement);
      const viewportSize = readViewerElementSize(viewportElement);

      if (bodySize.width > 0 && viewportSize.width === 0) {
        warnViewerDevelopmentOnce({
          code: "viewer_viewport_zero_width",
          message:
            "viewer body has nonzero width but surface viewport width is zero.",
          rootId: rootDiagnostics?.rootId ?? "unknown-root",
          details: {
            bodyHeight: bodySize.height,
            bodySlot: bodyElement?.dataset.slot,
            bodyWidth: bodySize.width,
            viewportHeight: viewportSize.height,
            viewportSlot: viewportElement?.dataset.slot,
            viewportWidth: viewportSize.width,
          },
        });
      }
    },
    keyParts: [
      "viewer-viewport-zero-width",
      rootDiagnostics?.rootId,
      viewportRef.current,
    ],
  });

  return (
    <div
      ref={setViewportElement}
      data-slot="viewer-viewport"
      className={cn(
        "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
        className,
      )}
      {...props}
    >
      <ViewerViewportPresenceProvider>
        {children}
      </ViewerViewportPresenceProvider>
    </div>
  );
});
registry/new-york-v4/ui/viewer-types.ts
import type * as React from "react";

export type ViewerSidebarMode = "inline" | "overlay";
export type ViewerSidebarRequestedMode = "auto" | ViewerSidebarMode;
export type ViewerSidebarGapTransition = "width" | "none";
export type ViewerSidebarState = "expanded" | "collapsed";
export type ViewerSidebarSide = "left" | "right";
export type ViewerSidebarCollapsible = "offcanvas" | "none";
export type ViewerDocumentFrameAlign = "start" | "center" | "end";
export type ViewerGeometryTransitionPhase = "idle" | "sliding";

export type ViewerDocumentReadingAnchorInput = {
  scrollTop: number;
  viewportBlockSize: number;
};

export type ViewerDocumentReadingAnchorTarget<Anchor> = {
  anchor: Anchor;
  viewportBlockSize: number;
};

export type ViewerDocumentTransitionSource =
  | "none"
  | "viewer-shell"
  | "document-layout";

// Commit-then-relax: layout commits its target inside the motion's first
// frame and scroll rebases in the same commit, so there is no frozen layout
// and no deferred scroll left in the vocabulary.
export type ViewerDocumentLayoutPolicy = "live" | "target";
export type ViewerDocumentScrollPolicy = "preserve" | "rebase";
export type ViewerDocumentVisualPolicy =
  | "none"
  | "document-flip"
  | "shell-transform";

export type ViewerDocumentTransition = {
  layoutPolicy: ViewerDocumentLayoutPolicy;
  scrollPolicy: ViewerDocumentScrollPolicy;
  source: ViewerDocumentTransitionSource;
  transitionId: number | string | null;
  visualPolicy: ViewerDocumentVisualPolicy;
};

export type ViewerDocumentLayoutModel<Anchor> = {
  blockSize: number;
  captureReadingAnchor: (
    input: ViewerDocumentReadingAnchorInput,
  ) => Anchor | null;
  getReadingAnchorScrollTop: (
    target: ViewerDocumentReadingAnchorTarget<Anchor>,
  ) => number | null;
  inlineSize: number;
  isTransitioning?: boolean;
  transition?: ViewerDocumentTransition;
};

export type ViewerDocumentPhysicalScrollPosition = {
  physicalScrollTop: number;
  scrollPageOffset: number;
};

export type ViewerDocumentResolvedScrollTarget = {
  left?: number;
  top: number;
};

export type ViewerDocumentScrollMapper = {
  getLogicalScrollTop: (input: {
    blockSize: number;
    physicalScrollTop: number;
    scrollPageOffset: number;
    viewportBlockSize: number;
  }) => number;
  getPhysicalScrollSize: (input: {
    blockSize: number;
    viewportBlockSize: number;
  }) => number;
  resolvePhysicalScrollPosition: (input: {
    blockSize: number;
    logicalScrollTop: number;
    scrollPageOffset: number;
    viewportBlockSize: number;
  }) => ViewerDocumentPhysicalScrollPosition;
};

export type ViewerDocumentScrollMetrics = {
  physicalScrollSize: number;
  physicalScrollTop: number;
  scrollPageOffset: number;
  scrollTop: number;
  viewportBlockSize: number;
};

export type ViewerDocumentScrollTargetResolver<Anchor, Target> = (input: {
  layout: ViewerDocumentLayoutModel<Anchor>;
  scrollTop: number;
  target: Target;
  viewportElement: HTMLDivElement;
}) => ViewerDocumentResolvedScrollTarget | null;

// A zoom step is the one geometry change whose intent is "zoom the camera",
// not "keep my reading position": it re-anchors the viewport CENTER on both
// axes and relaxes a FLIP about that fixed point. `capture` runs in the zoom
// gesture's own task against the pre-zoom layout and painted DOM;
// `resolveScrollTarget` and `play` run inside the geometry commit against the
// post-zoom layout (commit-then-relax).
export type ViewerDocumentZoomMotionBypassReason =
  | "resolve-failed"
  | "shell-transition"
  | "stale-intent";

export type ViewerDocumentZoomMotionController<Transaction = unknown> = {
  capture: (input: {
    scrollTop: number;
    viewportElement: HTMLDivElement;
  }) => Transaction | null;
  /**
   * Telemetry tap: a captured zoom intent reached a geometry commit but the
   * zoom lane declined it. Without this the bypass is invisible — the commit
   * falls back to the reading-anchor restore and no flight is recorded.
   */
  noteBypass?: (reason: ViewerDocumentZoomMotionBypassReason) => void;
  resolveScrollTarget: (input: {
    transaction: Transaction;
    viewportElement: HTMLDivElement;
  }) => ViewerDocumentResolvedScrollTarget | null;
  play: (input: {
    transaction: Transaction;
    viewportElement: HTMLDivElement;
  }) => (() => void) | null;
};

export type ViewerGeometrySnapshot = {
  bodyInlineSize: number;
  documentInlineSize: number;
  hasMeasuredBody: boolean;
  isTransitioning: boolean;
  mode: ViewerSidebarMode;
  open: boolean;
  progress: number;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarInlineSize: number;
  sidebarWidth: number;
  side: ViewerSidebarSide;
  state: ViewerSidebarState;
  transitionPhase: ViewerGeometryTransitionPhase;
};

export type ViewerGeometryStore = {
  getSnapshot: () => ViewerGeometrySnapshot;
  setTarget: (target: ViewerGeometryTarget) => void;
  subscribe: (listener: () => void) => () => void;
};

export type ViewerGeometryTarget = {
  bodyElement: HTMLElement | null;
  mode: ViewerSidebarMode;
  open: boolean;
  rootElement: HTMLElement | null;
  sidebarElement: HTMLElement | null;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarWidth: number;
  side: ViewerSidebarSide;
  state: ViewerSidebarState;
};

export type ViewerSidebarStateValue = {
  state: ViewerSidebarState;
  open: boolean;
  setOpen: (value: boolean | ((open: boolean) => boolean)) => void;
  toggleSidebar: () => void;
  canToggleSidebar: boolean;
  mode: ViewerSidebarMode;
  side: ViewerSidebarSide;
};

export type ViewerRootProps = React.ComponentProps<"div"> & {
  defaultOpen?: boolean;
  inlineBreakpoint?: number;
  mode?: ViewerSidebarRequestedMode;
  onOpenChange?: (open: boolean) => void;
  open?: boolean;
  sidebarCollapsible?: ViewerSidebarCollapsible;
  sidebarGapTransition?: ViewerSidebarGapTransition;
  sidebarSide?: ViewerSidebarSide;
  stateNamespace?: ViewerStateAttributeNamespace;
};

export type ViewerFrameProps = React.ComponentProps<"div">;
export type ViewerHeaderProps = React.ComponentProps<"div">;
export type ViewerBodyProps = React.ComponentProps<"div">;
export type ViewerSurfaceProps = React.ComponentProps<"div">;
export type ViewerViewportProps = React.ComponentProps<"div">;
export type ViewerDocumentFrameProps = React.ComponentProps<"div"> & {
  align?: ViewerDocumentFrameAlign;
  maxInlineSize?: React.CSSProperties["maxInlineSize"];
};

export type ViewerStateAttributeNamespace = {
  prefix: string;
  slots?: {
    body?: boolean;
    root?: boolean;
    sidebar?: boolean;
  };
};

export type ViewerSidebarRegistration = {
  collapsible: ViewerSidebarCollapsible;
  element: HTMLElement;
  id: string;
  instanceId: string;
  side: ViewerSidebarSide;
  width: string;
  widthPixels: number;
};

export type ViewerPortalContainmentAttributes = {
  "data-viewer-portal-root-id": string;
};

export type ViewerSidebarRegistrationState = {
  defaultSidebarCollapsible: ViewerSidebarCollapsible;
  defaultSidebarSide: ViewerSidebarSide;
  geometryStore: ViewerGeometryStore;
  getRootElement: () => HTMLElement | null;
  hasSidebar: boolean;
  registerBody: (element: HTMLElement) => () => void;
  registerSidebar: (registration: ViewerSidebarRegistration) => () => void;
  rootId: string;
  sidebarId: string;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarSide: ViewerSidebarSide;
  setLastTriggerElement: (element: HTMLElement | null) => void;
  stateNamespace?: ViewerStateAttributeNamespace;
};

export type ViewerRootDiagnostics = {
  getRootElement: () => HTMLElement | null;
  layoutSignature: string;
  rootId: string;
};

export type ViewerSurfaceMeasurement = {
  hasMeasured: boolean;
  setViewportElement: React.RefCallback<HTMLDivElement>;
  viewportElement: HTMLDivElement | null;
  viewportHeight: number | null;
  viewportWidth: number | null;
};

export type ViewerSidebarSlotNames = {
  container?: string;
  gap?: string;
  inner?: string;
};

export type ViewerStateAttributeSlot = "body" | "root" | "sidebar";
export type ViewerStateAttributeValues = {
  hasSidebar?: boolean;
  sidebarCollapsible?: ViewerSidebarCollapsible;
  sidebarMode?: ViewerSidebarMode;
  sidebarOpen?: boolean;
  sidebarSide?: ViewerSidebarSide;
  sidebarState?: ViewerSidebarState;
};
export type ViewerDataAttributes = Record<`data-${string}`, string | undefined>;

export type ViewerSidebarProps = React.ComponentProps<"aside"> &
  ViewerDataAttributes & {
    side?: ViewerSidebarSide;
    collapsible?: ViewerSidebarCollapsible;
    innerClassName?: string;
    namespacedSlot?: string;
    namespacedSlotNames?: ViewerSidebarSlotNames;
    slotNames?: ViewerSidebarSlotNames;
    width?: string;
  };
registry/new-york-v4/ui/viewer-zoom.tsx
"use client";

import * as React from "react";
import { Maximize, Minus, Plus } from "lucide-react";

import { Button } from "@/components/ui/button";

function IconButton({
  label,
  children,
  ...props
}: React.ComponentProps<typeof Button> & { label: string }) {
  return (
    <Button
      variant="ghost"
      size="iconSm"
      className="size-7"
      aria-label={label}
      title={label}
      {...props}
    >
      {children}
    </Button>
  );
}

function clamp(value: number, min: number, max: number) {
  return Math.min(max, Math.max(min, value));
}

export function useZoom() {
  const [scale, setScale] = React.useState(1);
  const zoom = React.useCallback(
    (factor: number) => setScale((s) => clamp(s * factor, 0.1, 5)),
    [],
  );
  const reset = React.useCallback(() => setScale(1), []);
  return { scale, zoom, reset };
}

export function ZoomActions({
  scale,
  zoom,
  reset,
}: {
  scale: number;
  zoom: (factor: number) => void;
  reset: () => void;
}) {
  return (
    <>
      <IconButton label="Zoom out" onClick={() => zoom(1 / 1.2)}>
        <Minus />
      </IconButton>
      <span className="text-muted-foreground w-12 text-center text-xs tabular-nums">
        {Math.round(scale * 100)}%
      </span>
      <IconButton label="Zoom in" onClick={() => zoom(1.2)}>
        <Plus />
      </IconButton>
      <IconButton label="Actual size" onClick={reset}>
        <Maximize />
      </IconButton>
    </>
  );
}
registry/new-york-v4/ui/viewer-abortable-request.ts
export interface SharedAbortableRequest<T> {
  controller: AbortController;
  promise: Promise<T>;
  subscriberPromises: WeakMap<AbortSignal, Promise<T>>;
  subscribers: Set<AbortSignal>;
  settled: boolean;
}

export function abortError(): DOMException {
  return new DOMException("Aborted", "AbortError");
}

export function isAbortError(error: unknown): boolean {
  return (
    (error instanceof DOMException && error.name === "AbortError") ||
    (error instanceof Error && error.name === "AbortError")
  );
}

export function promiseForSignal<T>(
  map: WeakMap<AbortSignal, Promise<T>>,
  signal: AbortSignal,
  create: () => Promise<T>,
): Promise<T> {
  if (signal.aborted) return Promise.reject(abortError());

  const existingPromise = map.get(signal);
  if (existingPromise) return existingPromise;

  const promise = create().catch((error: unknown) => {
    map.delete(signal);
    throw error;
  });
  map.set(signal, promise);
  return promise;
}

export function subscribeToAbortableRequest<T>(
  entry: SharedAbortableRequest<T>,
  signal: AbortSignal,
  onNoSubscribers: () => void,
): Promise<T> {
  return promiseForSignal(entry.subscriberPromises, signal, () => {
    entry.subscribers.add(signal);

    return new Promise<T>((resolve, reject) => {
      let done = false;

      const cleanup = () => {
        signal.removeEventListener("abort", onAbort);
        entry.subscribers.delete(signal);
      };

      const onAbort = () => {
        if (done) return;
        done = true;
        cleanup();
        if (!entry.settled && entry.subscribers.size === 0) {
          entry.controller.abort();
          onNoSubscribers();
        }
        reject(abortError());
      };

      signal.addEventListener("abort", onAbort, { once: true });

      entry.promise.then(
        (value) => {
          if (done) return;
          done = true;
          cleanup();
          resolve(value);
        },
        (error: unknown) => {
          if (done) return;
          done = true;
          cleanup();
          reject(error);
        },
      );
    });
  });
}
registry/new-york-v4/ui/viewer-lru-cache.ts
export const VIEWER_LRU_CACHE_MAX = 12;

export function lruGet<K, V>(map: Map<K, V>, key: K): V | undefined {
  const value = map.get(key);
  if (value !== undefined) {
    map.delete(key);
    map.set(key, value);
  }
  return value;
}

export function lruSet<K, V>(
  map: Map<K, V>,
  key: K,
  value: V,
  onEvict?: (key: K, value: V) => void,
  max = VIEWER_LRU_CACHE_MAX,
) {
  map.delete(key);
  map.set(key, value);
  while (map.size > max) {
    const oldest = map.keys().next().value as K | undefined;
    if (oldest === undefined) break;
    const dropped = map.get(oldest);
    map.delete(oldest);
    if (dropped !== undefined) onEvict?.(oldest, dropped);
  }
}
registry/new-york-v4/lib/pdf-viewer-diagnostics.ts
const FILE_VIEWER_DIAGNOSTIC_STORAGE_KEY = "retab-file-viewer-debug";
const PDF_DIAGNOSTIC_STORAGE_KEY = "retab-pdf-debug";
const PDF_HEADER = [0x25, 0x50, 0x44, 0x46, 0x2d];
const T0 = typeof performance !== "undefined" ? performance.now() : 0;
const seqRef = { n: 0 };
const sessionSeqRef = { n: 0 };

export type FileViewerDiagnosticLevel = "debug" | "info" | "warn" | "error";
export type PdfViewerDiagnosticLevel = FileViewerDiagnosticLevel;

export type FileViewerDiagnosticData = Record<string, unknown>;
export type PdfViewerDiagnosticData = FileViewerDiagnosticData;

function isTruthyDiagnosticFlag(value: string | null): boolean {
  return value === "1" || value === "true";
}

export function isFileViewerDiagnosticsEnabled(): boolean {
  if (typeof window === "undefined") return false;
  try {
    const explicit = window.localStorage.getItem(
      FILE_VIEWER_DIAGNOSTIC_STORAGE_KEY,
    );
    if (explicit !== null) return isTruthyDiagnosticFlag(explicit);
    const legacyPdf = window.localStorage.getItem(PDF_DIAGNOSTIC_STORAGE_KEY);
    if (legacyPdf !== null) return isTruthyDiagnosticFlag(legacyPdf);
    return false;
  } catch {
    return false;
  }
}

export function isPdfViewerDiagnosticsEnabled(): boolean {
  return isFileViewerDiagnosticsEnabled();
}

export function fileViewerDiagnostic(
  level: FileViewerDiagnosticLevel,
  event: string,
  data?: FileViewerDiagnosticData,
): void {
  if (!isFileViewerDiagnosticsEnabled()) return;
  const seq = ++seqRef.n;
  const elapsedMs =
    (typeof performance !== "undefined" ? performance.now() : 0) - T0;
  const method = console[level] ?? console.log;
  method(
    `%cfile-viewer%c #${seq} %c${elapsedMs.toFixed(1)}ms%c ${event}`,
    "background:#0f766e;color:#fff;padding:0 4px;border-radius:3px;font-weight:bold",
    "color:#888",
    "color:#06c",
    "color:inherit",
    data ?? "",
  );
}

export function pdfViewerDiagnostic(
  level: PdfViewerDiagnosticLevel,
  event: string,
  data?: PdfViewerDiagnosticData,
): void {
  fileViewerDiagnostic(level, event, data);
}

export function viewerDiagnosticNow(): number {
  return typeof performance !== "undefined" ? performance.now() : Date.now();
}

export function viewerDiagnosticDurationMs(startedAt: number): number {
  return Math.max(0, Math.round(viewerDiagnosticNow() - startedAt));
}

export function nextFileViewerSessionId(prefix = "fv"): string {
  sessionSeqRef.n += 1;
  return `${prefix}_${sessionSeqRef.n.toString(36)}`;
}

export function fileBufferDiagnostics(buffer: ArrayBuffer | Uint8Array) {
  const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
  const head = bytes.slice(0, Math.min(16, bytes.byteLength));

  return {
    byteLength: bytes.byteLength,
    headHex: bytesToHex(head),
    looksLikeHtml: looksLikeHtml(bytes),
    pdfHeaderOffset: pdfHeaderOffset(bytes),
    tiffHeader: tiffHeader(bytes),
  };
}

export function pdfBufferDiagnostics(buffer: ArrayBuffer | Uint8Array) {
  return fileBufferDiagnostics(buffer);
}

export function summarizeViewerError(error: unknown, depth = 0): unknown {
  if (!(error instanceof Error)) {
    const value = String(error);
    return {
      type: typeof error,
      valueHash: hashString(value),
      valuePreview: redactDiagnosticText(value, 160),
    };
  }

  const details: PdfViewerDiagnosticData = {
    name: error.name,
    message: redactDiagnosticText(error.message, 240),
    messageHash: hashString(error.message),
  };

  const maybeCode = (error as { code?: unknown }).code;
  if (maybeCode !== undefined) details.code = maybeCode;

  const maybeStatus = (error as { status?: unknown }).status;
  if (maybeStatus !== undefined) details.status = maybeStatus;

  const maybeReason = (error as { reason?: unknown }).reason;
  if (maybeReason !== undefined) details.reason = maybeReason;

  const cause = (error as { cause?: unknown }).cause;
  if (cause !== undefined && depth < 1) {
    details.cause = summarizeViewerError(cause, depth + 1);
  }

  return details;
}

export function summarizePdfError(error: unknown, depth = 0): unknown {
  return summarizeViewerError(error, depth);
}

export function safeUrlSummary(url: string | null | undefined) {
  if (!url) return null;
  try {
    const currentOrigin =
      typeof window !== "undefined" && window.location?.origin
        ? window.location.origin
        : "http://local";
    const parsed = new URL(url, currentOrigin);
    return {
      origin:
        typeof window !== "undefined" &&
        window.location?.origin &&
        parsed.origin === window.location.origin
          ? "same-origin"
          : parsed.origin,
      path: redactUrlPath(parsed.pathname),
      hasSearch: parsed.search.length > 0,
    };
  } catch {
    return {
      valueHash: hashString(url),
      valuePreview: redactDiagnosticText(url, 160),
    };
  }
}

export function safePdfUrlSummary(url: string | null | undefined) {
  return safeUrlSummary(url);
}

export function responseHeadersDiagnostics(headers: Headers) {
  const retabFileId = headers.get("x-retab-file-id");
  return {
    contentLength: headers.get("content-length"),
    contentRange: headers.get("content-range"),
    contentType: headers.get("content-type"),
    requestId: headers.get("x-request-id"),
    retabFileIdHash: summarizeDiagnosticId(retabFileId),
    retabPreviewKind: headers.get("x-retab-preview-kind"),
  };
}

export function summarizeViewerReadOptions(options?: {
  signal?: AbortSignal;
  maxBytes?: number;
  maxLines?: number;
}) {
  return {
    hasSignal: Boolean(options?.signal),
    isSignalAborted: options?.signal?.aborted ?? false,
    maxBytes: options?.maxBytes,
    maxLines: options?.maxLines,
  };
}

export function summarizeViewerByteRange(range: {
  start: number;
  end: number;
}) {
  return {
    start: range.start,
    end: range.end,
    requestedBytes: range.end >= range.start ? range.end - range.start + 1 : 0,
  };
}

export function summarizeViewerContent(content: {
  key: string;
  sourceKind: string;
  directUrl?: string | null;
  mimeType?: string;
  payload?: {
    kind: string;
    url?: string;
    blob?: Blob;
    text?: string;
  };
}) {
  return {
    keyHash: summarizeDiagnosticId(content.key),
    sourceKind: content.sourceKind,
    mimeType: content.mimeType,
    directUrl: safeUrlSummary(content.directUrl),
    payload: content.payload
      ? summarizeViewerPayload(content.payload)
      : undefined,
  };
}

export function summarizeViewerKeys(keys: {
  load: string;
  presentation: string;
  resource: string;
}) {
  return {
    loadHash: summarizeDiagnosticId(keys.load),
    presentationHash: summarizeDiagnosticId(keys.presentation),
    resourceHash: summarizeDiagnosticId(keys.resource),
  };
}

export function summarizeViewerResource(resource: {
  descriptor?: {
    category: string;
    displayName: string;
    fileName: string;
    identityKey: string;
    mimeType?: string;
  };
  fileName: string;
  identityKey: string;
  mimeType?: string;
  sourceKind: string;
  keys: {
    load: string;
    presentation: string;
    resource: string;
  };
  content?: {
    key: string;
    sourceKind: string;
    directUrl?: string | null;
    mimeType?: string;
    payload?: {
      kind: string;
      url?: string;
      blob?: Blob;
      text?: string;
    };
  };
}) {
  return {
    fileName: summarizeFileName(resource.fileName),
    identityKeyHash: summarizeDiagnosticId(resource.identityKey),
    mimeType: resource.mimeType,
    sourceKind: resource.sourceKind,
    descriptor: resource.descriptor
      ? summarizeViewerDescriptor(resource.descriptor)
      : undefined,
    content: resource.content
      ? summarizeViewerContent(resource.content)
      : undefined,
    keys: summarizeViewerKeys(resource.keys),
  };
}

export function summarizeViewerSource(source: {
  kind: string;
  fileName?: string;
  mimeType?: string;
  identityKey?: string;
  downloadUrl?: string;
  url?: string;
  text?: string;
  blob?: Blob;
}) {
  return {
    kind: source.kind,
    fileName: summarizeFileName(source.fileName),
    extension: extensionOf(source.fileName),
    mimeType: source.mimeType ?? source.blob?.type,
    identityKeyHash: summarizeDiagnosticId(source.identityKey),
    url: safeUrlSummary(source.url),
    downloadUrl: safeUrlSummary(source.downloadUrl),
    textLength: source.kind === "text" ? (source.text?.length ?? 0) : undefined,
    blobSize: source.blob?.size,
    blobType: source.blob?.type || undefined,
  };
}

export function summarizeViewerDescriptor(descriptor: {
  category: string;
  displayName: string;
  fileName: string;
  identityKey: string;
  mimeType?: string;
}) {
  return {
    category: descriptor.category,
    displayName: summarizeFileName(descriptor.displayName),
    fileName: summarizeFileName(descriptor.fileName),
    extension: extensionOf(descriptor.fileName),
    mimeType: descriptor.mimeType,
    identityKeyHash: summarizeDiagnosticId(descriptor.identityKey),
    extensionCategory: categoryFromExtension(extensionOf(descriptor.fileName)),
    mimeCategory: categoryFromMime(descriptor.mimeType),
  };
}

export function viewerCategoryMismatch(descriptor: {
  fileName: string;
  mimeType?: string;
}) {
  const extensionCategory = categoryFromExtension(
    extensionOf(descriptor.fileName),
  );
  const mimeCategory = categoryFromMime(descriptor.mimeType);
  if (
    !extensionCategory ||
    !mimeCategory ||
    extensionCategory === mimeCategory
  ) {
    return null;
  }
  return {
    extension: extensionOf(descriptor.fileName),
    extensionCategory,
    mimeCategory,
    mimeType: descriptor.mimeType,
  };
}

export function truncate(value: string, maxLength: number): string {
  if (value.length <= maxLength) return value;
  return `${value.slice(0, maxLength)}...`;
}

export function summarizeDiagnosticId(value: string | null | undefined) {
  if (!value) return null;
  return {
    hash: hashString(value),
    length: value.length,
  };
}

function summarizeFileName(name: string | null | undefined) {
  if (!name) return null;
  return {
    extension: extensionOf(name),
    hash: hashString(name),
    length: name.length,
  };
}

function redactUrlPath(pathname: string) {
  const segments = pathname.split("/").map((segment, index, parts) => {
    if (!segment) return segment;
    const previous = parts[index - 1];
    if (previous === "files") return ":file_id";
    if (previous === "primitive-executions") return ":primitive_execution_id";
    if (previous === "runs") return ":run_id";
    if (previous === "workflows") return ":workflow_id";
    return redactIdentifierSegment(segment);
  });
  return segments.join("/");
}

function redactIdentifierSegment(segment: string) {
  if (
    /^(file|pexec|run|wf|doc|artifact|clss|parse|split|part|edit|extr)[_-]/i.test(
      segment,
    )
  ) {
    return `:${segment.split(/[_-]/, 1)[0].toLowerCase()}_id`;
  }
  if (segment.length >= 18 && /^[a-z0-9_-]+$/i.test(segment)) {
    return ":id";
  }
  return segment;
}

function redactDiagnosticText(value: string, maxLength: number) {
  return truncate(
    value
      .replace(/https?:\/\/\S+/gi, "[url]")
      .replace(/data:[^\s"'<>]+/gi, "[data-url]")
      .replace(/[?&][^=\s"'<>]+=[^\s"'<>]+/g, "[query]"),
    maxLength,
  );
}

function hashString(value: string): string {
  let hash = 2166136261;
  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(16).padStart(8, "0");
}

function summarizeViewerPayload(payload: {
  kind: string;
  url?: string;
  blob?: Blob;
  text?: string;
}) {
  if (payload.kind === "url") {
    return {
      kind: payload.kind,
      url: safeUrlSummary(payload.url),
    };
  }
  if (payload.kind === "blob") {
    return {
      kind: payload.kind,
      blobSize: payload.blob?.size,
      blobType: payload.blob?.type || undefined,
    };
  }
  if (payload.kind === "text") {
    return {
      kind: payload.kind,
      textLength: payload.text?.length ?? 0,
    };
  }
  return { kind: payload.kind };
}

function pdfHeaderOffset(bytes: Uint8Array): number {
  const limit = Math.min(bytes.byteLength, 1024);
  for (let i = 0; i <= limit - PDF_HEADER.length; i += 1) {
    let matches = true;
    for (let offset = 0; offset < PDF_HEADER.length; offset += 1) {
      if (bytes[i + offset] !== PDF_HEADER[offset]) {
        matches = false;
        break;
      }
    }
    if (matches) return i;
  }
  return -1;
}

function tiffHeader(bytes: Uint8Array): "little_endian" | "big_endian" | null {
  if (bytes.byteLength < 4) return null;
  if (
    bytes[0] === 0x49 &&
    bytes[1] === 0x49 &&
    bytes[2] === 0x2a &&
    bytes[3] === 0x00
  ) {
    return "little_endian";
  }
  if (
    bytes[0] === 0x4d &&
    bytes[1] === 0x4d &&
    bytes[2] === 0x00 &&
    bytes[3] === 0x2a
  ) {
    return "big_endian";
  }
  return null;
}

function looksLikeHtml(bytes: Uint8Array): boolean {
  const sample = new TextDecoder()
    .decode(bytes.slice(0, Math.min(bytes.byteLength, 64)))
    .trimStart()
    .toLowerCase();
  return sample.startsWith("<!doctype html") || sample.startsWith("<html");
}

function bytesToHex(bytes: Uint8Array): string {
  return Array.from(bytes)
    .map((byte) => byte.toString(16).padStart(2, "0"))
    .join("");
}

function extensionOf(name: string | null | undefined): string | null {
  if (!name) return null;
  const clean = name.split(/[?#]/)[0];
  const base = clean.split("/").pop() ?? clean;
  const dot = base.lastIndexOf(".");
  return dot > 0 ? base.slice(dot + 1).toLowerCase() : null;
}

function categoryFromExtension(extension: string | null): string | null {
  if (!extension) return null;
  if (extension === "pdf") return "pdf";
  if (extension === "tif" || extension === "tiff") return "image";
  if (
    extension === "png" ||
    extension === "jpg" ||
    extension === "jpeg" ||
    extension === "gif" ||
    extension === "webp" ||
    extension === "avif" ||
    extension === "bmp" ||
    extension === "svg" ||
    extension === "ico"
  ) {
    return "image";
  }
  if (extension === "docx") return "docx";
  if (extension === "xlsx" || extension === "xls" || extension === "xlsm") {
    return "xlsx";
  }
  if (extension === "pptx") return "pptx";
  if (extension === "csv" || extension === "tsv") return "csv";
  if (extension === "md" || extension === "markdown") return "markdown";
  if (extension === "html" || extension === "htm") return "html";
  return null;
}

function categoryFromMime(mimeType: string | null | undefined): string | null {
  if (!mimeType) return null;
  const mime = mimeType.toLowerCase().split(";")[0].trim();
  if (mime === "application/pdf") return "pdf";
  if (mime.startsWith("image/")) return "image";
  if (mime.includes("wordprocessingml")) return "docx";
  if (mime.includes("spreadsheet") || mime.includes("ms-excel")) return "xlsx";
  if (mime.includes("presentation") || mime.includes("ms-powerpoint")) {
    return "pptx";
  }
  if (mime === "text/csv" || mime === "text/tab-separated-values") return "csv";
  if (mime === "text/markdown") return "markdown";
  if (mime === "text/html") return "html";
  if (mime.startsWith("text/")) return "text";
  return null;
}
registry/new-york-v4/lib/viewer-download-actions.ts
export type ViewerDownloadOrigin = "original" | "derived";

export type ViewerDownloadPayload =
  | { kind: "href"; href: string }
  | { kind: "blob"; blob: Blob }
  | { kind: "text"; text: string; mimeType?: string }
  | { kind: "none" };

export interface ViewerDownloadAction {
  id: string;
  label: string;
  fileName: string;
  origin: ViewerDownloadOrigin;
  isDisabled?: boolean;
  getPayload: (options?: {
    signal?: AbortSignal;
  }) => ViewerDownloadPayload | Promise<ViewerDownloadPayload>;
}

export type ViewerDownloadErrorKind =
  | "disabled"
  | "aborted"
  | "payload_failed"
  | "unsupported";

export class ViewerDownloadError extends Error {
  readonly kind: ViewerDownloadErrorKind;
  readonly actionId: string;
  override readonly cause?: unknown;

  constructor({
    actionId,
    kind,
    message,
    cause,
  }: {
    actionId: string;
    kind: ViewerDownloadErrorKind;
    message: string;
    cause?: unknown;
  }) {
    super(message);
    this.name = "ViewerDownloadError";
    this.actionId = actionId;
    this.kind = kind;
    this.cause = cause;
  }
}

export function createHrefDownloadAction({
  id,
  label = "Download",
  href,
  fileName,
  origin = "original",
}: {
  id: string;
  label?: string;
  href: string;
  fileName: string;
  origin?: ViewerDownloadOrigin;
}): ViewerDownloadAction {
  return {
    id,
    label,
    fileName,
    origin,
    getPayload: () => ({ kind: "href", href }),
  };
}

export function createBlobDownloadAction({
  id,
  label = "Download",
  blob,
  fileName,
  origin = "original",
}: {
  id: string;
  label?: string;
  blob: Blob;
  fileName: string;
  origin?: ViewerDownloadOrigin;
}): ViewerDownloadAction {
  return {
    id,
    label,
    fileName,
    origin,
    getPayload: () => ({ kind: "blob", blob }),
  };
}

export function createTextDownloadAction({
  id,
  label = "Download",
  text,
  fileName,
  mimeType,
  origin = "original",
}: {
  id: string;
  label?: string;
  text: string;
  fileName: string;
  mimeType?: string;
  origin?: ViewerDownloadOrigin;
}): ViewerDownloadAction {
  return {
    id,
    label,
    fileName,
    origin,
    getPayload: () => ({ kind: "text", text, mimeType }),
  };
}

export function createDisabledDownloadAction({
  id,
  label = "Download",
  fileName,
  origin = "original",
}: {
  id: string;
  label?: string;
  fileName: string;
  origin?: ViewerDownloadOrigin;
}): ViewerDownloadAction {
  return {
    id,
    label,
    fileName,
    origin,
    isDisabled: true,
    getPayload: () => ({ kind: "none" }),
  };
}
registry/new-york-v4/lib/viewer-errors.ts
export type ViewerFormat =
  | "pdf"
  | "image"
  | "text"
  | "csv"
  | "docx"
  | "xlsx"
  | "pptx"
  | "file";

export type ViewerErrorDomain =
  | "resource"
  | "format"
  | "state"
  | "unsupported"
  | "unknown";

export type ResourceErrorKind =
  | "fetch_failed"
  | "http_error"
  | "aborted"
  | "invalid_range"
  | "partial_content"
  | "too_large"
  | "unsupported_capability"
  | "unknown";

export type ResourceTooLargeReason = "bytes" | "lines";

export class ResourceError extends Error {
  readonly domain = "resource";
  readonly kind: ResourceErrorKind;
  readonly status?: number;
  readonly tooLargeReason?: ResourceTooLargeReason;
  override readonly cause?: unknown;

  constructor({
    kind,
    message,
    status,
    tooLargeReason,
    cause,
  }: {
    kind: ResourceErrorKind;
    message: string;
    status?: number;
    tooLargeReason?: ResourceTooLargeReason;
    cause?: unknown;
  }) {
    super(message);
    this.name = "ResourceError";
    this.kind = kind;
    this.status = status;
    this.tooLargeReason = tooLargeReason;
    this.cause = cause;
  }
}

export type ViewerFormatErrorKind =
  | "bounds"
  | "decode_failed"
  | "disposed"
  | "index_out_of_range"
  | "load_failed"
  | "parse_failed"
  | "render_failed"
  | "worker_failed"
  | "unknown";

export interface ViewerFormatErrorMapperOptions {
  kind: ViewerFormatErrorKind;
  message: string;
}

export class ViewerFormatError extends Error {
  readonly domain = "format";
  readonly format: ViewerFormat;
  readonly kind: ViewerFormatErrorKind;
  override readonly cause?: unknown;

  constructor({
    format,
    kind,
    message,
    cause,
  }: {
    format: ViewerFormat;
    kind: ViewerFormatErrorKind;
    message: string;
    cause?: unknown;
  }) {
    super(message);
    this.name = "ViewerFormatError";
    this.format = format;
    this.kind = kind;
    this.cause = cause;
  }
}

export type ViewerStateErrorKind =
  | "invalid_bounds"
  | "invalid_target"
  | "out_of_range"
  | "stale_resource"
  | "unknown";

export class ViewerStateError extends Error {
  readonly domain = "state";
  readonly format?: ViewerFormat;
  readonly kind: ViewerStateErrorKind;
  override readonly cause?: unknown;

  constructor({
    format,
    kind,
    message,
    cause,
  }: {
    format?: ViewerFormat;
    kind: ViewerStateErrorKind;
    message: string;
    cause?: unknown;
  }) {
    super(message);
    this.name = "ViewerStateError";
    this.format = format;
    this.kind = kind;
    this.cause = cause;
  }
}

export class ViewerUnsupportedError extends Error {
  readonly domain = "unsupported";
  readonly format?: ViewerFormat;
  readonly sourceKind?: string;
  override readonly cause?: unknown;

  constructor({
    format,
    sourceKind,
    message,
    cause,
  }: {
    format?: ViewerFormat;
    sourceKind?: string;
    message: string;
    cause?: unknown;
  }) {
    super(message);
    this.name = "ViewerUnsupportedError";
    this.format = format;
    this.sourceKind = sourceKind;
    this.cause = cause;
  }
}

export interface ViewerErrorInfo {
  domain: ViewerErrorDomain;
  format?: ViewerFormat;
  kind: string;
  message: string;
  status?: number;
  isRetryable: boolean;
  isDownloadUseful: boolean;
  userMessage: string;
  cause?: unknown;
}

export interface ViewerErrorContext {
  format?: ViewerFormat;
  sourceKind?: "url" | "blob" | "text";
  canDownload?: boolean;
  retry?: "auto" | "always" | "never";
}

export function isAbortError(error: unknown): boolean {
  return (
    (error instanceof DOMException && error.name === "AbortError") ||
    (error instanceof Error && error.name === "AbortError")
  );
}

export function isResourceError(error: unknown): error is ResourceError {
  return (
    error instanceof ResourceError ||
    isErrorLike(error, "ResourceError", "resource")
  );
}

export function isViewerFormatError(
  error: unknown,
): error is ViewerFormatError {
  return (
    error instanceof ViewerFormatError ||
    isErrorLike(error, "ViewerFormatError", "format")
  );
}

export function isViewerStateError(error: unknown): error is ViewerStateError {
  return (
    error instanceof ViewerStateError ||
    isErrorLike(error, "ViewerStateError", "state")
  );
}

export function isViewerUnsupportedError(
  error: unknown,
): error is ViewerUnsupportedError {
  return (
    error instanceof ViewerUnsupportedError ||
    isErrorLike(error, "ViewerUnsupportedError", "unsupported")
  );
}

export function toViewerErrorInfo(
  error: unknown,
  context: ViewerErrorContext = {},
): ViewerErrorInfo {
  const canDownload = context.canDownload ?? true;

  if (isResourceError(error)) {
    return {
      domain: "resource",
      format: context.format,
      kind: error.kind,
      message: error.message,
      status: error.status,
      isRetryable: retryable(
        context,
        resourceErrorDefaultRetry(error, context),
      ),
      isDownloadUseful: canDownload && error.kind !== "aborted",
      userMessage: resourceErrorUserMessage(error),
      cause: error.cause,
    };
  }

  if (isViewerFormatError(error)) {
    const format = error.format ?? context.format;
    return {
      domain: "format",
      format,
      kind: error.kind,
      message: error.message,
      isRetryable: retryable(
        context,
        formatErrorDefaultRetry(error, context, format),
      ),
      isDownloadUseful: canDownload,
      userMessage: formatErrorUserMessage(format, error.kind, error),
      cause: error.cause,
    };
  }

  if (isViewerStateError(error)) {
    return {
      domain: "state",
      format: error.format ?? context.format,
      kind: error.kind,
      message: error.message,
      isRetryable: retryable(context, false),
      isDownloadUseful: canDownload,
      userMessage: stateErrorUserMessage(error.kind),
      cause: error.cause,
    };
  }

  if (isViewerUnsupportedError(error)) {
    return {
      domain: "unsupported",
      format: error.format ?? context.format,
      kind: "unsupported",
      message: error.message,
      isRetryable: retryable(context, false),
      isDownloadUseful: canDownload,
      userMessage: "This file cannot be previewed here.",
      cause: error.cause,
    };
  }

  if (isAbortError(error)) {
    return {
      domain: "resource",
      format: context.format,
      kind: "aborted",
      message: "Loading was cancelled.",
      isRetryable: retryable(context, false),
      isDownloadUseful: false,
      userMessage: "Loading was cancelled.",
      cause: error,
    };
  }

  const message = error instanceof Error ? error.message : String(error);
  return {
    domain: "unknown",
    format: context.format,
    kind: "unknown",
    message,
    isRetryable: retryable(context, unknownErrorDefaultRetry(context)),
    isDownloadUseful: canDownload,
    userMessage: fallbackUserMessage(context.format),
    cause: error,
  };
}

function isErrorLike(error: unknown, name: string, domain: ViewerErrorDomain) {
  if (!error || typeof error !== "object") return false;
  const candidate = error as {
    name?: unknown;
    domain?: unknown;
    kind?: unknown;
  };
  return (
    (candidate.name === name || candidate.domain === domain) &&
    typeof candidate.kind === "string"
  );
}

function retryable(context: ViewerErrorContext, fallback: boolean) {
  if (context.retry === "always") return true;
  if (context.retry === "never") return false;
  return fallback;
}

function resourceErrorDefaultRetry(
  error: ResourceError,
  context: ViewerErrorContext,
) {
  if (error.kind === "aborted") return false;
  if (error.kind === "invalid_range") return false;
  if (error.kind === "too_large") return false;
  if (error.kind === "unsupported_capability") return false;
  return context.sourceKind === "url";
}

function formatErrorDefaultRetry(
  error: ViewerFormatError,
  context: ViewerErrorContext,
  format: ViewerFormat | undefined,
) {
  if (format === "text" && error.kind === "bounds") return false;
  if (error.kind === "disposed") return false;
  if (error.kind === "index_out_of_range") return false;
  if (format === "docx") return true;
  return context.sourceKind === "url";
}

function unknownErrorDefaultRetry(context: ViewerErrorContext) {
  if (context.format === "docx") return true;
  return context.sourceKind === "url";
}

function resourceErrorUserMessage(error: ResourceError) {
  if (error.kind === "http_error") {
    return error.status
      ? `Failed to load file: ${error.status}.`
      : "Couldn't load this file.";
  }
  if (error.kind === "fetch_failed") return "Couldn't load this file.";
  if (error.kind === "aborted") return "Loading was cancelled.";
  if (error.kind === "invalid_range") return "This source range is invalid.";
  if (error.kind === "too_large") {
    return error.tooLargeReason === "lines"
      ? "This file has too many lines to preview."
      : "This file is too large to preview.";
  }
  if (error.kind === "partial_content") {
    return "This source returned partial content and cannot be previewed here.";
  }
  if (error.kind === "unsupported_capability") {
    return "This source cannot be previewed here.";
  }
  return "Couldn't load this file.";
}

function formatErrorUserMessage(
  format: ViewerFormat | undefined,
  kind: string,
  error?: unknown,
) {
  if (format === "pdf") return "Couldn't load this PDF.";
  if (format === "image") {
    if (kind === "index_out_of_range")
      return "This image page is out of range.";
    if (kind === "decode_failed") return "Couldn't decode this image.";
    return "Couldn't load this image.";
  }
  if (format === "text") {
    if (kind === "render_failed") return "Couldn't render this text file.";
    if (kind === "bounds") {
      const boundsError = error as {
        reason?: unknown;
        boundName?: unknown;
      };
      if (boundsError.reason === "lines") {
        return "This text file has too many lines to preview.";
      }
      if (boundsError.reason === "bytes") {
        return "This text file is too large to preview.";
      }
      if (typeof boundsError.boundName === "string") {
        return "Text viewer bounds are invalid.";
      }
    }
    return "Couldn't load this text file.";
  }
  if (format === "csv") return "Couldn't parse this table.";
  if (format === "docx") return "Couldn't render this document.";
  if (format === "xlsx") return "Couldn't parse this spreadsheet.";
  if (format === "pptx") {
    if (kind === "render_failed") return "Couldn't render this slide.";
    return "Couldn't load this presentation.";
  }
  return "Couldn't load this file.";
}

function stateErrorUserMessage(kind: ViewerStateErrorKind) {
  if (kind === "invalid_bounds") return "Viewer bounds are invalid.";
  if (kind === "invalid_target") return "The requested target is invalid.";
  if (kind === "out_of_range") return "The requested item is out of range.";
  if (kind === "stale_resource") return "This viewer state is no longer valid.";
  return "Couldn't load this file.";
}

function fallbackUserMessage(format: ViewerFormat | undefined) {
  if (format === "pdf") return "Couldn't load this PDF.";
  if (format === "image") return "Couldn't load this image.";
  if (format === "text") return "Couldn't load this text file.";
  if (format === "csv") return "Couldn't parse this table.";
  if (format === "docx") return "Couldn't load this document.";
  if (format === "xlsx") return "Couldn't load this spreadsheet.";
  if (format === "pptx") return "Couldn't load this presentation.";
  return "Couldn't load this file.";
}
registry/new-york-v4/lib/viewer-resource.ts
import {
  createBlobDownloadAction,
  createHrefDownloadAction,
  createTextDownloadAction,
  type ViewerDownloadAction,
} from "@/lib/viewer-download-actions";
import {
  isAbortError,
  ResourceError,
  type ResourceTooLargeReason,
} from "@/lib/viewer-errors";
import {
  resolveViewerDescriptor,
  textPayloadKey,
  type BlobViewerSource,
  type FileCategory,
  type TextSource,
  type UrlViewerSource,
  type ViewerDescriptor,
  type ViewerSource,
} from "@/lib/viewer-source";

export interface ResourceReadOptions {
  cache?: RequestCache;
  signal?: AbortSignal;
}

export interface TextReadOptions extends ResourceReadOptions {
  maxBytes?: number;
  maxLines?: number;
}

export interface ByteRange {
  start: number;
  end: number;
}

export interface ByteRangeResult {
  buffer: ArrayBuffer;
  contentRange?: {
    start: number;
    end: number;
    total: number | null;
  };
  isComplete: boolean;
}

export interface ViewerResourceKeys {
  readonly load: string;
  readonly presentation: string;
  readonly resource: string;
}

export type ViewerResourcePayload =
  | { kind: "url"; url: string }
  | { kind: "blob"; blob: Blob }
  | { kind: "text"; text: string };

export interface ViewerResourceContent {
  readonly key: string;
  readonly sourceKind: ViewerSource["kind"];
  readonly directUrl: string | null;
  readonly mimeType?: string;
  readonly payload: ViewerResourcePayload;
  readBlob(options?: ResourceReadOptions): Promise<Blob>;
  readBytes(options?: ResourceReadOptions): Promise<ArrayBuffer>;
  readText(options?: TextReadOptions): Promise<string>;
  readStream(
    options?: ResourceReadOptions,
  ): Promise<ReadableStream<Uint8Array>>;
  readRange(
    range: ByteRange,
    options?: ResourceReadOptions,
  ): Promise<ByteRangeResult>;
}

export type ViewerContentIdentity = Pick<
  ViewerResourceContent,
  "key" | "sourceKind"
>;

export type ViewerContentDirectUrl = ViewerContentIdentity &
  Pick<ViewerResourceContent, "directUrl">;

export type ViewerContentPayload = ViewerContentIdentity &
  Pick<ViewerResourceContent, "payload">;

export type ViewerContentMime = ViewerContentIdentity &
  Pick<ViewerResourceContent, "mimeType">;

export type ViewerContentBlob = ViewerContentIdentity &
  Pick<ViewerResourceContent, "readBlob">;

export type ViewerContentBytes = ViewerContentIdentity &
  Pick<ViewerResourceContent, "readBytes">;

export type ViewerContentText = ViewerContentIdentity &
  Pick<ViewerResourceContent, "readText">;

export type ViewerContentStream = ViewerContentIdentity &
  Pick<ViewerResourceContent, "readStream">;

export type ViewerContentRange = ViewerContentIdentity &
  Pick<ViewerResourceContent, "readRange">;

export interface ViewerResource {
  readonly descriptor: ViewerDescriptor;
  readonly sourceKind: ViewerSource["kind"];
  readonly keys: ViewerResourceKeys;
  readonly identityKey: string;
  readonly fileName: string;
  readonly mimeType?: string;
  readonly content: ViewerResourceContent;
  readonly originalDownload: ViewerDownloadAction;
}

const URL_RESOURCE_REGISTRY_MAX = 128;
const TEXT_RESOURCE_REGISTRY_MAX = 64;
// LF, CR, CRLF, LINE SEPARATOR (U+2028), and PARAGRAPH SEPARATOR (U+2029) — the
// ECMAScript LineTerminator set, matching what a browser breaks on in a
// `white-space: pre` block. Kept in sync with text-viewer-resource's splitter.
const TEXT_LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/g;

const urlViewerResourceRegistry = new Map<string, ViewerResource>();
const urlViewerResourceContentRegistry = new Map<
  string,
  ViewerResourceContent
>();
const textViewerResourceRegistry = new Map<string, ViewerResource>();
const textViewerResourceContentRegistry = new Map<
  string,
  ViewerResourceContent
>();
let blobViewerResourceRegistry = new WeakMap<
  Blob,
  Map<string, ViewerResource>
>();
let blobViewerResourceContentRegistry = new WeakMap<
  Blob,
  Map<string, ViewerResourceContent>
>();
const blobObjectKeys = new WeakMap<Blob, string>();
let nextBlobObjectKey = 0;

export function createViewerResource(
  source: ViewerSource,
  category?: FileCategory,
): ViewerResource {
  const descriptor = resolveViewerDescriptor({ source, category });
  const keys = viewerResourceKeys(source, descriptor);

  if (source.kind === "url") {
    return internUrlResource(source, descriptor, keys);
  }
  if (source.kind === "blob") {
    return internBlobResource(source, descriptor, keys);
  }
  return internTextResource(source, descriptor, keys);
}

export function clearViewerResourceRegistryForTests() {
  urlViewerResourceRegistry.clear();
  urlViewerResourceContentRegistry.clear();
  textViewerResourceRegistry.clear();
  textViewerResourceContentRegistry.clear();
  blobViewerResourceRegistry = new WeakMap();
  blobViewerResourceContentRegistry = new WeakMap();
}

function internUrlResource(
  source: UrlViewerSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  const cached = urlViewerResourceRegistry.get(keys.resource);
  if (cached) return cached;

  const resource = createUrlResource(source, descriptor, keys);
  urlViewerResourceRegistry.set(keys.resource, resource);
  pruneUrlResourceRegistry();
  return resource;
}

function internBlobResource(
  source: BlobViewerSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  let resources = blobViewerResourceRegistry.get(source.blob);
  if (!resources) {
    resources = new Map();
    blobViewerResourceRegistry.set(source.blob, resources);
  }

  const cached = resources.get(keys.resource);
  if (cached) return cached;

  const resource = createBlobResource(source, descriptor, keys);
  resources.set(keys.resource, resource);
  return resource;
}

function internTextResource(
  source: TextSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  const cached = textViewerResourceRegistry.get(keys.resource);
  if (cached) return cached;

  const resource = createTextResource(source, descriptor, keys);
  textViewerResourceRegistry.set(keys.resource, resource);
  pruneTextResourceRegistry();
  return resource;
}

function pruneUrlResourceRegistry() {
  while (urlViewerResourceRegistry.size > URL_RESOURCE_REGISTRY_MAX) {
    const firstKey = urlViewerResourceRegistry.keys().next().value;
    if (!firstKey) return;
    urlViewerResourceRegistry.delete(firstKey);
  }
}

function pruneUrlResourceContentRegistry() {
  while (urlViewerResourceContentRegistry.size > URL_RESOURCE_REGISTRY_MAX) {
    const firstKey = urlViewerResourceContentRegistry.keys().next().value;
    if (!firstKey) return;
    urlViewerResourceContentRegistry.delete(firstKey);
  }
}

function pruneTextResourceRegistry() {
  while (textViewerResourceRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {
    const firstKey = textViewerResourceRegistry.keys().next().value;
    if (!firstKey) return;
    textViewerResourceRegistry.delete(firstKey);
  }
}

function pruneTextResourceContentRegistry() {
  while (textViewerResourceContentRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {
    const firstKey = textViewerResourceContentRegistry.keys().next().value;
    if (!firstKey) return;
    textViewerResourceContentRegistry.delete(firstKey);
  }
}

function viewerResourceKeys(
  source: ViewerSource,
  descriptor: ViewerDescriptor,
): ViewerResourceKeys {
  const load = viewerResourceLoadKey(source, descriptor);
  const presentation = viewerResourcePresentationKey(source, descriptor);
  return {
    load,
    presentation,
    resource: [load, presentation].join("\u0000"),
  };
}

function viewerResourceLoadKey(
  source: ViewerSource,
  descriptor: ViewerDescriptor,
) {
  return [
    source.kind,
    source.identityKey ?? "",
    sourceMimeType(source) ?? "",
    directLoadCacheKey(source),
    payloadCacheKey(source, descriptor),
  ].join("\u0000");
}

function viewerResourcePresentationKey(
  source: ViewerSource,
  descriptor: ViewerDescriptor,
) {
  return [
    descriptor.category,
    descriptor.displayName,
    descriptor.fileName,
    descriptor.mimeType ?? "",
    downloadCacheKey(source),
  ].join("\u0000");
}

function directLoadCacheKey(source: ViewerSource) {
  return source.kind === "url" ? source.url : "";
}

function downloadCacheKey(source: ViewerSource) {
  if (source.kind === "text") return "";
  return source.downloadUrl ?? "";
}

function payloadCacheKey(source: ViewerSource, descriptor: ViewerDescriptor) {
  if (source.kind === "url") return "";
  if (source.kind === "blob") return blobObjectKey(source.blob);
  return source.identityKey ? "" : descriptor.identityKey;
}

export function viewerResourceRenderKey(resource: ViewerResource): string {
  const load = [
    resource.sourceKind,
    resource.identityKey,
    resource.mimeType ?? resource.content.mimeType ?? "",
    resource.content.directUrl ?? "",
    viewerContentRenderKey(resource.content),
  ].join("\u0000");

  return [load, resource.keys.presentation].join("\u0000");
}

export function viewerContentRenderKey(content: ViewerResourceContent): string {
  if (content.payload.kind === "text")
    return textPayloadKey(content.payload.text);
  return content.key;
}

function sourceMimeType(source: ViewerSource) {
  if (source.kind === "blob") return source.mimeType ?? source.blob.type;
  return source.mimeType;
}

function blobObjectKey(blob: Blob) {
  let key = blobObjectKeys.get(blob);
  if (!key) {
    nextBlobObjectKey += 1;
    key = `blob-object:${nextBlobObjectKey}`;
    blobObjectKeys.set(blob, key);
  }
  return key;
}

export function blobSource(
  bytes: Blob | ArrayBuffer | Uint8Array,
  metadata: {
    identityKey: string;
    fileName?: string;
    mimeType?: string;
    downloadUrl?: string;
  },
): BlobViewerSource {
  const blob =
    bytes instanceof Blob
      ? bytes
      : new Blob(
          [bytes instanceof ArrayBuffer ? bytes : new Uint8Array(bytes)],
          {
            type: metadata.mimeType ?? "",
          },
        );
  return {
    kind: "blob",
    blob,
    fileName: metadata.fileName,
    mimeType: metadata.mimeType ?? blob.type,
    downloadUrl: metadata.downloadUrl,
    identityKey: metadata.identityKey,
  };
}

function createUrlResource(
  source: UrlViewerSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  const content = internUrlResourceContent(source, keys);
  const originalDownload = createHrefDownloadAction({
    id: "download-original",
    label: "Download",
    href: source.downloadUrl ?? source.url,
    fileName: descriptor.fileName,
  });

  return resourceBase(source, descriptor, keys, { content, originalDownload });
}

function internUrlResourceContent(
  source: UrlViewerSource,
  keys: ViewerResourceKeys,
): ViewerResourceContent {
  const cached = urlViewerResourceContentRegistry.get(keys.load);
  if (cached) return cached;

  const content = resourceContentBase(source, keys, {
    directUrl: source.url,
    payload: { kind: "url", url: source.url },
    readBlob: async ({ cache, signal } = {}) => {
      const response = await fetchResource(
        source.url,
        cache ? { cache, signal } : { signal },
      );
      validateFullContentResponse(response);
      return readResponseBlob(response);
    },
    readBytes: async ({ cache, signal } = {}) => {
      const response = await fetchResource(
        source.url,
        cache ? { cache, signal } : { signal },
      );
      validateFullContentResponse(response);
      return readResponseArrayBuffer(response);
    },
    readText: async ({ cache, signal, maxBytes, maxLines } = {}) => {
      const response = await fetchResource(
        source.url,
        cache ? { cache, signal } : { signal },
      );
      return readBoundedResponseText(response, { maxBytes, maxLines });
    },
    readStream: async ({ cache, signal } = {}) => {
      const response = await fetchResource(
        source.url,
        cache ? { cache, signal } : { signal },
      );
      validateFullContentResponse(response);
      if (!response.body) {
        if (response.status === 204 || response.status === 205) {
          return emptyByteStream();
        }
        throw new ResourceError({
          kind: "unsupported_capability",
          message: "This response cannot be streamed.",
        });
      }
      return response.body;
    },
    readRange: async (range, { cache, signal } = {}) => {
      validateByteRange(range);
      const { start, end } = range;
      const init = {
        signal,
        headers: { Range: `bytes=${start}-${end}` },
      };
      const response = await fetchResource(
        source.url,
        cache ? { ...init, cache } : init,
      );
      const buffer = await readResponseArrayBuffer(response);
      const contentRange = parseContentRange(
        response.headers.get("content-range"),
      );
      validateUrlRangeResponse({
        bufferLength: buffer.byteLength,
        contentRange,
        range,
        status: response.status,
      });
      return {
        buffer,
        contentRange,
        isComplete: isByteRangeComplete({
          bufferLength: buffer.byteLength,
          contentRange,
          requestedLength: end - start + 1,
          status: response.status,
        }),
      };
    },
  });
  urlViewerResourceContentRegistry.set(keys.load, content);
  pruneUrlResourceContentRegistry();
  return content;
}

function emptyByteStream() {
  return new ReadableStream<Uint8Array>({
    start(controller) {
      controller.close();
    },
  });
}

function createBlobResource(
  source: BlobViewerSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  const blob = source.blob;
  const content = internBlobResourceContent(source, keys);
  const originalDownload = source.downloadUrl
    ? createHrefDownloadAction({
        id: "download-original",
        label: "Download",
        href: source.downloadUrl,
        fileName: descriptor.fileName,
      })
    : createBlobDownloadAction({
        id: "download-original",
        label: "Download",
        blob,
        fileName: descriptor.fileName,
      });

  return resourceBase(source, descriptor, keys, { content, originalDownload });
}

function internBlobResourceContent(
  source: BlobViewerSource,
  keys: ViewerResourceKeys,
): ViewerResourceContent {
  const blob = source.blob;
  let contents = blobViewerResourceContentRegistry.get(blob);
  if (!contents) {
    contents = new Map();
    blobViewerResourceContentRegistry.set(blob, contents);
  }

  const cached = contents.get(keys.load);
  if (cached) return cached;

  const content = resourceContentBase(source, keys, {
    directUrl: null,
    payload: { kind: "blob", blob },
    readBlob: async () => blob,
    readBytes: async () => blob.arrayBuffer(),
    readText: async ({ maxBytes, maxLines } = {}) =>
      readBoundedBlobText(blob, { maxBytes, maxLines }),
    readStream: async () => blob.stream(),
    readRange: async (range) => {
      validateByteRange(range);
      const { start, end } = range;
      validateKnownByteRangeStart(start, blob.size);
      const rangeBlob = blob.slice(start, end + 1);
      return {
        buffer: await rangeBlob.arrayBuffer(),
        contentRange: {
          start,
          end: Math.min(end, blob.size - 1),
          total: blob.size,
        },
        isComplete: end >= blob.size - 1,
      };
    },
  });
  contents.set(keys.load, content);
  return content;
}

function createTextResource(
  source: TextSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
): ViewerResource {
  const content = internTextResourceContent(source, keys);
  const originalDownload = createTextDownloadAction({
    id: "download-original",
    label: "Download",
    text: source.text,
    fileName: descriptor.fileName,
    mimeType: descriptor.mimeType,
  });

  return resourceBase(source, descriptor, keys, { content, originalDownload });
}

function internTextResourceContent(
  source: TextSource,
  keys: ViewerResourceKeys,
): ViewerResourceContent {
  const cached = textViewerResourceContentRegistry.get(keys.load);
  if (cached) return cached;

  const content = resourceContentBase(source, keys, {
    directUrl: null,
    payload: { kind: "text", text: source.text },
    readBlob: async () =>
      new Blob([source.text], {
        type: "text/plain;charset=utf-8",
      }),
    readBytes: async () =>
      typedArrayBuffer(new TextEncoder().encode(source.text)),
    readText: async ({ maxBytes, maxLines } = {}) =>
      readBoundedInlineText(source.text, { maxBytes, maxLines }),
    readStream: async () => new Blob([source.text]).stream(),
    readRange: async (range) => {
      validateByteRange(range);
      const { start, end } = range;
      const buffer = new TextEncoder().encode(source.text);
      validateKnownByteRangeStart(start, buffer.byteLength);
      const slice = buffer.slice(start, end + 1);
      return {
        buffer: typedArrayBuffer(slice),
        contentRange: {
          start,
          end: Math.min(end, buffer.byteLength - 1),
          total: buffer.byteLength,
        },
        isComplete: end >= buffer.byteLength - 1,
      };
    },
  });
  textViewerResourceContentRegistry.set(keys.load, content);
  pruneTextResourceContentRegistry();
  return content;
}

function resourceBase(
  source: ViewerSource,
  descriptor: ViewerDescriptor,
  keys: ViewerResourceKeys,
  options: {
    content: ViewerResourceContent;
    originalDownload: ViewerDownloadAction;
  },
): ViewerResource {
  const { content, originalDownload } = options;
  return Object.freeze({
    descriptor,
    sourceKind: source.kind,
    keys,
    identityKey: descriptor.identityKey,
    fileName: descriptor.fileName,
    mimeType: descriptor.mimeType,
    content,
    originalDownload,
  });
}

function resourceContentBase(
  source: ViewerSource,
  keys: ViewerResourceKeys,
  methods: Omit<ViewerResourceContent, "key" | "sourceKind" | "mimeType">,
): ViewerResourceContent {
  return Object.freeze({
    key: keys.load,
    sourceKind: source.kind,
    mimeType: sourceMimeType(source),
    ...methods,
  });
}

function typedArrayBuffer(bytes: Uint8Array): ArrayBuffer {
  return bytes.buffer.slice(
    bytes.byteOffset,
    bytes.byteOffset + bytes.byteLength,
  ) as ArrayBuffer;
}

async function fetchResource(
  input: RequestInfo | URL,
  init?: RequestInit,
): Promise<Response> {
  let response: Response;
  try {
    response = await fetch(input, init);
  } catch (error) {
    if (isAbortError(error)) {
      throw new ResourceError({
        kind: "aborted",
        message: "Loading was cancelled.",
        cause: error,
      });
    }
    throw new ResourceError({
      kind: "fetch_failed",
      message: "Could not fetch this resource.",
      cause: error,
    });
  }

  if (!response.ok && response.status !== 206) {
    throw new ResourceError({
      kind: "http_error",
      message: `Failed to load resource: ${response.status}`,
      status: response.status,
    });
  }

  return response;
}

async function readBoundedResponseText(
  response: Response,
  bounds: { maxBytes?: number; maxLines?: number },
) {
  validateFullContentResponse(response);

  const maxBytes = bounds.maxBytes;
  if (
    isContentLengthOverLimit(response.headers.get("content-length"), maxBytes)
  ) {
    throw tooLarge("bytes");
  }

  const body = response.body;
  if (!body) {
    const buffer = await readResponseArrayBuffer(response);
    if (maxBytes != null && buffer.byteLength > maxBytes) {
      throw tooLarge("bytes");
    }
    const text = new TextDecoder().decode(buffer);
    assertLineLimit(text, bounds.maxLines);
    return text;
  }

  const reader = body.getReader();
  const decoder = new TextDecoder();
  const lineLimitTracker = createLineLimitTracker(bounds.maxLines);
  let receivedBytes = 0;
  let text = "";

  while (true) {
    const { done, value } = await readResponseStreamChunk(reader);
    if (done) break;
    receivedBytes += value.byteLength;
    if (maxBytes != null && receivedBytes > maxBytes) {
      await cancelReaderSilently(reader);
      throw tooLarge("bytes");
    }
    const chunkText = decoder.decode(value, { stream: true });
    try {
      lineLimitTracker.push(chunkText);
    } catch (error) {
      await cancelReaderSilently(reader);
      throw error;
    }
    text += chunkText;
  }

  const finalText = decoder.decode();
  lineLimitTracker.push(finalText);
  text += finalText;
  return text;
}

function isContentLengthOverLimit(
  contentLength: string | null,
  maxBytes: number | undefined,
) {
  if (maxBytes == null || contentLength == null) return false;

  const normalizedLength = contentLength.trim().replace(/^0+(?=\d)/, "");
  if (!/^\d+$/.test(normalizedLength)) return false;

  const maxLength = String(maxBytes);
  return (
    normalizedLength.length > maxLength.length ||
    (normalizedLength.length === maxLength.length &&
      normalizedLength > maxLength)
  );
}

function validateFullContentResponse(response: Response) {
  if (response.status !== 206) return;

  const contentRange = parseContentRange(response.headers.get("content-range"));
  if (
    contentRange?.total != null &&
    contentRange.start === 0 &&
    contentRange.end === contentRange.total - 1
  ) {
    return;
  }

  throw new ResourceError({
    kind: "partial_content",
    message: "Full response returned partial content.",
    status: response.status,
  });
}

async function readResponseStreamChunk(
  reader: ReadableStreamDefaultReader<Uint8Array>,
) {
  try {
    return await reader.read();
  } catch (error) {
    throw resourceReadError(error);
  }
}

async function readResponseArrayBuffer(response: Response) {
  try {
    return await response.arrayBuffer();
  } catch (error) {
    throw resourceReadError(error);
  }
}

async function readResponseBlob(response: Response) {
  try {
    return await response.blob();
  } catch (error) {
    throw resourceReadError(error);
  }
}

function resourceReadError(error: unknown) {
  if (isAbortError(error)) {
    return new ResourceError({
      kind: "aborted",
      message: "Loading was cancelled.",
      cause: error,
    });
  }
  return new ResourceError({
    kind: "fetch_failed",
    message: "Could not read this resource.",
    cause: error,
  });
}

async function readBoundedBlobText(
  blob: Blob,
  bounds: { maxBytes?: number; maxLines?: number },
) {
  if (bounds.maxBytes != null && blob.size > bounds.maxBytes) {
    throw tooLarge("bytes");
  }
  const text = await blob.text();
  assertLineLimit(text, bounds.maxLines);
  return text;
}

function readBoundedInlineText(
  text: string,
  { maxBytes, maxLines }: { maxBytes?: number; maxLines?: number },
) {
  // For inline sources the string *is* the resource, so its UTF-8 byte length
  // is the authoritative size to measure against maxBytes.
  if (
    maxBytes != null &&
    new TextEncoder().encode(text).byteLength > maxBytes
  ) {
    throw tooLarge("bytes");
  }
  assertLineLimit(text, maxLines);
  return text;
}

// Used after a transferred-byte check has already enforced maxBytes (URL/blob).
// Re-encoding the decoded text here would double-count: invalid UTF-8 decodes to
// U+FFFD (3 bytes each), inflating the measured size past the real wire bytes
// and falsely rejecting small resources as "too large".
function assertLineLimit(text: string, maxLines: number | undefined) {
  if (
    maxLines != null &&
    text.split(TEXT_LINE_BREAK_PATTERN).length > maxLines
  ) {
    throw tooLarge("lines");
  }
}

function tooLarge(reason: ResourceTooLargeReason) {
  return new ResourceError({
    kind: "too_large",
    tooLargeReason: reason,
    message: `Resource exceeds ${reason} limit.`,
  });
}

async function cancelReaderSilently(
  reader: ReadableStreamDefaultReader<Uint8Array>,
) {
  try {
    await reader.cancel();
  } catch {
    // Preserve the user-facing load failure; cancellation is best-effort cleanup.
  }
}

function validateByteRange({ start, end }: ByteRange) {
  if (
    !Number.isSafeInteger(start) ||
    !Number.isSafeInteger(end) ||
    start < 0 ||
    end < start
  ) {
    throw new ResourceError({
      kind: "invalid_range",
      message: "Byte range must use non-negative integer bounds.",
    });
  }
}

function validateKnownByteRangeStart(start: number, total: number) {
  if (start > 0 && start >= total) {
    throw new ResourceError({
      kind: "invalid_range",
      message: "Byte range starts past the available resource.",
    });
  }
}

function validateUrlRangeResponse({
  bufferLength,
  contentRange,
  range,
  status,
}: {
  bufferLength: number;
  contentRange: ByteRangeResult["contentRange"];
  range: ByteRange;
  status: number;
}) {
  if (status === 200) {
    if (range.start !== 0 || bufferLength > range.end - range.start + 1) {
      throw new ResourceError({
        kind: "invalid_range",
        message: "Full response does not match the requested byte range.",
      });
    }
    return;
  }
  if (status !== 206) {
    throw new ResourceError({
      kind: "invalid_range",
      message: "Range response must return full or partial content.",
    });
  }
  if (!contentRange) {
    throw new ResourceError({
      kind: "invalid_range",
      message: "Partial content response is missing a valid byte range.",
    });
  }
  const declaredLength = contentRange.end - contentRange.start + 1;
  if (
    contentRange.start !== range.start ||
    contentRange.end < contentRange.start ||
    contentRange.end > range.end ||
    (contentRange.total != null && contentRange.end >= contentRange.total) ||
    declaredLength !== bufferLength
  ) {
    throw new ResourceError({
      kind: "invalid_range",
      message: "Response byte range does not match the requested range.",
    });
  }
}

function isByteRangeComplete({
  bufferLength,
  contentRange,
  requestedLength,
  status,
}: {
  bufferLength: number;
  contentRange: ByteRangeResult["contentRange"];
  requestedLength: number;
  status: number;
}) {
  if (status === 200) return true;
  if (contentRange?.total != null) {
    if (contentRange.total <= 0) return true;
    return contentRange.end >= contentRange.total - 1;
  }
  if (contentRange) return false;
  return bufferLength < requestedLength;
}

function isStandaloneLineBreak(character: string) {
  const code = character.charCodeAt(0);
  return code === 0x0a || code === 0x2028 || code === 0x2029;
}

function createLineLimitTracker(maxLines: number | undefined) {
  let lineCount = 1;
  let previousWasCR = false;

  return {
    push(text: string) {
      if (maxLines == null || text.length === 0) return;

      for (const character of text) {
        if (previousWasCR) {
          previousWasCR = false;
          if (character === "\n") continue;
        }

        if (character === "\r") {
          lineCount += 1;
          previousWasCR = true;
        } else if (isStandaloneLineBreak(character)) {
          // LF, plus LINE/PARAGRAPH SEPARATOR (U+2028/U+2029); none pair with CR.
          lineCount += 1;
        }

        if (lineCount > maxLines) {
          throw tooLarge("lines");
        }
      }
    },
  };
}

function parseContentRange(value: string | null) {
  if (!value) return undefined;
  const match = value.match(/^bytes\s+(\d+)-(\d+)\/(\d+|\*)\s*$/i);
  if (!match) return undefined;
  const start = parseContentRangeNumber(match[1]);
  const end = parseContentRangeNumber(match[2]);
  const total =
    match[3] === "*" ? null : parseContentRangeNumber(match[3] ?? "");
  if (start == null || end == null || total === undefined) return undefined;
  return {
    start,
    end,
    total,
  };
}

function parseContentRangeNumber(value: string) {
  const number = Number(value);
  return Number.isSafeInteger(number) ? number : undefined;
}
registry/new-york-v4/lib/viewer-source.ts
export type FileCategory =
  | "pdf"
  | "docx"
  | "xlsx"
  | "pptx"
  | "csv"
  | "image"
  | "markdown"
  | "html"
  | "email"
  | "text"
  | "unsupported";

export type ViewerSource = UrlViewerSource | TextSource | BlobViewerSource;

export interface UrlViewerSource {
  kind: "url";
  url: string;
  fileName?: string;
  mimeType?: string;
  downloadUrl?: string;
  identityKey?: string;
}

export interface TextSource {
  kind: "text";
  text: string;
  fileName?: string;
  mimeType?: string;
  identityKey?: string;
}

export interface BlobViewerSource {
  kind: "blob";
  blob: Blob;
  identityKey: string;
  fileName?: string;
  mimeType?: string;
  downloadUrl?: string;
}

export interface ViewerDescriptor {
  source: ViewerSource;
  category: FileCategory;
  identityKey: string;
  displayName: string;
  fileName: string;
  mimeType?: string;
}

const EXTENSION_CATEGORY: Record<string, FileCategory> = {
  pdf: "pdf",
  docx: "docx",
  xlsx: "xlsx",
  xls: "xlsx",
  xlsm: "xlsx",
  pptx: "pptx",
  csv: "csv",
  tsv: "csv",
  png: "image",
  jpg: "image",
  jpeg: "image",
  gif: "image",
  webp: "image",
  avif: "image",
  bmp: "image",
  svg: "image",
  ico: "image",
  tif: "image",
  tiff: "image",
  md: "markdown",
  markdown: "markdown",
  mdx: "text",
  html: "html",
  htm: "html",
  eml: "email",
  txt: "text",
  text: "text",
  log: "text",
  json: "text",
  jsonl: "text",
  json5: "text",
  ndjson: "text",
  xml: "text",
  yaml: "text",
  yml: "text",
  toml: "text",
  ini: "text",
  env: "text",
  js: "text",
  mjs: "text",
  cjs: "text",
  jsx: "text",
  ts: "text",
  tsx: "text",
  css: "text",
  scss: "text",
  less: "text",
  py: "text",
  rb: "text",
  go: "text",
  rs: "text",
  java: "text",
  kt: "text",
  c: "text",
  h: "text",
  cpp: "text",
  cc: "text",
  cs: "text",
  php: "text",
  sh: "text",
  bash: "text",
  zsh: "text",
  sql: "text",
  graphql: "text",
  proto: "text",
  lua: "text",
  r: "text",
  swift: "text",
  scala: "text",
  pl: "text",
  vue: "text",
  svelte: "text",
};

export function extensionOf(name: string): string | null {
  const clean = name.split(/[?#]/)[0];
  const base = clean.split("/").pop() ?? clean;
  const dot = base.lastIndexOf(".");
  return dot > 0 ? base.slice(dot + 1).toLowerCase() : null;
}

export function extractName(url: string): string {
  const clean = url.split(/[?#]/)[0];
  return clean.split("/").pop() || "file";
}

export function detectCategory(
  fileName: string,
  mimeType?: string,
): FileCategory {
  const ext = extensionOf(fileName);
  if (ext && EXTENSION_CATEGORY[ext]) return EXTENSION_CATEGORY[ext];
  if (mimeType) {
    const fromMime = categoryFromMime(mimeType);
    if (fromMime) return fromMime;
  }
  return "unsupported";
}

export function resolveViewerDescriptor({
  source,
  category,
}: {
  source: ViewerSource;
  category?: FileCategory;
}): ViewerDescriptor {
  const resolvedMimeType =
    source.mimeType ??
    (source.kind === "blob" && source.blob.type ? source.blob.type : undefined);
  const displayName = source.fileName ?? defaultDisplayName(source);
  const fileName = source.fileName ?? defaultFileName(source);
  const resolvedCategory =
    category ?? detectCategory(displayName, resolvedMimeType);

  return {
    source,
    category: resolvedCategory,
    identityKey: source.identityKey ?? defaultIdentityKey(source),
    displayName,
    fileName,
    mimeType: resolvedMimeType,
  };
}

function categoryFromMime(mimeType: string): FileCategory | null {
  const mime = mimeType.toLowerCase().split(";")[0].trim();
  if (mime === "application/pdf") return "pdf";
  if (mime.includes("wordprocessingml")) return "docx";
  if (mime.includes("spreadsheet") || mime.includes("ms-excel")) return "xlsx";
  if (mime.includes("presentation") || mime.includes("ms-powerpoint")) {
    return "pptx";
  }
  if (mime === "text/csv" || mime === "text/tab-separated-values") return "csv";
  if (mime === "text/markdown") return "markdown";
  if (mime === "text/html") return "html";
  if (mime === "message/rfc822" || mime === "message/global") {
    return "email";
  }
  if (mime.startsWith("image/")) return "image";
  if (mime === "application/json" || mime === "application/xml") return "text";
  if (mime.startsWith("text/")) return "text";
  return null;
}

function defaultDisplayName(source: ViewerSource) {
  if (source.kind === "url") return source.url;
  if (source.kind === "text") return "text.txt";
  return "file";
}

function defaultFileName(source: ViewerSource) {
  if (source.kind === "url") return extractName(source.url);
  if (source.kind === "text") return "text.txt";
  return "file";
}

function defaultIdentityKey(source: ViewerSource) {
  if (source.kind === "url") return `url:${source.url}`;
  if (source.kind === "text") return textPayloadIdentityKey(source.text);
  return source.identityKey;
}

export function textPayloadIdentityKey(text: string) {
  return textPayloadKey(text);
}

export function textPayloadKey(text: string) {
  return `text:${text.length}:${hashString(text)}`;
}

function hashString(text: string) {
  let hash = 0x811c9dc5;
  for (let index = 0; index < text.length; index += 1) {
    hash ^= text.charCodeAt(index);
    hash = Math.imul(hash, 0x01000193);
  }
  return (hash >>> 0).toString(36);
}
registry/new-york-v4/ui/use-is-client.ts
"use client";

import * as React from "react";

const emptySubscribe = () => () => {};
const getClientSnapshot = () => true;
const getServerSnapshot = () => false;

/**
 * SSR gate: `false` on the server (and during hydration's first pass),
 * `true` on the client.
 *
 * This must stay a synchronous external-store read, NOT the
 * `useState(false)` + mount-effect flip. The flip pattern makes every
 * viewer mount its Suspense boundary in a later update; when two such
 * boundaries suspend on pending resources in the same flush as other
 * commit-phase updates (viewer sidebar/geometry registration), React 19's
 * retry lanes desynchronize and re-attempt each other's boundary on every
 * commit — an unbounded synchronous suspend/retry loop that starves the
 * event loop (jsdom tests OOM; browsers busy-spin until the resource
 * resolves). With the store read, client renders suspend on mount, which
 * never enters that loop. Regression-guarded in
 * tests/pdf-viewer-thumbnails.test.tsx ("shares one document resource…").
 */
export function useIsClient(): boolean {
  return React.useSyncExternalStore(
    emptySubscribe,
    getClientSnapshot,
    getServerSnapshot,
  );
}
registry/new-york-v4/ui/file-viewer-fit-width-motion.ts
"use client";

import type { FileViewerDocumentSurfaceMotionResolver } from "./file-viewer-motion-kernel";
import type { FileViewerMotionFrame } from "./file-viewer-motion-plan";
import type {
  FileViewerDocumentAlign,
  FileViewerInlineDirection,
} from "./file-viewer-renderer-contract";

export const FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY =
  "--file-viewer-fit-width-anchor-block";

// Commit-then-relax: the renderer lays out at the motion's TARGET width from
// the first commit, and this resolver reprojects that settled layout to the
// in-flight visual width with one uniform transform. The transform terminates
// on identity, so settle removes a no-op style instead of committing layout.
//
// The anchor custom property is the reading line's block offset in the settled
// stage's own coordinates (post-rebase scrollTop + marker offset). It is read
// live via var(), so the renderer writes it once per motion (in a layout
// effect after the slide-start scroll rebase) without re-entering the kernel.
export function createFileViewerFitWidthSurfaceMotionResolver({
  align,
  anchorBlockProperty = FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY,
  direction = "ltr",
  isFitWidth,
  stageInlineSize,
  stageOuterInlinePadding = 0,
  stageInlinePadding = 0,
  stageInlineSlope = 1,
  stageBlockSlope = stageInlineSlope,
}: {
  align: FileViewerDocumentAlign;
  anchorBlockProperty?: string;
  direction?: FileViewerInlineDirection;
  isFitWidth: boolean;
  stageInlineSize: number;
  /** Constant symmetric padding around the transformed stage. */
  stageOuterInlinePadding?: number;
  /** Constant symmetric inline padding inside the transformed stage. */
  stageInlinePadding?: number;
  stageInlineSlope?: number;
  /**
   * Slope for the BLOCK axis when it differs from the inline one. A stage
   * whose inline box carries constant padding while its block stack scales
   * with the content (the image viewer: fit subtracts the horizontal
   * padding, vertical gaps/padding scale) has two different affine models —
   * X tracks the pane 1:1 while Y scales by the content ratio — and a
   * uniform scale cannot land both axes exactly. Defaults to the inline
   * slope (uniform scale) for fully proportional stages like the PDF.
   */
  stageBlockSlope?: number;
}): FileViewerDocumentSurfaceMotionResolver {
  return (frame) => {
    if (!isFitWidth || frame.phase !== "sliding") {
      return {
        transform: "",
        transformOrigin: "",
        willChange: "",
      };
    }

    return {
      transform: getFileViewerFitWidthSurfaceMotionTransform({
        align,
        anchorBlockProperty,
        direction,
        frame,
        stageInlineSize,
        stageOuterInlinePadding,
        stageInlinePadding,
        stageInlineSlope,
        stageBlockSlope,
      }),
      transformOrigin: "0px 0px",
      willChange: "transform",
    };
  };
}

// Commit-then-relax for a CLAMPED reading column rather than a fit-width
// stage: the stage's inline size is min(canvas, maxStageInlineSize), so it
// does not scale with the pane — the only thing a width change moves is the
// align margin. The canvas commits the motion's TARGET width from the first
// sliding frame (minWidth under layoutPolicy "target"), which means a
// widening pane's chunks land at the settled margin synchronously with the
// click; this resolver reprojects them back to the live width's margin with
// a translate that terminates on identity. A narrowing pane never engages it
// (the canvas tracks the live width above its minWidth, so live and settled
// margins agree) — exactly the leg that already glides on layout.
export function createFileViewerAlignTranslateSurfaceMotionResolver({
  align,
  direction = "ltr",
  maxStageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  direction?: FileViewerInlineDirection;
  /** The column's max inline size (the chunk's max-width, in px). */
  maxStageInlineSize: number;
}): FileViewerDocumentSurfaceMotionResolver {
  return (frame) => {
    if (frame.phase !== "sliding") {
      return {
        transform: "",
        transformOrigin: "",
        willChange: "",
      };
    }

    return {
      transform: getFileViewerAlignTranslateSurfaceMotionTransform({
        align,
        direction,
        frame,
        maxStageInlineSize,
      }),
      transformOrigin: "0px 0px",
      willChange: "transform",
    };
  };
}

function getFileViewerAlignTranslateSurfaceMotionTransform({
  align,
  direction,
  frame,
  maxStageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  direction: FileViewerInlineDirection;
  frame: FileViewerMotionFrame;
  maxStageInlineSize: number;
}) {
  if (
    !Number.isFinite(maxStageInlineSize) ||
    maxStageInlineSize <= 0 ||
    frame.layoutInlineSize <= 0 ||
    frame.toInlineSize <= 0
  ) {
    return "";
  }

  // The canvas lays out at max(live, target): minWidth holds the committed
  // target under a still-narrow pane, and a pane wider than the target just
  // fills. The stage (reading column) centers/aligns INSIDE the canvas, and
  // an overflowing canvas itself pins to the pane's start edge — left in
  // LTR, right in RTL — so the stage's pane-space position carries the
  // canvas offset too.
  const canvasInlineSize = Math.max(frame.layoutInlineSize, frame.toInlineSize);
  const stageInlineSize = Math.min(canvasInlineSize, maxStageInlineSize);
  const canvasInlineOffset =
    direction === "rtl"
      ? Math.min(0, frame.layoutInlineSize - canvasInlineSize)
      : 0;
  const settledStageLeft =
    canvasInlineOffset +
    getFileViewerStageInlineMargin({
      align,
      availableInlineSize: canvasInlineSize,
      direction,
      stageInlineSize,
    });
  const liveStageLeft = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: frame.layoutInlineSize,
    direction,
    stageInlineSize,
  });
  const translateX = liveStageLeft - settledStageLeft;

  if (Math.abs(translateX) <= 0.001) return "";

  return `translate3d(${formatFileViewerMotionPixel(translateX)}px, 0px, 0)`;
}

export function getFileViewerFitWidthScale({
  availableInlineSize,
  contentInlineSize,
  stageInlinePadding = 0,
}: {
  availableInlineSize: number;
  contentInlineSize: number;
  stageInlinePadding?: number;
}) {
  if (availableInlineSize <= 0 || contentInlineSize <= 0) return 1;

  const contentAvailableInlineSize = Math.max(
    1,
    availableInlineSize - stageInlinePadding,
  );
  return contentAvailableInlineSize / contentInlineSize;
}

// The visual scale the resolver renders for a given live width — the same
// affine reprojection as the transform itself. Renderers use it to reason
// about the on-screen state (anchor capture/solve) without duplicating the
// formula.
//
// stageInlineSlope is how many stage pixels the settled stage grows per pane
// pixel. It is 1 whenever the stage IS the fit-width content (image, docx,
// pptx, uniform-width PDFs: stage = pane − constant padding), but a stage
// that is WIDER than its fit basis grows faster than the pane — a PDF fits
// its FIRST page while the stage spans its WIDEST page, so a mixed-width
// document has slope maxBase/fitBase > 1. A unit-slope assumption there
// under-scales the first frame by (slope − 1)·delta/stage — measured as a
// ~7px content step at the anchor-hold frame of a 355-page prospectus.
export function getFileViewerFitWidthVisualScale({
  liveInlineSize,
  stageInlineSize,
  stageInlineSlope = 1,
  targetInlineSize,
}: {
  liveInlineSize: number;
  stageInlineSize: number;
  stageInlineSlope?: number;
  targetInlineSize: number;
}) {
  if (
    stageInlineSize <= 0 ||
    !Number.isFinite(liveInlineSize) ||
    !Number.isFinite(targetInlineSize)
  ) {
    return 1;
  }
  const slope =
    Number.isFinite(stageInlineSlope) && stageInlineSlope > 0
      ? stageInlineSlope
      : 1;
  return (
    Math.max(1, stageInlineSize + slope * (liveInlineSize - targetInlineSize)) /
    stageInlineSize
  );
}

// Capture side of the motion anchor: the probe content line's on-screen block
// offset relative to the scroll box, taken just before a motion (or retarget)
// commits. When a motion is already in flight the DOM is the settled layout
// PLUS the live transform, so the capture applies that transform — otherwise
// a retarget would solve continuity against a picture the reader never saw.
export function captureFileViewerFitWidthAnchorScreenOffset({
  lastAnchorBlock,
  liveFrame,
  probeStageOffset,
  scrollTop,
  stageInlineSize,
  stageInlinePadding = 0,
  stageBlockSlope = 1,
}: {
  lastAnchorBlock: number | null;
  liveFrame: FileViewerMotionFrame | null;
  probeStageOffset: number;
  scrollTop: number;
  stageInlineSize: number;
  stageInlinePadding?: number;
  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */
  stageBlockSlope?: number;
}) {
  const untransformed = probeStageOffset - scrollTop;
  if (!liveFrame || liveFrame.phase !== "sliding") return untransformed;

  const liveScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: liveFrame.layoutInlineSize,
    stageInlineSize: getFileViewerFitWidthContentInlineSize({
      stageInlinePadding,
      stageInlineSize,
    }),
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: liveFrame.toInlineSize,
  });
  if (Math.abs(1 - liveScale) <= 0.001) return untransformed;

  return (
    liveScale * probeStageOffset +
    (1 - liveScale) * (lastAnchorBlock ?? 0) -
    scrollTop
  );
}

// Solve side: the anchor block offset that puts the probe content line back on
// its captured screen position under the NEW layout model at the motion's
// first-frame scale. Exact regardless of how the rebase clamped or how the
// old/new layout models relate (measured page sizes, constant gaps/padding).
// Returns null when the motion is degenerate (caller falls back to the live
// reading marker).
export function resolveFileViewerFitWidthMotionAnchorBlock({
  fromInlineSize,
  probeScreenOffset,
  probeStageOffset,
  scrollTop,
  stageInlineSize,
  stageInlinePadding = 0,
  stageBlockSlope = 1,
  toInlineSize,
}: {
  fromInlineSize: number | null;
  probeScreenOffset: number;
  probeStageOffset: number;
  scrollTop: number;
  stageInlineSize: number;
  stageInlinePadding?: number;
  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */
  stageBlockSlope?: number;
  toInlineSize: number | null;
}) {
  if (fromInlineSize == null || toInlineSize == null) return null;

  const startScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: fromInlineSize,
    stageInlineSize: getFileViewerFitWidthContentInlineSize({
      stageInlinePadding,
      stageInlineSize,
    }),
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: toInlineSize,
  });
  if (!Number.isFinite(startScale) || Math.abs(1 - startScale) <= 0.001) {
    return null;
  }

  return (
    (probeScreenOffset + scrollTop - startScale * probeStageOffset) /
    (1 - startScale)
  );
}

function getFileViewerFitWidthSurfaceMotionTransform({
  align,
  anchorBlockProperty,
  direction,
  frame,
  stageInlineSize,
  stageOuterInlinePadding,
  stageInlinePadding,
  stageInlineSlope,
  stageBlockSlope,
}: {
  align: FileViewerDocumentAlign;
  anchorBlockProperty: string;
  direction: FileViewerInlineDirection;
  frame: FileViewerMotionFrame;
  stageInlineSize: number;
  stageOuterInlinePadding: number;
  stageInlinePadding: number;
  stageInlineSlope: number;
  stageBlockSlope: number;
}) {
  if (
    stageInlineSize <= 0 ||
    frame.layoutInlineSize <= 0 ||
    frame.toInlineSize <= 0
  ) {
    return "";
  }

  // Fit-width renderers size their stage as an affine function of the
  // available width (stage = slope × width − constant padding), so the
  // in-flight visual stage is the settled stage plus the scaled live width
  // delta. At the first frame this resolves to exactly the pre-toggle stage
  // size, and at the last frame to the settled stage — identity. Each axis
  // carries its own slope: they differ when the stage's inline box holds
  // constant padding while its block stack scales with the content.
  const contentInlineSize = getFileViewerFitWidthContentInlineSize({
    stageInlinePadding,
    stageInlineSize,
  });
  const inlineScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: frame.layoutInlineSize,
    stageInlineSize: contentInlineSize,
    stageInlineSlope,
    targetInlineSize: frame.toInlineSize,
  });
  const blockScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: frame.layoutInlineSize,
    stageInlineSize: contentInlineSize,
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: frame.toInlineSize,
  });
  const visualStageInlineSize =
    inlineScale * contentInlineSize + stageInlinePadding;
  const availableStageInlineSize = Math.max(
    1,
    frame.layoutInlineSize - Math.max(0, stageOuterInlinePadding),
  );
  const settledMargin = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: availableStageInlineSize,
    direction,
    stageInlineSize,
  });
  const visualMargin = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: availableStageInlineSize,
    direction,
    stageInlineSize: visualStageInlineSize,
  });
  // The padding is constant in both endpoint layouts. Scaling the outer stage
  // would scale that inset too, making the visible page briefly too wide or
  // narrow on the first frame. Rebase the symmetric start inset so the inner
  // content edge, not the transparent wrapper edge, is pixel-continuous.
  const inlinePaddingStart = stageInlinePadding / 2;
  const translateX =
    visualMargin - settledMargin + (1 - inlineScale) * inlinePaddingStart;

  if (Math.abs(frame.layoutInlineSize - frame.toInlineSize) <= 0.001) {
    return "";
  }

  const formattedInlineScale = formatFileViewerMotionScale(inlineScale);
  const formattedBlockScale = formatFileViewerMotionScale(blockScale);
  const formattedTranslateX = formatFileViewerMotionPixel(translateX);
  // Scale about the stage origin; the anchor term keeps the reading line
  // fixed on the block axis: y' = s·y + (1 − s)·anchor equals y at
  // y = anchor.
  const translateY = `calc((1 - ${formattedBlockScale}) * var(${anchorBlockProperty}, 0px))`;
  const formattedScale =
    formattedInlineScale === formattedBlockScale
      ? formattedInlineScale
      : `${formattedInlineScale}, ${formattedBlockScale}`;

  return `translate3d(${formattedTranslateX}px, ${translateY}, 0) scale(${formattedScale})`;
}

function getFileViewerFitWidthContentInlineSize({
  stageInlinePadding,
  stageInlineSize,
}: {
  stageInlinePadding: number;
  stageInlineSize: number;
}) {
  const padding = Number.isFinite(stageInlinePadding)
    ? Math.max(0, stageInlinePadding)
    : 0;
  return Math.max(1, stageInlineSize - padding);
}

// Physical LEFT offset of the stage box inside the available inline size —
// translateX shifts along the physical X axis, so the model must speak
// physical-left in both directions. Stages align with physical auto margins
// (mx-auto for center, ml-auto for end, plain flow for start), so:
// - free space ≥ 0: center splits it; end pins right in both directions
//   (ml-auto is physical); start follows flow (left in LTR, right in RTL).
// - free space < 0 (the settled stage overflows the live container — the
//   close leg's early frames): auto margins collapse to 0 and CSS resolves
//   the over-constraint against the direction's end edge, pinning the box to
//   the start edge — left edge at 0 in LTR, at the negative free space in
//   RTL. The old unconditional max(0, …) clamp encoded only the LTR half and
//   made the RTL close leg overshoot by the overflow amount.
function getFileViewerStageInlineMargin({
  align,
  availableInlineSize,
  direction,
  stageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  availableInlineSize: number;
  direction: FileViewerInlineDirection;
  stageInlineSize: number;
}) {
  const freeInlineSize = availableInlineSize - stageInlineSize;
  if (freeInlineSize < 0) return direction === "rtl" ? freeInlineSize : 0;

  switch (align) {
    case "start":
      return direction === "rtl" ? freeInlineSize : 0;
    case "end":
      return freeInlineSize;
    case "center":
      return freeInlineSize / 2;
  }
}

function formatFileViewerMotionPixel(value: number) {
  return Number.isFinite(value) ? Number(value.toFixed(3)) : 0;
}

function formatFileViewerMotionScale(value: number) {
  return Number.isFinite(value) ? String(Number(value.toFixed(6))) : "1";
}

More from retab

Browse all components in this library →