shadimage
retab
uicode-viewersource: ok · hit

Code Viewer

A fixed-line code and log viewer with line numbers, custom virtualization, source-line highlights, retry, zoom, and download.

npx shadcn add "https://ui.retab.com/r/code-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

Registry dependencies

buttondropdown-menu@retab/scroll-area@retab/skeleton@retab/utils@retab/viewer-controls@retab/use-mount-effect@retab/use-keyed-layout-effect

Files32 · source cached 10/11/2026, 3:43:02 AM

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

import * as React from "react";

import type { ViewerResource } from "@/lib/viewer-resource";

import { CodeViewerFallback } from "./code-viewer-chrome";
import { CodeViewerContent } from "./code-viewer-content";
import type { CodeViewerHandle, CodeViewerProps } from "./code-viewer-types";
import { PlainTextViewerFrame } from "./plain-text-viewer-frame";

export type {
  CodeDocumentSource,
  CodeLineRange,
  CodeViewerHandle,
  CodeViewerProps,
} from "./code-viewer-types";

export type CodeResourceContentProps = Omit<CodeViewerProps, "source"> & {
  resource: ViewerResource;
};

export const CodeViewer = React.forwardRef<CodeViewerHandle, CodeViewerProps>(
  function CodeViewer(props, ref) {
    return (
      <PlainTextViewerFrame
        props={props}
        forwardedRef={ref}
        clientFallbackPolicy="always"
        contentResetPolicy="inline-retry"
        Fallback={CodeViewerFallback}
        Content={CodeViewerContent}
      />
    );
  },
);

export const CodeResourceContent = React.forwardRef<
  CodeViewerHandle,
  CodeResourceContentProps
>(function CodeResourceContent({ resource, ...props }, ref) {
  return (
    <PlainTextViewerFrame
      props={props}
      resource={resource}
      forwardedRef={ref}
      clientFallbackPolicy="always"
      contentResetPolicy="inline-retry"
      Fallback={CodeViewerFallback}
      Content={CodeViewerContent}
    />
  );
});
registry/new-york-v4/ui/code-viewer-chrome.tsx
"use client";

import * as React from "react";

import { type ViewerDownloadAction } from "@/lib/viewer-download-actions";

import { Skeleton } from "./skeleton";
import { TextCodeViewerFrame } from "./text-code-viewer-chrome";
import {
  ViewerControls,
  ViewerControlsSkeleton,
  type ViewerControlsState,
} from "./viewer-controls";

export function CodeViewerFrame({
  className,
  bare,
  children,
}: {
  className?: string;
  bare?: boolean;
  children: React.ReactNode;
}) {
  return (
    <TextCodeViewerFrame
      bare={bare}
      bareClassName="h-full bg-muted/20"
      className={className}
      dataSlot="code-viewer"
      framedClassName="rounded-xl border bg-muted/30"
    >
      {children}
    </TextCodeViewerFrame>
  );
}

export function CodeViewerControls({
  lineCount,
  fontScale,
  downloadAction,
  onZoomOut,
  onZoomIn,
  onResetZoom,
}: {
  lineCount: number;
  fontScale: number;
  downloadAction?: ViewerDownloadAction | null;
  onZoomOut: () => void;
  onZoomIn: () => void;
  onResetZoom: () => void;
}) {
  const controlsState = codeViewerControlsState({
    lineCount,
    fontScale,
    downloadAction,
    onZoomOut,
    onZoomIn,
    onResetZoom,
  });

  return (
    <ViewerControls
      title={controlsState.title}
      zoom={controlsState.zoom}
      downloads={controlsState.downloads}
    />
  );
}

export function codeViewerControlsState({
  lineCount,
  fontScale,
  downloadAction,
  onZoomOut,
  onZoomIn,
  onResetZoom,
}: {
  lineCount: number;
  fontScale: number;
  downloadAction?: ViewerDownloadAction | null;
  onZoomOut: () => void;
  onZoomIn: () => void;
  onResetZoom: () => void;
}): ViewerControlsState {
  return {
    title: `${lineCount} line${lineCount === 1 ? "" : "s"}`,
    zoom: {
      scale: fontScale,
      onZoomOut,
      onZoomIn,
      onFit: onResetZoom,
      fitLabel: "Reset zoom",
    },
    downloads: downloadAction ? [downloadAction] : [],
  };
}

export function CodeViewerFallback({
  className,
  controls = true,
  download = true,
  bare,
}: {
  className?: string;
  controls?: boolean;
  download?: boolean;
  bare?: boolean;
}) {
  return (
    <CodeViewerFrame className={className} bare={bare}>
      {controls ? (
        <ViewerControlsSkeleton title zoom download={download} />
      ) : null}
      <div
        className="min-h-0 flex-1 space-y-2 overflow-hidden p-4"
        data-slot="code-body-skeleton"
      >
        {Array.from({ length: 12 }, (_, index) => (
          <Skeleton
            key={index}
            className="h-4"
            style={{ width: `${40 + ((index * 13) % 55)}%` }}
          />
        ))}
      </div>
    </CodeViewerFrame>
  );
}
registry/new-york-v4/ui/text-code-viewer-chrome.tsx
"use client";

import * as React from "react";

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

export function TextCodeViewerFrame({
  bare,
  bareClassName,
  children,
  className,
  dataSlot,
  framedClassName,
}: {
  bare?: boolean;
  bareClassName: string;
  children: React.ReactNode;
  className?: string;
  dataSlot: string;
  framedClassName: string;
}) {
  return (
    <div
      className={cn(
        "flex min-h-0 flex-col overflow-hidden",
        bare ? bareClassName : framedClassName,
        className,
      )}
      data-slot={dataSlot}
    >
      {children}
    </div>
  );
}
registry/new-york-v4/ui/plain-text-viewer-frame.tsx
"use client";

import * as React from "react";

import {
  isResourceError,
  isViewerFormatError,
  isViewerStateError,
  isViewerUnsupportedError,
  ViewerFormatError,
} from "@/lib/viewer-errors";
import {
  createViewerResource,
  viewerContentRenderKey,
  viewerResourceRenderKey,
  type ViewerResource,
} from "@/lib/viewer-resource";
import type {
  BlobViewerSource,
  TextSource,
  UrlViewerSource,
} from "@/lib/viewer-source";

import {
  DEFAULT_MAX_BYTES,
  DEFAULT_MAX_LINES,
  type TextViewerBounds,
} from "./plain-text-resource";
import { useIsClient } from "./use-is-client";
import { ViewerErrorBoundary } from "./viewer-error";

type TextResourceSource = UrlViewerSource | BlobViewerSource | TextSource;
type ClientFallbackPolicy = "always" | "non-inline-source";
type ContentResetPolicy = "content" | "inline-retry";

export interface PlainTextViewerFrameProps<
  THandle,
  TProps extends PlainTextViewerFramePublicProps,
> {
  props: TProps;
  resource?: ViewerResource;
  forwardedRef: React.ForwardedRef<THandle>;
  clientFallbackPolicy: ClientFallbackPolicy;
  contentResetPolicy?: ContentResetPolicy;
  Fallback: React.ComponentType<PlainTextViewerFallbackProps>;
  Content: React.ComponentType<
    TProps & {
      resource: ViewerResource;
      retryVersion: number;
      forwardedRef?: React.ForwardedRef<THandle>;
    }
  >;
}

export interface PlainTextViewerFramePublicProps extends TextViewerBounds {
  source?: TextResourceSource;
  className?: string;
  controls?: boolean;
  download?: boolean;
  bare?: boolean;
}

export interface PlainTextViewerFallbackProps {
  className?: string;
  controls?: boolean;
  download?: boolean;
  bare?: boolean;
}

export function PlainTextViewerFrame<
  THandle,
  TProps extends PlainTextViewerFramePublicProps,
>({
  props,
  resource: resourceProp,
  forwardedRef,
  clientFallbackPolicy,
  contentResetPolicy = "content",
  Fallback,
  Content,
}: PlainTextViewerFrameProps<THandle, TProps>) {
  const [retryState, setRetryState] = React.useState({
    contentKey: "",
    version: 0,
  });
  const isClient = useIsClient();
  const { source } = props;
  const createdResource = React.useMemo(
    () => (source ? createViewerResource(source) : null),
    [source],
  );
  const resource = resourceProp ?? createdResource;
  if (!resource) {
    throw new Error("PlainTextViewerFrame requires a source or resource.");
  }
  const contentBaseKey = plainTextViewerContentBaseKey(resource, props);
  const retryVersion =
    retryState.contentKey === contentBaseKey ? retryState.version : 0;
  const resetKey = plainTextViewerResetKey(resource, props, retryVersion);
  const contentResetKey = plainTextViewerContentResetKey(
    contentBaseKey,
    retryVersion,
  );
  const suspenseResetKey =
    contentResetPolicy === "inline-retry" && resource.sourceKind === "text"
      ? String(retryVersion)
      : contentResetKey;

  if (
    !isClient &&
    shouldRenderClientFallback(clientFallbackPolicy, resource.sourceKind)
  ) {
    return (
      <Fallback
        className={props.className}
        controls={props.controls}
        download={props.download}
        bare={props.bare}
      />
    );
  }

  return (
    <ViewerErrorBoundary
      bare={props.bare}
      className={props.className}
      download={
        props.controls === false || props.download === false
          ? null
          : plainTextViewerDownloadAction(resource)
      }
      format="text"
      mapError={plainTextViewerBoundaryError}
      resetKey={resetKey}
      sourceKind={resource.sourceKind}
      onRetry={() =>
        setRetryState((state) => ({
          contentKey: contentBaseKey,
          version: state.contentKey === contentBaseKey ? state.version + 1 : 1,
        }))
      }
    >
      <React.Suspense
        key={suspenseResetKey}
        fallback={
          <Fallback
            className={props.className}
            controls={props.controls}
            download={props.download}
            bare={props.bare}
          />
        }
      >
        <Content
          {...props}
          forwardedRef={forwardedRef}
          retryVersion={retryVersion}
          resource={resource}
        />
      </React.Suspense>
    </ViewerErrorBoundary>
  );
}

function plainTextViewerDownloadAction(resource: ViewerResource) {
  return resource.originalDownload;
}

function plainTextViewerBoundaryError(error: unknown) {
  if (
    isResourceError(error) ||
    isViewerFormatError(error) ||
    isViewerStateError(error) ||
    isViewerUnsupportedError(error)
  ) {
    return error;
  }

  return new ViewerFormatError({
    format: "text",
    kind: "render_failed",
    message: "Failed to render text.",
    cause: error,
  });
}

function plainTextViewerResetKey(
  resource: ViewerResource,
  props: Pick<PlainTextViewerFramePublicProps, "maxBytes" | "maxLines">,
  retryVersion: number,
): string {
  const [maxBytesKey, maxLinesKey] = plainTextViewerBoundsResetKey(props);
  return [
    viewerResourceRenderKey(resource),
    retryVersion,
    maxBytesKey,
    maxLinesKey,
  ].join("\u0000");
}

function plainTextViewerContentResetKey(
  contentBaseKey: string,
  retryVersion: number,
): string {
  return [contentBaseKey, retryVersion].join("\u0000");
}

function plainTextViewerContentBaseKey(
  resource: ViewerResource,
  props: Pick<PlainTextViewerFramePublicProps, "maxBytes" | "maxLines">,
): string {
  const [maxBytesKey, maxLinesKey] = plainTextViewerBoundsResetKey(props);
  return [
    viewerContentRenderKey(resource.content),
    maxBytesKey,
    maxLinesKey,
  ].join("\u0000");
}

function plainTextViewerBoundsResetKey(
  props: Pick<PlainTextViewerFramePublicProps, "maxBytes" | "maxLines">,
) {
  return [
    plainTextViewerBoundResetKeyPart(props.maxBytes, DEFAULT_MAX_BYTES),
    plainTextViewerBoundResetKeyPart(props.maxLines, DEFAULT_MAX_LINES),
  ] as const;
}

function plainTextViewerBoundResetKeyPart(
  value: number | undefined,
  defaultValue: number,
) {
  return String(value === undefined ? defaultValue : value);
}

function shouldRenderClientFallback(
  policy: ClientFallbackPolicy,
  sourceKind: TextResourceSource["kind"],
) {
  return policy === "always" || sourceKind !== "text";
}
registry/new-york-v4/ui/code-viewer-content.tsx
"use client";

import * as React from "react";

import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { useMountEffect } from "@/hooks/use-mount-effect";
import type { ViewerResource } from "@/lib/viewer-resource";

import {
  CodeViewerControls,
  codeViewerControlsState,
  CodeViewerFrame,
} from "./code-viewer-chrome";
import { scrollTopForLineRangeMetrics } from "./code-viewer-layout";
import { getCodeLongLineSelectionText } from "./code-viewer-long-lines";
import { useCodeProjectionScheduler } from "./code-viewer-projection-scheduler";
import { createCodeProjector } from "./code-viewer-projector";
import {
  clampCodeViewerScale,
  CODE_VIEWER_BASE_LINE_PX,
  CODE_VIEWER_BLOCK_PADDING,
} from "./code-viewer-scale";
import { createCodeSyntax } from "./code-viewer-syntax";
import { useCodeViewerSyntaxStyle } from "./code-viewer-syntax-style";
import type {
  CodeLineRange,
  CodeViewerHandle,
  CodeViewerProps,
} from "./code-viewer-types";
import { CodeViewerViewport } from "./code-viewer-viewport";
import { normalizeTextLineRange } from "./line-ranges";
import {
  readTextDocument,
  resolvedTextViewerBounds,
} from "./plain-text-resource";
import { useViewerControlsRegistration } from "./viewer-controls";
import { joinEffectKey } from "@/lib/effect-key";

type CodeReadingAnchor = {
  lineIndex: number;
  offsetPx: number;
};

type NativeFindCodeChunk = {
  endLine: number;
  startLine: number;
  text: string;
};

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

const CODE_VIEWER_DEFERRED_SYNTAX_LINE_COUNT = 500;
const CODE_VIEWER_NATIVE_FIND_CHUNK_SIZE = 128;

type CodeViewerContentProps = Omit<CodeViewerProps, "source"> & {
  resource: ViewerResource;
  retryVersion: number;
  forwardedRef?: React.ForwardedRef<CodeViewerHandle>;
};

export function CodeViewerContent({
  resource,
  className,
  controls = true,
  download = true,
  highlight,
  bare = false,
  maxBytes,
  maxLines,
  retryVersion,
  forwardedRef,
}: CodeViewerContentProps) {
  const bounds = resolvedTextViewerBounds({ maxBytes, maxLines });
  const textDocument = readTextDocument({
    content: resource.content,
    retryVersion,
    bounds,
  });
  const textLines = textDocument.lines;
  const [syntaxVersion, setSyntaxVersion] = React.useState(0);
  const syntax = React.useMemo(
    () =>
      createCodeSyntax(resource, {
        deferTokens: textLines.length > CODE_VIEWER_DEFERRED_SYNTAX_LINE_COUNT,
        onTokensChanged: () => setSyntaxVersion((version) => version + 1),
      }),
    [resource, textLines.length],
  );
  const syntaxIdentity =
    syntaxVersion === 0
      ? syntax.identity
      : `${syntax.identity}\u0000${syntaxVersion}`;
  const highlightStart = highlight?.start;
  const highlightEnd = highlight?.end;
  const highlightRange = React.useMemo(
    () =>
      normalizeTextLineRange(
        highlightStart == null || highlightEnd == null
          ? null
          : { start: highlightStart, end: highlightEnd },
        textLines.length,
      ),
    [highlightStart, highlightEnd, textLines.length],
  );
  const downloadAction = download ? resource.originalDownload : null;

  const [fontScale, setFontScale] = React.useState(1);
  const viewportRef = React.useRef<HTMLDivElement | null>(null);
  const rowHostRef = React.useRef<HTMLPreElement | null>(null);
  const pendingScrollAnchorRef = React.useRef<CodeReadingAnchor | null>(null);
  const projector = React.useMemo(() => createCodeProjector(), []);
  const lineHeight = CODE_VIEWER_BASE_LINE_PX * fontScale;
  const contentIdentity = React.useMemo(
    () =>
      codeContentIdentity({
        contentKey: resource.content.key,
        maxBytes: bounds.maxBytes,
        maxLines: bounds.maxLines,
        retryVersion,
      }),
    [bounds.maxBytes, bounds.maxLines, resource.content.key, retryVersion],
  );
  const gutterWidth = `calc(${String(textLines.length).length + 1}ch + 1.25rem)`;
  const layoutIdentity = codeLayoutIdentity({ gutterWidth, lineHeight });

  useCodeViewerSyntaxStyle();

  const commitFontScale = React.useCallback(
    (nextScale: number) => {
      const clampedScale = clampCodeViewerScale(nextScale);
      if (clampedScale === fontScale) return;

      pendingScrollAnchorRef.current = captureCodeReadingAnchor({
        lineCount: textLines.length,
        lineHeight,
        projector,
        viewportElement: viewportRef.current,
      });
      setFontScale(clampedScale);
    },
    [fontScale, lineHeight, projector, textLines.length],
  );

  const zoom = React.useCallback(
    (factor: number) => commitFontScale(fontScale * factor),
    [commitFontScale, fontScale],
  );
  const onZoomOut = React.useCallback(() => zoom(1 / 1.2), [zoom]);
  const onZoomIn = React.useCallback(() => zoom(1.2), [zoom]);
  const onResetZoom = React.useCallback(
    () => commitFontScale(1),
    [commitFontScale],
  );

  useKeyedMountEffect(
    joinEffectKey(["code-anchor", lineHeight, textLines.length]),
    () => {
      const anchor = pendingScrollAnchorRef.current;
      const viewportElement = viewportRef.current;
      if (!anchor || !viewportElement) return;

      pendingScrollAnchorRef.current = null;
      projector.scrollToLogical({
        lineCount: textLines.length,
        lineHeight,
        logicalScrollTop: restoreCodeReadingAnchor({
          anchor,
          lineCount: textLines.length,
          lineHeight,
        }),
        viewport: viewportElement,
      });
    },
  );

  const scrollLineRange = React.useCallback(
    (range: CodeLineRange | null, options?: ScrollToOptions) => {
      const viewportElement = viewportRef.current;
      const normalizedRange = normalizeTextLineRange(range, textLines.length);
      if (!viewportElement || !normalizedRange) return;

      projector.scrollToLogical({
        behavior: options?.behavior ?? "smooth",
        lineCount: textLines.length,
        lineHeight,
        logicalScrollTop: scrollTopForLineRangeMetrics({
          startLine: normalizedRange.start,
          endLine: normalizedRange.end,
          lineHeight,
          paddingStart: CODE_VIEWER_BLOCK_PADDING,
          viewportHeight: viewportElement.clientHeight,
        }),
        viewport: viewportElement,
      });
    },
    [lineHeight, projector, textLines.length],
  );

  React.useImperativeHandle(
    forwardedRef ?? null,
    () => ({
      scrollToLineRange: scrollLineRange,
      getViewportElement: () => viewportRef.current,
    }),
    [scrollLineRange],
  );

  useKeyedMountEffect(
    joinEffectKey(["code-highlight-scroll", highlightRange, scrollLineRange]),
    () => {
      if (!highlightRange) return;
      scrollLineRange(highlightRange, { behavior: "smooth" });
    },
  );

  const project = React.useCallback(() => {
    const rowHost = rowHostRef.current;
    const viewport = viewportRef.current;
    if (!rowHost || !viewport) return false;

    return projector.project({
      contentIdentity,
      gutterWidth,
      highlightRange,
      layoutIdentity,
      lineHeight,
      rowHost,
      syntax,
      syntaxIdentity,
      textLines,
      viewport,
    });
  }, [
    contentIdentity,
    gutterWidth,
    highlightRange,
    layoutIdentity,
    lineHeight,
    projector,
    syntax,
    syntaxIdentity,
    textLines,
  ]);

  const copyLongLineSelection = React.useCallback(
    (event: React.ClipboardEvent<HTMLPreElement>) => {
      const rowHost = rowHostRef.current;
      if (!rowHost) return;

      const selectedText = getCodeLongLineSelectionText({
        rowHost,
        selection: window.getSelection(),
        textLines,
      });
      if (selectedText == null) return;

      event.clipboardData.setData("text/plain", selectedText);
      event.preventDefault();
    },
    [textLines],
  );

  useCodeProjectionScheduler({
    project,
    rowHostRef,
    viewportRef,
  });

  useMountEffect(() => () => projector.destroy());
  useKeyedMountEffect(joinEffectKey(["code-syntax", syntax]), () => {
    setSyntaxVersion(0);
    return () => syntax.destroy?.();
  });

  useCodeControlsRegistration({
    downloadAction,
    fontScale,
    lineCount: textLines.length,
    onResetZoom,
    onZoomIn,
    onZoomOut,
  });

  return (
    <CodeViewerFrame className={className} bare={bare}>
      {controls ? (
        <CodeViewerControls
          lineCount={textLines.length}
          fontScale={fontScale}
          downloadAction={downloadAction}
          onZoomOut={onZoomOut}
          onZoomIn={onZoomIn}
          onResetZoom={onResetZoom}
        />
      ) : null}
      <DeferredNativeFindIndex
        lineCount={textLines.length}
        lines={textLines}
        scrollToLineRange={scrollLineRange}
      />
      <CodeViewerViewport
        fontScale={fontScale}
        gutterWidth={gutterWidth}
        lineCount={textLines.length}
        lineHeight={lineHeight}
        onCopy={copyLongLineSelection}
        rowHostRef={rowHostRef}
        viewportRef={viewportRef}
      />
    </CodeViewerFrame>
  );
}

function DeferredNativeFindIndex({
  lines,
  lineCount,
  scrollToLineRange,
}: {
  lines: readonly string[];
  lineCount: number;
  scrollToLineRange: (
    range: CodeLineRange | null,
    options?: ScrollToOptions,
  ) => void;
}) {
  const [isReady, setIsReady] = React.useState(false);

  useKeyedMountEffect(joinEffectKey(["code-native-find", lines]), () => {
    setIsReady(false);
    const show = () => setIsReady(true);
    if (typeof window === "undefined") return;
    const browserWindow = window as CodeIdleWindow;
    if (browserWindow.requestIdleCallback && browserWindow.cancelIdleCallback) {
      const idleId = browserWindow.requestIdleCallback(show, { timeout: 400 });
      return () => browserWindow.cancelIdleCallback?.(idleId);
    }
    const timeoutId = browserWindow.setTimeout(show, 80);
    return () => browserWindow.clearTimeout(timeoutId);
  });

  if (!isReady) return null;
  return (
    <NativeFindIndex
      lineCount={lineCount}
      lines={lines}
      scrollToLineRange={scrollToLineRange}
    />
  );
}

function NativeFindIndex({
  lines,
  lineCount,
  scrollToLineRange,
}: {
  lines: readonly string[];
  lineCount: number;
  scrollToLineRange: (
    range: CodeLineRange | null,
    options?: ScrollToOptions,
  ) => void;
}) {
  const entries = React.useMemo(() => chunkNativeFindLines(lines), [lines]);

  return (
    <div
      aria-hidden="true"
      className="pointer-events-none h-0 w-0 overflow-hidden opacity-0"
      data-native-find-indexed-chunks={entries.length}
      data-native-find-indexed-lines={lines.length}
      data-slot="code-native-find-index"
    >
      {entries.map((entry) => (
        <NativeFindEntry
          key={entry.startLine}
          entry={entry}
          lineCount={lineCount}
          scrollToLineRange={scrollToLineRange}
        />
      ))}
    </div>
  );
}

function NativeFindEntry({
  entry,
  lineCount,
  scrollToLineRange,
}: {
  entry: NativeFindCodeChunk;
  lineCount: number;
  scrollToLineRange: (
    range: CodeLineRange | null,
    options?: ScrollToOptions,
  ) => void;
}) {
  const ref = React.useRef<HTMLSpanElement | null>(null);

  useKeyedLayoutEffect(
    joinEffectKey([
      "code-native-find-entry",
      entry.startLine,
      entry.endLine,
      lineCount,
      scrollToLineRange,
    ]),
    () => {
      const element = ref.current;
      if (!element) return;
      element.setAttribute("hidden", "until-found");

      const handleBeforeMatch = () => {
        scrollToLineRange(
          normalizeTextLineRange(
            {
              end: entry.endLine,
              start: entry.startLine,
            },
            lineCount,
          ),
          { behavior: "auto" },
        );
        if (typeof requestAnimationFrame === "function") {
          requestAnimationFrame(() => {
            element.setAttribute("hidden", "until-found");
          });
          return;
        }
        element.setAttribute("hidden", "until-found");
      };

      element.addEventListener("beforematch", handleBeforeMatch);
      return () => {
        element.removeEventListener("beforematch", handleBeforeMatch);
      };
    },
  );

  return (
    <span
      ref={ref}
      className="block h-px w-px overflow-hidden whitespace-pre"
      data-native-find-end-line={entry.endLine}
      data-native-find-start-line={entry.startLine}
    >
      {entry.text || " "}
    </span>
  );
}

function chunkNativeFindLines(lines: readonly string[]): NativeFindCodeChunk[] {
  const chunks: NativeFindCodeChunk[] = [];
  for (
    let startIndex = 0;
    startIndex < lines.length;
    startIndex += CODE_VIEWER_NATIVE_FIND_CHUNK_SIZE
  ) {
    const endIndex = Math.min(
      lines.length,
      startIndex + CODE_VIEWER_NATIVE_FIND_CHUNK_SIZE,
    );
    chunks.push({
      endLine: endIndex,
      startLine: startIndex + 1,
      text: lines.slice(startIndex, endIndex).join("\n"),
    });
  }
  return chunks;
}

function useCodeControlsRegistration({
  downloadAction,
  fontScale,
  lineCount,
  onResetZoom,
  onZoomIn,
  onZoomOut,
}: {
  downloadAction: ViewerResource["originalDownload"] | null;
  fontScale: number;
  lineCount: number;
  onResetZoom: () => void;
  onZoomIn: () => void;
  onZoomOut: () => void;
}) {
  const onControlsChange = useViewerControlsRegistration();
  const controlsState = React.useMemo(
    () =>
      codeViewerControlsState({
        downloadAction,
        fontScale,
        lineCount,
        onResetZoom,
        onZoomIn,
        onZoomOut,
      }),
    [downloadAction, fontScale, lineCount, onResetZoom, onZoomIn, onZoomOut],
  );

  useKeyedMountEffect(
    joinEffectKey(["code-controls", onControlsChange, controlsState]),
    () => {
      if (!onControlsChange) return;
      onControlsChange(controlsState);
      return () => onControlsChange(null);
    },
  );
}

function captureCodeReadingAnchor({
  lineCount,
  lineHeight,
  projector,
  viewportElement,
}: {
  lineCount: number;
  lineHeight: number;
  projector: ReturnType<typeof createCodeProjector>;
  viewportElement: HTMLDivElement | null;
}): CodeReadingAnchor | null {
  if (!viewportElement || lineCount <= 0 || lineHeight <= 0) return null;

  const scrollTop = projector.getLogicalScrollTop({
    lineCount,
    lineHeight,
    viewport: viewportElement,
  });
  const contentTop = Math.max(0, scrollTop - CODE_VIEWER_BLOCK_PADDING);
  const lineIndex = Math.min(
    lineCount - 1,
    Math.max(0, Math.floor(contentTop / lineHeight)),
  );

  return {
    lineIndex,
    offsetPx: Math.max(0, contentTop - lineIndex * lineHeight),
  };
}

function restoreCodeReadingAnchor({
  anchor,
  lineCount,
  lineHeight,
}: {
  anchor: CodeReadingAnchor;
  lineCount: number;
  lineHeight: number;
}) {
  if (lineCount <= 0 || lineHeight <= 0) return 0;

  const lineIndex = Math.min(lineCount - 1, Math.max(0, anchor.lineIndex));
  return (
    CODE_VIEWER_BLOCK_PADDING +
    lineIndex * lineHeight +
    Math.min(anchor.offsetPx, Math.max(0, lineHeight - 1))
  );
}

function codeContentIdentity({
  contentKey,
  maxBytes,
  maxLines,
  retryVersion,
}: {
  contentKey: string;
  maxBytes: number;
  maxLines: number;
  retryVersion: number;
}) {
  return [contentKey, retryVersion, maxBytes, maxLines].join("\u0000");
}

function codeLayoutIdentity({
  gutterWidth,
  lineHeight,
}: {
  gutterWidth: string;
  lineHeight: number;
}) {
  return [lineHeight, gutterWidth].join("\u0000");
}
registry/new-york-v4/ui/code-viewer-projector.ts
import {
  CODE_VIEWER_BLOCK_PADDING,
  CODE_VIEWER_INITIAL_VIEWPORT_HEIGHT,
  CODE_VIEWER_OVERSCAN_PX,
} from "./code-viewer-scale";
import type { CodeSyntax, CodeTokenLeaf } from "./code-viewer-syntax";
import {
  getCodeLogicalScrollTop,
  getCodePagedLayoutTop,
  getCodePhysicalScrollSize,
  getCodeVirtualLineWindow,
  getCodeVirtualTotalSize,
  resolveCodePhysicalScrollPosition,
  type CodeVirtualLine,
} from "./code-viewer-virtualization";
import { getCodeLineRenderText } from "./code-viewer-long-lines";
import { isLineInRange, type NormalizedTextLineRange } from "./line-ranges";

// Opaque fills mixed from the theme's opaque `--foreground`/`--background`
// tokens (the `--muted`/`--accent` tokens are alpha-based, so they cannot mask
// scrolled content). Shared with the viewport's full-height gutter rail.
export const CODE_GUTTER_BACKGROUND =
  "color-mix(in oklab, var(--foreground) 3%, var(--background))";
const CODE_HIGHLIGHT_BACKGROUND =
  "color-mix(in oklab, var(--foreground) 8%, var(--background))";
const CODE_HIGHLIGHT_ACCENT_SHADOW = "inset 2px 0 0 0 var(--primary)";
const CODE_HIGHLIGHT_NUMBER_COLOR =
  "color-mix(in oklab, var(--foreground) 70%, transparent)";

export type CodeProjectionIdentity = {
  contentIdentity: string;
  layoutIdentity: string;
  syntaxIdentity: string;
};

export type CodeProjectionInput = CodeProjectionIdentity & {
  rowHost: HTMLPreElement;
  viewport: HTMLDivElement;
  textLines: readonly string[];
  lineHeight: number;
  gutterWidth: string;
  highlightRange: NormalizedTextLineRange | null;
  syntax: CodeSyntax;
};

export type CodeProjector = {
  getLogicalScrollTop(input: CodeProjectionScrollInput): number;
  project(input: CodeProjectionInput): boolean;
  scrollToLogical(input: CodeProjectionScrollToInput): void;
  destroy(): void;
};

export type CodeProjectionScrollInput = {
  lineCount: number;
  lineHeight: number;
  viewport: HTMLDivElement;
};

export type CodeProjectionScrollToInput = CodeProjectionScrollInput & {
  behavior?: ScrollBehavior;
  logicalScrollTop: number;
};

export type CodeProjectionMetrics = {
  contentPatches: number;
  layoutPatches: number;
  noops: number;
  projections: number;
  rowsCreated: number;
  rowsRemoved: number;
  rowsReused: number;
  tokenSpanRebuilds: number;
  visibleEnd: number;
  visibleStart: number;
};

export type CodeProjectorOptions = {
  metrics?: CodeProjectionMetrics;
};

type CodeRowCache = {
  contentIdentity: string;
  contentSpan: HTMLSpanElement;
  gutterSpan: HTMLSpanElement;
  layoutIdentity: string;
  row: HTMLDivElement;
};

type VisibleRange = {
  end: number;
  start: number;
};

type LastProjection = CodeProjectionIdentity & {
  highlightIdentity: string;
  horizontalScrollLeft: number;
  renderedWindowHeight: number;
  renderedWindowStickyOffset: number;
  renderedWindowTop: number;
  logicalScrollTop: number;
  scrollPageOffset: number;
  totalHeight: string;
  visibleEnd: number;
  visibleStart: number;
};

type CodeRenderedWindow = {
  height: number;
  rowOffset: number;
  stickyOffset: number;
  top: number;
};

const MAX_RECYCLED_CODE_ROWS = 512;

export function createCodeProjectionMetrics(): CodeProjectionMetrics {
  return {
    contentPatches: 0,
    layoutPatches: 0,
    noops: 0,
    projections: 0,
    rowsCreated: 0,
    rowsRemoved: 0,
    rowsReused: 0,
    tokenSpanRebuilds: 0,
    visibleEnd: 0,
    visibleStart: 0,
  };
}

export function createCodeProjector(
  options: CodeProjectorOptions = {},
): CodeProjector {
  let identity: CodeProjectionIdentity | null = null;
  let lastProjection: LastProjection | null = null;
  let metrics = options.metrics;
  let rowHost: HTMLPreElement | null = null;
  let recycledRows: CodeRowCache[] = [];
  const rowsByLineIndex = new Map<number, CodeRowCache>();
  let scrollPageOffset = 0;
  let visibleRange: VisibleRange | null = null;

  return {
    getLogicalScrollTop(input) {
      return getCodeLogicalScrollTop({
        physicalScrollTop: input.viewport.scrollTop,
        scrollPageOffset,
        totalSize: codeTotalSize(input),
        viewportHeight: codeViewportHeight(input.viewport),
      });
    },
    project(input) {
      metrics = options.metrics;
      incrementMetric(metrics, "projections");

      if (rowHost !== input.rowHost) {
        clearRows();
        rowHost = input.rowHost;
      }

      const nextIdentity = codeProjectionIdentity(input);
      if (
        !identity ||
        identity.contentIdentity !== nextIdentity.contentIdentity
      ) {
        identity = nextIdentity;
        clearRows();
      } else {
        identity = nextIdentity;
      }

      const totalSize = codeTotalSize({
        lineCount: input.textLines.length,
        lineHeight: input.lineHeight,
        viewport: input.viewport,
      });
      const viewportHeight = codeViewportHeight(input.viewport);
      const previousProjection = lastProjection;
      const logicalScrollTop = getCodeLogicalScrollTop({
        physicalScrollTop: input.viewport.scrollTop,
        scrollPageOffset,
        totalSize,
        viewportHeight,
      });
      const physicalScrollPosition = resolveCodePhysicalScrollPosition({
        logicalScrollTop,
        scrollPageOffset,
        totalSize,
        viewportHeight,
      });
      const previousScrollPageOffset = scrollPageOffset;
      scrollPageOffset = physicalScrollPosition.scrollPageOffset;

      if (
        physicalScrollPosition.physicalScrollTop !== input.viewport.scrollTop
      ) {
        input.viewport.scrollTop = physicalScrollPosition.physicalScrollTop;
      }

      const physicalTotalSize = getCodePhysicalScrollSize({
        totalSize,
        viewportHeight,
      });
      const totalHeight = `${physicalTotalSize}px`;
      const fitPerfectly = shouldFitCodePerfectly({
        previousProjection,
        logicalScrollTop,
        viewportHeight,
      });

      const virtualWindow = getCodeVirtualLineWindow({
        lineCount: input.textLines.length,
        lineHeight: input.lineHeight,
        overscanPx: fitPerfectly
          ? getCodeFitPerfectlyOverscanPx(input.lineHeight)
          : CODE_VIEWER_OVERSCAN_PX,
        paddingStart: CODE_VIEWER_BLOCK_PADDING,
        scrollTop: logicalScrollTop,
        viewportHeight,
      });
      const visibleLines = virtualWindow.lines;
      const renderedWindow = codeRenderedWindow({
        physicalTotalSize,
        rowHost: input.rowHost,
        scrollPageOffset,
        totalSize,
        viewportHeight,
        visibleLines,
      });
      const nextVisibleRange = codeVisibleRange(visibleLines);
      const nextProjection = codeLastProjection({
        input,
        logicalScrollTop,
        renderedWindow,
        scrollPageOffset,
        totalHeight,
        visibleRange: nextVisibleRange,
      });
      if (
        lastProjection &&
        isSameCodeProjection(lastProjection, nextProjection) &&
        isRenderedDomValid(nextVisibleRange)
      ) {
        incrementMetric(metrics, "noops");
        return false;
      }
      lastProjection = nextProjection;
      setMetric(metrics, "visibleStart", nextVisibleRange.start);
      setMetric(metrics, "visibleEnd", nextVisibleRange.end);

      syncCodeScrollLayers({
        renderedWindow,
        rowHost: input.rowHost,
        totalHeight,
      });

      syncVisibleRows({
        input,
        nextVisibleRange,
        previousScrollPageOffset,
        renderedWindow,
        totalSize,
        viewportHeight,
        visibleLines,
      });
      visibleRange = nextVisibleRange;
      return fitPerfectly;
    },
    scrollToLogical(input) {
      const totalSize = codeTotalSize(input);
      const viewportHeight = codeViewportHeight(input.viewport);
      const physicalScrollPosition = resolveCodePhysicalScrollPosition({
        logicalScrollTop: input.logicalScrollTop,
        scrollPageOffset,
        totalSize,
        viewportHeight,
      });
      scrollPageOffset = physicalScrollPosition.scrollPageOffset;
      if (typeof input.viewport.scrollTo === "function") {
        input.viewport.scrollTo({
          top: physicalScrollPosition.physicalScrollTop,
          behavior: input.behavior,
        });
      } else {
        input.viewport.scrollTop = physicalScrollPosition.physicalScrollTop;
      }
    },
    destroy() {
      clearRows();
      recycledRows = [];
      rowHost = null;
      identity = null;
      scrollPageOffset = 0;
    },
  };

  function syncVisibleRows({
    input,
    nextVisibleRange,
    previousScrollPageOffset,
    renderedWindow,
    totalSize,
    viewportHeight,
    visibleLines,
  }: {
    input: CodeProjectionInput;
    nextVisibleRange: VisibleRange;
    previousScrollPageOffset: number;
    renderedWindow: CodeRenderedWindow;
    totalSize: number;
    viewportHeight: number;
    visibleLines: readonly CodeVirtualLine[];
  }) {
    if (
      previousScrollPageOffset === scrollPageOffset &&
      applyPartialRender({
        input,
        nextVisibleRange,
        renderedWindow,
        totalSize,
        viewportHeight,
        visibleLines,
      })
    ) {
      return;
    }

    clearMountedRows();
    syncVisibleRowOrder({
      input,
      renderedWindow,
      totalSize,
      viewportHeight,
      visibleLines,
    });
  }

  function syncVisibleRowOrder({
    input,
    renderedWindow,
    totalSize,
    viewportHeight,
    visibleLines,
  }: {
    input: CodeProjectionInput;
    renderedWindow: CodeRenderedWindow;
    totalSize: number;
    viewportHeight: number;
    visibleLines: readonly CodeVirtualLine[];
  }) {
    let cursor = input.rowHost.firstChild;

    for (const visibleLine of visibleLines) {
      const row = prepareCodeRow({
        input,
        renderedWindow,
        totalSize,
        viewportHeight,
        visibleLine,
      });
      if (row !== cursor) {
        input.rowHost.insertBefore(row, cursor);
      }
      cursor = row.nextSibling;
    }

    while (cursor) {
      const nextCursor = cursor.nextSibling;
      cursor.parentNode?.removeChild(cursor);
      cursor = nextCursor;
    }
  }

  function applyPartialRender({
    input,
    nextVisibleRange,
    renderedWindow,
    totalSize,
    viewportHeight,
    visibleLines,
  }: {
    input: CodeProjectionInput;
    nextVisibleRange: VisibleRange;
    renderedWindow: CodeRenderedWindow;
    totalSize: number;
    viewportHeight: number;
    visibleLines: readonly CodeVirtualLine[];
  }): boolean {
    const previousRange = visibleRange;
    if (!previousRange) return false;
    if (!isRenderedDomValid(previousRange)) return false;

    const overlapStart = Math.max(previousRange.start, nextVisibleRange.start);
    const overlapEnd = Math.min(previousRange.end, nextVisibleRange.end);
    if (overlapStart >= overlapEnd) {
      return false;
    }

    removeCodeRowRange(previousRange.start, overlapStart);
    removeCodeRowRange(overlapEnd, previousRange.end);

    syncVisibleRowOrder({
      input,
      renderedWindow,
      totalSize,
      viewportHeight,
      visibleLines,
    });
    return true;
  }

  function clearRows() {
    for (const row of rowsByLineIndex.values()) {
      recycleCodeRow(row);
    }
    rowHost?.replaceChildren();
    rowsByLineIndex.clear();
    lastProjection = null;
    visibleRange = null;
  }

  function clearMountedRows() {
    if (!visibleRange) return;
    removeCodeRowRange(visibleRange.start, visibleRange.end);
    visibleRange = null;
  }

  function removeCodeRowRange(start: number, end: number) {
    for (
      let index = Math.max(0, start);
      index < Math.max(start, end);
      index++
    ) {
      const row = rowsByLineIndex.get(index);
      if (!row) continue;
      row.row.remove();
      incrementMetric(metrics, "rowsRemoved");
      recycleCodeRow(row);
      rowsByLineIndex.delete(index);
    }
  }

  function isRenderedDomValid(range: VisibleRange) {
    if (!rowHost) return false;
    const expectedLength = Math.max(0, range.end - range.start);
    if (rowHost.children.length !== expectedLength) return false;

    for (let offset = 0; offset < expectedLength; offset += 1) {
      const index = range.start + offset;
      const element = rowHost.children[offset];
      const row = rowsByLineIndex.get(index);
      if (!(element instanceof HTMLDivElement) || row?.row !== element) {
        return false;
      }
      if (element.dataset.lineIndex !== String(index)) {
        return false;
      }
    }
    return true;
  }

  function recycleCodeRow(row: CodeRowCache) {
    row.contentIdentity = "";
    row.layoutIdentity = "";
    if (recycledRows.length < MAX_RECYCLED_CODE_ROWS) {
      recycledRows.push(row);
    }
  }

  function prepareCodeRow({
    input,
    renderedWindow,
    totalSize,
    viewportHeight,
    visibleLine,
  }: {
    input: CodeProjectionInput;
    renderedWindow: CodeRenderedWindow;
    totalSize: number;
    viewportHeight: number;
    visibleLine: CodeVirtualLine;
  }): HTMLDivElement {
    const lineNumber = visibleLine.index + 1;
    const text = input.textLines[visibleLine.index] ?? "";
    const isHighlighted = isLineInRange(lineNumber, input.highlightRange);
    const layoutIdentity = [
      lineNumber,
      input.layoutIdentity,
      isHighlighted ? "highlighted" : "",
    ].join("\u0000");
    const contentIdentity = codeRowContentIdentity({
      syntax: input.syntax,
      text,
    });

    let row = rowsByLineIndex.get(visibleLine.index);
    if (!row) {
      row = recycledRows.pop();
      if (row) {
        incrementMetric(metrics, "rowsReused");
      } else {
        row = createCodeRow();
        incrementMetric(metrics, "rowsCreated");
      }
      rowsByLineIndex.set(visibleLine.index, row);
    }

    setStyleValue(row.row.style, "height", `${visibleLine.size}px`);
    setStyleValue(
      row.row.style,
      "transform",
      `translateY(${
        getCodePagedLayoutTop({
          logicalTop: visibleLine.start,
          scrollPageOffset,
          totalSize,
          viewportHeight,
        }) - renderedWindow.rowOffset
      }px)`,
    );

    if (row.layoutIdentity !== layoutIdentity) {
      row.layoutIdentity = layoutIdentity;
      patchCodeRowLayout({
        gutterWidth: input.gutterWidth,
        isHighlighted,
        metrics,
        lineNumber,
        row,
      });
    }

    if (row.contentIdentity !== contentIdentity) {
      row.contentIdentity = contentIdentity;
      patchCodeRowContent({
        metrics,
        row,
        syntax: input.syntax,
        text,
      });
    }

    return row.row;
  }
}

function codeLastProjection({
  input,
  logicalScrollTop,
  renderedWindow,
  scrollPageOffset,
  totalHeight,
  visibleRange,
}: {
  input: CodeProjectionInput;
  logicalScrollTop: number;
  renderedWindow: CodeRenderedWindow;
  scrollPageOffset: number;
  totalHeight: string;
  visibleRange: VisibleRange;
}): LastProjection {
  return {
    contentIdentity: input.contentIdentity,
    highlightIdentity: codeHighlightIdentity(input.highlightRange),
    horizontalScrollLeft: input.viewport.scrollLeft,
    layoutIdentity: input.layoutIdentity,
    logicalScrollTop,
    renderedWindowHeight: renderedWindow.height,
    renderedWindowStickyOffset: renderedWindow.stickyOffset,
    renderedWindowTop: renderedWindow.top,
    scrollPageOffset,
    syntaxIdentity: input.syntaxIdentity,
    totalHeight,
    visibleEnd: visibleRange.end,
    visibleStart: visibleRange.start,
  };
}

function codeHighlightIdentity(range: NormalizedTextLineRange | null) {
  return range ? `${range.start}:${range.end}` : "";
}

function isSameCodeProjection(previous: LastProjection, next: LastProjection) {
  return (
    previous.contentIdentity === next.contentIdentity &&
    previous.highlightIdentity === next.highlightIdentity &&
    previous.horizontalScrollLeft === next.horizontalScrollLeft &&
    previous.layoutIdentity === next.layoutIdentity &&
    previous.renderedWindowHeight === next.renderedWindowHeight &&
    previous.renderedWindowStickyOffset === next.renderedWindowStickyOffset &&
    previous.renderedWindowTop === next.renderedWindowTop &&
    previous.scrollPageOffset === next.scrollPageOffset &&
    previous.syntaxIdentity === next.syntaxIdentity &&
    previous.totalHeight === next.totalHeight &&
    previous.visibleEnd === next.visibleEnd &&
    previous.visibleStart === next.visibleStart
  );
}

function codeProjectionIdentity({
  contentIdentity,
  layoutIdentity,
  syntaxIdentity,
}: CodeProjectionIdentity): CodeProjectionIdentity {
  return {
    contentIdentity,
    layoutIdentity,
    syntaxIdentity,
  };
}

function codeVisibleRange(
  visibleLines: readonly CodeVirtualLine[],
): VisibleRange {
  const start = visibleLines[0]?.index ?? 0;
  const end = visibleLines.length
    ? visibleLines[visibleLines.length - 1]!.index + 1
    : start;
  return { end, start };
}

function codeTotalSize({
  lineCount,
  lineHeight,
}: {
  lineCount: number;
  lineHeight: number;
  viewport?: HTMLDivElement;
}) {
  return getCodeVirtualTotalSize({
    lineCount,
    lineHeight,
  });
}

function codeViewportHeight(viewport: HTMLDivElement) {
  return viewport.clientHeight || CODE_VIEWER_INITIAL_VIEWPORT_HEIGHT;
}

function shouldFitCodePerfectly({
  previousProjection,
  logicalScrollTop,
  viewportHeight,
}: {
  previousProjection: LastProjection | null;
  logicalScrollTop: number;
  viewportHeight: number;
}) {
  if (!previousProjection) return false;
  return (
    Math.abs(logicalScrollTop - previousProjection.logicalScrollTop) >
    viewportHeight + CODE_VIEWER_OVERSCAN_PX * 2
  );
}

function getCodeFitPerfectlyOverscanPx(lineHeight: number) {
  return Math.max(
    CODE_VIEWER_BLOCK_PADDING,
    Number.isFinite(lineHeight) ? lineHeight : 0,
  );
}

function codeRenderedWindow({
  physicalTotalSize,
  rowHost,
  scrollPageOffset,
  totalSize,
  viewportHeight,
  visibleLines,
}: {
  physicalTotalSize: number;
  rowHost: HTMLPreElement;
  scrollPageOffset: number;
  totalSize: number;
  viewportHeight: number;
  visibleLines: readonly CodeVirtualLine[];
}): CodeRenderedWindow {
  const renderedWindowElement = getCodeRenderedWindowElement(rowHost);
  if (!renderedWindowElement) {
    return {
      height: physicalTotalSize,
      rowOffset: 0,
      stickyOffset: 0,
      top: 0,
    };
  }

  const firstLine = visibleLines[0];
  const lastLine = visibleLines[visibleLines.length - 1];
  if (!firstLine || !lastLine) {
    return {
      height: 0,
      rowOffset: 0,
      stickyOffset: 0,
      top: 0,
    };
  }

  const top = getCodePagedLayoutTop({
    logicalTop: firstLine.start,
    scrollPageOffset,
    totalSize,
    viewportHeight,
  });
  const bottom = getCodePagedLayoutTop({
    logicalTop: lastLine.start + lastLine.size,
    scrollPageOffset,
    totalSize,
    viewportHeight,
  });
  const height = Math.max(0, bottom - top);

  return {
    height,
    rowOffset: top,
    stickyOffset: Math.min(0, viewportHeight - height),
    top,
  };
}

function syncCodeScrollLayers({
  renderedWindow,
  rowHost,
  totalHeight,
}: {
  renderedWindow: CodeRenderedWindow;
  rowHost: HTMLPreElement;
  totalHeight: string;
}) {
  const renderedWindowElement = getCodeRenderedWindowElement(rowHost);
  if (!renderedWindowElement) {
    setStyleValue(rowHost.style, "height", totalHeight);
    if (rowHost.parentElement instanceof HTMLElement) {
      setStyleValue(rowHost.parentElement.style, "height", totalHeight);
    }
    return;
  }

  const height = `${renderedWindow.height}px`;
  const top = `${renderedWindow.top}px`;
  const stickyOffset = `${renderedWindow.stickyOffset}px`;
  const offsetElement = getCodeRenderedWindowOffsetElement(
    renderedWindowElement,
  );

  setStyleValue(rowHost.style, "height", height);
  if (offsetElement) setStyleValue(offsetElement.style, "height", top);
  setStyleValue(renderedWindowElement.style, "height", height);
  setStyleValue(renderedWindowElement.style, "margin-top", "");
  setStyleValue(renderedWindowElement.style, "top", stickyOffset);
  setStyleValue(renderedWindowElement.style, "bottom", stickyOffset);

  if (renderedWindowElement.parentElement instanceof HTMLElement) {
    setStyleValue(renderedWindowElement.parentElement.style, "height", totalHeight);
  }
}

function getCodeRenderedWindowElement(rowHost: HTMLPreElement) {
  const element = rowHost.parentElement;
  if (
    element instanceof HTMLElement &&
    element.dataset.codeRenderWindow != null
  ) {
    return element;
  }
  return null;
}

function getCodeRenderedWindowOffsetElement(renderedWindowElement: HTMLElement) {
  const element = renderedWindowElement.previousElementSibling;
  if (
    element instanceof HTMLElement &&
    element.dataset.codeRenderOffset != null
  ) {
    return element;
  }
  return null;
}

function codeRowContentIdentity({
  syntax,
  text,
}: {
  syntax: CodeSyntax;
  text: string;
}) {
  return [text, syntax.identity, syntax.getLineVersion(text)].join("\u0000");
}

function createCodeRow(): CodeRowCache {
  const row = document.createElement("div");
  const gutterSpan = document.createElement("span");
  const contentSpan = document.createElement("span");

  row.className = codeRowClassName();
  row.style.position = "absolute";
  row.style.top = "0";
  row.style.left = "0";

  gutterSpan.className =
    "sticky left-0 z-10 flex-shrink-0 border-r px-2 pr-3 text-right text-muted-foreground/60 select-none";
  // The gutter must paint an opaque background so horizontally-scrolled code
  // never shows through the sticky line-number column. The theme's `--muted`
  // token is alpha-based (translucent), so the fill is mixed from the opaque
  // `--foreground`/`--background` pair instead.
  gutterSpan.style.backgroundColor = CODE_GUTTER_BACKGROUND;
  gutterSpan.dataset.codeGutter = "";
  gutterSpan.setAttribute("aria-hidden", "true");
  contentSpan.className = "whitespace-pre px-2";

  row.append(gutterSpan, contentSpan);

  return {
    contentIdentity: "",
    contentSpan,
    gutterSpan,
    layoutIdentity: "",
    row,
  };
}

function patchCodeRowLayout({
  gutterWidth,
  isHighlighted,
  metrics,
  lineNumber,
  row,
}: {
  gutterWidth: string;
  isHighlighted: boolean;
  metrics: CodeProjectionMetrics | undefined;
  lineNumber: number;
  row: CodeRowCache;
}) {
  incrementMetric(metrics, "layoutPatches");
  row.row.dataset.lineIndex = String(lineNumber - 1);
  row.row.dataset.lineNumber = String(lineNumber);
  setStyleValue(row.gutterSpan.style, "width", gutterWidth);
  setTextContent(row.gutterSpan, String(lineNumber));

  // Highlighted range: a continuous opaque band (no per-row borders) with a
  // single left accent stripe on the gutter that merges across adjacent lines.
  setStyleValue(
    row.row.style,
    "background-color",
    isHighlighted ? CODE_HIGHLIGHT_BACKGROUND : "",
  );
  setStyleValue(
    row.gutterSpan.style,
    "background-color",
    isHighlighted ? CODE_HIGHLIGHT_BACKGROUND : CODE_GUTTER_BACKGROUND,
  );
  setStyleValue(
    row.gutterSpan.style,
    "box-shadow",
    isHighlighted ? CODE_HIGHLIGHT_ACCENT_SHADOW : "",
  );
  setStyleValue(
    row.gutterSpan.style,
    "color",
    isHighlighted ? CODE_HIGHLIGHT_NUMBER_COLOR : "",
  );
}

function patchCodeRowContent({
  metrics,
  row,
  syntax,
  text,
}: {
  metrics: CodeProjectionMetrics | undefined;
  row: CodeRowCache;
  syntax: CodeSyntax;
  text: string;
}) {
  incrementMetric(metrics, "contentPatches");
  const renderText = getCodeLineRenderText(text);
  if (renderText.isTruncated) {
    row.row.dataset.codeLineTruncated = "";
    row.contentSpan.dataset.codeLineTruncated = "";
    row.contentSpan.setAttribute(
      "aria-label",
      `${text.length} character line preview; ${renderText.omittedCharacterCount} middle characters omitted.`,
    );
    row.contentSpan.title =
      "Long line preview. Copying the selected row copies the complete line.";
  } else {
    delete row.row.dataset.codeLineTruncated;
    delete row.contentSpan.dataset.codeLineTruncated;
    row.contentSpan.removeAttribute("aria-label");
    row.contentSpan.removeAttribute("title");
  }

  patchCodeContent(
    row.contentSpan,
    renderText.isTruncated ? null : syntax.getLineTokens(text),
    renderText.text,
    metrics,
  );
}

function patchCodeContent(
  contentSpan: HTMLSpanElement,
  leaves: readonly CodeTokenLeaf[] | null,
  text: string,
  metrics: CodeProjectionMetrics | undefined,
) {
  if (text === "") {
    contentSpan.replaceChildren();
    contentSpan.textContent = " ";
    return;
  }
  if (!leaves) {
    setTextContent(contentSpan, text);
    return;
  }

  contentSpan.replaceChildren();
  incrementMetric(metrics, "tokenSpanRebuilds");
  const fragment = document.createDocumentFragment();
  for (const leaf of leaves) {
    if (!leaf.kind) {
      fragment.append(document.createTextNode(leaf.text));
      continue;
    }
    const span = document.createElement("span");
    span.className = "cv-token-" + leaf.kind;
    span.textContent = leaf.text;
    fragment.append(span);
  }
  contentSpan.append(fragment);
}

function codeRowClassName() {
  return "absolute top-0 left-0 flex min-w-full";
}

function setStyleValue(
  style: CSSStyleDeclaration,
  propertyName: string,
  value: string,
) {
  if (style.getPropertyValue(propertyName) !== value) {
    style.setProperty(propertyName, value);
  }
}

function setTextContent(element: HTMLElement, text: string) {
  if (element.textContent !== text) {
    element.textContent = text;
  }
}

function incrementMetric(
  metrics: CodeProjectionMetrics | undefined,
  key: keyof CodeProjectionMetrics,
) {
  if (!metrics) return;
  metrics[key] += 1;
}

function setMetric(
  metrics: CodeProjectionMetrics | undefined,
  key: keyof CodeProjectionMetrics,
  value: number,
) {
  if (!metrics) return;
  metrics[key] = value;
}
registry/new-york-v4/ui/code-viewer-projection-scheduler.ts
"use client";

import * as React from "react";

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

import { joinEffectKey } from "@/lib/effect-key";
import {
  restoreTextViewerScrollInteractions,
  suspendTextViewerScrollInteractions,
  TEXT_SCROLL_INTERACTION_RESTORE_DELAY_MS,
  type ScrollInteractionSnapshot,
} from "./text-viewer-scroll-interactions";

export function useCodeProjectionScheduler({
  project,
  rowHostRef,
  viewportRef,
}: {
  project: () => boolean | void;
  rowHostRef?: React.RefObject<HTMLPreElement | null>;
  viewportRef: React.RefObject<HTMLDivElement | null>;
}) {
  const scheduledProjectionRef = React.useRef(0);
  const scrollInteractionRestoreRef = React.useRef(0);
  const scrollInteractionSnapshotRef =
    React.useRef<ScrollInteractionSnapshot | null>(null);

  const scheduleProjection = React.useCallback(() => {
    if (scheduledProjectionRef.current) return;
    const runProjection = () => {
      scheduledProjectionRef.current = requestAnimationFrame(() => {
        scheduledProjectionRef.current = 0;
        if (project()) {
          runProjection();
        }
      });
    };
    runProjection();
  }, [project]);

  useKeyedMountEffect(
    joinEffectKey(["code-project", project, scheduleProjection]),
    () => {
      if (project()) {
        scheduleProjection();
      }
      return () => {
        cancelScheduledProjection(scheduledProjectionRef);
      };
    },
  );

  useKeyedMountEffect(
    joinEffectKey([
      "code-project-listeners",
      scheduleProjection,
      rowHostRef,
      viewportRef,
    ]),
    () => {
      const viewport = viewportRef.current;
      if (!viewport) return;

      const handleScroll = () => {
        suspendTextViewerScrollInteractions({
          getInteractionTarget: () => rowHostRef?.current,
          getOverflowTarget: () => rowHostRef?.current?.parentElement,
          snapshotRef: scrollInteractionSnapshotRef,
        });
        if (scrollInteractionRestoreRef.current) {
          window.clearTimeout(scrollInteractionRestoreRef.current);
        }
        scrollInteractionRestoreRef.current = window.setTimeout(() => {
          scrollInteractionRestoreRef.current = 0;
          restoreTextViewerScrollInteractions(scrollInteractionSnapshotRef);
        }, TEXT_SCROLL_INTERACTION_RESTORE_DELAY_MS);
        scheduleProjection();
      };

      viewport.addEventListener("scroll", handleScroll, {
        passive: true,
      });
      const observer =
        typeof ResizeObserver === "undefined"
          ? null
          : new ResizeObserver(scheduleProjection);
      observer?.observe(viewport);

      return () => {
        viewport.removeEventListener("scroll", handleScroll);
        observer?.disconnect();
        cancelScheduledProjection(scheduledProjectionRef);
        if (scrollInteractionRestoreRef.current) {
          window.clearTimeout(scrollInteractionRestoreRef.current);
          scrollInteractionRestoreRef.current = 0;
        }
        restoreTextViewerScrollInteractions(scrollInteractionSnapshotRef);
      };
    },
  );
}

function cancelScheduledProjection(ref: React.MutableRefObject<number>) {
  if (!ref.current) return;
  cancelAnimationFrame(ref.current);
  ref.current = 0;
}
registry/new-york-v4/ui/code-viewer-layout.ts
import type { NormalizedTextLineRange } from "./line-ranges";

export const LINE_SCROLL_HEADROOM = 64;

export interface LineRangeMetrics {
  startLine: number;
  endLine: number;
  lineHeight: number;
  viewportHeight: number;
  paddingStart?: number;
}

export function scrollTopForLineRangeMetrics({
  startLine,
  endLine,
  lineHeight,
  viewportHeight,
  paddingStart = 0,
}: LineRangeMetrics) {
  const rangeTop = paddingStart + (startLine - 1) * lineHeight;
  const rangeBottom = paddingStart + endLine * lineHeight;
  const rangeHeight = rangeBottom - rangeTop;
  const targetTop =
    rangeHeight <= viewportHeight
      ? rangeTop - (viewportHeight - rangeHeight) / 2
      : rangeTop - LINE_SCROLL_HEADROOM;

  return Math.max(0, targetTop);
}

export function scrollLineRangeMetricsIntoView({
  viewportElement,
  range,
  lineHeight,
  paddingStart,
  options,
}: {
  viewportElement: HTMLDivElement | null;
  range: NormalizedTextLineRange | null;
  lineHeight: number;
  paddingStart?: number;
  options?: ScrollToOptions;
}) {
  if (!viewportElement || !range) return;
  if (typeof viewportElement.scrollTo !== "function") return;

  viewportElement.scrollTo({
    top: scrollTopForLineRangeMetrics({
      startLine: range.start,
      endLine: range.end,
      lineHeight,
      paddingStart,
      viewportHeight: viewportElement.clientHeight,
    }),
    behavior: "smooth",
    ...options,
  });
}
registry/new-york-v4/ui/code-viewer-scale.ts
export const CODE_VIEWER_BASE_FONT_PX = 12;
export const CODE_VIEWER_BASE_LINE_PX = 20;
export const CODE_VIEWER_MIN_SCALE = 0.1;
export const CODE_VIEWER_MAX_SCALE = 5;
export const CODE_VIEWER_OVERSCAN_PX = 1000;
export const CODE_VIEWER_BLOCK_PADDING = 8;
export const CODE_VIEWER_INITIAL_VIEWPORT_HEIGHT = 600;
export const CODE_VIEWER_SCROLL_REBASE_CONTAINER_PX = 12_000_000;
export const CODE_VIEWER_SCROLL_REBASE_TRIGGER_PX = 1_000_000;
export const CODE_VIEWER_SCROLL_REBASE_TARGET_PX = 2_000_000;
export const CODE_VIEWER_SCROLL_REBASE_TARGET_BOTTOM_PX =
  CODE_VIEWER_SCROLL_REBASE_CONTAINER_PX - CODE_VIEWER_SCROLL_REBASE_TARGET_PX;
export const CODE_VIEWER_SCROLL_REBASE_THRESHOLD_PX =
  CODE_VIEWER_SCROLL_REBASE_CONTAINER_PX -
  CODE_VIEWER_SCROLL_REBASE_TRIGGER_PX;
export const CODE_VIEWER_LINE_CHECKPOINT_INTERVAL = 4096;

export function clampCodeViewerScale(value: number) {
  return Math.min(
    CODE_VIEWER_MAX_SCALE,
    Math.max(CODE_VIEWER_MIN_SCALE, value),
  );
}
registry/new-york-v4/ui/code-viewer-types.ts
import type {
  BlobViewerSource,
  TextSource,
  UrlViewerSource,
} from "@/lib/viewer-source";

import type { TextLineRange } from "./text-viewer-ranges";
import type { TextViewerBounds } from "./text-viewer-resource";

export type CodeLineRange = TextLineRange;

export interface CodeViewerHandle {
  scrollToLineRange: (range: CodeLineRange, options?: ScrollToOptions) => void;
  getViewportElement: () => HTMLDivElement | null;
}

export type CodeDocumentSource =
  | UrlViewerSource
  | BlobViewerSource
  | TextSource;

export interface CodeViewerProps extends TextViewerBounds {
  source: CodeDocumentSource;
  className?: string;
  controls?: boolean;
  download?: boolean;
  /** 1-based inclusive line range to highlight, or null. */
  highlight?: CodeLineRange | null;
  /** Drop the outer border/rounded/background so the viewer fills its container. */
  bare?: boolean;
}
registry/new-york-v4/ui/code-viewer-syntax.ts
import type { ViewerResource } from "@/lib/viewer-resource";

import {
  type CodeSyntaxWorkerRequest,
  type CodeSyntaxWorkerResponse,
  type CodeTokenLeaf,
  shouldTokenizeCodeLine,
} from "./code-viewer-syntax-protocol";
import {
  ensureCodePrismLanguage,
  isCodePrismLanguageLoaded,
  isCodePrismLanguageSupported,
  tokenizeCodeLine,
} from "./code-viewer-syntax-prism";
import { createCodeSyntaxWorker } from "./code-viewer-syntax-worker";

export type { CodeTokenLeaf } from "./code-viewer-syntax-protocol";

export type CodeSyntax = {
  identity: string;
  destroy?: () => void;
  getLineVersion(line: string): number;
  getLineTokens(line: string): readonly CodeTokenLeaf[] | null;
  preload?: () => Promise<void>;
};

export type CodeSyntaxMode = "auto" | "main-thread" | "worker";

export type CodeSyntaxOptions = {
  deferTokens?: boolean;
  onTokensChanged?: () => void;
  syntaxMode?: CodeSyntaxMode;
  createWorker?: () => Worker;
};

const CODE_DEFERRED_TOKENIZE_BATCH_SIZE = 12;
const CODE_DEFERRED_TOKENIZE_BUDGET_MS = 6;
export const CODE_GLOBAL_TOKEN_CACHE_LIMIT = 1024;
const CODE_WORKER_TOKENIZE_BATCH_SIZE = 64;

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

type CodeSyntaxTaskHandle =
  | { kind: "idle"; id: number }
  | { kind: "timeout"; id: number };

type CodeSyntaxTaskDeadline = {
  timeRemaining?: () => number;
};

type CodeSyntaxNotifyHandle = { id: number };

type CodeSyntaxWorkerFactory = () => Worker;

type CodeSyntaxWorkerRelease = () => void;

type CodeSyntaxWorkerSubscription = {
  isReleased: boolean;
  onDone: () => void;
  onError: (line: string) => void;
  onTokens: (line: string, tokens: readonly CodeTokenLeaf[]) => void;
  pendingCount: number;
};

type CodeSyntaxWorkerJob = {
  key: string;
  languageId: string;
  line: string;
  status: "active" | "pending";
  subscribers: Set<CodeSyntaxWorkerSubscription>;
};

type CodeSyntaxWorkerBatch = {
  generation: number;
  jobs: CodeSyntaxWorkerJob[];
  languageId: string;
  requestId: number;
};

type CodeSyntaxWorkerSlot = {
  activeBatch: CodeSyntaxWorkerBatch | null;
  worker: Worker;
};

type CodeSyntaxWorkerPool = {
  createWorker: CodeSyntaxWorkerFactory;
  dispatchHandle: CodeSyntaxTaskHandle | null;
  jobsByKey: Map<string, CodeSyntaxWorkerJob>;
  maxWorkers: number;
  pendingJobs: Map<string, CodeSyntaxWorkerJob>;
  requestId: number;
  slots: CodeSyntaxWorkerSlot[];
};

const globalTokenCache = new Map<string, readonly CodeTokenLeaf[]>();
const globalWorkerPoolsByFactory = new WeakMap<
  CodeSyntaxWorkerFactory,
  CodeSyntaxWorkerPool
>();
const globalWorkerPools = new Set<CodeSyntaxWorkerPool>();
const CODE_GLOBAL_WORKER_POOL_SIZE = 2;

// File extension -> Prism language id. Prism does not map extensions to
// languages, so the viewer keeps the small explicit map.
const LANGUAGE_BY_EXTENSION: Record<string, string> = {
  json: "json",
  json5: "json",
  js: "javascript",
  mjs: "javascript",
  cjs: "javascript",
  jsx: "jsx",
  ts: "typescript",
  mts: "typescript",
  cts: "typescript",
  tsx: "tsx",
  py: "python",
  yaml: "yaml",
  yml: "yaml",
  sh: "bash",
  bash: "bash",
  zsh: "bash",
  sql: "sql",
  go: "go",
  rs: "rust",
  java: "java",
  md: "markdown",
  markdown: "markdown",
  css: "css",
  html: "markup",
  htm: "markup",
  xml: "markup",
  svg: "markup",
};

// MIME -> Prism language id, used only for inline sources with no extension.
const LANGUAGE_BY_MIME: Record<string, string> = {
  "application/json": "json",
  "text/javascript": "javascript",
  "application/javascript": "javascript",
  "text/typescript": "typescript",
  "application/typescript": "typescript",
  "text/x-python": "python",
  "application/x-python": "python",
  "text/yaml": "yaml",
  "application/yaml": "yaml",
  "application/x-yaml": "yaml",
  "text/x-sh": "bash",
  "application/x-sh": "bash",
  "application/sql": "sql",
  "text/markdown": "markdown",
  "text/css": "css",
  "text/html": "markup",
  "application/xml": "markup",
  "text/xml": "markup",
};

export const CODE_VIEWER_SYNTAX_STYLE = `
.cv-token-comment { color: var(--cv-token-comment, #6e7781); font-style: italic; }
.cv-token-property,
.cv-token-tag,
.cv-token-attr-name,
.cv-token-symbol { color: var(--cv-token-property, #0550ae); }
.cv-token-string,
.cv-token-char,
.cv-token-attr-value,
.cv-token-url,
.cv-token-regex { color: var(--cv-token-string, #0a7d33); }
.cv-token-number { color: var(--cv-token-number, #b5690c); }
.cv-token-keyword,
.cv-token-boolean,
.cv-token-null,
.cv-token-constant,
.cv-token-atrule,
.cv-token-important { color: var(--cv-token-keyword, #8250df); }
.cv-token-function,
.cv-token-class-name,
.cv-token-builtin { color: var(--cv-token-function, #8250df); }
.cv-token-variable { color: var(--cv-token-variable, #953800); }
.cv-token-punctuation,
.cv-token-operator { color: var(--cv-token-punctuation, color-mix(in oklab, var(--foreground) 55%, transparent)); }
.dark .cv-token-comment { color: var(--cv-token-comment, #8b949e); }
.dark .cv-token-property,
.dark .cv-token-tag,
.dark .cv-token-attr-name,
.dark .cv-token-symbol { color: var(--cv-token-property, #6cb6ff); }
.dark .cv-token-string,
.dark .cv-token-char,
.dark .cv-token-attr-value,
.dark .cv-token-url,
.dark .cv-token-regex { color: var(--cv-token-string, #8ddb8c); }
.dark .cv-token-number { color: var(--cv-token-number, #e3b341); }
.dark .cv-token-keyword,
.dark .cv-token-boolean,
.dark .cv-token-null,
.dark .cv-token-constant,
.dark .cv-token-atrule,
.dark .cv-token-important { color: var(--cv-token-keyword, #dcbdfb); }
.dark .cv-token-function,
.dark .cv-token-class-name,
.dark .cv-token-builtin { color: var(--cv-token-function, #d2a8ff); }
.dark .cv-token-variable { color: var(--cv-token-variable, #ffa657); }
`;

export function createCodeSyntax(
  resource: ViewerResource,
  options: CodeSyntaxOptions = {},
): CodeSyntax {
  const detectedLanguageId = codeLanguageId(resource);
  if (!detectedLanguageId || !isCodePrismLanguageSupported(detectedLanguageId)) {
    return {
      identity: "plain",
      getLineVersion: () => 0,
      getLineTokens: () => null,
    };
  }
  const languageId = detectedLanguageId;

  const tokenVersions = new Map<string, number>();
  const asyncTokenSnapshots = new Map<string, readonly CodeTokenLeaf[]>();
  const pendingLines = new Set<string>();
  const workerReleases = new Set<CodeSyntaxWorkerRelease>();
  const workerFactory = options.createWorker ?? createCodeSyntaxWorker;
  const useWorker = shouldUseWorker(options);
  let flushHandle: CodeSyntaxTaskHandle | null = null;
  let notifyHandle: CodeSyntaxNotifyHandle | null = null;
  let grammarPromise: Promise<void> | null = null;
  let isGrammarReady = isCodePrismLanguageLoaded(languageId);
  let isGrammarFailed = false;
  let isWorkerFailed = false;
  let isDestroyed = false;
  let hasPendingTokenChanges = false;

  if (!useWorker) {
    void preloadMainThreadGrammar();
  }

  return {
    destroy,
    getLineVersion,
    getLineTokens,
    identity: languageId,
    preload: preloadMainThreadGrammar,
  };

  function destroy() {
    isDestroyed = true;
    pendingLines.clear();
    cancelFlush();
    cancelNotification();
    for (const releaseWorker of workerReleases) {
      releaseWorker();
    }
    workerReleases.clear();
  }

  function getLineVersion(line: string) {
    return tokenVersions.get(line) ?? 0;
  }

  function getLineTokens(line: string) {
    if (isDestroyed) return null;
    if (!shouldTokenizeCodeLine(line) || isGrammarFailed) return null;

    const cachedTokens = getGlobalLineTokens(languageId, line);
    if (cachedTokens) return cachedTokens;

    if (!shouldTokenizeInWorker() && isGrammarReady && !options.deferTokens) {
      const tokens = tokenizeCodeLine(languageId, line);
      if (tokens) {
        return setGlobalLineTokens(languageId, line, tokens);
      }
    }

    pendingLines.add(line);
    scheduleTokenization();
    return null;
  }

  function scheduleTokenization() {
    if (isDestroyed || pendingLines.size === 0) return;
    if (shouldTokenizeInWorker()) {
      scheduleWorkerTokenization();
      return;
    }
    scheduleMainThreadTokenization();
  }

  function scheduleWorkerTokenization() {
    if (!shouldTokenizeInWorker() || flushHandle) return;
    flushHandle = scheduleCodeSyntaxTask(() => {
      flushHandle = null;
      flushWorkerTokenBatch();
    });
  }

  function flushWorkerTokenBatch() {
    if (!shouldTokenizeInWorker() || isDestroyed) return;

    const lines = takePendingLines(CODE_WORKER_TOKENIZE_BATCH_SIZE);
    if (lines.length === 0) return;

    const uncachedLines: string[] = [];
    let didCacheTokens = false;
    for (const line of lines) {
      const cachedTokens = getGlobalLineTokens(languageId, line);
      if (cachedTokens) {
        didCacheTokens =
          cacheAsyncLineTokens(line, cachedTokens) || didCacheTokens;
      } else {
        uncachedLines.push(line);
      }
    }
    if (didCacheTokens) queueTokenChangeNotification();
    if (uncachedLines.length === 0) {
      scheduleTokenization();
      return;
    }

    let didFinishWorkerRequest = false;
    let releaseWorker: CodeSyntaxWorkerRelease | null = null;
    releaseWorker = requestCodeSyntaxWorkerTokens({
      createWorker: workerFactory,
      languageId,
      lines: uncachedLines,
      onDone: () => {
        didFinishWorkerRequest = true;
        if (releaseWorker) workerReleases.delete(releaseWorker);
        scheduleTokenization();
      },
      onError: (line) => {
        if (isDestroyed) return;
        isWorkerFailed = true;
        pendingLines.add(line);
        scheduleMainThreadTokenization();
      },
      onTokens: (line, tokens) => {
        if (isDestroyed) return;
        if (cacheAsyncLineTokens(line, tokens)) queueTokenChangeNotification();
      },
    });
    if (!didFinishWorkerRequest) workerReleases.add(releaseWorker);
    scheduleTokenization();
  }

  function scheduleMainThreadTokenization() {
    if (isDestroyed || flushHandle || isGrammarFailed) return;
    if (!isGrammarReady) {
      void preloadMainThreadGrammar();
      return;
    }
    flushHandle = scheduleCodeSyntaxTask(flushMainThreadTokenBatch);
  }

  async function preloadMainThreadGrammar() {
    if (shouldTokenizeInWorker() || isGrammarReady || isGrammarFailed) return;
    if (!grammarPromise) {
      grammarPromise = ensureCodePrismLanguage(languageId)
        .then(() => {
          if (isDestroyed) return;
          isGrammarReady = true;
          scheduleMainThreadTokenization();
        })
        .catch(() => {
          if (isDestroyed) return;
          isGrammarFailed = true;
          pendingLines.clear();
        });
    }
    await grammarPromise;
  }

  function flushMainThreadTokenBatch(deadline?: CodeSyntaxTaskDeadline) {
    flushHandle = null;
    if (isDestroyed || isGrammarFailed || !isGrammarReady) return;

    const startedAt = codeSyntaxNow();
    let processedLineCount = 0;
    while (pendingLines.size > 0) {
      const pendingLine = pendingLines.values().next().value;
      if (pendingLine == null) break;
      pendingLines.delete(pendingLine);

      const cachedTokens = getGlobalLineTokens(languageId, pendingLine);
      if (cachedTokens) {
        hasPendingTokenChanges =
          cacheAsyncLineTokens(pendingLine, cachedTokens) ||
          hasPendingTokenChanges;
      } else {
        const tokens = tokenizeCodeLine(languageId, pendingLine);
        if (tokens) {
          hasPendingTokenChanges =
            cacheAsyncLineTokens(pendingLine, tokens) || hasPendingTokenChanges;
        }
      }

      processedLineCount += 1;
      if (
        shouldYieldDeferredTokenization({
          deadline,
          processedLineCount,
          startedAt,
        })
      ) {
        break;
      }
    }

    if (pendingLines.size > 0) {
      scheduleMainThreadTokenization();
      return;
    }

    if (hasPendingTokenChanges) {
      hasPendingTokenChanges = false;
      queueTokenChangeNotification();
    }
  }

  function takePendingLines(limit: number) {
    const lines: string[] = [];
    for (const line of pendingLines) {
      pendingLines.delete(line);
      lines.push(line);
      if (lines.length >= limit) break;
    }
    return lines;
  }

  function cacheAsyncLineTokens(
    line: string,
    tokens: readonly CodeTokenLeaf[],
  ) {
    const cachedTokens = setGlobalLineTokens(languageId, line, tokens);
    const previousTokens = asyncTokenSnapshots.get(line);
    if (
      previousTokens &&
      areCodeTokenLeavesEqual(previousTokens, cachedTokens)
    ) {
      return false;
    }

    asyncTokenSnapshots.set(line, cachedTokens);
    tokenVersions.set(line, (tokenVersions.get(line) ?? 0) + 1);
    return true;
  }

  function shouldTokenizeInWorker() {
    return useWorker && !isWorkerFailed;
  }

  function queueTokenChangeNotification() {
    if (isDestroyed || notifyHandle) return;
    notifyHandle = scheduleCodeSyntaxNotification(() => {
      notifyHandle = null;
      if (!isDestroyed) options.onTokensChanged?.();
    });
  }

  function cancelFlush() {
    if (!flushHandle) return;
    cancelCodeSyntaxTask(flushHandle);
    flushHandle = null;
  }

  function cancelNotification() {
    if (!notifyHandle) return;
    cancelCodeSyntaxNotification(notifyHandle);
    notifyHandle = null;
  }
}

export function clearCodeSyntaxGlobalTokenCacheForTests() {
  globalTokenCache.clear();
  for (const pool of globalWorkerPools) {
    cancelCodeSyntaxWorkerPoolDispatch(pool);
    for (const slot of pool.slots) {
      slot.worker.terminate();
    }
    pool.slots = [];
    pool.pendingJobs.clear();
    pool.jobsByKey.clear();
  }
}

function shouldUseWorker(options: CodeSyntaxOptions) {
  if (options.syntaxMode === "main-thread") return false;
  if (options.syntaxMode === "worker") return true;
  return typeof Worker !== "undefined";
}

function getGlobalLineTokens(languageId: string, line: string) {
  const key = codeGlobalTokenCacheKey(languageId, line);
  const tokens = globalTokenCache.get(key);
  if (!tokens) return null;

  globalTokenCache.delete(key);
  globalTokenCache.set(key, tokens);
  return tokens;
}

function setGlobalLineTokens(
  languageId: string,
  line: string,
  tokens: readonly CodeTokenLeaf[],
) {
  const key = codeGlobalTokenCacheKey(languageId, line);
  const cachedTokens = getGlobalLineTokens(languageId, line);
  if (cachedTokens && areCodeTokenLeavesEqual(cachedTokens, tokens)) {
    return cachedTokens;
  }

  globalTokenCache.delete(key);
  globalTokenCache.set(key, tokens);
  while (globalTokenCache.size > CODE_GLOBAL_TOKEN_CACHE_LIMIT) {
    const firstKey = globalTokenCache.keys().next().value;
    if (firstKey === undefined) return tokens;
    globalTokenCache.delete(firstKey);
  }
  return tokens;
}

function codeGlobalTokenCacheKey(languageId: string, line: string) {
  return `${languageId}\0${line}`;
}

function requestCodeSyntaxWorkerTokens({
  createWorker,
  languageId,
  lines,
  onDone,
  onError,
  onTokens,
}: {
  createWorker: CodeSyntaxWorkerFactory;
  languageId: string;
  lines: readonly string[];
  onDone: () => void;
  onError: (line: string) => void;
  onTokens: (line: string, tokens: readonly CodeTokenLeaf[]) => void;
}): CodeSyntaxWorkerRelease {
  const pool = getCodeSyntaxWorkerPool(createWorker);
  const subscription: CodeSyntaxWorkerSubscription = {
    isReleased: false,
    onDone,
    onError,
    onTokens,
    pendingCount: 0,
  };
  const jobs: CodeSyntaxWorkerJob[] = [];

  for (const line of lines) {
    const cachedTokens = getGlobalLineTokens(languageId, line);
    if (cachedTokens) {
      onTokens(line, cachedTokens);
      continue;
    }

    const key = codeGlobalTokenCacheKey(languageId, line);
    let job = pool.jobsByKey.get(key);
    if (!job) {
      job = {
        key,
        languageId,
        line,
        status: "pending",
        subscribers: new Set(),
      };
      pool.jobsByKey.set(key, job);
      pool.pendingJobs.set(key, job);
    }
    job.subscribers.add(subscription);
    subscription.pendingCount += 1;
    jobs.push(job);
  }

  if (subscription.pendingCount === 0) {
    onDone();
    return () => undefined;
  }

  dispatchCodeSyntaxWorkerPool(pool);

  return () => {
    if (subscription.isReleased) return;
    subscription.isReleased = true;
    for (const job of jobs) {
      releaseCodeSyntaxWorkerSubscription(pool, job, subscription);
    }
  };
}

function getCodeSyntaxWorkerPool(createWorker: CodeSyntaxWorkerFactory) {
  let pool = globalWorkerPoolsByFactory.get(createWorker);
  if (!pool) {
    pool = {
      createWorker,
      dispatchHandle: null,
      jobsByKey: new Map(),
      maxWorkers:
        createWorker === createCodeSyntaxWorker
          ? CODE_GLOBAL_WORKER_POOL_SIZE
          : 1,
      pendingJobs: new Map(),
      requestId: 0,
      slots: [],
    };
    globalWorkerPoolsByFactory.set(createWorker, pool);
    globalWorkerPools.add(pool);
  }
  return pool;
}

function scheduleCodeSyntaxWorkerPoolDispatch(pool: CodeSyntaxWorkerPool) {
  if (pool.pendingJobs.size === 0 || pool.dispatchHandle) return;
  pool.dispatchHandle = scheduleCodeSyntaxTask(() => {
    pool.dispatchHandle = null;
    dispatchCodeSyntaxWorkerPool(pool);
  });
}

function cancelCodeSyntaxWorkerPoolDispatch(pool: CodeSyntaxWorkerPool) {
  if (!pool.dispatchHandle) return;
  cancelCodeSyntaxTask(pool.dispatchHandle);
  pool.dispatchHandle = null;
}

function dispatchCodeSyntaxWorkerPool(pool: CodeSyntaxWorkerPool) {
  while (pool.pendingJobs.size > 0) {
    const slot = getIdleCodeSyntaxWorkerSlot(pool);
    if (!slot) return;

    const jobs = takeCodeSyntaxWorkerJobs(pool, CODE_WORKER_TOKENIZE_BATCH_SIZE);
    if (jobs.length === 0) return;

    pool.requestId += 1;
    const requestId = pool.requestId;
    const languageId = jobs[0]?.languageId ?? "";
    const batch: CodeSyntaxWorkerBatch = {
      generation: requestId,
      jobs,
      languageId,
      requestId,
    };
    slot.activeBatch = batch;

    const request: CodeSyntaxWorkerRequest = {
      type: "tokenize",
      generation: batch.generation,
      languageId,
      lines: jobs.map((job) => job.line),
      requestId,
    };
    slot.worker.postMessage(request);
  }
}

function getIdleCodeSyntaxWorkerSlot(pool: CodeSyntaxWorkerPool) {
  const idleSlot = pool.slots.find((slot) => !slot.activeBatch);
  if (idleSlot) return idleSlot;
  if (pool.slots.length >= pool.maxWorkers) return null;

  try {
    const slot: CodeSyntaxWorkerSlot = {
      activeBatch: null,
      worker: pool.createWorker(),
    };
    slot.worker.onmessage = (event: MessageEvent<CodeSyntaxWorkerResponse>) => {
      handleCodeSyntaxWorkerPoolMessage(pool, slot, event.data);
    };
    slot.worker.onerror = () => {
      handleCodeSyntaxWorkerPoolFailure(pool, slot);
    };
    slot.worker.onmessageerror = () => {
      handleCodeSyntaxWorkerPoolFailure(pool, slot);
    };
    pool.slots.push(slot);
    return slot;
  } catch {
    failPendingCodeSyntaxWorkerJobs(pool);
    return null;
  }
}

function takeCodeSyntaxWorkerJobs(
  pool: CodeSyntaxWorkerPool,
  limit: number,
) {
  const firstJob = pool.pendingJobs.values().next().value;
  if (!firstJob) return [];

  const jobs: CodeSyntaxWorkerJob[] = [];
  for (const job of pool.pendingJobs.values()) {
    if (job.languageId !== firstJob.languageId) continue;
    pool.pendingJobs.delete(job.key);
    job.status = "active";
    jobs.push(job);
    if (jobs.length >= limit) break;
  }
  return jobs;
}

function handleCodeSyntaxWorkerPoolMessage(
  pool: CodeSyntaxWorkerPool,
  slot: CodeSyntaxWorkerSlot,
  message: CodeSyntaxWorkerResponse,
) {
  const batch = slot.activeBatch;
  if (
    !batch ||
    message.generation !== batch.generation ||
    message.languageId !== batch.languageId ||
    message.requestId !== batch.requestId
  ) {
    return;
  }

  slot.activeBatch = null;

  if (message.type === "error") {
    terminateCodeSyntaxWorkerSlot(pool, slot);
    finishCodeSyntaxWorkerJobsWithError(pool, batch.jobs);
    scheduleCodeSyntaxWorkerPoolDispatch(pool);
    return;
  }

  const resultsByLine = new Map(
    message.results.map((result) => [result.line, result.tokens] as const),
  );

  for (const job of batch.jobs) {
    pool.jobsByKey.delete(job.key);
    const tokens = resultsByLine.get(job.line) ?? null;
    if (tokens) setGlobalLineTokens(job.languageId, job.line, tokens);
    for (const subscriber of job.subscribers) {
      if (!subscriber.isReleased && tokens) {
        subscriber.onTokens(job.line, tokens);
      }
      completeCodeSyntaxWorkerSubscription(subscriber);
    }
    job.subscribers.clear();
  }

  dispatchCodeSyntaxWorkerPool(pool);
}

function handleCodeSyntaxWorkerPoolFailure(
  pool: CodeSyntaxWorkerPool,
  slot: CodeSyntaxWorkerSlot,
) {
  const batch = slot.activeBatch;
  slot.activeBatch = null;
  terminateCodeSyntaxWorkerSlot(pool, slot);
  if (batch) finishCodeSyntaxWorkerJobsWithError(pool, batch.jobs);
  scheduleCodeSyntaxWorkerPoolDispatch(pool);
}

function terminateCodeSyntaxWorkerSlot(
  pool: CodeSyntaxWorkerPool,
  slot: CodeSyntaxWorkerSlot,
) {
  slot.worker.terminate();
  pool.slots = pool.slots.filter((candidate) => candidate !== slot);
}

function failPendingCodeSyntaxWorkerJobs(pool: CodeSyntaxWorkerPool) {
  const jobs = Array.from(pool.pendingJobs.values());
  pool.pendingJobs.clear();
  finishCodeSyntaxWorkerJobsWithError(pool, jobs);
}

function finishCodeSyntaxWorkerJobsWithError(
  pool: CodeSyntaxWorkerPool,
  jobs: readonly CodeSyntaxWorkerJob[],
) {
  for (const job of jobs) {
    pool.jobsByKey.delete(job.key);
    pool.pendingJobs.delete(job.key);
    for (const subscriber of job.subscribers) {
      if (!subscriber.isReleased) subscriber.onError(job.line);
      completeCodeSyntaxWorkerSubscription(subscriber);
    }
    job.subscribers.clear();
  }
}

function releaseCodeSyntaxWorkerSubscription(
  pool: CodeSyntaxWorkerPool,
  job: CodeSyntaxWorkerJob,
  subscriber: CodeSyntaxWorkerSubscription,
) {
  job.subscribers.delete(subscriber);
  if (job.subscribers.size > 0 || job.status === "active") return;
  pool.pendingJobs.delete(job.key);
  pool.jobsByKey.delete(job.key);
}

function completeCodeSyntaxWorkerSubscription(
  subscriber: CodeSyntaxWorkerSubscription,
) {
  if (subscriber.isReleased) return;
  subscriber.pendingCount -= 1;
  if (subscriber.pendingCount <= 0) {
    subscriber.isReleased = true;
    subscriber.onDone();
  }
}

function scheduleCodeSyntaxTask(
  callback: (deadline?: CodeSyntaxTaskDeadline) => void,
): CodeSyntaxTaskHandle {
  const browserWindow = window as CodeSyntaxIdleWindow;
  if (browserWindow.requestIdleCallback) {
    return {
      kind: "idle",
      id: browserWindow.requestIdleCallback(callback, { timeout: 80 }),
    };
  }

  return {
    kind: "timeout",
    id: browserWindow.setTimeout(() => callback(), 0),
  };
}

function cancelCodeSyntaxTask(handle: CodeSyntaxTaskHandle) {
  const browserWindow = window as CodeSyntaxIdleWindow;
  if (handle.kind === "idle") {
    browserWindow.cancelIdleCallback?.(handle.id);
    return;
  }
  browserWindow.clearTimeout(handle.id);
}

function scheduleCodeSyntaxNotification(
  callback: () => void,
): CodeSyntaxNotifyHandle {
  return { id: window.setTimeout(callback, 0) };
}

function cancelCodeSyntaxNotification(handle: CodeSyntaxNotifyHandle) {
  window.clearTimeout(handle.id);
}

function areCodeTokenLeavesEqual(
  first: readonly CodeTokenLeaf[],
  second: readonly CodeTokenLeaf[],
) {
  if (first === second) return true;
  if (first.length !== second.length) return false;
  for (let index = 0; index < first.length; index += 1) {
    const firstLeaf = first[index];
    const secondLeaf = second[index];
    if (
      firstLeaf?.kind !== secondLeaf?.kind ||
      firstLeaf?.text !== secondLeaf?.text
    ) {
      return false;
    }
  }
  return true;
}

function shouldYieldDeferredTokenization({
  deadline,
  processedLineCount,
  startedAt,
}: {
  deadline?: CodeSyntaxTaskDeadline;
  processedLineCount: number;
  startedAt: number;
}) {
  if (processedLineCount <= 0) return false;
  if (deadline?.timeRemaining && deadline.timeRemaining() <= 1) return true;
  if (processedLineCount >= CODE_DEFERRED_TOKENIZE_BATCH_SIZE) return true;
  return codeSyntaxNow() - startedAt >= CODE_DEFERRED_TOKENIZE_BUDGET_MS;
}

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

function codeLanguageId(resource: ViewerResource): string | null {
  const extension = resource.fileName.toLowerCase().split(".").pop();
  const byExtension = extension ? LANGUAGE_BY_EXTENSION[extension] : undefined;
  if (byExtension) return byExtension;

  const mimeType = resource.content.mimeType
    ?.toLowerCase()
    .split(";")[0]
    .trim();
  return (mimeType && LANGUAGE_BY_MIME[mimeType]) ?? null;
}
registry/new-york-v4/ui/code-viewer-syntax-protocol.ts
export const CODE_LINE_TOKENIZE_MAX = 2000;

export type CodeTokenLeaf = {
  kind: string;
  text: string;
};

export type CodeSyntaxWorkerRequest = {
  type: "tokenize";
  requestId: number;
  generation: number;
  languageId: string;
  lines: string[];
};

export type CodeSyntaxWorkerResponse =
  | {
      type: "tokens";
      requestId: number;
      generation: number;
      languageId: string;
      results: CodeSyntaxWorkerTokenResult[];
    }
  | {
      type: "error";
      requestId: number;
      generation: number;
      languageId: string;
      message: string;
    };

export type CodeSyntaxWorkerTokenResult = {
  line: string;
  tokens: CodeTokenLeaf[] | null;
};

export function shouldTokenizeCodeLine(line: string) {
  return line.length > 0 && line.length <= CODE_LINE_TOKENIZE_MAX;
}
registry/new-york-v4/ui/code-viewer-syntax-prism.ts
import Prism from "prismjs";

import type { CodeTokenLeaf } from "./code-viewer-syntax-protocol";

Prism.manual = true;

const coreLanguages = new Set(["css", "javascript", "markup"]);
const languageLoaders: Record<string, () => Promise<unknown>> = {
  bash: () => import("prismjs/components/prism-bash"),
  diff: () => import("prismjs/components/prism-diff"),
  dockerfile: () => import("prismjs/components/prism-docker"),
  go: () => import("prismjs/components/prism-go"),
  java: () => import("prismjs/components/prism-java"),
  json: () => import("prismjs/components/prism-json"),
  jsx: () => import("prismjs/components/prism-jsx"),
  markdown: () => import("prismjs/components/prism-markdown"),
  python: () => import("prismjs/components/prism-python"),
  rust: () => import("prismjs/components/prism-rust"),
  ruby: () => import("prismjs/components/prism-ruby"),
  sql: () => import("prismjs/components/prism-sql"),
  tsx: async () => {
    await Promise.all([
      import("prismjs/components/prism-typescript"),
      import("prismjs/components/prism-jsx"),
    ]);
    return import("prismjs/components/prism-tsx");
  },
  typescript: () => import("prismjs/components/prism-typescript"),
  yaml: () => import("prismjs/components/prism-yaml"),
};

const loadingLanguages = new Map<string, Promise<void>>();

export function isCodePrismLanguageLoaded(languageId: string) {
  return Boolean(Prism.languages[languageId]);
}

export function isCodePrismLanguageSupported(languageId: string) {
  return coreLanguages.has(languageId) || languageId in languageLoaders;
}

export async function ensureCodePrismLanguage(languageId: string) {
  if (isCodePrismLanguageLoaded(languageId)) return;
  if (coreLanguages.has(languageId)) return;

  let loading = loadingLanguages.get(languageId);
  if (!loading) {
    const loadLanguage = languageLoaders[languageId];
    if (!loadLanguage) {
      loading = Promise.reject(
        new Error(`Unsupported code syntax language: ${languageId}`),
      );
    } else {
      loading = loadLanguage().then(() => undefined);
    }
    loadingLanguages.set(languageId, loading);
  }

  await loading;
}

export function tokenizeCodeLine(languageId: string, line: string) {
  const grammar = Prism.languages[languageId] ?? null;
  if (!grammar) return null;
  return flattenCodeTokens(Prism.tokenize(line, grammar));
}

function flattenCodeTokens(
  tokens: Array<string | Prism.Token>,
  parentKind = "",
  leaves: CodeTokenLeaf[] = [],
): CodeTokenLeaf[] {
  for (const token of tokens) {
    if (typeof token === "string") {
      leaves.push({ kind: parentKind, text: token });
    } else if (Array.isArray(token.content)) {
      flattenCodeTokens(
        token.content as Array<string | Prism.Token>,
        token.type,
        leaves,
      );
    } else if (typeof token.content === "string") {
      leaves.push({ kind: token.type, text: token.content });
    } else {
      flattenCodeTokens([token.content as Prism.Token], token.type, leaves);
    }
  }
  return leaves;
}
registry/new-york-v4/ui/code-viewer-syntax-worker.ts
export function createCodeSyntaxWorker(): Worker {
  return new Worker(new URL("./code-viewer-syntax.worker.ts", import.meta.url), {
    type: "module",
  });
}
registry/new-york-v4/ui/code-viewer-syntax.worker.ts
import {
  type CodeSyntaxWorkerRequest,
  type CodeSyntaxWorkerResponse,
  shouldTokenizeCodeLine,
} from "./code-viewer-syntax-protocol";
import {
  ensureCodePrismLanguage,
  tokenizeCodeLine,
} from "./code-viewer-syntax-prism";

const workerSelf = self as unknown as {
  onmessage: ((event: MessageEvent<CodeSyntaxWorkerRequest>) => void) | null;
  postMessage(message: CodeSyntaxWorkerResponse): void;
};

function post(message: CodeSyntaxWorkerResponse) {
  workerSelf.postMessage(message);
}

async function tokenizeInWorker(request: CodeSyntaxWorkerRequest) {
  await ensureCodePrismLanguage(request.languageId);
  post({
    type: "tokens",
    requestId: request.requestId,
    generation: request.generation,
    languageId: request.languageId,
    results: request.lines.map((line) => ({
      line,
      tokens: shouldTokenizeCodeLine(line)
        ? tokenizeCodeLine(request.languageId, line)
        : null,
    })),
  });
}

workerSelf.onmessage = (event: MessageEvent<CodeSyntaxWorkerRequest>) => {
  const request = event.data;
  if (request.type !== "tokenize") return;

  void tokenizeInWorker(request).catch((error) => {
    post({
      type: "error",
      requestId: request.requestId,
      generation: request.generation,
      languageId: request.languageId,
      message: error instanceof Error ? error.message : String(error),
    });
  });
};
registry/new-york-v4/ui/code-viewer-prism-components.d.ts
declare module "prismjs/components/prism-bash";
declare module "prismjs/components/prism-diff";
declare module "prismjs/components/prism-docker";
declare module "prismjs/components/prism-go";
declare module "prismjs/components/prism-java";
declare module "prismjs/components/prism-json";
declare module "prismjs/components/prism-jsx";
declare module "prismjs/components/prism-markdown";
declare module "prismjs/components/prism-python";
declare module "prismjs/components/prism-rust";
declare module "prismjs/components/prism-ruby";
declare module "prismjs/components/prism-sql";
declare module "prismjs/components/prism-tsx";
declare module "prismjs/components/prism-typescript";
declare module "prismjs/components/prism-yaml";
registry/new-york-v4/ui/code-viewer-syntax-style.tsx
"use client";

import * as React from "react";

import { CODE_VIEWER_SYNTAX_STYLE } from "./code-viewer-syntax";

const CODE_VIEWER_SYNTAX_STYLE_ID = "retab-code-viewer-syntax-style";

export function useCodeViewerSyntaxStyle() {
  React.useInsertionEffect(() => {
    let style = document.getElementById(CODE_VIEWER_SYNTAX_STYLE_ID);
    if (style) return;

    style = document.createElement("style");
    style.id = CODE_VIEWER_SYNTAX_STYLE_ID;
    style.textContent = CODE_VIEWER_SYNTAX_STYLE;
    document.head.append(style);
  }, []);
}
registry/new-york-v4/ui/code-viewer-virtualization.ts
import {
  CODE_VIEWER_BLOCK_PADDING,
  CODE_VIEWER_LINE_CHECKPOINT_INTERVAL,
  CODE_VIEWER_SCROLL_REBASE_CONTAINER_PX,
  CODE_VIEWER_SCROLL_REBASE_TARGET_BOTTOM_PX,
  CODE_VIEWER_SCROLL_REBASE_TARGET_PX,
  CODE_VIEWER_SCROLL_REBASE_THRESHOLD_PX,
  CODE_VIEWER_SCROLL_REBASE_TRIGGER_PX,
} from "./code-viewer-scale";

export interface CodeVirtualLine {
  index: number;
  key: number;
  size: number;
  start: number;
}

export interface CodeVirtualLineWindow {
  lineCount: number;
  lineHeight: number;
  overscanPx: number;
  paddingStart: number;
  scrollTop: number;
  viewportHeight: number;
}

export interface CodeVirtualPixelWindow {
  bottom: number;
  top: number;
}

export interface CodeVirtualLineWindowResult {
  lines: CodeVirtualLine[];
  pixelWindow: CodeVirtualPixelWindow;
}

export interface CodeLineCheckpoint {
  index: number;
  start: number;
}

export interface CodeScrollRebaseState {
  scrollPageOffset: number;
}

export interface CodeScrollRebasePosition extends CodeScrollRebaseState {
  physicalScrollTop: number;
}

export function getCodeVirtualLines({
  lineCount,
  lineHeight,
  overscanPx,
  paddingStart,
  scrollTop,
  viewportHeight,
}: CodeVirtualLineWindow): CodeVirtualLine[] {
  return getCodeVirtualLineWindow({
    lineCount,
    lineHeight,
    overscanPx,
    paddingStart,
    scrollTop,
    viewportHeight,
  }).lines;
}

export function getCodeVirtualLineWindow({
  lineCount,
  lineHeight,
  overscanPx,
  paddingStart,
  scrollTop,
  viewportHeight,
}: CodeVirtualLineWindow): CodeVirtualLineWindowResult {
  const safeLineCount = safeCount(lineCount);
  const safeLineHeight = safeSize(lineHeight);
  if (safeLineCount === 0) {
    return {
      lines: [],
      pixelWindow: { bottom: 0, top: 0 },
    };
  }

  const safeScrollTop = Math.max(0, finiteNumber(scrollTop));
  const safeViewportHeight = Math.max(0, finiteNumber(viewportHeight));
  const safePaddingStart = safePadding(paddingStart);
  const totalSize = getCodeVirtualTotalSize({
    lineCount: safeLineCount,
    lineHeight: safeLineHeight,
    paddingStart: safePaddingStart,
  });
  const window = getCodeVirtualPixelWindow({
    overscanPx,
    scrollHeight: totalSize,
    scrollTop: safeScrollTop,
    viewportHeight: safeViewportHeight,
  });
  const start = getCodeLineIndexAtOffset({
    lineCount: safeLineCount,
    lineHeight: safeLineHeight,
    offset: window.top,
    paddingStart: safePaddingStart,
  });
  const end = Math.min(
    safeLineCount,
    getCodeLineIndexAfterOffset({
      lineCount: safeLineCount,
      lineHeight: safeLineHeight,
      offset: window.bottom,
      paddingStart: safePaddingStart,
    }),
  );

  return {
    lines: Array.from({ length: end - start }, (_, offset) => {
      const index = start + offset;
      return {
        index,
        key: index,
        size: safeLineHeight,
        start: safePaddingStart + index * safeLineHeight,
      };
    }),
    pixelWindow: window,
  };
}

export function getCodeLineCheckpoint({
  lineCount,
  lineHeight,
  offset,
  paddingStart = CODE_VIEWER_BLOCK_PADDING,
}: {
  lineCount: number;
  lineHeight: number;
  offset: number;
  paddingStart?: number;
}): CodeLineCheckpoint {
  const safeLineCount = safeCount(lineCount);
  if (safeLineCount === 0) {
    return {
      index: 0,
      start: safePadding(paddingStart),
    };
  }

  const safeLineHeight = safeSize(lineHeight);
  const safePaddingStart = safePadding(paddingStart);
  const lineIndex = clamp(
    Math.floor(Math.max(0, finiteNumber(offset) - safePaddingStart) / safeLineHeight),
    0,
    safeLineCount - 1,
  );
  const checkpointIndex =
    Math.floor(lineIndex / CODE_VIEWER_LINE_CHECKPOINT_INTERVAL) *
    CODE_VIEWER_LINE_CHECKPOINT_INTERVAL;

  return {
    index: checkpointIndex,
    start: safePaddingStart + checkpointIndex * safeLineHeight,
  };
}

export function getCodeLineIndexAtOffset({
  lineCount,
  lineHeight,
  offset,
  paddingStart = CODE_VIEWER_BLOCK_PADDING,
}: {
  lineCount: number;
  lineHeight: number;
  offset: number;
  paddingStart?: number;
}) {
  const safeLineCount = safeCount(lineCount);
  if (safeLineCount === 0) return 0;

  const safeLineHeight = safeSize(lineHeight);
  const checkpoint = getCodeLineCheckpoint({
    lineCount: safeLineCount,
    lineHeight: safeLineHeight,
    offset,
    paddingStart,
  });
  const indexFromCheckpoint = Math.floor(
    Math.max(0, finiteNumber(offset) - checkpoint.start) / safeLineHeight,
  );

  return clamp(checkpoint.index + indexFromCheckpoint, 0, safeLineCount - 1);
}

export function getCodeLineIndexAfterOffset({
  lineCount,
  lineHeight,
  offset,
  paddingStart = CODE_VIEWER_BLOCK_PADDING,
}: {
  lineCount: number;
  lineHeight: number;
  offset: number;
  paddingStart?: number;
}) {
  const safeLineCount = safeCount(lineCount);
  if (safeLineCount === 0) return 0;

  const safeLineHeight = safeSize(lineHeight);
  const checkpoint = getCodeLineCheckpoint({
    lineCount: safeLineCount,
    lineHeight: safeLineHeight,
    offset,
    paddingStart,
  });
  const indexFromCheckpoint = Math.ceil(
    Math.max(0, finiteNumber(offset) - checkpoint.start) / safeLineHeight,
  );

  return clamp(checkpoint.index + indexFromCheckpoint, 0, safeLineCount);
}

export function getCodeVirtualPixelWindow({
  overscanPx,
  scrollHeight,
  scrollTop,
  viewportHeight,
}: {
  overscanPx: number;
  scrollHeight: number;
  scrollTop: number;
  viewportHeight: number;
}): CodeVirtualPixelWindow {
  const safeOverscanPx = safePadding(overscanPx);
  const safeViewportHeight = Math.max(0, finiteNumber(viewportHeight));
  const windowHeight = safeViewportHeight + safeOverscanPx * 2;
  const safeScrollHeight = Math.max(0, finiteNumber(scrollHeight));

  if (windowHeight >= safeScrollHeight) {
    return { bottom: safeScrollHeight, top: 0 };
  }

  const scrollCenter = Math.max(0, finiteNumber(scrollTop)) + safeViewportHeight / 2;
  let top = scrollCenter - windowHeight / 2;
  let bottom = top + windowHeight;

  if (top < 0) {
    top = 0;
    bottom = windowHeight;
  }
  if (bottom > safeScrollHeight) {
    bottom = safeScrollHeight;
    top = safeScrollHeight - windowHeight;
  }

  return {
    bottom: Math.ceil(Math.max(bottom, top)),
    top: Math.floor(Math.max(0, top)),
  };
}

export function getCodeVirtualTotalSize({
  lineCount,
  lineHeight,
  paddingEnd = CODE_VIEWER_BLOCK_PADDING,
  paddingStart = CODE_VIEWER_BLOCK_PADDING,
}: {
  lineCount: number;
  lineHeight: number;
  paddingEnd?: number;
  paddingStart?: number;
}) {
  return (
    safePadding(paddingStart) +
    safeCount(lineCount) * safeSize(lineHeight) +
    safePadding(paddingEnd)
  );
}

export function getCodePhysicalScrollSize({
  totalSize,
  viewportHeight,
}: {
  totalSize: number;
  viewportHeight: number;
}) {
  const safeTotalSize = safeSize(totalSize);
  return shouldRebaseCodeScroll({ totalSize: safeTotalSize, viewportHeight })
    ? Math.min(safeTotalSize, CODE_VIEWER_SCROLL_REBASE_CONTAINER_PX)
    : safeTotalSize;
}

export function getCodeLogicalScrollTop({
  physicalScrollTop,
  scrollPageOffset,
  totalSize,
  viewportHeight,
}: {
  physicalScrollTop: number;
  scrollPageOffset: number;
  totalSize: number;
  viewportHeight: number;
}) {
  return clamp(
    finiteNumber(physicalScrollTop) + safePadding(scrollPageOffset),
    0,
    getCodeMaxLogicalScrollTop({ totalSize, viewportHeight }),
  );
}

export function resolveCodePhysicalScrollPosition({
  logicalScrollTop,
  scrollPageOffset,
  totalSize,
  viewportHeight,
}: {
  logicalScrollTop: number;
  scrollPageOffset: number;
  totalSize: number;
  viewportHeight: number;
}): CodeScrollRebasePosition {
  const safeLogicalScrollTop = clamp(
    finiteNumber(logicalScrollTop),
    0,
    getCodeMaxLogicalScrollTop({ totalSize, viewportHeight }),
  );

  if (!shouldRebaseCodeScroll({ totalSize, viewportHeight })) {
    return {
      physicalScrollTop: clamp(
        safeLogicalScrollTop,
        0,
        getCodeMaxPhysicalScrollTop({ totalSize, viewportHeight }),
      ),
      scrollPageOffset: 0,
    };
  }

  const currentPageOffset = clampCodeScrollPageOffset({
    scrollPageOffset,
    totalSize,
    viewportHeight,
  });
  const physicalScrollTop = safeLogicalScrollTop - currentPageOffset;
  const maxPhysicalScrollTop = getCodeMaxPhysicalScrollTop({
    totalSize,
    viewportHeight,
  });
  const maxPageOffset = getCodeMaxScrollPageOffset({ totalSize, viewportHeight });
  const shouldMoveDown =
    physicalScrollTop > CODE_VIEWER_SCROLL_REBASE_THRESHOLD_PX &&
    currentPageOffset < maxPageOffset;
  const shouldMoveUp =
    physicalScrollTop < CODE_VIEWER_SCROLL_REBASE_TRIGGER_PX &&
    currentPageOffset > 0;

  if (
    physicalScrollTop < 0 ||
    physicalScrollTop > maxPhysicalScrollTop ||
    shouldMoveDown ||
    shouldMoveUp
  ) {
    return resolveCodeScrollPageWindow({
      logicalScrollTop: safeLogicalScrollTop,
      preferredPhysicalScrollTop: shouldMoveUp
        ? Math.min(
            CODE_VIEWER_SCROLL_REBASE_TARGET_BOTTOM_PX,
            maxPhysicalScrollTop,
          )
        : CODE_VIEWER_SCROLL_REBASE_TARGET_PX,
      totalSize,
      viewportHeight,
    });
  }

  return {
    physicalScrollTop: roundCodeScrollPixel(
      clamp(physicalScrollTop, 0, maxPhysicalScrollTop),
    ),
    scrollPageOffset: currentPageOffset,
  };
}

export function getCodePagedLayoutTop({
  logicalTop,
  scrollPageOffset,
  totalSize,
  viewportHeight,
}: {
  logicalTop: number;
  scrollPageOffset: number;
  totalSize: number;
  viewportHeight: number;
}) {
  if (!shouldRebaseCodeScroll({ totalSize, viewportHeight })) {
    return finiteNumber(logicalTop);
  }
  return Math.max(0, finiteNumber(logicalTop) - safePadding(scrollPageOffset));
}

function shouldRebaseCodeScroll({
  totalSize,
  viewportHeight,
}: {
  totalSize: number;
  viewportHeight: number;
}) {
  return (
    getCodeMaxLogicalScrollTop({ totalSize, viewportHeight }) >
    CODE_VIEWER_SCROLL_REBASE_THRESHOLD_PX
  );
}

function getCodeMaxLogicalScrollTop({
  totalSize,
  viewportHeight,
}: {
  totalSize: number;
  viewportHeight: number;
}) {
  return Math.max(safeSize(totalSize) - Math.max(0, finiteNumber(viewportHeight)), 0);
}

function getCodeMaxPhysicalScrollTop({
  totalSize,
  viewportHeight,
}: {
  totalSize: number;
  viewportHeight: number;
}) {
  return Math.max(
    getCodePhysicalScrollSize({ totalSize, viewportHeight }) -
      Math.max(0, finiteNumber(viewportHeight)),
    0,
  );
}

function getCodeMaxScrollPageOffset({
  totalSize,
  viewportHeight,
}: {
  totalSize: number;
  viewportHeight: number;
}) {
  return Math.max(
    getCodeMaxLogicalScrollTop({ totalSize, viewportHeight }) -
      getCodeMaxPhysicalScrollTop({ totalSize, viewportHeight }),
    0,
  );
}

function clampCodeScrollPageOffset({
  scrollPageOffset,
  totalSize,
  viewportHeight,
}: {
  scrollPageOffset: number;
  totalSize: number;
  viewportHeight: number;
}) {
  return clamp(
    safePadding(scrollPageOffset),
    0,
    getCodeMaxScrollPageOffset({ totalSize, viewportHeight }),
  );
}

function resolveCodeScrollPageWindow({
  logicalScrollTop,
  preferredPhysicalScrollTop,
  totalSize,
  viewportHeight,
}: {
  logicalScrollTop: number;
  preferredPhysicalScrollTop: number;
  totalSize: number;
  viewportHeight: number;
}): CodeScrollRebasePosition {
  let physicalScrollTop = roundCodeScrollPixel(
    clamp(
      finiteNumber(preferredPhysicalScrollTop),
      0,
      getCodeMaxPhysicalScrollTop({ totalSize, viewportHeight }),
    ),
  );
  let scrollPageOffset = clampCodeScrollPageOffset({
    scrollPageOffset: logicalScrollTop - physicalScrollTop,
    totalSize,
    viewportHeight,
  });

  physicalScrollTop = roundCodeScrollPixel(
    clamp(
      logicalScrollTop - scrollPageOffset,
      0,
      getCodeMaxPhysicalScrollTop({ totalSize, viewportHeight }),
    ),
  );
  scrollPageOffset = clampCodeScrollPageOffset({
    scrollPageOffset: logicalScrollTop - physicalScrollTop,
    totalSize,
    viewportHeight,
  });

  return { physicalScrollTop, scrollPageOffset };
}

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

function roundCodeScrollPixel(value: number) {
  if (typeof window === "undefined") return Math.round(value);
  const ratio = window.devicePixelRatio || 1;
  return Math.round(value * ratio) / ratio;
}

function safeCount(value: number) {
  return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
}

function safeSize(value: number) {
  return Number.isFinite(value) && value > 0 ? value : 1;
}

function safePadding(value: number) {
  return Number.isFinite(value) && value > 0 ? value : 0;
}

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

import * as React from "react";

import { CODE_GUTTER_BACKGROUND } from "./code-viewer-projector";
import {
  CODE_VIEWER_BASE_FONT_PX,
  CODE_VIEWER_INITIAL_VIEWPORT_HEIGHT,
} from "./code-viewer-scale";
import {
  getCodePhysicalScrollSize,
  getCodeVirtualTotalSize,
} from "./code-viewer-virtualization";
import { ScrollArea } from "./scroll-area";

const CODE_VIEWER_DEFAULT_VIEWPORT_WIDTH = 800;

export function CodeViewerViewport({
  fontScale,
  gutterWidth,
  lineCount,
  lineHeight,
  onCopy,
  rowHostRef,
  viewportRef,
}: {
  fontScale: number;
  gutterWidth: string;
  lineCount: number;
  lineHeight: number;
  onCopy?: React.ClipboardEventHandler<HTMLPreElement>;
  rowHostRef: React.RefObject<HTMLPreElement | null>;
  viewportRef: React.RefObject<HTMLDivElement | null>;
}) {
  const fontSize = `${CODE_VIEWER_BASE_FONT_PX * fontScale}px`;
  const totalSize = getCodeVirtualTotalSize({
    lineCount,
    lineHeight,
  });
  const physicalTotalSize = getCodePhysicalScrollSize({
    totalSize,
    viewportHeight: CODE_VIEWER_INITIAL_VIEWPORT_HEIGHT,
  });

  return (
    <div className="bg-background relative min-h-0 flex-1">
      {/* Fixed full-height gutter rail, behind the scrolling content: the
          line-number column and its divider always reach the bottom of the
          viewport and never move while scrolling, because it lives outside the
          scroll container. The per-row gutters paint the numbers — and mask
          horizontally-scrolled code — on top of it. The viewport itself is
          transparent so this shows through below the last line. */}
      <div
        aria-hidden
        data-code-gutter-rail=""
        className="pointer-events-none absolute inset-y-0 left-0 z-0 border-r font-mono"
        style={{
          width: gutterWidth,
          backgroundColor: CODE_GUTTER_BACKGROUND,
          fontSize,
        }}
      />
      <ScrollArea
        className="absolute inset-0 z-10"
        viewportProps={{ style: { overflowAnchor: "none" } }}
        viewportRef={viewportRef}
      >
        <div
          data-code-scroll-spacer=""
          className="relative w-max min-w-full font-mono"
          style={{
            contain: "layout style",
            fontSize,
            height: physicalTotalSize,
            lineHeight: `${lineHeight}px`,
            minWidth: CODE_VIEWER_DEFAULT_VIEWPORT_WIDTH,
          }}
        >
          <div
            aria-hidden
            data-code-render-offset=""
            style={{
              contain: "layout size",
              height: 0,
            }}
          />
          <div
            data-code-render-window=""
            className="w-full"
            style={{
              bottom: 0,
              contain: "layout style inline-size",
              display: "flex",
              flexDirection: "column",
              height: physicalTotalSize,
              isolation: "isolate",
              position: "sticky",
              top: 0,
            }}
          >
            <pre
              onCopy={onCopy}
              ref={rowHostRef}
              className="relative w-full"
              suppressHydrationWarning
              style={{
                fontSize,
                height: physicalTotalSize,
                lineHeight: `${lineHeight}px`,
              }}
            />
          </div>
        </div>
      </ScrollArea>
    </div>
  );
}
registry/new-york-v4/ui/viewer-download.tsx
"use client";

import * as React from "react";
import { Download } from "lucide-react";

import { cn } from "@/lib/utils";
import {
  ViewerDownloadError,
  type ViewerDownloadAction,
  type ViewerDownloadPayload,
} from "@/lib/viewer-download-actions";

import { Spinner } from "@/components/ui/spinner";

import { Button, buttonVariants } from "./button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "./dropdown-menu";
import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { joinEffectKey } from "@/lib/effect-key";

export interface TriggerViewerDownloadOptions {
  signal?: AbortSignal;
}

export async function triggerViewerDownload(
  action: ViewerDownloadAction,
  options?: TriggerViewerDownloadOptions,
): Promise<void> {
  if (action.isDisabled) {
    throw new ViewerDownloadError({
      actionId: action.id,
      kind: "disabled",
      message: "This download is disabled.",
    });
  }

  let payload: ViewerDownloadPayload;
  try {
    payload = await action.getPayload(options);
  } catch (error) {
    if (isAbortError(error)) {
      throw new ViewerDownloadError({
        actionId: action.id,
        kind: "aborted",
        message: "Download was cancelled.",
        cause: error,
      });
    }
    throw new ViewerDownloadError({
      actionId: action.id,
      kind: "payload_failed",
      message: "Could not prepare this download.",
      cause: error,
    });
  }

  if (payload.kind === "none") return;
  if (payload.kind === "href") {
    try {
      clickDownload(payload.href, action.fileName);
    } catch (error) {
      throw new ViewerDownloadError({
        actionId: action.id,
        kind: "unsupported",
        message: "Could not start this download.",
        cause: error,
      });
    }
    return;
  }

  try {
    const blob =
      payload.kind === "blob"
        ? payload.blob
        : new Blob([payload.text], {
            type: payload.mimeType ?? "text/plain;charset=utf-8",
          });
    const url = URL.createObjectURL(blob);
    try {
      clickDownload(url, action.fileName);
    } finally {
      URL.revokeObjectURL(url);
    }
  } catch (error) {
    throw new ViewerDownloadError({
      actionId: action.id,
      kind: "unsupported",
      message: "Could not start this download.",
      cause: error,
    });
  }
}

export type ViewerDownloadErrorHandler = (
  error: ViewerDownloadError,
  action: ViewerDownloadAction,
) => void;

export interface ViewerDownloadTrigger {
  pendingActionId: string | null;
  triggerDownload: (action: ViewerDownloadAction) => void;
}

export interface ViewerDownloadTriggerOptions {
  /** Reports non-aborted action failures; visible failure UI belongs to consumers. */
  onError?: ViewerDownloadErrorHandler;
  resetKey?: unknown;
}

export interface ViewerDownloadControlProps {
  actions: Array<ViewerDownloadAction | null | undefined>;
  variant?: React.ComponentProps<typeof Button>["variant"];
  size?: React.ComponentProps<typeof Button>["size"];
  className?: string;
  showLabel?: boolean;
  /** Reports non-aborted action failures; visible failure UI belongs to consumers. */
  onError?: ViewerDownloadErrorHandler;
}

export interface ViewerDownloadButtonProps
  extends Omit<ViewerDownloadControlProps, "actions"> {
  action: ViewerDownloadAction | null;
}

export interface ViewerDownloadMenuProps
  extends Omit<ViewerDownloadControlProps, "actions"> {
  actions: ViewerDownloadAction[];
}

export function useViewerDownloadHref(
  action: ViewerDownloadAction | null,
): string | null {
  const shouldCreateHref = action?.origin !== "derived";
  const payload = shouldCreateHref ? getSynchronousPayload(action) : null;

  return shouldCreateHref && payload?.kind === "href" ? payload.href : null;
}

export function useViewerDownloadTrigger({
  onError,
  resetKey = "",
}: ViewerDownloadTriggerOptions = {}): ViewerDownloadTrigger {
  const [pendingActionId, setPendingActionId] = React.useState<string | null>(
    null,
  );
  const abortControllerRef = React.useRef<AbortController | null>(null);

  useKeyedMountEffect(joinEffectKey([resetKey]), () => {
    return () => {
      abortControllerRef.current?.abort();
      abortControllerRef.current = null;
    };
  });

  const triggerDownload = React.useCallback(
    (action: ViewerDownloadAction) => {
      abortControllerRef.current?.abort();
      const abortController = new AbortController();
      abortControllerRef.current = abortController;
      setPendingActionId(action.id);
      void triggerViewerDownload(action, { signal: abortController.signal })
        .catch((error) => {
          reportDownloadError(error, action, onError);
        })
        .finally(() => {
          if (abortControllerRef.current === abortController) {
            abortControllerRef.current = null;
            setPendingActionId(null);
          }
        });
    },
    [onError],
  );

  return { pendingActionId, triggerDownload };
}

export function ViewerDownloadControl({
  actions,
  variant = "ghost",
  size = "iconSm",
  className = "size-7",
  showLabel = false,
  onError,
}: ViewerDownloadControlProps) {
  const enabledActions = actions.filter(
    (action): action is ViewerDownloadAction => Boolean(action),
  );

  if (enabledActions.length <= 1) {
    return (
      <ViewerDownloadButton
        action={enabledActions[0] ?? null}
        variant={variant}
        size={size}
        className={className}
        showLabel={showLabel}
        onError={onError}
      />
    );
  }

  return (
    <ViewerDownloadMenu
      actions={enabledActions}
      variant={variant}
      size={size}
      className={className}
      showLabel={showLabel}
      onError={onError}
    />
  );
}

export function ViewerDownloadButton({
  action,
  variant = "ghost",
  size = "iconSm",
  className = "size-7",
  showLabel = false,
  onError,
}: ViewerDownloadButtonProps) {
  const href = useViewerDownloadHref(action);
  const label = action?.label ?? "Download";
  const disabled = !action || action.isDisabled;
  const hasDownloadHref = Boolean(href);
  const { pendingActionId, triggerDownload } = useViewerDownloadTrigger({
    onError,
    resetKey: action,
  });
  const isPending = Boolean(action && pendingActionId === action.id);

  const handleClick = React.useCallback(() => {
    if (!action || hasDownloadHref) return;
    triggerDownload(action);
  }, [action, hasDownloadHref, triggerDownload]);

  if (href) {
    return (
      <a
        href={href}
        download={action?.fileName}
        className={cn(buttonVariants({ variant, size }), className)}
        aria-label={label}
        title={label}
        data-slot="button"
      >
        <Download className={showLabel ? "mr-1.5 size-4" : undefined} />
        {showLabel ? label : null}
      </a>
    );
  }

  return (
    <Button
      variant={variant}
      size={size}
      className={className}
      aria-label={label}
      title={label}
      disabled={disabled || isPending}
      onClick={handleClick}
    >
      {isPending ? (
        <Spinner className="size-4 animate-spin" />
      ) : (
        <Download className={showLabel ? "mr-1.5 size-4" : undefined} />
      )}
      {showLabel ? label : null}
    </Button>
  );
}

export function ViewerDownloadMenu({
  actions,
  variant = "ghost",
  size = "iconSm",
  className = "size-7",
  showLabel = false,
  onError,
}: ViewerDownloadMenuProps) {
  const actionSetKey = actions.map((action) => action.id).join("\u0000");
  const label = "Download";
  const { pendingActionId, triggerDownload } = useViewerDownloadTrigger({
    onError,
    resetKey: actionSetKey,
  });

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button
          variant={variant}
          size={size}
          className={className}
          aria-label={label}
          title={label}
          disabled={pendingActionId != null}
        >
          {pendingActionId != null ? (
            <Spinner className="size-4 animate-spin" />
          ) : (
            <Download className={showLabel ? "mr-1.5 size-4" : undefined} />
          )}
          {showLabel ? label : null}
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        {actions.map((action) => (
          <DropdownMenuItem
            key={action.id}
            disabled={action.isDisabled || pendingActionId != null}
            onClick={() => triggerDownload(action)}
          >
            <Download />
            <span>{action.label}</span>
          </DropdownMenuItem>
        ))}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

function clickDownload(href: string, fileName: string) {
  const anchor = document.createElement("a");
  anchor.href = href;
  anchor.download = fileName;
  anchor.rel = "noreferrer";
  document.body.appendChild(anchor);
  anchor.click();
  anchor.remove();
}

function getSynchronousPayload(
  action: ViewerDownloadAction | null,
): ViewerDownloadPayload | null {
  if (!action || action.isDisabled) return null;
  const payload = action.getPayload();
  return payload instanceof Promise ? null : payload;
}

function reportDownloadError(
  error: unknown,
  action: ViewerDownloadAction,
  onError: ViewerDownloadErrorHandler | undefined,
) {
  if (!(error instanceof ViewerDownloadError)) return;
  if (error.kind === "aborted") return;
  onError?.(error, action);
}

function isAbortError(error: unknown) {
  return error instanceof DOMException && error.name === "AbortError";
}
registry/new-york-v4/ui/viewer-error.tsx
"use client";

import * as React from "react";
import { RotateCcw } from "lucide-react";

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

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

export interface ViewerErrorStateProps extends ViewerErrorContext {
  error: unknown;
  download?: ViewerDownloadAction | null;
  className?: string;
  bare?: boolean;
  variant?: "card" | "document" | "inline";
  onRetry?: () => void;
}

export function ViewerErrorState({
  error,
  format,
  sourceKind,
  canDownload,
  retry,
  download,
  className,
  bare = false,
  variant = "card",
  onRetry,
}: ViewerErrorStateProps) {
  const info = toViewerErrorInfo(error, {
    format,
    sourceKind,
    canDownload: canDownload ?? Boolean(download && !download.isDisabled),
    retry,
  });
  const showRetry = info.isRetryable && onRetry;
  const showDownload =
    info.isDownloadUseful && download != null && !download.isDisabled;

  return (
    <div
      className={cn(errorStateClassName({ bare, variant }), className)}
      data-error-domain={info.domain}
      data-error-format={info.format}
      data-error-kind={info.kind}
      data-error-message={info.message}
      data-slot="viewer-error"
      role="alert"
    >
      <p>{info.userMessage}</p>
      {showRetry || showDownload ? (
        <div className="flex items-center gap-2">
          {showRetry ? (
            <Button variant="outline" size="sm" onClick={onRetry}>
              <RotateCcw className="mr-1.5 size-4" />
              Retry
            </Button>
          ) : null}
          {showDownload ? (
            <ViewerDownloadButton
              action={download}
              variant={showRetry ? "ghost" : "outline"}
              size="sm"
              className=""
              showLabel
            />
          ) : null}
        </div>
      ) : null}
    </div>
  );
}

export interface ViewerErrorBoundaryProps extends ViewerErrorContext {
  children: React.ReactNode;
  resetKey?: unknown;
  download?: ViewerDownloadAction | null;
  className?: string;
  bare?: boolean;
  variant?: "card" | "document" | "inline";
  mapError?: (error: unknown) => unknown;
  onRetry?: (error: unknown) => void;
  onCaughtError?: (error: unknown, errorInfo: React.ErrorInfo) => void;
}

export class ViewerErrorBoundary extends React.Component<
  ViewerErrorBoundaryProps,
  { error: unknown | null; retryKey: number }
> {
  state: Readonly<{ error: unknown | null; retryKey: number }> = {
    error: null,
    retryKey: 0,
  };

  componentDidUpdate(previousProps: ViewerErrorBoundaryProps) {
    if (
      previousProps.resetKey !== this.props.resetKey &&
      this.state.error != null
    ) {
      this.setState({ error: null });
    }
  }

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

  componentDidCatch(error: unknown, errorInfo: React.ErrorInfo) {
    this.props.onCaughtError?.(error, errorInfo);
  }

  render() {
    if (this.state.error != null) {
      const error = this.props.mapError
        ? this.props.mapError(this.state.error)
        : this.state.error;

      return (
        <ViewerErrorState
          error={error}
          format={this.props.format}
          sourceKind={this.props.sourceKind}
          canDownload={this.props.canDownload}
          retry={this.props.retry}
          download={this.props.download}
          className={this.props.className}
          bare={this.props.bare}
          variant={this.props.variant}
          onRetry={() => {
            this.setState((state) => ({
              error: null,
              retryKey: state.retryKey + 1,
            }));
            this.props.onRetry?.(this.state.error);
          }}
        />
      );
    }

    return (
      <React.Fragment key={this.state.retryKey}>
        {this.props.children}
      </React.Fragment>
    );
  }
}

function errorStateClassName({
  bare,
  variant,
}: {
  bare: boolean;
  variant: "card" | "document" | "inline";
}) {
  if (variant === "inline") {
    return "flex h-24 flex-col items-center justify-center gap-3 px-3 text-center text-xs text-muted-foreground";
  }
  return cn(
    "flex min-h-64 flex-col items-center justify-center gap-3 p-6 text-center text-sm text-muted-foreground",
    bare ? "bg-muted/20" : "rounded-xl border bg-muted/30",
    variant === "document" && "min-h-full",
  );
}

export type { ViewerFormat };
registry/new-york-v4/ui/text-viewer-ranges.ts
export interface TextLineRange {
  start: number;
  end: number;
}

export interface NormalizedTextLineRange extends TextLineRange {
  readonly normalized: true;
}

const clamp = (value: number, min: number, max: number) =>
  Math.min(max, Math.max(min, value));

export function normalizeTextLineRange(
  range: TextLineRange | null | undefined,
  lineCount: number,
): NormalizedTextLineRange | null {
  if (
    !range ||
    !Number.isFinite(range.start) ||
    !Number.isFinite(range.end) ||
    !Number.isFinite(lineCount) ||
    lineCount <= 0
  ) {
    return null;
  }

  const maxLine = Math.floor(lineCount);
  const rawStart = Math.trunc(range.start);
  const rawEnd = Math.trunc(range.end);
  const start = Math.min(rawStart, rawEnd);
  const end = Math.max(rawStart, rawEnd);

  if (end < 1 || start > maxLine) return null;

  return {
    start: clamp(start, 1, maxLine),
    end: clamp(end, 1, maxLine),
    normalized: true,
  };
}

export function isLineInRange(
  lineNumber: number,
  range: NormalizedTextLineRange | null,
) {
  return range != null && lineNumber >= range.start && lineNumber <= range.end;
}
registry/new-york-v4/ui/line-ranges.ts
export * from "./text-viewer-ranges";
registry/new-york-v4/ui/text-viewer-resource.ts
import {
  isResourceError,
  isViewerFormatError,
  ViewerFormatError,
  type ViewerFormatErrorMapperOptions,
} from "@/lib/viewer-errors";
import type {
  ViewerContentIdentity,
  ViewerContentPayload,
  ViewerContentText,
} from "@/lib/viewer-resource";

export const DEFAULT_MAX_BYTES = 1_000_000;
export const DEFAULT_MAX_LINES = 10_000;
export const MAX_TEXT_RESOURCE_CACHE_ENTRIES = 64;
export const TEXT_LINE_DETACHMENT_SOURCE_MIN_LENGTH = 64 * 1024;
export const TEXT_LINE_DETACHMENT_MAX_LINE_LENGTH = 16 * 1024;

export interface TextViewerBounds {
  maxBytes?: number;
  maxLines?: number;
}

export type TextViewerTooLargeReason = "bytes" | "lines";
export type TextViewerBoundName = "maxBytes" | "maxLines";

export class TextViewerTooLargeError extends ViewerFormatError {
  readonly reason: TextViewerTooLargeReason;

  constructor(reason: TextViewerTooLargeReason) {
    super({
      format: "text",
      kind: "bounds",
      message: `Text file exceeds ${reason} limit`,
    });
    this.name = "TextViewerTooLargeError";
    this.reason = reason;
  }
}

export class TextViewerInvalidBoundsError extends ViewerFormatError {
  readonly boundName: TextViewerBoundName;

  constructor(boundName: TextViewerBoundName) {
    super({
      format: "text",
      kind: "bounds",
      message: `${boundName} must be a positive integer`,
    });
    this.name = "TextViewerInvalidBoundsError";
    this.boundName = boundName;
  }
}

export function toTextFormatError(
  error: unknown,
  options: ViewerFormatErrorMapperOptions = {
    kind: "load_failed",
    message: "Failed to load text.",
  },
): ViewerFormatError {
  if (isViewerFormatError(error)) return error;
  return new ViewerFormatError({
    format: "text",
    kind: options.kind,
    message: options.message,
    cause: error,
  });
}

export interface PreparedTextDocument {
  text: string;
  lines: readonly string[];
  lineCount: number;
}

interface TextResource {
  promise: Promise<PreparedTextDocument>;
  status: "pending" | "resolved" | "rejected";
  value?: PreparedTextDocument;
  error?: unknown;
}

const textResourceCache = new Map<string, TextResource>();

export type TextViewerContent = ViewerContentIdentity &
  ViewerContentPayload &
  ViewerContentText;

function textViewerResourceKey({
  content,
  retryVersion,
  bounds,
}: {
  content: ViewerContentIdentity;
  retryVersion: number;
  bounds: Required<TextViewerBounds>;
}) {
  return `${content.key}\0${retryVersion}\0${bounds.maxBytes}\0${bounds.maxLines}`;
}

export function clearTextViewerResourceCacheForTests() {
  textResourceCache.clear();
}

export function resolvedTextViewerBounds({
  maxBytes = DEFAULT_MAX_BYTES,
  maxLines = DEFAULT_MAX_LINES,
}: TextViewerBounds = {}): Required<TextViewerBounds> {
  return {
    maxBytes: resolveTextViewerBound(maxBytes, "maxBytes"),
    maxLines: resolveTextViewerBound(maxLines, "maxLines"),
  };
}

export function assertTextWithinBounds(
  text: string,
  bounds: Required<TextViewerBounds>,
) {
  prepareTextDocument(text, bounds);
}

// Split into stable source lines. The prose viewer may wrap each source line
// into several visual lines, but highlighting and scroll APIs stay source-line
// based.
export function splitTextLines(text: string) {
  return splitTextLinesForDocument(text).lines;
}

export function shouldDetachTextLine({
  lineLength,
  sourceLength,
}: {
  lineLength: number;
  sourceLength: number;
}) {
  return (
    sourceLength >= TEXT_LINE_DETACHMENT_SOURCE_MIN_LENGTH &&
    lineLength > 0 &&
    lineLength <= TEXT_LINE_DETACHMENT_MAX_LINE_LENGTH
  );
}

export function detachTextLine(line: string) {
  return line.length === 0 ? line : ` ${line}`.slice(1);
}

export function readTextResource({
  content,
  retryVersion,
  bounds,
}: {
  content: TextViewerContent;
  retryVersion: number;
  bounds: Required<TextViewerBounds>;
}) {
  return readTextDocument({ content, retryVersion, bounds }).text;
}

export function readTextDocument({
  content,
  retryVersion,
  bounds,
}: {
  content: TextViewerContent;
  retryVersion: number;
  bounds: Required<TextViewerBounds>;
}): PreparedTextDocument {
  const resourceKey = textViewerResourceKey({ content, retryVersion, bounds });
  const inlineText = inlineTextResource(content);
  if (inlineText != null) {
    return getInlineTextDocument({
      bounds,
      resourceKey,
      text: inlineText,
    });
  }

  const textResource = getTextResource({ content, resourceKey, bounds });

  if (textResource.status === "resolved") {
    return textResource.value ?? emptyPreparedTextDocument(bounds);
  }
  if (textResource.status === "rejected") throw textResource.error;

  throw textResource.promise;
}

export function prepareTextDocument(
  text: string,
  bounds: Required<TextViewerBounds>,
  options: { isByteLengthChecked?: boolean } = {},
): PreparedTextDocument {
  if (
    !options.isByteLengthChecked &&
    new TextEncoder().encode(text).byteLength > bounds.maxBytes
  ) {
    throw new TextViewerTooLargeError("bytes");
  }

  const lines = splitTextLinesForDocument(text, {
    maxLines: bounds.maxLines,
  }).lines;

  return {
    lineCount: lines.length,
    lines,
    text,
  };
}

function inlineTextResource(content: ViewerContentPayload) {
  return content.payload.kind === "text" ? content.payload.text : null;
}

function splitTextLinesForDocument(
  text: string,
  options: { maxLines?: number } = {},
) {
  const maxLines = options.maxLines ?? Number.POSITIVE_INFINITY;
  const lines: string[] = [];
  let lineStart = 0;

  for (let index = 0; index < text.length; index += 1) {
    const breakLength = textLineBreakLength(text, index);
    if (breakLength === 0) continue;

    appendTextLine(lines, text, lineStart, index, maxLines);
    index += breakLength - 1;
    lineStart = index + 1;
  }

  appendTextLine(lines, text, lineStart, text.length, maxLines);
  return { lines };
}

function appendTextLine(
  lines: string[],
  text: string,
  start: number,
  end: number,
  maxLines: number,
) {
  if (lines.length >= maxLines) {
    throw new TextViewerTooLargeError("lines");
  }

  const line = text.slice(start, end);
  lines.push(
    shouldDetachTextLine({
      lineLength: end - start,
      sourceLength: text.length,
    })
      ? detachTextLine(line)
      : line,
  );
}

function textLineBreakLength(text: string, index: number) {
  const code = text.charCodeAt(index);
  if (code === 0x0d) {
    return text.charCodeAt(index + 1) === 0x0a ? 2 : 1;
  }
  return code === 0x0a || code === 0x2028 || code === 0x2029 ? 1 : 0;
}

function getInlineTextDocument({
  bounds,
  resourceKey,
  text,
}: {
  bounds: Required<TextViewerBounds>;
  resourceKey: string;
  text: string;
}) {
  const cached = textResourceCache.get(resourceKey);
  if (cached?.status === "resolved" && cached.value) return cached.value;

  const document = prepareTextDocument(text, bounds);
  textResourceCache.set(resourceKey, {
    promise: Promise.resolve(document),
    status: "resolved",
    value: document,
  });
  trimTextResourceCache();
  return document;
}

function getTextResource({
  content,
  resourceKey,
  bounds,
}: {
  content: TextViewerContent;
  resourceKey: string;
  bounds: Required<TextViewerBounds>;
}) {
  let textResource = textResourceCache.get(resourceKey);
  if (!textResource) {
    const nextResource: TextResource = {
      status: "pending",
      promise: readBoundedTextResource(content, bounds).then((text) =>
        prepareTextDocument(text, bounds, { isByteLengthChecked: true }),
      ),
    };
    nextResource.promise.then(
      (value) => {
        nextResource.status = "resolved";
        nextResource.value = value;
      },
      (error) => {
        nextResource.status = "rejected";
        nextResource.error = error;
      },
    );
    textResource = nextResource;
    textResourceCache.set(resourceKey, textResource);
    trimTextResourceCache();
  }
  return textResource;
}

async function readBoundedTextResource(
  content: ViewerContentText,
  bounds: Required<TextViewerBounds>,
) {
  try {
    return await content.readText(bounds);
  } catch (error) {
    if (isResourceError(error)) throw error;
    throw toTextFormatError(error);
  }
}

function emptyPreparedTextDocument(
  bounds: Required<TextViewerBounds>,
): PreparedTextDocument {
  return prepareTextDocument("", bounds);
}

function resolveTextViewerBound(value: number, boundName: TextViewerBoundName) {
  if (!Number.isSafeInteger(value) || value < 1) {
    throw new TextViewerInvalidBoundsError(boundName);
  }
  return value;
}

function trimTextResourceCache() {
  while (textResourceCache.size > MAX_TEXT_RESOURCE_CACHE_ENTRIES) {
    const firstKey = textResourceCache.keys().next().value;
    if (firstKey === undefined) return;
    textResourceCache.delete(firstKey);
  }
}
registry/new-york-v4/ui/plain-text-resource.ts
export * from "./text-viewer-resource";
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/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-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/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/code-viewer-long-lines.ts
export const CODE_VIEWER_LONG_LINE_RENDER_MAX = 4096;

const CODE_VIEWER_LONG_LINE_HEAD_CHARS = 3072;
const CODE_VIEWER_LONG_LINE_TAIL_CHARS = 768;

export type CodeLineRenderText = {
  isTruncated: boolean;
  omittedCharacterCount: number;
  text: string;
};

export function getCodeLineRenderText(text: string): CodeLineRenderText {
  if (text.length <= CODE_VIEWER_LONG_LINE_RENDER_MAX) {
    return {
      isTruncated: false,
      omittedCharacterCount: 0,
      text,
    };
  }

  const omittedCharacterCount =
    text.length -
    CODE_VIEWER_LONG_LINE_HEAD_CHARS -
    CODE_VIEWER_LONG_LINE_TAIL_CHARS;

  return {
    isTruncated: true,
    omittedCharacterCount,
    text:
      text.slice(0, CODE_VIEWER_LONG_LINE_HEAD_CHARS) +
      ` ... ${omittedCharacterCount} chars omitted ... ` +
      text.slice(-CODE_VIEWER_LONG_LINE_TAIL_CHARS),
  };
}

export function getCodeLongLineSelectionText({
  rowHost,
  selection,
  textLines,
}: {
  rowHost: HTMLPreElement;
  selection: Selection | null;
  textLines: readonly string[];
}) {
  if (!selection || selection.isCollapsed || selection.rangeCount === 0) {
    return null;
  }

  const selectedLineIndexes = new Set<number>();
  let includesTruncatedLine = false;

  for (let rangeIndex = 0; rangeIndex < selection.rangeCount; rangeIndex += 1) {
    const range = selection.getRangeAt(rangeIndex);
    if (!rangeIntersectsNode(range, rowHost)) continue;

    for (const row of rowHost.querySelectorAll<HTMLElement>(
      "[data-line-index]",
    )) {
      if (!rangeIntersectsNode(range, row)) continue;

      const lineIndex = Number(row.dataset.lineIndex);
      if (
        Number.isInteger(lineIndex) &&
        lineIndex >= 0 &&
        lineIndex < textLines.length
      ) {
        selectedLineIndexes.add(lineIndex);
      }
      if (row.dataset.codeLineTruncated != null) {
        includesTruncatedLine = true;
      }
    }
  }

  if (!includesTruncatedLine || selectedLineIndexes.size === 0) return null;

  return Array.from(selectedLineIndexes)
    .sort((first, second) => first - second)
    .map((lineIndex) => textLines[lineIndex] ?? "")
    .join("\n");
}

function rangeIntersectsNode(range: Range, node: Node) {
  try {
    if (range.intersectsNode(node)) return true;
  } catch {
    // Detached test DOM can make intersectsNode throw even when the selected
    // content is plainly inside the row.
  }
  return (
    node === range.commonAncestorContainer ||
    node.contains(range.commonAncestorContainer)
  );
}
registry/new-york-v4/ui/text-viewer-scroll-interactions.ts
"use client";

import * as React from "react";

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

export const TEXT_SCROLL_INTERACTION_RESTORE_DELAY_MS = 120;

export type ScrollInteractionSnapshot = {
  overflowTarget: HTMLElement | null;
  overflowX: InlineStyleSnapshot | null;
  pointerEvents: InlineStyleSnapshot;
  target: HTMLElement;
};

type InlineStyleSnapshot = {
  priority: string;
  value: string;
};

export function useTextViewerScrollInteractions<Viewport extends HTMLElement>({
  getInteractionTarget,
  getOverflowTarget,
  onScroll,
  viewportRef,
}: {
  getInteractionTarget: () => HTMLElement | null | undefined;
  getOverflowTarget?: () => HTMLElement | null | undefined;
  onScroll: (viewport: Viewport) => void;
  viewportRef: React.RefObject<Viewport | null>;
}) {
  const restoreTimerRef = React.useRef(0);
  const snapshotRef = React.useRef<ScrollInteractionSnapshot | null>(null);

  useKeyedMountEffect(
    joinEffectKey([
      getInteractionTarget,
      getOverflowTarget,
      onScroll,
      viewportRef,
    ]),
    () => {
      const viewport = viewportRef.current;
      if (!viewport) return;

      const handleScroll = () => {
        suspendTextViewerScrollInteractions({
          getInteractionTarget,
          getOverflowTarget,
          snapshotRef,
        });
        if (restoreTimerRef.current) {
          window.clearTimeout(restoreTimerRef.current);
        }
        restoreTimerRef.current = window.setTimeout(() => {
          restoreTimerRef.current = 0;
          restoreTextViewerScrollInteractions(snapshotRef);
        }, TEXT_SCROLL_INTERACTION_RESTORE_DELAY_MS);
        onScroll(viewport);
      };

      viewport.addEventListener("scroll", handleScroll, { passive: true });
      return () => {
        viewport.removeEventListener("scroll", handleScroll);
        if (restoreTimerRef.current) {
          window.clearTimeout(restoreTimerRef.current);
          restoreTimerRef.current = 0;
        }
        restoreTextViewerScrollInteractions(snapshotRef);
      };
    },
  );
}

export function suspendTextViewerScrollInteractions({
  getInteractionTarget,
  getOverflowTarget,
  snapshotRef,
}: {
  getInteractionTarget: () => HTMLElement | null | undefined;
  getOverflowTarget?: () => HTMLElement | null | undefined;
  snapshotRef: React.MutableRefObject<ScrollInteractionSnapshot | null>;
}) {
  const target = getInteractionTarget();
  if (!target) {
    restoreTextViewerScrollInteractions(snapshotRef);
    return;
  }

  const overflowTarget = isMobileSafari()
    ? (getOverflowTarget?.() ?? target.parentElement ?? target)
    : null;
  const current = snapshotRef.current;
  if (
    current &&
    current.target === target &&
    current.overflowTarget === overflowTarget
  ) {
    target.style.pointerEvents = "none";
    overflowTarget?.style.setProperty("overflow-x", "hidden");
    return;
  }

  restoreTextViewerScrollInteractions(snapshotRef);
  snapshotRef.current = {
    overflowTarget,
    overflowX: overflowTarget
      ? readInlineStyle(overflowTarget, "overflow-x")
      : null,
    pointerEvents: readInlineStyle(target, "pointer-events"),
    target,
  };
  target.style.pointerEvents = "none";
  overflowTarget?.style.setProperty("overflow-x", "hidden");
}

export function restoreTextViewerScrollInteractions(
  snapshotRef: React.MutableRefObject<ScrollInteractionSnapshot | null>,
) {
  const snapshot = snapshotRef.current;
  if (!snapshot) return;

  restoreInlineStyle(snapshot.target, "pointer-events", snapshot.pointerEvents);
  if (snapshot.overflowTarget && snapshot.overflowX) {
    restoreInlineStyle(
      snapshot.overflowTarget,
      "overflow-x",
      snapshot.overflowX,
    );
  }
  snapshotRef.current = null;
}

function isMobileSafari() {
  if (typeof navigator === "undefined") return false;
  const userAgent = navigator.userAgent;
  return (
    /Safari/i.test(userAgent) &&
    /Mobile/i.test(userAgent) &&
    !/CriOS|FxiOS|EdgiOS/i.test(userAgent)
  );
}

function readInlineStyle(
  element: HTMLElement,
  propertyName: string,
): InlineStyleSnapshot {
  return {
    priority: element.style.getPropertyPriority(propertyName),
    value: element.style.getPropertyValue(propertyName),
  };
}

function restoreInlineStyle(
  element: HTMLElement,
  propertyName: string,
  snapshot: InlineStyleSnapshot,
) {
  if (!snapshot.value) {
    element.style.removeProperty(propertyName);
    return;
  }
  element.style.setProperty(propertyName, snapshot.value, snapshot.priority);
}

More from retab

Browse all components in this library →