shadimage
retab
uimarkdown-viewersource: ok · refreshed

Markdown Viewer

A continuous Markdown viewer forked from the Chenglou Pretext text viewer, with block virtualization and no visible page model.

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

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
iconslucide-react

Files50

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

import * as React from "react";

import {
  MarkdownGreenfieldContent,
  type MarkdownViewerProps,
} from "./markdown-greenfield-content";
import type { ViewerResource } from "@/lib/viewer-resource";
import { PlainTextViewerFrame } from "./plain-text-viewer-frame";
import { TextViewerFallback } from "./text-viewer-chrome";
import type { TextViewerHandle } from "./text-viewer-types";

export type { MarkdownViewerProps } from "./markdown-greenfield-content";
export type {
  TextDocumentSource,
  TextLineRange,
  TextViewerHandle,
  TextViewerProps,
} from "./text-viewer-types";

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

export const MarkdownViewer = React.forwardRef<
  TextViewerHandle,
  MarkdownViewerProps
>(function MarkdownViewer(props, ref) {
  return (
    <PlainTextViewerFrame
      props={props}
      forwardedRef={ref}
      clientFallbackPolicy="non-inline-source"
      Fallback={TextViewerFallback}
      Content={MarkdownGreenfieldContent}
    />
  );
});

export const MarkdownResourceContent = React.forwardRef<
  TextViewerHandle,
  MarkdownResourceContentProps
>(function MarkdownResourceContent({ resource, ...props }, ref) {
  return (
    <PlainTextViewerFrame
      props={props}
      resource={resource}
      forwardedRef={ref}
      clientFallbackPolicy="non-inline-source"
      Fallback={TextViewerFallback}
      Content={MarkdownGreenfieldContent}
    />
  );
});
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/text-viewer-chrome.tsx
"use client";

import * as React from "react";
import { AlertCircle, Check, Copy } from "lucide-react";
import { useMountEffect } from "@/hooks/use-mount-effect";

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

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

export type ViewerClipboardCopyStatus = "copied" | "failed" | "idle";

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

export function TextViewerFallback({
  className,
  controls = true,
  download = true,
  bare,
}: {
  className?: string;
  controls?: boolean;
  download?: boolean;
  bare?: boolean;
}) {
  return (
    <TextViewerFrame className={className} bare={bare}>
      {controls ? (
        <ViewerControlsSkeleton title zoom download={download} />
      ) : null}
      <div
        className="min-h-0 flex-1 space-y-3 overflow-hidden p-5"
        data-slot="text-body-skeleton"
      >
        {Array.from({ length: 10 }, (_, index) => (
          <Skeleton
            key={index}
            className="h-4"
            style={{ width: `${48 + ((index * 17) % 44)}%` }}
          />
        ))}
      </div>
    </TextViewerFrame>
  );
}

export function TextViewerControls({
  wordCount,
  fontScale,
  copyText,
  copyLabel = "Copy text",
  downloadAction,
  extra,
  leading,
  onDownloadError,
  onZoomOut,
  onZoomIn,
  onResetZoom,
}: {
  wordCount: number;
  fontScale: number;
  copyText?: string;
  copyLabel?: string;
  downloadAction?: ViewerDownloadAction | null;
  extra?: React.ReactNode;
  leading?: React.ReactNode;
  onDownloadError?: ViewerDownloadErrorHandler;
  onZoomOut: () => void;
  onZoomIn: () => void;
  onResetZoom: () => void;
}) {
  const copyControl =
    copyText == null ? null : (
      <TextViewerCopyControl label={copyLabel} text={copyText} />
    );

  return (
    <ViewerControls
      title={leading ?? `${wordCount} word${wordCount === 1 ? "" : "s"}`}
      zoom={{
        scale: fontScale,
        onZoomOut,
        onZoomIn,
        onFit: onResetZoom,
        fitLabel: "Reset zoom",
      }}
      downloads={downloadAction ? [downloadAction] : undefined}
      onDownloadError={onDownloadError}
      extra={
        extra == null && copyControl == null ? null : (
          <span className="flex min-w-0 items-center gap-1">
            {extra}
            {copyControl}
          </span>
        )
      }
    />
  );
}

function TextViewerCopyControl({
  label,
  text,
}: {
  label: string;
  text: string;
}) {
  const { copy, status } = useViewerClipboardCopy();

  const copyText = () => {
    copy(text);
  };

  const buttonLabel =
    status === "copied"
      ? "Copied"
      : status === "failed"
        ? "Copy failed"
        : label;

  return (
    <ViewerControlButton label={buttonLabel} onClick={copyText} type="button">
      {status === "copied" ? (
        <Check />
      ) : status === "failed" ? (
        <AlertCircle />
      ) : (
        <Copy />
      )}
    </ViewerControlButton>
  );
}

export function useViewerClipboardCopy({
  resetDelay = 1200,
}: {
  resetDelay?: number;
} = {}) {
  const [status, setStatus] = React.useState<ViewerClipboardCopyStatus>("idle");
  const timeoutRef = React.useRef<number | null>(null);
  const isMountedRef = React.useRef(true);
  const copyAttemptRef = React.useRef(0);

  useMountEffect(() => {
    isMountedRef.current = true;
    return () => {
      isMountedRef.current = false;
      clearViewerClipboardCopyReset(timeoutRef);
    };
  });

  const scheduleReset = React.useCallback(() => {
    clearViewerClipboardCopyReset(timeoutRef);
    timeoutRef.current = window.setTimeout(() => {
      timeoutRef.current = null;
      if (isMountedRef.current) setStatus("idle");
    }, resetDelay);
  }, [resetDelay]);

  const copy = React.useCallback(
    (text: string) => {
      clearViewerClipboardCopyReset(timeoutRef);
      const copyAttempt = copyAttemptRef.current + 1;
      copyAttemptRef.current = copyAttempt;
      const isCurrentAttempt = () =>
        isMountedRef.current && copyAttemptRef.current === copyAttempt;

      try {
        const clipboard = navigator.clipboard;
        const writeText = clipboard?.writeText;
        if (typeof writeText !== "function") {
          setStatus("failed");
          scheduleReset();
          return;
        }

        void Promise.resolve(writeText.call(clipboard, text)).then(
          () => {
            if (!isCurrentAttempt()) return;
            setStatus("copied");
            scheduleReset();
          },
          () => {
            if (!isCurrentAttempt()) return;
            setStatus("failed");
            scheduleReset();
          },
        );
      } catch {
        setStatus("failed");
        scheduleReset();
      }
    },
    [scheduleReset],
  );

  return { copy, status };
}

function clearViewerClipboardCopyReset(
  timeoutRef: React.MutableRefObject<number | null>,
) {
  if (timeoutRef.current === null) return;
  window.clearTimeout(timeoutRef.current);
  timeoutRef.current = null;
}
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/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-layout.ts
"use client";

import {
  layoutNextLineRange,
  materializeLineRange,
  measureLineStats,
  measureNaturalWidth,
  prepareWithSegments,
  type LayoutCursor,
  type LayoutLine,
  type PreparedTextWithSegments,
} from "@chenglou/pretext";
import {
  layoutNextRichInlineLineRange,
  materializeRichInlineLineRange,
  measureRichInlineStats,
  prepareRichInline,
  type PreparedRichInline,
  type RichInlineCursor,
  type RichInlineLine,
} from "@chenglou/pretext/rich-inline";
import { marked, type Token, type Tokens } from "marked";

import { splitTextLines } from "./text-viewer-resource";

export type TextViewerMode = "markdown" | "text";

export interface TextStyleConfig {
  fontEpoch?: number;
  fontScale: number;
}

export interface PreparedTextDocument {
  blocks: PreparedTextBlock[];
  mode: TextViewerMode;
  sourceLineCount: number;
  wordCount: number;
}

export type PreparedTextBlock =
  | PreparedInlineTextBlock
  | PreparedCodeTextBlock
  | PreparedImageTextBlock
  | PreparedRuleTextBlock
  | PreparedTableTextBlock;

export interface PreparedTextBlockBase {
  contentLeft: number;
  listDepth: number;
  marginTop: number;
  markerClassName: string | null;
  markerLeft: number | null;
  markerText: string | null;
  quoteDepth: number;
  quoteRailLefts: number[];
  sourceEndLine: number;
  sourceStartLine: number;
}

export interface PreparedInlineTextBlock extends PreparedTextBlockBase {
  classNames: string[];
  fallbackText: string;
  flow: PreparedRichInline | null;
  fonts: string[];
  headingId: string | null;
  hrefs: Array<string | null>;
  kind: "inline";
  lineHeight: number;
  texts: string[];
  titles: Array<string | null>;
  variant: InlineVariant;
}

export interface PreparedCodeTextBlock extends PreparedTextBlockBase {
  fallbackText: string;
  font: string;
  kind: "code";
  language: string | null;
  lineHeight: number;
  prepared: PreparedTextWithSegments | null;
}

export interface PreparedImageTextBlock extends PreparedTextBlockBase {
  alt: string;
  href: string | null;
  kind: "image";
  src: string | null;
  title: string | null;
}

export interface PreparedRuleTextBlock extends PreparedTextBlockBase {
  height: number;
  kind: "rule";
}

export interface PreparedTableTextBlock extends PreparedTextBlockBase {
  alignments: TableColumnAlignment[];
  columnWidths: number[];
  header: PreparedTableCell[];
  kind: "table";
  rowSourceStartLines: number[];
  rows: PreparedTableCell[][];
}

export interface PreparedTableCell {
  className: string;
  href: string | null;
  text: string;
  title: string | null;
}

export interface TextDocumentFrame {
  frames: TextBlockFrame[];
  totalHeight: number;
  width: number;
}

export type TextBlockFrame =
  | InlineTextBlockFrame
  | CodeTextBlockFrame
  | ImageTextBlockFrame
  | RuleTextBlockFrame
  | TableTextBlockFrame;

interface TextBlockFrameBase {
  blockIndex: number;
  bottom: number;
  contentLeft: number;
  height: number;
  listDepth: number;
  markerClassName: string | null;
  markerLeft: number | null;
  markerText: string | null;
  quoteDepth: number;
  quoteRailLefts: number[];
  sourceEndLine: number;
  sourceStartLine: number;
  scale: number;
  top: number;
}

export interface InlineTextBlockFrame extends TextBlockFrameBase {
  kind: "inline";
  lineCount: number;
  lineHeight: number;
  usedWidth: number;
}

export interface CodeTextBlockFrame extends TextBlockFrameBase {
  kind: "code";
  language: string | null;
  lineCount: number;
  lineHeight: number;
  width: number;
}

export interface ImageTextBlockFrame extends TextBlockFrameBase {
  alt: string;
  imageHeight: number;
  imageWidth: number;
  kind: "image";
}

export interface RuleTextBlockFrame extends TextBlockFrameBase {
  kind: "rule";
  width: number;
}

export interface TableTextBlockFrame extends TextBlockFrameBase {
  columnWidths: number[];
  headerHeight: number;
  kind: "table";
  rowHeights: number[];
  rowOffsets: number[];
  rowCount: number;
  rowSourceStartLines: number[];
  tableWidth: number;
}

export interface InlineFragmentLayout {
  className: string;
  font: string;
  href: string | null;
  leadingGap: number;
  text: string;
  title: string | null;
}

export interface InlineLineLayout {
  fragments: InlineFragmentLayout[];
  lineIndex: number;
  top: number;
  width: number;
}

export interface CodeLineLayout {
  lineIndex: number;
  line: LayoutLine;
  top: number;
}

export interface TextLineWindow {
  firstLine: number;
  lastLine: number;
}

export type TableColumnAlignment = "center" | "left" | "right";

export interface TableRowWindow {
  afterHeight: number;
  beforeHeight: number;
  endIndex: number;
  startIndex: number;
}

type InlineVariant = "body" | "heading-1" | "heading-2";

type MarkState = {
  bold: boolean;
  italic: boolean;
  strike: boolean;
  href: string | null;
  title: string | null;
};

type ParseContext = {
  listDepth: number;
  quoteDepth: number;
};

type HeadingIdRegistry = Map<string, number>;

type InlinePiece = {
  breakMode: "never" | "normal";
  className: string;
  extraWidth: number;
  font: string;
  href: string | null;
  text: string;
  title: string | null;
};

const BODY_FONT_PX = 15;
const BODY_LINE_PX = 24;
const HEADING_ONE_FONT_PX = 22;
const HEADING_ONE_LINE_PX = 32;
const HEADING_TWO_FONT_PX = 18;
const HEADING_TWO_LINE_PX = 28;
const CODE_FONT_PX = 13;
const CODE_LINE_PX = 20;
const MARKER_FONT_PX = 12;
const CHIP_FONT_PX = 12;
const INLINE_CODE_EXTRA_WIDTH = 12;
const IMAGE_EXTRA_WIDTH = 16;
const CODE_BLOCK_PADDING_X = 12;
const CODE_BLOCK_PADDING_Y = 10;
const DOCUMENT_PADDING_Y = 16;
const BLOCK_GAP = 12;
const RICH_BLOCK_GAP = 6;
const HARD_BREAK_GAP = 4;
const LIST_ITEM_GAP = 4;
const LIST_MARKER_GAP = 10;
const LIST_NESTING_INDENT = 20;
const BLOCKQUOTE_INDENT = 18;
const RAIL_OFFSET = 5;
const RULE_HEIGHT = 20;
const IMAGE_BLOCK_HEIGHT = 220;
const IMAGE_BLOCK_MAX_WIDTH = 720;
const IMAGE_BLOCK_MIN_WIDTH = 220;
const IMAGE_PLACEHOLDER_HEIGHT = 72;
const TABLE_CELL_FONT_PX = 13;
const TABLE_CELL_PADDING_X = 14;
const TABLE_COLUMN_MIN_WIDTH = 72;
const TABLE_COLUMN_MAX_WIDTH = 320;
const TABLE_HEADER_HEIGHT = 38;
const TABLE_ROW_MIN_HEIGHT = 34;
const TABLE_ROW_LINE_HEIGHT = 20;
const TABLE_ROW_OVERSCAN = 4;
const HARD_WRAPPED_LINE_MIN_LENGTH = 52;
const HARD_WRAPPED_RUN_AVERAGE_LENGTH = 64;
const SANS_FAMILY = "Arial, Helvetica, sans-serif";
const SERIF_FAMILY = "Georgia, Times New Roman, serif";
const MONO_FAMILY = '"SF Mono", Menlo, Monaco, Consolas, monospace';
const EMPTY_MARK_STATE: MarkState = {
  bold: false,
  href: null,
  italic: false,
  strike: false,
  title: null,
};
const PREPARED_TEXT_DOCUMENT_CACHE_LIMIT = 32;
const PREPARED_TEXT_DOCUMENT_CACHE_VERSION = "prepared-text-v1";
const TEXT_DOCUMENT_FRAME_CACHE_LIMIT = 12;
const LINE_RANGE_WIDTH_CACHE_LIMIT = 8;
const LINE_RANGE_MATERIALIZED_LINE_CACHE_LIMIT = 1024;
const LINE_RANGE_CHECKPOINT_CACHE_LIMIT = 128;
const LINE_RANGE_CHECKPOINT_INTERVAL = 64;
const markerWidthCache = new Map<string, number>();
const preparedTextDocumentCache = new Map<string, PreparedTextDocument>();
const preparedTextLineSourceIds = new WeakMap<readonly string[], number>();
const textDocumentFrameCache = new WeakMap<
  PreparedTextDocument,
  Map<string, TextDocumentFrame>
>();
const inlineLineRangeCaches = new WeakMap<
  PreparedInlineTextBlock,
  Map<string, RichInlineLineRangeCache>
>();
const codeLineRangeCaches = new WeakMap<
  PreparedCodeTextBlock,
  Map<string, CodeLineRangeCache>
>();
let nextPreparedTextLineSourceId = 1;

type RichInlineLineRangeCache = {
  checkpoints: Array<LineRangeCheckpoint<RichInlineCursor>>;
  lines: Map<number, RichInlineLine>;
};

type CodeLineRangeCache = {
  checkpoints: Array<LineRangeCheckpoint<LayoutCursor>>;
  lines: Map<number, LayoutLine>;
};

type LineRangeCheckpoint<Cursor> = {
  cursor: Cursor;
  lineIndex: number;
};

export function resolveTextViewerMode({
  fileName,
  mimeType,
}: {
  fileName: string;
  mimeType?: string;
}): TextViewerMode {
  const lowerName = fileName.toLowerCase();
  const lowerMime = mimeType?.toLowerCase().split(";")[0].trim();
  return lowerName.endsWith(".md") ||
    lowerName.endsWith(".markdown") ||
    lowerMime === "text/markdown"
    ? "markdown"
    : "text";
}

export function createPreparedTextDocument({
  lines,
  mode,
  text,
  style,
}: {
  lines?: readonly string[];
  mode: TextViewerMode;
  text: string;
  style: TextStyleConfig;
}): PreparedTextDocument {
  const sourceLines = mode === "text" ? lines : undefined;
  const cacheKey = preparedTextDocumentCacheKey({
    lines: sourceLines,
    mode,
    text,
    style,
  });
  const cached = preparedTextDocumentCache.get(cacheKey);
  if (cached) {
    preparedTextDocumentCache.delete(cacheKey);
    preparedTextDocumentCache.set(cacheKey, cached);
    return cached;
  }

  const document = createUncachedPreparedTextDocument({
    lines: sourceLines,
    mode,
    text,
    style,
  });
  preparedTextDocumentCache.set(cacheKey, document);
  trimPreparedTextDocumentCache();
  return document;
}

export function clearPreparedTextDocumentCacheForTests() {
  preparedTextDocumentCache.clear();
}

function createUncachedPreparedTextDocument({
  lines,
  mode,
  text,
  style,
}: {
  lines?: readonly string[];
  mode: TextViewerMode;
  text: string;
  style: TextStyleConfig;
}): PreparedTextDocument {
  const sourceLines = mode === "text" ? lines : undefined;
  const sourceLineCount = sourceLines?.length ?? splitTextLines(text).length;
  const blocks =
    mode === "markdown"
      ? parseMarkdownBlocks(text, style)
      : buildPlainTextBlocks(sourceLines ?? splitTextLines(text), style);

  return {
    blocks,
    mode,
    sourceLineCount,
    wordCount: sourceLines
      ? countTextLineWords(sourceLines)
      : countTextWords(text),
  };
}

function preparedTextDocumentCacheKey({
  lines,
  mode,
  text,
  style,
}: {
  lines?: readonly string[];
  mode: TextViewerMode;
  text: string;
  style: TextStyleConfig;
}) {
  const sourceKey = lines
    ? ["lines", preparedTextLineSourceId(lines), lines.length]
    : ["text", text.length, hashTextForPreparedDocument(text)];
  return [
    PREPARED_TEXT_DOCUMENT_CACHE_VERSION,
    mode,
    style.fontEpoch ?? 0,
    safeScale(style.fontScale),
    ...sourceKey,
  ].join("\u0000");
}

function preparedTextLineSourceId(lines: readonly string[]) {
  const cached = preparedTextLineSourceIds.get(lines);
  if (cached !== undefined) return cached;

  const id = nextPreparedTextLineSourceId;
  nextPreparedTextLineSourceId += 1;
  preparedTextLineSourceIds.set(lines, id);
  return id;
}

function trimPreparedTextDocumentCache() {
  while (preparedTextDocumentCache.size > PREPARED_TEXT_DOCUMENT_CACHE_LIMIT) {
    const firstKey = preparedTextDocumentCache.keys().next().value;
    if (firstKey === undefined) return;
    preparedTextDocumentCache.delete(firstKey);
  }
}

function getTextDocumentFrameCache(document: PreparedTextDocument) {
  let cache = textDocumentFrameCache.get(document);
  if (!cache) {
    cache = new Map();
    textDocumentFrameCache.set(document, cache);
  }
  return cache;
}

function textDocumentFrameCacheKey({
  contentWidth,
  fontScale,
}: {
  contentWidth: number;
  fontScale: number;
}) {
  return [contentWidth, fontScale].join("\u0000");
}

function getRichInlineLineRangeCache(
  block: PreparedInlineTextBlock,
  lineWidth: number,
) {
  let blockCache = inlineLineRangeCaches.get(block);
  if (!blockCache) {
    blockCache = new Map();
    inlineLineRangeCaches.set(block, blockCache);
  }

  const cacheKey = textLineWidthCacheKey(lineWidth);
  let cache = blockCache.get(cacheKey);
  if (cache) {
    setBoundedCacheEntry(
      blockCache,
      cacheKey,
      cache,
      LINE_RANGE_WIDTH_CACHE_LIMIT,
    );
    return cache;
  }

  cache = {
    checkpoints: [
      {
        cursor: { graphemeIndex: 0, itemIndex: 0, segmentIndex: 0 },
        lineIndex: 0,
      },
    ],
    lines: new Map(),
  };
  setBoundedCacheEntry(
    blockCache,
    cacheKey,
    cache,
    LINE_RANGE_WIDTH_CACHE_LIMIT,
  );
  return cache;
}

function getCodeLineRangeCache(
  block: PreparedCodeTextBlock,
  innerWidth: number,
) {
  let blockCache = codeLineRangeCaches.get(block);
  if (!blockCache) {
    blockCache = new Map();
    codeLineRangeCaches.set(block, blockCache);
  }

  const cacheKey = textLineWidthCacheKey(innerWidth);
  let cache = blockCache.get(cacheKey);
  if (cache) {
    setBoundedCacheEntry(
      blockCache,
      cacheKey,
      cache,
      LINE_RANGE_WIDTH_CACHE_LIMIT,
    );
    return cache;
  }

  cache = {
    checkpoints: [
      {
        cursor: { graphemeIndex: 0, segmentIndex: 0 },
        lineIndex: 0,
      },
    ],
    lines: new Map(),
  };
  setBoundedCacheEntry(
    blockCache,
    cacheKey,
    cache,
    LINE_RANGE_WIDTH_CACHE_LIMIT,
  );
  return cache;
}

function textLineWidthCacheKey(width: number) {
  return String(width);
}

function setBoundedCacheEntry<K, V>(
  cache: Map<K, V>,
  key: K,
  value: V,
  limit: number,
) {
  cache.delete(key);
  cache.set(key, value);
  while (cache.size > limit) {
    const firstKey = cache.keys().next().value;
    if (firstKey === undefined) return;
    cache.delete(firstKey);
  }
}

function touchCacheEntry<K, V>(cache: Map<K, V>, key: K, value: V) {
  cache.delete(key);
  cache.set(key, value);
}

function trimMaterializedLineCache<Line>(cache: Map<number, Line>) {
  while (cache.size > LINE_RANGE_MATERIALIZED_LINE_CACHE_LIMIT) {
    const firstKey = cache.keys().next().value;
    if (firstKey === undefined) return;
    cache.delete(firstKey);
  }
}

function nearestLineRangeCheckpoint<Cursor>(
  checkpoints: readonly LineRangeCheckpoint<Cursor>[],
  lineIndex: number,
) {
  let low = 0;
  let high = checkpoints.length - 1;
  let match = checkpoints[0]!;

  while (low <= high) {
    const middle = Math.floor((low + high) / 2);
    const checkpoint = checkpoints[middle]!;
    if (checkpoint.lineIndex <= lineIndex) {
      match = checkpoint;
      low = middle + 1;
    } else {
      high = middle - 1;
    }
  }

  return match;
}

function maybeStoreLineRangeCheckpoint<Cursor>(
  checkpoints: Array<LineRangeCheckpoint<Cursor>>,
  lineIndex: number,
  cursor: Cursor,
  force = false,
) {
  if (
    !force &&
    (lineIndex === 0 || lineIndex % LINE_RANGE_CHECKPOINT_INTERVAL !== 0)
  ) {
    return;
  }

  let low = 0;
  let high = checkpoints.length - 1;
  while (low <= high) {
    const middle = Math.floor((low + high) / 2);
    const checkpoint = checkpoints[middle]!;
    if (checkpoint.lineIndex === lineIndex) {
      checkpoints[middle] = { cursor, lineIndex };
      return;
    }
    if (checkpoint.lineIndex < lineIndex) {
      low = middle + 1;
    } else {
      high = middle - 1;
    }
  }
  checkpoints.splice(low, 0, { cursor, lineIndex });
  while (checkpoints.length > LINE_RANGE_CHECKPOINT_CACHE_LIMIT) {
    checkpoints.splice(checkpoints.length > 1 ? 1 : 0, 1);
  }
}

function cloneLayoutCursor(cursor: LayoutCursor): LayoutCursor {
  return {
    graphemeIndex: cursor.graphemeIndex,
    segmentIndex: cursor.segmentIndex,
  };
}

function cloneRichInlineCursor(cursor: RichInlineCursor): RichInlineCursor {
  return {
    graphemeIndex: cursor.graphemeIndex,
    itemIndex: cursor.itemIndex,
    segmentIndex: cursor.segmentIndex,
  };
}

export function layoutTextDocument({
  contentWidth,
  document,
  fontScale = 1,
}: {
  contentWidth: number;
  document: PreparedTextDocument;
  fontScale?: number;
}): TextDocumentFrame {
  const safeContentWidth = safeWidth(contentWidth);
  const safeFontScale = safeScale(fontScale);
  const cacheKey = textDocumentFrameCacheKey({
    contentWidth: safeContentWidth,
    fontScale: safeFontScale,
  });
  const cachedFrame = getTextDocumentFrameCache(document).get(cacheKey);
  if (cachedFrame) return cachedFrame;

  const frames: TextBlockFrame[] = [];
  let y = DOCUMENT_PADDING_Y;

  for (let index = 0; index < document.blocks.length; index++) {
    const block = document.blocks[index]!;
    y += block.marginTop;
    const frame = layoutTextBlock({
      block,
      blockIndex: index,
      contentWidth: safeContentWidth,
      scale: safeFontScale,
      top: y,
    });
    frames.push(frame);
    y = frame.bottom;
  }

  const frame = {
    frames,
    totalHeight: y + DOCUMENT_PADDING_Y,
    width: safeContentWidth,
  };
  setBoundedCacheEntry(
    getTextDocumentFrameCache(document),
    cacheKey,
    frame,
    TEXT_DOCUMENT_FRAME_CACHE_LIMIT,
  );
  return frame;
}

export function materializeInlineVisibleLines({
  block,
  frame,
  lineWindow,
  maxWidth,
  viewportBottom,
  viewportTop,
}: {
  block: PreparedInlineTextBlock;
  frame: InlineTextBlockFrame;
  lineWindow?: TextLineWindow | null;
  maxWidth: number;
  viewportBottom: number;
  viewportTop: number;
}): InlineLineLayout[] {
  const window =
    lineWindow ??
    getInlineVisibleLineWindow({ frame, viewportBottom, viewportTop });
  if (!window) return [];

  if (!block.flow) {
    return [
      {
        fragments: fallbackInlineFragments(block),
        lineIndex: 0,
        top: 0,
        width: frame.usedWidth,
      },
    ];
  }

  const lineWidth = safeWidth((maxWidth - frame.contentLeft) / frame.scale);
  return getRichInlineMaterializedLineWindow({
    block,
    lineWidth,
    window,
  }).map(({ line, lineIndex }) => ({
    fragments: richInlineFragments(block, line),
    lineIndex,
    top: lineIndex * frame.lineHeight,
    width: line.width * frame.scale,
  }));
}

export function materializeCodeVisibleLines({
  block,
  contentWidth,
  frame,
  lineWindow,
  viewportBottom,
  viewportTop,
}: {
  block: PreparedCodeTextBlock;
  contentWidth: number;
  frame: CodeTextBlockFrame;
  lineWindow?: TextLineWindow | null;
  viewportBottom: number;
  viewportTop: number;
}): CodeLineLayout[] {
  const window =
    lineWindow ??
    getCodeVisibleLineWindow({ frame, viewportBottom, viewportTop });
  if (!window) return [];

  if (!block.prepared) {
    return [
      {
        lineIndex: 0,
        line: {
          end: { graphemeIndex: 0, segmentIndex: 0 },
          start: { graphemeIndex: 0, segmentIndex: 0 },
          text: block.fallbackText,
          width: frame.width,
        },
        top: CODE_BLOCK_PADDING_Y,
      },
    ];
  }

  const boxWidth = safeWidth(contentWidth - frame.contentLeft);
  const innerWidth = safeWidth(
    (boxWidth - CODE_BLOCK_PADDING_X * 2) / frame.scale,
  );
  return getCodeMaterializedLineWindow({
    block,
    innerWidth,
    window,
  }).map(({ line, lineIndex }) => ({
    lineIndex,
    line,
    top: CODE_BLOCK_PADDING_Y + lineIndex * frame.lineHeight,
  }));
}

function getRichInlineMaterializedLineWindow({
  block,
  lineWidth,
  window,
}: {
  block: PreparedInlineTextBlock;
  lineWidth: number;
  window: TextLineWindow;
}) {
  if (!block.flow) return [];

  const cache = getRichInlineLineRangeCache(block, lineWidth);
  const materializedLines = new Map<number, RichInlineLine>();
  let firstMissing = Number.POSITIVE_INFINITY;
  let lastMissing = -1;

  for (
    let lineIndex = window.firstLine;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const cachedLine = cache.lines.get(lineIndex);
    if (cachedLine) {
      touchCacheEntry(cache.lines, lineIndex, cachedLine);
      materializedLines.set(lineIndex, cachedLine);
      continue;
    }

    firstMissing = Math.min(firstMissing, lineIndex);
    lastMissing = lineIndex;
  }

  if (lastMissing >= firstMissing) {
    fillRichInlineMaterializedLineCache({
      cache,
      flow: block.flow,
      lineWidth,
      materializedLines,
      window: {
        firstLine: firstMissing,
        lastLine: lastMissing,
      },
    });
  }

  const lines: Array<{ line: RichInlineLine; lineIndex: number }> = [];
  for (
    let lineIndex = window.firstLine;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const line = materializedLines.get(lineIndex);
    if (line) lines.push({ line, lineIndex });
  }

  trimMaterializedLineCache(cache.lines);
  return lines;
}

function fillRichInlineMaterializedLineCache({
  cache,
  flow,
  lineWidth,
  materializedLines,
  window,
}: {
  cache: RichInlineLineRangeCache;
  flow: PreparedRichInline;
  lineWidth: number;
  materializedLines: Map<number, RichInlineLine>;
  window: TextLineWindow;
}) {
  const checkpoint = nearestLineRangeCheckpoint(
    cache.checkpoints,
    window.firstLine,
  );
  let cursor = cloneRichInlineCursor(checkpoint.cursor);

  for (
    let lineIndex = checkpoint.lineIndex;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const cachedLine = cache.lines.get(lineIndex);
    if (cachedLine) {
      cursor = cloneRichInlineCursor(cachedLine.end);
      if (lineIndex >= window.firstLine) {
        touchCacheEntry(cache.lines, lineIndex, cachedLine);
        materializedLines.set(lineIndex, cachedLine);
      }
      maybeStoreLineRangeCheckpoint(
        cache.checkpoints,
        lineIndex + 1,
        cloneRichInlineCursor(cachedLine.end),
        true,
      );
      continue;
    }

    const range = layoutNextRichInlineLineRange(flow, lineWidth, cursor);
    if (!range) return;

    const nextCursor = cloneRichInlineCursor(range.end);
    const nextLineIndex = lineIndex + 1;
    if (lineIndex >= window.firstLine) {
      const line = materializeRichInlineLineRange(flow, range);
      cache.lines.set(lineIndex, line);
      materializedLines.set(lineIndex, line);
      maybeStoreLineRangeCheckpoint(
        cache.checkpoints,
        lineIndex,
        cloneRichInlineCursor(cursor),
        true,
      );
    }
    maybeStoreLineRangeCheckpoint(
      cache.checkpoints,
      nextLineIndex,
      nextCursor,
      lineIndex >= window.firstLine,
    );
    cursor = nextCursor;
  }
}

function getCodeMaterializedLineWindow({
  block,
  innerWidth,
  window,
}: {
  block: PreparedCodeTextBlock;
  innerWidth: number;
  window: TextLineWindow;
}) {
  if (!block.prepared) return [];

  const cache = getCodeLineRangeCache(block, innerWidth);
  const materializedLines = new Map<number, LayoutLine>();
  let firstMissing = Number.POSITIVE_INFINITY;
  let lastMissing = -1;

  for (
    let lineIndex = window.firstLine;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const cachedLine = cache.lines.get(lineIndex);
    if (cachedLine) {
      touchCacheEntry(cache.lines, lineIndex, cachedLine);
      materializedLines.set(lineIndex, cachedLine);
      continue;
    }

    firstMissing = Math.min(firstMissing, lineIndex);
    lastMissing = lineIndex;
  }

  if (lastMissing >= firstMissing) {
    fillCodeMaterializedLineCache({
      cache,
      innerWidth,
      materializedLines,
      prepared: block.prepared,
      window: {
        firstLine: firstMissing,
        lastLine: lastMissing,
      },
    });
  }

  const lines: Array<{ line: LayoutLine; lineIndex: number }> = [];
  for (
    let lineIndex = window.firstLine;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const line = materializedLines.get(lineIndex);
    if (line) lines.push({ line, lineIndex });
  }

  trimMaterializedLineCache(cache.lines);
  return lines;
}

function fillCodeMaterializedLineCache({
  cache,
  innerWidth,
  materializedLines,
  prepared,
  window,
}: {
  cache: CodeLineRangeCache;
  innerWidth: number;
  materializedLines: Map<number, LayoutLine>;
  prepared: PreparedTextWithSegments;
  window: TextLineWindow;
}) {
  const checkpoint = nearestLineRangeCheckpoint(
    cache.checkpoints,
    window.firstLine,
  );
  let cursor = cloneLayoutCursor(checkpoint.cursor);

  for (
    let lineIndex = checkpoint.lineIndex;
    lineIndex <= window.lastLine;
    lineIndex++
  ) {
    const cachedLine = cache.lines.get(lineIndex);
    if (cachedLine) {
      cursor = cloneLayoutCursor(cachedLine.end);
      if (lineIndex >= window.firstLine) {
        touchCacheEntry(cache.lines, lineIndex, cachedLine);
        materializedLines.set(lineIndex, cachedLine);
      }
      maybeStoreLineRangeCheckpoint(
        cache.checkpoints,
        lineIndex + 1,
        cloneLayoutCursor(cachedLine.end),
        true,
      );
      continue;
    }

    const range = layoutNextLineRange(prepared, cursor, innerWidth);
    if (!range) return;

    const nextCursor = cloneLayoutCursor(range.end);
    const nextLineIndex = lineIndex + 1;
    if (lineIndex >= window.firstLine) {
      const line = materializeLineRange(prepared, range);
      cache.lines.set(lineIndex, line);
      materializedLines.set(lineIndex, line);
      maybeStoreLineRangeCheckpoint(
        cache.checkpoints,
        lineIndex,
        cloneLayoutCursor(cursor),
        true,
      );
    }
    maybeStoreLineRangeCheckpoint(
      cache.checkpoints,
      nextLineIndex,
      nextCursor,
      lineIndex >= window.firstLine,
    );
    cursor = nextCursor;
  }
}

export function getInlineVisibleLineWindow({
  frame,
  viewportBottom,
  viewportTop,
}: {
  frame: InlineTextBlockFrame;
  viewportBottom: number;
  viewportTop: number;
}): TextLineWindow | null {
  return getVisibleLineWindow({
    lineCount: frame.lineCount,
    lineHeight: frame.lineHeight,
    originTop: frame.top,
    viewportBottom,
    viewportTop,
  });
}

export function getCodeVisibleLineWindow({
  frame,
  viewportBottom,
  viewportTop,
}: {
  frame: CodeTextBlockFrame;
  viewportBottom: number;
  viewportTop: number;
}): TextLineWindow | null {
  return getVisibleLineWindow({
    lineCount: frame.lineCount,
    lineHeight: frame.lineHeight,
    originTop: frame.top + CODE_BLOCK_PADDING_Y,
    viewportBottom,
    viewportTop,
  });
}

function getVisibleLineWindow({
  lineCount,
  lineHeight,
  originTop,
  viewportBottom,
  viewportTop,
}: {
  lineCount: number;
  lineHeight: number;
  originTop: number;
  viewportBottom: number;
  viewportTop: number;
}): TextLineWindow | null {
  const firstLine = Math.max(
    0,
    Math.floor((viewportTop - originTop) / lineHeight) - 1,
  );
  const lastLine = Math.min(
    lineCount - 1,
    Math.ceil((viewportBottom - originTop) / lineHeight) + 1,
  );
  return lastLine < firstLine ? null : { firstLine, lastLine };
}

export function getTableVisibleRowWindow({
  frame,
  viewportBottom,
  viewportTop,
}: {
  frame: TableTextBlockFrame;
  viewportBottom: number;
  viewportTop: number;
}): TableRowWindow {
  const relativeTop = viewportTop - frame.top - frame.headerHeight;
  const relativeBottom = viewportBottom - frame.top - frame.headerHeight;
  const bodyHeight = frame.rowOffsets[frame.rowOffsets.length - 1] ?? 0;
  const startIndex = Math.max(
    0,
    findTableRowAtOffset(frame.rowOffsets, relativeTop) - TABLE_ROW_OVERSCAN,
  );
  const endIndex = Math.min(
    frame.rowCount,
    findTableRowEndAtOffset(frame.rowOffsets, relativeBottom) +
      TABLE_ROW_OVERSCAN,
  );
  return {
    afterHeight: Math.max(0, bodyHeight - (frame.rowOffsets[endIndex] ?? 0)),
    beforeHeight: frame.rowOffsets[startIndex] ?? 0,
    endIndex,
    startIndex,
  };
}

export function textFrameIntersectsLineRange({
  frame,
  range,
}: {
  frame: TextBlockFrame;
  range: { end: number; start: number } | null;
}) {
  return (
    range != null &&
    frame.sourceStartLine <= range.end &&
    frame.sourceEndLine >= range.start
  );
}

export function serializeMarkdownTableForClipboard(
  block: PreparedTableTextBlock,
) {
  const rows = [block.header, ...block.rows];
  return rows
    .map((row) => {
      return block.header
        .map((_headerCell, index) => tableClipboardCell(row[index]?.text ?? ""))
        .join("\t");
    })
    .join("\n");
}

function parseMarkdownBlocks(
  markdown: string,
  style: TextStyleConfig,
): PreparedTextBlock[] {
  const sourceEndLine = splitTextLines(markdown).length;
  try {
    const headingIds: HeadingIdRegistry = new Map();
    const frontmatter = extractMarkdownFrontmatter(markdown);
    if (frontmatter) {
      const blocks: PreparedTextBlock[] = [];
      appendBlockGroup(
        blocks,
        [
          buildCodeBlock({
            ctx: { listDepth: 0, quoteDepth: 0 },
            language: "yaml",
            sourceEndLine: frontmatter.endLine,
            sourceStartLine: 1,
            style,
            text: frontmatter.text,
          }),
        ],
        0,
      );
      appendBlockGroup(
        blocks,
        parseBlockTokens(marked.lexer(frontmatter.body, { gfm: true }), {
          ctx: { listDepth: 0, quoteDepth: 0 },
          headingIds,
          sourceEndLine,
          sourceStartLine: frontmatter.endLine + 1,
          style,
        }),
        BLOCK_GAP,
      );
      return blocks;
    }

    return parseBlockTokens(marked.lexer(markdown, { gfm: true }), {
      ctx: { listDepth: 0, quoteDepth: 0 },
      headingIds,
      sourceEndLine,
      sourceStartLine: 1,
      style,
    });
  } catch {
    return buildPlainTextBlocks(splitTextLines(markdown), style);
  }
}

function parseBlockTokens(
  tokens: readonly Token[],
  {
    ctx,
    headingIds,
    sourceEndLine,
    sourceStartLine,
    style,
  }: {
    ctx: ParseContext;
    headingIds: HeadingIdRegistry;
    sourceEndLine: number;
    sourceStartLine: number;
    style: TextStyleConfig;
  },
): PreparedTextBlock[] {
  const blocks: PreparedTextBlock[] = [];
  let cursorLine = sourceStartLine;

  for (const token of tokens) {
    // Advance by the number of line breaks the token's raw spans. `marked`
    // concatenates token raws to reconstruct the source, so this keeps the
    // cursor aligned even when a block's trailing newline is absorbed into the
    // following `space` token (a "\n\n" gap is two breaks, i.e. one blank line).
    const breaks = countSourceLineBreaks(token.raw);
    const tokenStartLine = Math.min(cursorLine, sourceEndLine);
    const tokenEndLine = Math.min(
      sourceEndLine,
      Math.max(
        tokenStartLine,
        cursorLine + breaks - (endsWithSourceLineBreak(token.raw) ? 1 : 0),
      ),
    );
    cursorLine += breaks;

    switch (token.type) {
      case "space":
      case "def":
        continue;

      case "paragraph":
        if (isStandaloneImageParagraph(token)) {
          appendBlockGroup(
            blocks,
            [
              buildImageBlock({
                ctx,
                sourceEndLine: tokenEndLine,
                sourceStartLine: tokenStartLine,
                token: token.tokens[0] as Tokens.Image,
              }),
            ],
            BLOCK_GAP,
          );
          continue;
        }
        appendBlockGroup(
          blocks,
          buildInlineBlocks({
            ctx,
            lines: collectInlinePieceLines(token.tokens ?? [], "body", style),
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            variant: "body",
          }),
          BLOCK_GAP,
        );
        continue;

      case "heading": {
        const variant = headingVariant(token.depth);
        const lines = collectInlinePieceLines(
          token.tokens ?? [],
          variant,
          style,
        );
        appendBlockGroup(
          blocks,
          buildInlineBlocks({
            ctx,
            headingId: createMarkdownHeadingId(lines, headingIds),
            lines,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            variant,
          }),
          BLOCK_GAP + 4,
        );
        continue;
      }

      case "code":
        appendBlockGroup(
          blocks,
          [
            buildCodeBlock({
              ctx,
              language: sanitizeMarkdownLanguage(token.lang),
              sourceEndLine: tokenEndLine,
              sourceStartLine: tokenStartLine,
              style,
              text: token.text,
            }),
          ],
          RICH_BLOCK_GAP,
        );
        continue;

      case "list":
        appendBlockGroup(
          blocks,
          buildListBlocks({
            ctx,
            headingIds,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            token: token as Tokens.List,
          }),
          BLOCK_GAP,
        );
        continue;

      case "blockquote":
        appendBlockGroup(
          blocks,
          parseBlockTokens(token.tokens ?? [], {
            ctx: {
              listDepth: ctx.listDepth,
              quoteDepth: ctx.quoteDepth + 1,
            },
            headingIds,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
          }),
          RICH_BLOCK_GAP,
        );
        continue;

      case "hr":
        appendBlockGroup(
          blocks,
          [
            buildRuleBlock({
              ctx,
              sourceEndLine: tokenEndLine,
              sourceStartLine: tokenStartLine,
            }),
          ],
          BLOCK_GAP + 2,
        );
        continue;

      case "table":
        appendBlockGroup(
          blocks,
          [
            buildTableBlock({
              ctx,
              sourceEndLine: tokenEndLine,
              sourceStartLine: tokenStartLine,
              token: token as Tokens.Table,
            }),
          ],
          RICH_BLOCK_GAP,
        );
        continue;

      case "html": {
        const htmlText = token.text.trim().length > 0 ? token.text : token.raw;
        if (token.block || ("pre" in token && token.pre === true)) {
          appendBlockGroup(
            blocks,
            [
              buildCodeBlock({
                ctx,
                language: null,
                sourceEndLine: tokenEndLine,
                sourceStartLine: tokenStartLine,
                style,
                text: htmlText,
              }),
            ],
            RICH_BLOCK_GAP,
          );
        } else {
          appendPlainTextFallback({
            blocks,
            ctx,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            text: htmlText,
          });
        }
        continue;
      }

      case "text":
        if (Array.isArray(token.tokens) && token.tokens.length > 0) {
          appendBlockGroup(
            blocks,
            buildInlineBlocks({
              ctx,
              lines: collectInlinePieceLines(token.tokens, "body", style),
              sourceEndLine: tokenEndLine,
              sourceStartLine: tokenStartLine,
              style,
              variant: "body",
            }),
            BLOCK_GAP,
          );
        } else {
          appendPlainTextFallback({
            blocks,
            ctx,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            text: token.text,
          });
        }
        continue;

      default: {
        const fallbackText = fallbackTextForToken(token);
        if (fallbackText) {
          appendPlainTextFallback({
            blocks,
            ctx,
            sourceEndLine: tokenEndLine,
            sourceStartLine: tokenStartLine,
            style,
            text: fallbackText,
          });
        }
      }
    }
  }

  return blocks;
}

function buildPlainTextBlocks(
  lines: readonly string[],
  style: TextStyleConfig,
): PreparedTextBlock[] {
  const blocks: PreparedTextBlock[] = [];
  let run: string[] = [];
  let runStartLine = 1;

  function appendLine(line: string, sourceLine: number) {
    blocks.push(buildPlainTextLineBlock(line, sourceLine, style));
  }

  function flushRun(endLine: number) {
    if (run.length === 0) return;
    if (shouldJoinPlainTextRun(run)) {
      blocks.push(
        buildInlineBlock({
          ctx: { listDepth: 0, quoteDepth: 0 },
          pieces: [
            createTextPiece(
              joinPlainTextRun(run),
              EMPTY_MARK_STATE,
              "body",
              style,
            )!,
          ],
          sourceEndLine: endLine,
          sourceStartLine: runStartLine,
          style,
          variant: "body",
        }),
      );
    } else {
      for (let index = 0; index < run.length; index++) {
        appendLine(run[index]!, runStartLine + index);
      }
    }
    run = [];
  }

  for (let index = 0; index < lines.length; index++) {
    const line = lines[index]!;
    const sourceLine = index + 1;
    if (line.trim() === "") {
      flushRun(sourceLine - 1);
      appendLine(" ", sourceLine);
      runStartLine = sourceLine + 1;
      continue;
    }
    if (run.length === 0) runStartLine = sourceLine;
    run.push(line);
  }

  flushRun(lines.length);
  return blocks;
}

function buildPlainTextLineBlock(
  line: string,
  sourceLine: number,
  style: TextStyleConfig,
): PreparedInlineTextBlock {
  return buildInlineBlock({
    ctx: { listDepth: 0, quoteDepth: 0 },
    pieces: [createTextPiece(line || " ", EMPTY_MARK_STATE, "body", style)!],
    sourceEndLine: sourceLine,
    sourceStartLine: sourceLine,
    style,
    variant: "body",
  });
}

function shouldJoinPlainTextRun(lines: readonly string[]) {
  if (lines.length < 2) return false;

  const trimmed = lines.map((line) => line.trim()).filter(Boolean);
  if (
    trimmed.length < 2 ||
    countRecordLikeLines(trimmed) > trimmed.length / 3
  ) {
    return false;
  }

  const lengths = trimmed.map((line) => line.length);
  const average =
    lengths.reduce((total, length) => total + length, 0) / lengths.length;
  if (average < HARD_WRAPPED_RUN_AVERAGE_LENGTH) return false;

  const internal = lengths.slice(0, -1);
  const shortInternalCount = internal.filter(
    (length) => length < HARD_WRAPPED_LINE_MIN_LENGTH,
  ).length;
  return shortInternalCount <= Math.floor(internal.length * 0.15);
}

function countRecordLikeLines(lines: readonly string[]) {
  return lines.reduce((count, line) => {
    return count + (isRecordLikePlainTextLine(line) ? 1 : 0);
  }, 0);
}

function isRecordLikePlainTextLine(line: string) {
  return (
    /^\d{4}-\d{2}-\d{2}(?:[T\s]|\b)/.test(line) ||
    /^\d{1,6}[:.)\]]\s/.test(line) ||
    /^[-*+]\s/.test(line) ||
    /^(?:TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\b/.test(line) ||
    /\b(?:TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\b/.test(line.slice(0, 80)) ||
    /^[{[]/.test(line)
  );
}

function joinPlainTextRun(lines: readonly string[]) {
  return lines.map((line) => line.trim()).join(" ");
}

function appendPlainTextFallback({
  blocks,
  ctx,
  sourceEndLine,
  sourceStartLine,
  style,
  text,
}: {
  blocks: PreparedTextBlock[];
  ctx: ParseContext;
  sourceEndLine: number;
  sourceStartLine: number;
  style: TextStyleConfig;
  text: string;
}) {
  appendBlockGroup(
    blocks,
    buildInlineBlocks({
      ctx,
      lines: [
        [createTextPiece(text, EMPTY_MARK_STATE, "body", style)].filter(
          Boolean,
        ) as InlinePiece[],
      ],
      sourceEndLine,
      sourceStartLine,
      style,
      variant: "body",
    }),
    BLOCK_GAP,
  );
}

function buildListBlocks({
  ctx,
  headingIds,
  sourceEndLine,
  sourceStartLine,
  style,
  token,
}: {
  ctx: ParseContext;
  headingIds: HeadingIdRegistry;
  sourceEndLine: number;
  sourceStartLine: number;
  style: TextStyleConfig;
  token: Tokens.List;
}): PreparedTextBlock[] {
  const blocks: PreparedTextBlock[] = [];
  const itemCtx = {
    listDepth: ctx.listDepth + 1,
    quoteDepth: ctx.quoteDepth,
  };

  let itemCursorLine = sourceStartLine;
  for (let index = 0; index < token.items.length; index++) {
    const item = token.items[index]!;
    const breaks = countSourceLineBreaks(item.raw);
    const itemStartLine = Math.min(sourceEndLine, itemCursorLine);
    const itemEndLine = Math.min(
      sourceEndLine,
      Math.max(
        itemStartLine,
        itemCursorLine + breaks - (endsWithSourceLineBreak(item.raw) ? 1 : 0),
      ),
    );
    itemCursorLine += breaks;

    let itemBlocks = parseBlockTokens(item.tokens, {
      ctx: itemCtx,
      headingIds,
      sourceEndLine: itemEndLine,
      sourceStartLine: itemStartLine,
      style,
    });
    if (itemBlocks.length === 0) {
      itemBlocks = buildInlineBlocks({
        ctx: itemCtx,
        lines: [
          [createTextPiece(item.text, EMPTY_MARK_STATE, "body", style)].filter(
            Boolean,
          ) as InlinePiece[],
        ],
        sourceEndLine: itemEndLine,
        sourceStartLine: itemStartLine,
        style,
        variant: "body",
      });
    }

    decorateListItemBlocks(
      itemBlocks,
      resolveListMarkerText(token, item, index),
      resolveListMarkerClassName(token, item),
      style,
    );
    appendBlockGroup(blocks, itemBlocks, LIST_ITEM_GAP);
  }

  return blocks;
}

function decorateListItemBlocks(
  blocks: PreparedTextBlock[],
  markerText: string,
  markerClassName: string,
  style: TextStyleConfig,
) {
  if (blocks.length === 0) return;

  const markerArea = measureMarkerWidth(markerText, style) + LIST_MARKER_GAP;
  for (let index = 0; index < blocks.length; index++) {
    blocks[index] = shiftBlock(blocks[index]!, markerArea);
  }

  const firstBlock = blocks[0]!;
  blocks[0] = {
    ...firstBlock,
    markerClassName,
    markerLeft: firstBlock.contentLeft - markerArea,
    markerText,
  } satisfies PreparedTextBlock;
}

function buildInlineBlocks({
  ctx,
  headingId = null,
  lines,
  sourceEndLine,
  sourceStartLine,
  style,
  variant,
}: {
  ctx: ParseContext;
  headingId?: string | null;
  lines: InlinePiece[][];
  sourceEndLine: number;
  sourceStartLine: number;
  style: TextStyleConfig;
  variant: InlineVariant;
}) {
  const blocks: PreparedTextBlock[] = [];
  for (const pieces of lines) {
    if (pieces.length === 0) continue;
    const isFirstBlock = blocks.length === 0;
    blocks.push({
      ...buildInlineBlock({
        ctx,
        pieces,
        sourceEndLine,
        sourceStartLine,
        style,
        variant,
      }),
      headingId: isFirstBlock ? headingId : null,
      marginTop: isFirstBlock ? 0 : HARD_BREAK_GAP,
    });
  }
  return blocks;
}

function buildInlineBlock({
  ctx,
  pieces,
  sourceEndLine,
  sourceStartLine,
  style,
  variant,
}: {
  ctx: ParseContext;
  pieces: InlinePiece[];
  sourceEndLine: number;
  sourceStartLine: number;
  style: TextStyleConfig;
  variant: InlineVariant;
}): PreparedInlineTextBlock {
  return {
    ...createBlockBase(ctx, sourceStartLine, sourceEndLine),
    classNames: pieces.map((piece) => piece.className),
    fallbackText: pieces.map((piece) => piece.text).join(""),
    flow: prepareRichInlineSafe(pieces),
    fonts: pieces.map((piece) => piece.font),
    headingId: null,
    hrefs: pieces.map((piece) => piece.href),
    kind: "inline",
    lineHeight: lineHeightForVariant(variant, style),
    texts: pieces.map((piece) => piece.text),
    titles: pieces.map((piece) => piece.title),
    variant,
  };
}

function buildCodeBlock({
  ctx,
  language,
  sourceEndLine,
  sourceStartLine,
  style,
  text,
}: {
  ctx: ParseContext;
  language: string | null;
  sourceEndLine: number;
  sourceStartLine: number;
  style: TextStyleConfig;
  text: string;
}): PreparedCodeTextBlock {
  const code = stripSingleTrailingNewline(text);
  const font = codeFont(style);
  return {
    ...createBlockBase(ctx, sourceStartLine, sourceEndLine),
    fallbackText: code,
    font,
    kind: "code",
    language,
    lineHeight: CODE_LINE_PX * style.fontScale,
    prepared: prepareWithSegmentsSafe(code || " ", font, {
      whiteSpace: "pre-wrap",
    }),
  };
}

function buildImageBlock({
  ctx,
  sourceEndLine,
  sourceStartLine,
  token,
}: {
  ctx: ParseContext;
  sourceEndLine: number;
  sourceStartLine: number;
  token: Tokens.Image;
}): PreparedImageTextBlock {
  const alt = token.text?.trim() || token.href || "Markdown image";
  return {
    ...createBlockBase(ctx, sourceStartLine, sourceEndLine),
    alt,
    href: parseMarkdownHref(token.href),
    kind: "image",
    src: parseMarkdownImageSrc(token.href),
    title: sanitizeMarkdownTitle(token.title),
  };
}

function buildRuleBlock({
  ctx,
  sourceEndLine,
  sourceStartLine,
}: {
  ctx: ParseContext;
  sourceEndLine: number;
  sourceStartLine: number;
}): PreparedRuleTextBlock {
  return {
    ...createBlockBase(ctx, sourceStartLine, sourceEndLine),
    height: RULE_HEIGHT,
    kind: "rule",
  };
}

function buildTableBlock({
  ctx,
  sourceEndLine,
  sourceStartLine,
  token,
}: {
  ctx: ParseContext;
  sourceEndLine: number;
  sourceStartLine: number;
  token: Tokens.Table;
}): PreparedTableTextBlock {
  const header = token.header.map((cell) => tableCellFromTokens(cell.tokens));
  const rows = token.rows.map((row) =>
    row.map((cell) => tableCellFromTokens(cell.tokens)),
  );
  const rowSourceStartLines = token.rows.map(
    (_row, index) => sourceStartLine + 2 + index,
  );
  const alignments = token.align.map((alignment) =>
    tableColumnAlignment(alignment),
  );
  return {
    ...createBlockBase(ctx, sourceStartLine, sourceEndLine),
    alignments,
    columnWidths: measureTableColumnWidths(header, rows),
    header,
    kind: "table",
    rowSourceStartLines,
    rows,
  };
}

function createBlockBase(
  ctx: ParseContext,
  sourceStartLine: number,
  sourceEndLine: number,
): PreparedTextBlockBase {
  const listIndent = Math.max(0, ctx.listDepth - 1) * LIST_NESTING_INDENT;
  const contentLeft = listIndent + ctx.quoteDepth * BLOCKQUOTE_INDENT;
  const quoteRailLefts = Array.from({ length: ctx.quoteDepth }, (_, depth) => {
    return listIndent + depth * BLOCKQUOTE_INDENT + RAIL_OFFSET;
  });

  return {
    contentLeft,
    listDepth: ctx.listDepth,
    marginTop: 0,
    markerClassName: null,
    markerLeft: null,
    markerText: null,
    quoteDepth: ctx.quoteDepth,
    quoteRailLefts,
    sourceEndLine,
    sourceStartLine,
  };
}

function collectInlinePieceLines(
  tokens: readonly Token[],
  variant: InlineVariant,
  style: TextStyleConfig,
): InlinePiece[][] {
  const lines: InlinePiece[][] = [[]];

  function currentLine() {
    return lines[lines.length - 1]!;
  }

  function pushLineBreak() {
    lines.push([]);
  }

  function pushPiece(piece: InlinePiece | null) {
    if (!piece) return;
    const line = currentLine();
    const previous = line[line.length - 1];
    if (previous && canMergeInlinePieces(previous, piece)) {
      previous.text += piece.text;
      return;
    }
    line.push(piece);
  }

  function walk(tokenList: readonly Token[], marks: MarkState) {
    for (const token of tokenList) {
      switch (token.type) {
        case "text":
          if (Array.isArray(token.tokens) && token.tokens.length > 0) {
            walk(token.tokens, marks);
          } else {
            pushPiece(createTextPiece(token.text, marks, variant, style));
          }
          continue;
        case "escape":
          pushPiece(createTextPiece(token.text, marks, variant, style));
          continue;
        case "strong":
          walk(token.tokens ?? [], { ...marks, bold: true });
          continue;
        case "em":
          walk(token.tokens ?? [], { ...marks, italic: true });
          continue;
        case "del":
          walk(token.tokens ?? [], { ...marks, strike: true });
          continue;
        case "codespan":
          pushPiece(createCodePiece(token.text, style));
          continue;
        case "link":
          walk(token.tokens ?? [], {
            ...marks,
            href: parseMarkdownHref(token.href),
            title: sanitizeMarkdownTitle(token.title),
          });
          continue;
        case "image":
          pushPiece(createImagePiece(token.text || token.href, style));
          continue;
        case "br":
          pushLineBreak();
          continue;
        case "checkbox":
          pushPiece(
            createTextPiece(
              token.checked ? "[x] " : "[ ] ",
              marks,
              variant,
              style,
            ),
          );
          continue;
        case "html":
          pushPiece(createTextPiece(token.text, marks, variant, style));
          continue;
        default: {
          const fallback = fallbackTextForToken(token);
          if (fallback) {
            pushPiece(createTextPiece(fallback, marks, variant, style));
          }
        }
      }
    }
  }

  walk(tokens, EMPTY_MARK_STATE);
  while (lines.length > 0 && lines[lines.length - 1]!.length === 0) {
    lines.pop();
  }
  return lines;
}

function createTextPiece(
  text: string,
  marks: MarkState,
  variant: InlineVariant,
  style: TextStyleConfig,
): InlinePiece | null {
  if (!text) return null;
  return {
    breakMode: "normal",
    className: inlineClassName(variant, marks),
    extraWidth: 0,
    font: inlineFont(variant, marks, style),
    href: marks.href,
    text,
    title: marks.title,
  };
}

function createCodePiece(
  text: string,
  style: TextStyleConfig,
): InlinePiece | null {
  if (!text) return null;
  return {
    breakMode: "normal",
    className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[0.92em]",
    extraWidth: INLINE_CODE_EXTRA_WIDTH,
    font: codeInlineFont(style),
    href: null,
    text,
    title: null,
  };
}

function codeFont(style: TextStyleConfig) {
  return `500 ${CODE_FONT_PX * style.fontScale}px ${MONO_FAMILY}`;
}

function codeInlineFont(style: TextStyleConfig) {
  return `600 ${CODE_FONT_PX * style.fontScale}px ${MONO_FAMILY}`;
}

function createImagePiece(text: string, style: TextStyleConfig): InlinePiece {
  return {
    breakMode: "never",
    className:
      "inline-flex min-h-5 items-center rounded-full bg-muted px-2 text-xs font-medium text-muted-foreground",
    extraWidth: IMAGE_EXTRA_WIDTH,
    font: `600 ${CHIP_FONT_PX * style.fontScale}px ${SANS_FAMILY}`,
    href: null,
    text: text || "image",
    title: null,
  };
}

function layoutTextBlock({
  block,
  blockIndex,
  contentWidth,
  scale,
  top,
}: {
  block: PreparedTextBlock;
  blockIndex: number;
  contentWidth: number;
  scale: number;
  top: number;
}): TextBlockFrame {
  switch (block.kind) {
    case "inline": {
      const lineWidth = safeWidth((contentWidth - block.contentLeft) / scale);
      const stats = block.flow
        ? measureRichInlineStatsSafe(block.flow, lineWidth, block.fallbackText)
        : estimateInlineStats(block.fallbackText, lineWidth);
      const lineCount = Math.max(1, stats.lineCount);
      const lineHeight = block.lineHeight * scale;
      const height = lineCount * lineHeight;
      return {
        ...frameBase(block, blockIndex, top, height, scale),
        kind: "inline",
        lineCount,
        lineHeight,
        usedWidth: stats.maxLineWidth * scale,
      };
    }

    case "code": {
      const boxWidth = safeWidth(contentWidth - block.contentLeft);
      const innerWidth = safeWidth(
        (boxWidth - CODE_BLOCK_PADDING_X * 2) / scale,
      );
      const stats = block.prepared
        ? measureLineStatsSafe(block.prepared, innerWidth, block.fallbackText)
        : estimateInlineStats(block.fallbackText, innerWidth);
      const lineCount = Math.max(1, stats.lineCount);
      const width = Math.min(
        boxWidth,
        Math.max(1, stats.maxLineWidth * scale + CODE_BLOCK_PADDING_X * 2),
      );
      const lineHeight = block.lineHeight * scale;
      const height = lineCount * lineHeight + CODE_BLOCK_PADDING_Y * 2;
      return {
        ...frameBase(block, blockIndex, top, height, scale),
        kind: "code",
        language: block.language,
        lineCount,
        lineHeight,
        width,
      };
    }

    case "image": {
      const availableWidth = safeWidth(contentWidth - block.contentLeft);
      const imageWidth = Math.min(
        IMAGE_BLOCK_MAX_WIDTH,
        Math.max(IMAGE_BLOCK_MIN_WIDTH, availableWidth),
      );
      const imageHeight = block.src
        ? IMAGE_BLOCK_HEIGHT
        : IMAGE_PLACEHOLDER_HEIGHT;
      return {
        ...frameBase(block, blockIndex, top, imageHeight, scale),
        alt: block.alt,
        imageHeight,
        imageWidth,
        kind: "image",
      };
    }

    case "rule": {
      return {
        ...frameBase(block, blockIndex, top, block.height, scale),
        kind: "rule",
        width: safeWidth(contentWidth - block.contentLeft),
      };
    }

    case "table": {
      const availableWidth = safeWidth(contentWidth - block.contentLeft);
      const intrinsicWidth = block.columnWidths.reduce(
        (total, width) => total + width,
        0,
      );
      const tableWidth = Math.max(availableWidth, intrinsicWidth);
      const rowHeights = measureTableRowHeights(block.rows, block.columnWidths);
      const rowOffsets = buildRowOffsets(rowHeights);
      const bodyHeight = rowOffsets[rowOffsets.length - 1] ?? 0;
      const height = TABLE_HEADER_HEIGHT + bodyHeight + 2;
      return {
        ...frameBase(block, blockIndex, top, height, scale),
        columnWidths: block.columnWidths,
        headerHeight: TABLE_HEADER_HEIGHT,
        kind: "table",
        rowHeights,
        rowOffsets,
        rowCount: block.rows.length,
        rowSourceStartLines: block.rowSourceStartLines,
        tableWidth,
      };
    }
  }
}

function frameBase(
  block: PreparedTextBlock,
  blockIndex: number,
  top: number,
  height: number,
  scale: number,
): TextBlockFrameBase {
  return {
    blockIndex,
    bottom: top + height,
    contentLeft: block.contentLeft,
    height,
    listDepth: block.listDepth,
    markerClassName: block.markerClassName,
    markerLeft: block.markerLeft,
    markerText: block.markerText,
    quoteDepth: block.quoteDepth,
    quoteRailLefts: block.quoteRailLefts,
    scale,
    sourceEndLine: block.sourceEndLine,
    sourceStartLine: block.sourceStartLine,
    top,
  };
}

function richInlineFragments(
  block: PreparedInlineTextBlock,
  line: RichInlineLine,
): InlineFragmentLayout[] {
  return line.fragments.map((fragment) => ({
    className: block.classNames[fragment.itemIndex] ?? "",
    font:
      block.fonts[fragment.itemIndex] ??
      inlineFont("body", EMPTY_MARK_STATE, { fontScale: 1 }),
    href: block.hrefs[fragment.itemIndex] ?? null,
    leadingGap: fragment.gapBefore,
    text: fragment.text,
    title: block.titles[fragment.itemIndex] ?? null,
  }));
}

function fallbackInlineFragments(
  block: PreparedInlineTextBlock,
): InlineFragmentLayout[] {
  const fragments = block.texts.map((text, index) => ({
    className: block.classNames[index] ?? "",
    font:
      block.fonts[index] ??
      inlineFont(block.variant, EMPTY_MARK_STATE, { fontScale: 1 }),
    href: block.hrefs[index] ?? null,
    leadingGap: 0,
    text,
    title: block.titles[index] ?? null,
  }));

  return fragments.length > 0
    ? fragments
    : [
        {
          className: inlineClassName(block.variant, EMPTY_MARK_STATE),
          font: inlineFont(block.variant, EMPTY_MARK_STATE, { fontScale: 1 }),
          href: null,
          leadingGap: 0,
          text: block.fallbackText || " ",
          title: null,
        },
      ];
}

function prepareRichInlineSafe(pieces: InlinePiece[]) {
  try {
    return prepareRichInline(
      pieces.map((piece) => ({
        break: piece.breakMode,
        extraWidth: piece.extraWidth,
        font: piece.font,
        text: piece.text,
      })),
    );
  } catch {
    return null;
  }
}

function prepareWithSegmentsSafe(
  text: string,
  font: string,
  options?: Parameters<typeof prepareWithSegments>[2],
) {
  try {
    return prepareWithSegments(text, font, options);
  } catch {
    return null;
  }
}

function measureRichInlineStatsSafe(
  flow: PreparedRichInline,
  width: number,
  fallbackText: string,
) {
  try {
    return measureRichInlineStats(flow, width);
  } catch {
    return estimateInlineStats(fallbackText, width);
  }
}

function measureLineStatsSafe(
  prepared: PreparedTextWithSegments,
  width: number,
  fallbackText: string,
) {
  try {
    return measureLineStats(prepared, width);
  } catch {
    return estimateInlineStats(fallbackText, width);
  }
}

function estimateInlineStats(text: string, width: number) {
  const columns = Math.max(1, Math.floor(width / 8));
  const lines = splitTextLines(text || " ");
  const lineCount = lines.reduce(
    (sum, line) => sum + Math.max(1, Math.ceil((line || " ").length / columns)),
    0,
  );
  const maxLineWidth = Math.min(
    width,
    Math.max(...lines.map((line) => (line || " ").length * 8), 1),
  );
  return { lineCount, maxLineWidth };
}

function measureMarkerWidth(text: string, style: TextStyleConfig) {
  const font = `600 ${MARKER_FONT_PX * style.fontScale}px ${MONO_FAMILY}`;
  const cacheKey = `${font}\u0000${text}`;
  const cached = markerWidthCache.get(cacheKey);
  if (cached != null) return cached;

  let width = Math.max(8, text.length * MARKER_FONT_PX * style.fontScale);
  const prepared = prepareWithSegmentsSafe(text, font);
  if (prepared) {
    try {
      width = measureNaturalWidth(prepared);
    } catch {
      // keep estimate
    }
  }
  markerWidthCache.set(cacheKey, width);
  return width;
}

function appendBlockGroup(
  target: PreparedTextBlock[],
  group: PreparedTextBlock[],
  firstMargin: number,
) {
  if (group.length === 0) return;

  for (let index = 0; index < group.length; index++) {
    const block = group[index]!;
    target.push({
      ...block,
      marginTop:
        index === 0 ? (target.length === 0 ? 0 : firstMargin) : block.marginTop,
    } satisfies PreparedTextBlock);
  }
}

function extractMarkdownFrontmatter(markdown: string) {
  const lines = splitTextLines(markdown);
  if (lines[0]?.trim() !== "---") return null;

  for (let index = 1; index < lines.length; index++) {
    if (lines[index]!.trim() !== "---") continue;
    if (index === 1) return null;

    return {
      body: lines.slice(index + 1).join("\n"),
      endLine: index + 1,
      text: lines.slice(1, index).join("\n"),
    };
  }

  return null;
}

function createMarkdownHeadingId(
  lines: readonly InlinePiece[][],
  headingIds: HeadingIdRegistry,
) {
  const text = lines
    .flatMap((line) => line.map((piece) => piece.text))
    .join(" ");
  const base = slugifyMarkdownHeading(text) || "section";
  const count = headingIds.get(base) ?? 0;
  headingIds.set(base, count + 1);
  return count === 0 ? base : `${base}-${count}`;
}

function slugifyMarkdownHeading(text: string) {
  return text
    .trim()
    .toLowerCase()
    .normalize("NFKD")
    .replace(/[^\p{Letter}\p{Number}\s-]/gu, "")
    .replace(/\s+/g, "-")
    .replace(/-+/g, "-")
    .replace(/^-|-$/g, "");
}

function shiftBlock(
  block: PreparedTextBlock,
  delta: number,
): PreparedTextBlock {
  return {
    ...block,
    contentLeft: block.contentLeft + delta,
  } satisfies PreparedTextBlock;
}

function resolveListMarkerText(
  list: Tokens.List,
  item: Tokens.ListItem,
  index: number,
) {
  if (item.task) return item.checked ? "☑" : "☐";
  if (list.ordered) {
    const start = typeof list.start === "number" ? list.start : 1;
    return `${start + index}.`;
  }
  return "•";
}

function resolveListMarkerClassName(list: Tokens.List, item: Tokens.ListItem) {
  if (item.task) return "text-muted-foreground";
  return list.ordered ? "text-muted-foreground" : "text-muted-foreground";
}

function isStandaloneImageParagraph(
  token: Token,
): token is Tokens.Paragraph & { tokens: [Tokens.Image] } {
  return (
    token.type === "paragraph" &&
    Array.isArray(token.tokens) &&
    token.tokens.length === 1 &&
    token.tokens[0]?.type === "image"
  );
}

function parseMarkdownHref(href: string | null | undefined) {
  return sanitizeMarkdownUrl(href, {
    allowedAbsoluteProtocols: new Set(["http:", "https:", "mailto:"]),
    allowRelative: true,
  });
}

function parseMarkdownImageSrc(src: string | null | undefined) {
  return sanitizeMarkdownUrl(src, {
    allowedAbsoluteProtocols: new Set(["http:", "https:", "blob:"]),
    allowRelative: true,
  });
}

function sanitizeMarkdownTitle(title: string | null | undefined) {
  if (!title) return null;
  return title.replace(/[\u0000-\u001f\u007f]/g, "").trim() || null;
}

function sanitizeMarkdownLanguage(language: string | null | undefined) {
  if (!language) return null;
  return (
    language
      .replace(/[\u0000-\u001f\u007f]/g, "")
      .trim()
      .slice(0, 40) || null
  );
}

function sanitizeMarkdownUrl(
  value: string | null | undefined,
  {
    allowedAbsoluteProtocols,
    allowRelative,
  }: {
    allowedAbsoluteProtocols: ReadonlySet<string>;
    allowRelative: boolean;
  },
) {
  if (!value) return null;
  const trimmed = value.trim();
  if (!trimmed || /[\u0000-\u001f\u007f]/.test(trimmed)) return null;
  if (trimmed.startsWith("#")) return trimmed;
  if (allowRelative && /^\.{0,2}\//.test(trimmed)) return trimmed;
  if (allowRelative && /^\//.test(trimmed)) return trimmed;

  try {
    const url = new URL(trimmed);
    return allowedAbsoluteProtocols.has(url.protocol) ? url.href : null;
  } catch {
    return null;
  }
}

function fallbackTextForToken(token: Token) {
  if ("text" in token && typeof token.text === "string") return token.text;
  return token.raw ?? "";
}

function countSourceLineBreaks(raw: string | null | undefined) {
  if (!raw) return 0;
  const matches = raw.match(/\r\n|[\n\r\u2028\u2029]/g);
  return matches ? matches.length : 0;
}

function endsWithSourceLineBreak(raw: string | null | undefined) {
  return raw ? /(?:\r\n|[\n\r\u2028\u2029])$/.test(raw) : false;
}

function tableCellFromTokens(tokens: readonly Token[]): PreparedTableCell {
  const { className, href, text, title } = inlineTokensToTableCell(tokens);
  return {
    className,
    href,
    text: text || " ",
    title,
  };
}

function inlineTokensToTableCell(tokens: readonly Token[]): PreparedTableCell {
  let className = "";
  let href: string | null = null;
  let text = "";
  let title: string | null = null;
  for (const token of tokens) {
    switch (token.type) {
      case "strong":
        text += inlineTokensToTableCell(token.tokens ?? []).text;
        className = cnClassNames(className, "font-semibold");
        break;
      case "em":
        text += inlineTokensToTableCell(token.tokens ?? []).text;
        className = cnClassNames(className, "italic");
        break;
      case "del":
        text += inlineTokensToTableCell(token.tokens ?? []).text;
        className = cnClassNames(className, "line-through");
        break;
      case "link": {
        const child = inlineTokensToTableCell(token.tokens ?? []);
        text += child.text;
        href = href ?? parseMarkdownHref(token.href);
        title = title ?? sanitizeMarkdownTitle(token.title);
        className = cnClassNames(className, "text-primary underline");
        break;
      }
      case "codespan":
        text += token.text;
        className = cnClassNames(className, "font-mono");
        break;
      case "escape":
      case "text":
      case "html":
        text += token.text;
        break;
      case "br":
        text += " ";
        break;
      case "image":
        text += token.text || token.href;
        break;
      default:
        text += fallbackTextForToken(token);
    }
  }
  return { className, href, text, title };
}

function measureTableColumnWidths(
  header: readonly PreparedTableCell[],
  rows: readonly PreparedTableCell[][],
) {
  return header.map((cell, columnIndex) => {
    const columnTexts = [
      cell.text,
      ...rows.map((row) => row[columnIndex]?.text ?? ""),
    ];
    const maxLength = Math.max(
      1,
      ...columnTexts.map((text) => Math.min(48, text.length)),
    );
    return Math.min(
      TABLE_COLUMN_MAX_WIDTH,
      Math.max(
        TABLE_COLUMN_MIN_WIDTH,
        maxLength * TABLE_CELL_FONT_PX * 0.58 + TABLE_CELL_PADDING_X * 2,
      ),
    );
  });
}

function measureTableRowHeights(
  rows: readonly PreparedTableCell[][],
  columnWidths: readonly number[],
) {
  return rows.map((row) => {
    const lineCount = Math.max(
      1,
      ...row.map((cell, index) =>
        measureTableCellLineCount(cell.text, columnWidths[index] ?? 0),
      ),
    );
    return Math.max(
      TABLE_ROW_MIN_HEIGHT,
      lineCount * TABLE_ROW_LINE_HEIGHT + 12,
    );
  });
}

function measureTableCellLineCount(text: string, columnWidth: number) {
  const innerWidth = Math.max(1, columnWidth - TABLE_CELL_PADDING_X * 2);
  const charsPerLine = Math.max(
    1,
    Math.floor(innerWidth / (TABLE_CELL_FONT_PX * 0.58)),
  );
  return splitTextLines(text || " ").reduce((count, line) => {
    return count + Math.max(1, Math.ceil((line || " ").length / charsPerLine));
  }, 0);
}

function buildRowOffsets(rowHeights: readonly number[]) {
  const offsets = [0];
  for (const height of rowHeights) {
    offsets.push(offsets[offsets.length - 1]! + height);
  }
  return offsets;
}

function findTableRowAtOffset(rowOffsets: readonly number[], offset: number) {
  if (offset <= 0) return 0;
  const rowCount = Math.max(0, rowOffsets.length - 1);
  let low = 0;
  let high = rowCount;
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if ((rowOffsets[mid + 1] ?? 0) <= offset) low = mid + 1;
    else high = mid;
  }
  return low;
}

function findTableRowEndAtOffset(
  rowOffsets: readonly number[],
  offset: number,
) {
  if (offset <= 0) return 0;
  const rowCount = Math.max(0, rowOffsets.length - 1);
  let low = 0;
  let high = rowCount;
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if ((rowOffsets[mid] ?? 0) < offset) low = mid + 1;
    else high = mid;
  }
  return low;
}

function tableColumnAlignment(
  alignment: "center" | "left" | "right" | null,
): TableColumnAlignment {
  if (alignment === "center" || alignment === "right") return alignment;
  return "left";
}

function tableClipboardCell(text: string) {
  return text.replace(/[\t\r\n]+/g, " ").trim();
}

function cnClassNames(...classes: Array<string | null | undefined>) {
  return classes.filter(Boolean).join(" ");
}

function headingVariant(depth: number): InlineVariant {
  if (depth <= 1) return "heading-1";
  if (depth === 2) return "heading-2";
  return "body";
}

function lineHeightForVariant(variant: InlineVariant, style: TextStyleConfig) {
  switch (variant) {
    case "heading-1":
      return HEADING_ONE_LINE_PX * style.fontScale;
    case "heading-2":
      return HEADING_TWO_LINE_PX * style.fontScale;
    case "body":
      return BODY_LINE_PX * style.fontScale;
  }
}

function inlineFont(
  variant: InlineVariant,
  marks: MarkState,
  style: TextStyleConfig,
) {
  const italic = marks.italic ? "italic " : "";
  switch (variant) {
    case "heading-1":
      return `${italic}${marks.bold ? 800 : 700} ${HEADING_ONE_FONT_PX * style.fontScale}px ${SERIF_FAMILY}`;
    case "heading-2":
      return `${italic}${marks.bold ? 800 : 700} ${HEADING_TWO_FONT_PX * style.fontScale}px ${SERIF_FAMILY}`;
    case "body":
      return `${italic}${marks.bold ? 700 : marks.href ? 500 : 400} ${BODY_FONT_PX * style.fontScale}px ${SANS_FAMILY}`;
  }
}

function inlineClassName(variant: InlineVariant, marks: MarkState) {
  const classes = [
    "inline-block wrap-break-word whitespace-pre-wrap whitespace-pre align-baseline leading-none",
  ];
  if (variant === "heading-1")
    classes.push("font-serif text-[1.45em] font-semibold");
  if (variant === "heading-2")
    classes.push("font-serif text-[1.18em] font-semibold");
  if (marks.bold) classes.push("font-semibold");
  if (marks.italic) classes.push("italic");
  if (marks.strike) classes.push("line-through");
  if (marks.href) classes.push("text-primary underline underline-offset-2");
  return classes.join(" ");
}

function canMergeInlinePieces(a: InlinePiece, b: InlinePiece) {
  return (
    a.breakMode === b.breakMode &&
    a.className === b.className &&
    a.extraWidth === b.extraWidth &&
    a.font === b.font &&
    a.href === b.href &&
    a.title === b.title
  );
}

function stripSingleTrailingNewline(text: string) {
  return text.endsWith("\n") ? text.slice(0, -1) : text;
}

function countTextWords(text: string) {
  const matches = text.trim().match(/\S+/g);
  return matches?.length ?? 0;
}

function countTextLineWords(lines: readonly string[]) {
  return lines.reduce((count, line) => count + countTextWords(line), 0);
}

function hashTextForPreparedDocument(text: string) {
  let hash = 2166136261;
  for (let index = 0; index < text.length; index += 1) {
    hash ^= text.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(36);
}

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

function safeScale(scale: number) {
  return Number.isFinite(scale) && scale > 0 ? scale : 1;
}
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/ui/text-viewer-scale.ts
export const TEXT_VIEWER_MIN_SCALE = 0.1;
export const TEXT_VIEWER_MAX_SCALE = 5;
export const TEXT_VIEWER_BLOCK_PADDING = 8;

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

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

export type { TextLineRange };

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

export type TextDocumentSource =
  | UrlViewerSource
  | BlobViewerSource
  | TextSource;

export interface TextViewerProps extends TextViewerBounds {
  source: TextDocumentSource;
  className?: string;
  controls?: boolean;
  download?: boolean;
  /** 1-based inclusive line range to highlight, or null. */
  highlight?: TextLineRange | null;
  /** Drop the outer border/rounded/background so the viewer fills its container. */
  bare?: boolean;
  /** Explicitly select text or markdown when the caller has already classified the source. */
  mode?: TextViewerMode;
}
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/markdown-greenfield-content.tsx
"use client";

import * as React from "react";
import { useMountEffect } from "@/hooks/use-mount-effect";

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

import {
  createMarkdownGreenfieldDocument,
  findMarkdownGreenfieldChunkByBlockId,
  findMarkdownGreenfieldChunkBySourceLine,
  findMarkdownGreenfieldFragmentTargetById,
  type MarkdownGreenfieldChunk,
} from "./markdown-greenfield-document";
import { useMarkdownGreenfieldDocument } from "./markdown-greenfield-document-store";
import {
  layoutMarkdownGreenfieldDocument,
  MARKDOWN_GREENFIELD_CHUNK_MAX_INLINE_SIZE,
  MARKDOWN_GREENFIELD_LAYOUT_POLICY_VERSION,
  type MarkdownGreenfieldChunkFrame,
  type MarkdownGreenfieldMeasurementContext,
} from "./markdown-greenfield-layout";
import { useMarkdownGreenfieldRendererFrame } from "./markdown-greenfield-renderer-frame";
import { MarkdownGreenfieldChunkRenderer } from "./markdown-greenfield-renderer";
import {
  createMarkdownGreenfieldVisibleProjection,
  getMarkdownGreenfieldProjectedVisibleFrames,
  getMarkdownGreenfieldScrollAnchor,
  getMarkdownGreenfieldScrollTopForLineRange,
  getMarkdownGreenfieldVisibleProjection,
  getMarkdownGreenfieldVisibleRange,
  isMarkdownGreenfieldVisibleProjectionSameWindow,
  resolveMarkdownGreenfieldScrollAnchor,
  type MarkdownGreenfieldScrollAnchor,
  type MarkdownGreenfieldVisibleProjection,
} from "./markdown-greenfield-virtualizer";
import { ScrollArea } from "./scroll-area";
import { TextViewerControls, TextViewerFrame } from "./text-viewer-chrome";
import { normalizeTextLineRange } from "./text-viewer-ranges";
import {
  readTextResource,
  resolvedTextViewerBounds,
} from "./text-viewer-resource";
import { clampTextViewerScale } from "./text-viewer-scale";
import { useTextViewerScrollInteractions } from "./text-viewer-scroll-interactions";
import type { TextViewerHandle, TextViewerProps } from "./text-viewer-types";
import { getTextInverseStickyWindow } from "./text-viewer-virtualization";
import type { ViewerDownloadErrorHandler } from "./viewer-download";
import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";
import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

const DEFAULT_VIEWPORT_HEIGHT = 640;
const DEFAULT_VIEWPORT_WIDTH = 900;
const INITIAL_CONTENT_WIDTH = 820;
const VIEWER_HORIZONTAL_PADDING = 32;
const OVERSCAN_PX = 800;
const NATIVE_FIND_TEXT_BATCH_CHUNKS = 16;
const NATIVE_FIND_TEXT_IDLE_MIN_REMAINING_MS = 4;
const MARKDOWN_GREENFIELD_HIGHLIGHT_STYLE = {
  backgroundColor:
    "color-mix(in oklab, var(--foreground) 8%, var(--background))",
  boxShadow: "inset 2px 0 0 0 var(--primary)",
} satisfies React.CSSProperties;

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

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

type MarkdownScrollToLineOptions = ScrollToOptions & {
  preferredChunkId?: string | null;
};

type NativeFindTextRecord = {
  chunk: MarkdownGreenfieldChunk;
  text: string;
};

type NativeFindTextState = {
  entries: readonly NativeFindTextRecord[];
  key: string | null;
};

export type MarkdownViewerProps = TextViewerProps & {
  /** Bind document fragment IDs to window.location.hash. Disable for embedded previews. */
  urlFragmentNavigation?: boolean;
};

export type MarkdownGreenfieldContentProps = Omit<
  MarkdownViewerProps,
  "source"
> & {
  resource: ViewerResource;
  retryVersion: number;
  forwardedRef?: React.ForwardedRef<TextViewerHandle>;
};

export function MarkdownGreenfieldContent({
  resource,
  className,
  controls = true,
  download = true,
  highlight,
  bare = false,
  maxBytes,
  maxLines,
  retryVersion,
  forwardedRef,
  urlFragmentNavigation = true,
}: MarkdownGreenfieldContentProps) {
  const bounds = React.useMemo(
    () => resolvedTextViewerBounds({ maxBytes, maxLines }),
    [maxBytes, maxLines],
  );
  const text = React.useMemo(
    () =>
      readTextResource({
        bounds,
        content: resource.content,
        retryVersion,
      }),
    [bounds, resource.content, retryVersion],
  );
  const parsedDocument = useMarkdownGreenfieldDocument(text);
  const pendingDocument = React.useMemo(
    () => createMarkdownGreenfieldDocument(" "),
    [],
  );
  const document = parsedDocument ?? pendingDocument;
  const isDocumentPending = parsedDocument == null;
  const documentMeasurementId = React.useMemo(
    () => measurementDocumentIdForText(text),
    [text],
  );
  const downloadAction = download ? resource.originalDownload : null;
  const [downloadError, setDownloadError] = React.useState("");
  const [fontScale, setFontScale] = React.useState(1);
  const [measuredHeightsRevision, setMeasuredHeightsRevision] =
    React.useState(0);
  const [viewportSize, setViewportSize] = React.useState<ViewportSize>({
    height: 0,
    width: 0,
  });
  const [contentWidth, setContentWidth] = React.useState(INITIAL_CONTENT_WIDTH);
  const documentRef = React.useRef(document);
  const measuredHeightsRef = React.useRef(new Map<string, number>());
  const viewportRef = React.useRef<HTMLDivElement | null>(null);
  const stickyWindowRef = React.useRef<HTMLDivElement | null>(null);
  const scrollTopRef = React.useRef(0);
  const scrollFrameRef = React.useRef<number | null>(null);
  const frameChunksRef = React.useRef<readonly MarkdownGreenfieldChunkFrame[]>(
    [],
  );
  const viewportHeightRef = React.useRef(DEFAULT_VIEWPORT_HEIGHT);
  const pendingMeasuredHeightsRef = React.useRef(new Map<string, number>());
  const measuredHeightFrameRef = React.useRef<number | null>(null);
  const pendingAnchorRef = React.useRef<MarkdownGreenfieldScrollAnchor | null>(
    null,
  );
  const prevFrameChunksRef = React.useRef<
    readonly MarkdownGreenfieldChunkFrame[] | null
  >(null);
  const viewportHeight = viewportSize.height || DEFAULT_VIEWPORT_HEIGHT;
  const measuredViewportWidth = viewportSize.width || DEFAULT_VIEWPORT_WIDTH;
  const captureAnchor = React.useCallback(() => {
    // Read the live scroll position from the DOM rather than the `scrollTop`
    // state, which lags behind during fast scrolling. Capturing a stale value
    // here makes the anchor-restore effect yank the viewport back to an old
    // (often near-top) position when a freshly revealed chunk is measured.
    const liveScrollTop =
      viewportRef.current?.scrollTop ?? scrollTopRef.current;
    pendingAnchorRef.current = getMarkdownGreenfieldScrollAnchor({
      frames: frameChunksRef.current,
      scrollTop: liveScrollTop,
    });
  }, []);
  const markdownRendererFrame = useMarkdownGreenfieldRendererFrame({
    fallbackViewportInlineSize: measuredViewportWidth,
    onBeforeLayoutMotion: captureAnchor,
  });
  const viewportWidth = markdownRendererFrame.viewportInlineSize;
  const measuredHeightLookup = React.useMemo(
    () => ({
      get: (
        chunk: MarkdownGreenfieldChunk,
        context: MarkdownGreenfieldMeasurementContext,
      ) =>
        measuredHeightsRef.current.get(
          measuredHeightKey({
            chunk,
            context,
            documentMeasurementId,
          }),
        ),
      cacheKey: `${documentMeasurementId}:${measuredHeightsRevision}`,
    }),
    [documentMeasurementId, measuredHeightsRevision],
  );
  const frame = React.useMemo(
    () =>
      layoutMarkdownGreenfieldDocument({
        contentWidth,
        document,
        fontScale,
        measuredHeights: measuredHeightLookup,
      }),
    [contentWidth, document, fontScale, measuredHeightLookup],
  );
  documentRef.current = document;
  frameChunksRef.current = frame.chunks;
  viewportHeightRef.current = viewportHeight;
  const highlightRange = React.useMemo(
    () => normalizeTextLineRange(highlight, document.lineCount),
    [document.lineCount, highlight],
  );
  const visibleHighlightRange = highlightRange;
  const [visibleProjection, setVisibleProjection] =
    React.useState<MarkdownGreenfieldVisibleProjection>(() =>
      getMarkdownGreenfieldVisibleProjection({
        frames: frame.chunks,
        overscanPx: OVERSCAN_PX,
        scrollTop: scrollTopRef.current,
        viewportHeight,
      }),
    );
  const visibleProjectionRef = React.useRef(visibleProjection);
  const visibleFrames = React.useMemo(
    () =>
      getMarkdownGreenfieldProjectedVisibleFrames({
        frames: frame.chunks,
        projection: visibleProjection,
      }),
    [frame.chunks, visibleProjection],
  );
  const stickyWindow = React.useMemo(
    () =>
      getTextInverseStickyWindow({
        renderedBottom: visibleFrames.at(-1)?.bottom ?? 0,
        renderedTop:
          visibleFrames[0]?.index === 0 ? 0 : (visibleFrames[0]?.top ?? 0),
        totalHeight: frame.totalHeight,
        viewportHeight,
      }),
    [frame.totalHeight, viewportHeight, visibleFrames],
  );
  const projectVisibleProjection = React.useCallback(() => {
    const frames = frameChunksRef.current;
    const nextRange = getMarkdownGreenfieldVisibleRange({
      frames,
      overscanPx: OVERSCAN_PX,
      scrollTop: scrollTopRef.current,
      viewportHeight: viewportHeightRef.current,
    });
    const currentProjection = visibleProjectionRef.current;
    if (
      isMarkdownGreenfieldVisibleProjectionSameWindow({
        frames,
        projection: currentProjection,
        range: nextRange,
      })
    ) {
      if (
        currentProjection.range.start !== nextRange.start ||
        currentProjection.range.end !== nextRange.end
      ) {
        visibleProjectionRef.current = {
          frameIds: currentProjection.frameIds,
          range: nextRange,
        };
      }
      return;
    }
    const nextProjection = createMarkdownGreenfieldVisibleProjection({
      frames,
      range: nextRange,
    });
    visibleProjectionRef.current = nextProjection;
    setVisibleProjection(nextProjection);
  }, []);
  const commitScrollTop = React.useCallback(
    (nextScrollTop: number) => {
      scrollTopRef.current = nextScrollTop;
      projectVisibleProjection();
    },
    [projectVisibleProjection],
  );

  const scheduleScrollTop = React.useCallback(
    (nextScrollTop: number) => {
      scrollTopRef.current = nextScrollTop;
      if (scrollFrameRef.current !== null) return;
      if (typeof requestAnimationFrame !== "function") {
        commitScrollTop(scrollTopRef.current);
        return;
      }
      scrollFrameRef.current = requestAnimationFrame(() => {
        scrollFrameRef.current = null;
        commitScrollTop(scrollTopRef.current);
      });
    },
    [commitScrollTop],
  );
  const handleViewportScroll = React.useCallback(
    (viewport: HTMLElement) => scheduleScrollTop(viewport.scrollTop),
    [scheduleScrollTop],
  );
  const getScrollInteractionTarget = React.useCallback(
    () => stickyWindowRef.current,
    [],
  );
  const getScrollOverflowTarget = React.useCallback(
    () => stickyWindowRef.current,
    [],
  );

  const flushMeasuredHeights = React.useCallback(() => {
    measuredHeightFrameRef.current = null;
    const pending = pendingMeasuredHeightsRef.current;
    if (!pending.size) return;
    pendingMeasuredHeightsRef.current = new Map();
    let didChange = false;
    const measuredHeights = measuredHeightsRef.current;
    for (const [key, height] of pending) {
      if (Math.abs((measuredHeights.get(key) ?? 0) - height) < 1) continue;
      measuredHeights.set(key, height);
      didChange = true;
    }
    if (didChange) {
      setMeasuredHeightsRevision((revision) => revision + 1);
    }
  }, []);

  const scheduleMeasuredHeightsFlush = React.useCallback(() => {
    if (measuredHeightFrameRef.current !== null) return;
    if (typeof requestAnimationFrame !== "function") {
      flushMeasuredHeights();
      return;
    }
    measuredHeightFrameRef.current =
      requestAnimationFrame(flushMeasuredHeights);
  }, [flushMeasuredHeights]);

  useKeyedLayoutEffect(joinEffectKey([commitScrollTop, document]), () => {
    measuredHeightsRef.current = new Map();
    pendingMeasuredHeightsRef.current = new Map();
    setMeasuredHeightsRevision((revision) => revision + 1);
    commitScrollTop(0);
    viewportRef.current?.scrollTo({ left: 0, top: 0 });
  });

  useMountEffect(() => () => {
    if (
      scrollFrameRef.current !== null &&
      typeof cancelAnimationFrame === "function"
    ) {
      cancelAnimationFrame(scrollFrameRef.current);
    }
    if (
      measuredHeightFrameRef.current !== null &&
      typeof cancelAnimationFrame === "function"
    ) {
      cancelAnimationFrame(measuredHeightFrameRef.current);
    }
  });

  useTextViewerScrollInteractions({
    getInteractionTarget: getScrollInteractionTarget,
    getOverflowTarget: getScrollOverflowTarget,
    onScroll: handleViewportScroll,
    viewportRef,
  });

  useKeyedLayoutEffect(
    joinEffectKey([
      "markdown-viewport-resize",
      markdownRendererFrame.usesShellGeometry,
    ]),
    () => {
      const viewport = viewportRef.current;
      if (!viewport) return;

      const readSize = () => {
        setViewportSize((current) => {
          const nextHeight = viewport.clientHeight;
          const nextWidth =
            markdownRendererFrame.usesShellGeometry && current.width > 0
              ? current.width
              : viewport.clientWidth;
          const next = {
            height: nextHeight,
            width: nextWidth,
          };
          return current.height === next.height && current.width === next.width
            ? current
            : next;
        });
      };

      readSize();
      const observer =
        typeof ResizeObserver === "undefined"
          ? null
          : new ResizeObserver(readSize);
      observer?.observe(viewport);
      return () => observer?.disconnect();
    },
  );

  useKeyedLayoutEffect(joinEffectKey([captureAnchor, viewportWidth]), () => {
    const nextWidth = Math.max(
      1,
      viewportWidth - VIEWER_HORIZONTAL_PADDING * 2,
    );
    setContentWidth((current) => {
      if (current === nextWidth) return current;
      captureAnchor();
      return nextWidth;
    });
  });

  useKeyedLayoutEffect(joinEffectKey([commitScrollTop, frame.chunks]), () => {
    const viewport = viewportRef.current;
    const previousChunks = prevFrameChunksRef.current;
    prevFrameChunksRef.current = frame.chunks;
    if (!viewport) return;

    // Intentional full reflows (width, zoom, font readiness, mode switch) set an
    // explicit anchor and want to restore the reader's relative position.
    const anchor = pendingAnchorRef.current;
    if (anchor) {
      pendingAnchorRef.current = null;
      const nextScrollTop = resolveMarkdownGreenfieldScrollAnchor({
        anchor,
        frames: frame.chunks,
      });
      if (nextScrollTop == null) return;
      viewport.scrollTop = nextScrollTop;
      commitScrollTop(nextScrollTop);
      return;
    }

    // Measurement-driven changes keep whatever the reader is looking at stable by
    // compensating scrollTop for the height delta of chunks ENTIRELY above the
    // viewport top. Only already-measured chunks (real height in both layouts)
    // count: a chunk's first estimate->measured correction happens as it is
    // revealed near/below the viewport, never while the reader sits above it, so
    // excluding it avoids the estimate-collapse plunge while still pinning the
    // viewport when async rich blocks above re-settle.
    if (!previousChunks) return;
    const liveScrollTop = viewport.scrollTop;
    const previousById = new Map(
      previousChunks.map((chunkFrame) => [chunkFrame.id, chunkFrame]),
    );
    let delta = 0;
    for (const chunkFrame of frame.chunks) {
      const previous = previousById.get(chunkFrame.id);
      if (
        previous &&
        previous.measuredHeight != null &&
        chunkFrame.measuredHeight != null &&
        previous.bottom <= liveScrollTop
      ) {
        delta += chunkFrame.measuredHeight - previous.measuredHeight;
      }
    }
    if (delta === 0) return;
    const nextScrollTop = Math.max(0, liveScrollTop + delta);
    viewport.scrollTop = nextScrollTop;
    commitScrollTop(nextScrollTop);
  });

  useKeyedLayoutEffect(
    joinEffectKey(["markdown-visible-range", frame.chunks, viewportHeight]),
    () => {
      projectVisibleProjection();
    },
  );

  const scrollToLineRange = React.useCallback(
    (
      range: ReturnType<typeof normalizeTextLineRange>,
      options?: MarkdownScrollToLineOptions,
    ) => {
      const viewport = viewportRef.current;
      if (!viewport || !range) return;
      const currentDocument = documentRef.current;

      const preferredChunkId =
        options?.preferredChunkId ??
        findMarkdownGreenfieldChunkBySourceLine(currentDocument, range.start)
          ?.id;
      const top = getMarkdownGreenfieldScrollTopForLineRange({
        chunks: currentDocument.chunks,
        frames: frameChunksRef.current,
        preferredChunkId,
        range,
        viewportHeight: viewport.clientHeight || viewportHeightRef.current,
      });
      if (top == null) return;
      viewport.scrollTo({
        behavior: resolveScrollBehavior(options?.behavior),
        left: options?.left,
        top,
      });
      commitScrollTop(top);
    },
    [commitScrollTop],
  );

  // A stable handle to the latest scrollToLineRange. Target-driven effects use
  // this ref so layout measurement changes cannot yank the viewport back to a
  // highlight/search/hash target while the reader is scrolling.
  const scrollToLineRangeRef = React.useRef(scrollToLineRange);
  useKeyedLayoutEffect(joinEffectKey([scrollToLineRange]), () => {
    scrollToLineRangeRef.current = scrollToLineRange;
  });

  React.useImperativeHandle(
    forwardedRef ?? null,
    () => ({
      getViewportElement: () => viewportRef.current,
      scrollToLineRange: (range, options) => {
        scrollToLineRangeRef.current(
          normalizeTextLineRange(range, document.lineCount),
          options,
        );
      },
    }),
    [document.lineCount],
  );

  // Scrolling is an imperative DOM mutation that must run before paint to avoid
  // a visible jump, so these reactions to a changed target line/range live in
  // layout-timed reactions.
  useKeyedLayoutEffect(joinEffectKey([highlightRange]), () => {
    if (!highlightRange) return;
    scrollToLineRangeRef.current(highlightRange);
  });

  // Subscribes to the browser's hash/history (a non-React external source) and
  // scrolls the matching fragment into view before paint.
  useKeyedLayoutEffect(joinEffectKey([document, urlFragmentNavigation]), () => {
    if (!urlFragmentNavigation) return;

    const scrollToCurrentHash = () => {
      const hash = window.location.hash;
      if (!hash) return;
      const target = findMarkdownGreenfieldFragmentTargetById(document, hash);
      if (!target) return;
      const chunk = findMarkdownGreenfieldChunkByBlockId(
        document,
        target.blockId,
      );
      scrollToLineRangeRef.current(
        normalizeTextLineRange(
          { end: target.sourceLine, start: target.sourceLine },
          document.lineCount,
        ),
        { behavior: "auto", preferredChunkId: chunk?.id },
      );
    };

    scrollToCurrentHash();
    window.addEventListener("hashchange", scrollToCurrentHash);
    window.addEventListener("popstate", scrollToCurrentHash);
    return () => {
      window.removeEventListener("hashchange", scrollToCurrentHash);
      window.removeEventListener("popstate", scrollToCurrentHash);
    };
  });

  const recordMeasuredHeight = React.useCallback(
    (chunk: MarkdownGreenfieldChunk, height: number) => {
      if (!Number.isFinite(height) || height <= 0) return;
      const key = measuredHeightKey({
        chunk,
        context: {
          fontScale,
          policyVersion: MARKDOWN_GREENFIELD_LAYOUT_POLICY_VERSION,
          width: Math.max(1, contentWidth),
        },
        documentMeasurementId,
      });
      pendingMeasuredHeightsRef.current.set(key, height);
      scheduleMeasuredHeightsFlush();
    },
    [
      contentWidth,
      documentMeasurementId,
      fontScale,
      scheduleMeasuredHeightsFlush,
    ],
  );
  const handleDownloadError = React.useCallback<ViewerDownloadErrorHandler>(
    (error) => {
      if (error.kind === "aborted") return;
      setDownloadError(error.message || "Could not download Markdown.");
    },
    [],
  );
  const zoom = (factor: number) => {
    captureAnchor();
    setFontScale((scale) => clampTextViewerScale(scale * factor));
  };
  const resetZoom = () => {
    captureAnchor();
    setFontScale(1);
  };

  return (
    <TextViewerFrame className={className} bare={bare}>
      {controls ? (
        <TextViewerControls
          downloadAction={downloadAction}
          extra={<DownloadError message={downloadError} />}
          fontScale={fontScale}
          wordCount={
            isDocumentPending
              ? estimateMarkdownWordCount(text)
              : document.wordCount
          }
          onDownloadError={handleDownloadError}
          onResetZoom={resetZoom}
          onZoomIn={() => zoom(1.2)}
          onZoomOut={() => zoom(1 / 1.2)}
        />
      ) : null}
      {/* overflow-anchor:none disables the browser's native scroll anchoring,
          which otherwise fights this component's virtualization: as chunks
          mount/unmount and the canvas height corrects, the browser shifts
          scrollTop on its own, jerking the viewport while the reader scrolls. */}
      <ScrollArea
        className="bg-background min-h-0 flex-1"
        orientation="vertical"
        viewportClassName="bg-background [overflow-anchor:none]"
        viewportRef={viewportRef}
        viewportProps={{
          onClickCapture: (event) =>
            handleRenderedClick({ document, event, scrollToLineRange }),
        }}
      >
        <div
          ref={markdownRendererFrame.setDocumentSurfaceElement}
          className="relative min-w-0"
          data-projection="unified-hast-markdown"
          data-slot="markdown-virtual-canvas"
          style={{
            height: Math.max(frame.totalHeight, viewportHeight),
            minWidth: viewportWidth,
            transformOrigin: markdownRendererFrame.transformOrigin,
          }}
        >
          {isDocumentPending ? null : (
            <DeferredNativeFindIndex
              chunks={document.chunks}
              lineCount={document.lineCount}
              scrollToLineRange={scrollToLineRange}
            />
          )}
          {isDocumentPending ? (
            <div
              aria-label="Preparing Markdown document"
              className="text-muted-foreground absolute inset-x-4 top-0 flex min-h-40 items-center justify-center text-sm"
              data-slot="markdown-loading-state"
              role="status"
            >
              Preparing Markdown...
            </div>
          ) : document.text.trim() ? (
            <>
              <div
                aria-hidden="true"
                data-slot="markdown-sticky-before-buffer"
                style={{
                  contain: "layout size",
                  height: stickyWindow.beforeHeight,
                }}
              />
              <div
                ref={stickyWindowRef}
                data-slot="markdown-sticky-window"
                style={{
                  bottom: stickyWindow.stickyOffset,
                  contain: "layout style inline-size",
                  display: "flex",
                  flexDirection: "column",
                  height: stickyWindow.renderedHeight,
                  isolation: "isolate",
                  left: 0,
                  overflow: "visible",
                  position: "sticky",
                  top: stickyWindow.stickyOffset,
                  width: "100%",
                }}
              >
                <div
                  data-slot="markdown-sticky-content"
                  style={{
                    height: stickyWindow.renderedHeight,
                    position: "relative",
                    width: "100%",
                  }}
                >
                  {visibleFrames.map((chunkFrame) => {
                    const chunk = document.chunks[chunkFrame.index];
                    if (!chunk) return null;
                    return (
                      <ChunkFrame
                        key={chunk.id}
                        chunk={chunk}
                        frame={chunkFrame}
                        highlightRange={visibleHighlightRange}
                        highlighted={chunkIntersectsLineRange({
                          chunkFrame,
                          range: visibleHighlightRange,
                        })}
                        measurementKey={measuredHeightKey({
                          chunk,
                          context: {
                            fontScale,
                            policyVersion:
                              MARKDOWN_GREENFIELD_LAYOUT_POLICY_VERSION,
                            width: Math.max(1, contentWidth),
                          },
                          documentMeasurementId,
                        })}
                        renderedTop={stickyWindow.renderedTop}
                        onMeasuredHeight={recordMeasuredHeight}
                      >
                        <MarkdownGreenfieldChunkRenderer
                          chunk={chunk}
                          fontScale={fontScale}
                          urlFragmentNavigation={urlFragmentNavigation}
                        />
                      </ChunkFrame>
                    );
                  })}
                </div>
              </div>
              <div
                aria-hidden="true"
                data-slot="markdown-sticky-after-buffer"
                style={{
                  contain: "layout size",
                  height: stickyWindow.afterHeight,
                }}
              />
            </>
          ) : (
            <div
              aria-label="Empty Markdown document"
              className="text-muted-foreground absolute inset-x-4 top-0 flex min-h-40 items-center justify-center text-sm"
              data-slot="markdown-empty-state"
              role="status"
            >
              Empty Markdown document
            </div>
          )}
        </div>
      </ScrollArea>
    </TextViewerFrame>
  );
}

function DeferredNativeFindIndex({
  chunks,
  lineCount,
  scrollToLineRange,
}: {
  chunks: readonly MarkdownGreenfieldChunk[];
  lineCount: number;
  scrollToLineRange: (
    range: ReturnType<typeof normalizeTextLineRange>,
    options?: MarkdownScrollToLineOptions,
  ) => void;
}) {
  const [isReady, setIsReady] = React.useState(false);

  useKeyedMountEffect(joinEffectKey([chunks]), () => {
    setIsReady(false);
    const show = () => setIsReady(true);
    if (typeof window === "undefined") return;
    const browserWindow = window as MarkdownIdleWindow;
    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
      chunks={chunks}
      lineCount={lineCount}
      scrollToLineRange={scrollToLineRange}
    />
  );
}

function NativeFindIndex({
  chunks,
  lineCount,
  scrollToLineRange,
}: {
  chunks: readonly MarkdownGreenfieldChunk[];
  lineCount: number;
  scrollToLineRange: (
    range: ReturnType<typeof normalizeTextLineRange>,
    options?: MarkdownScrollToLineOptions,
  ) => void;
}) {
  const entries = useBatchedNativeFindTextEntries(chunks);
  return (
    <div
      aria-hidden="true"
      className="pointer-events-none absolute top-0 left-0 h-px w-px overflow-hidden opacity-0"
      data-slot="markdown-native-find-index"
      data-native-find-indexed-chunks={entries.length}
      data-native-find-total-chunks={chunks.length}
    >
      {entries.map(({ chunk, text }) => (
        <NativeFindEntry
          key={chunk.id}
          chunk={chunk}
          lineCount={lineCount}
          scrollToLineRange={scrollToLineRange}
          text={text}
        />
      ))}
    </div>
  );
}

function NativeFindEntry({
  chunk,
  lineCount,
  scrollToLineRange,
  text,
}: {
  chunk: MarkdownGreenfieldChunk;
  lineCount: number;
  scrollToLineRange: (
    range: ReturnType<typeof normalizeTextLineRange>,
    options?: MarkdownScrollToLineOptions,
  ) => void;
  text: string;
}) {
  const ref = React.useRef<HTMLSpanElement | null>(null);

  useKeyedLayoutEffect(
    joinEffectKey([
      chunk.id,
      chunk.sourceEndLine,
      chunk.sourceStartLine,
      lineCount,
      scrollToLineRange,
    ]),
    () => {
      const element = ref.current;
      if (!element) return;
      element.setAttribute("hidden", "until-found");

      const handleBeforeMatch = () => {
        scrollToLineRange(
          normalizeTextLineRange(
            {
              end: chunk.sourceEndLine,
              start: chunk.sourceStartLine,
            },
            lineCount,
          ),
          { behavior: "auto", preferredChunkId: chunk.id },
        );
        requestAnimationFrame(() => {
          element.setAttribute("hidden", "until-found");
        });
      };

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

  return (
    <span
      ref={ref}
      className="absolute top-0 left-0 block h-px w-px overflow-hidden whitespace-pre"
      data-native-find-chunk-id={chunk.id}
      data-native-find-end-line={chunk.sourceEndLine}
      data-native-find-start-line={chunk.sourceStartLine}
    >
      {text || " "}
    </span>
  );
}

function useBatchedNativeFindTextEntries(
  chunks: readonly MarkdownGreenfieldChunk[],
) {
  const buildKey = React.useMemo(
    () => joinEffectKey(["native-find-text", chunks]),
    [chunks],
  );
  const [state, setState] = React.useState<NativeFindTextState>({
    entries: [],
    key: null,
  });

  useKeyedMountEffect(buildKey, () => {
    let isCancelled = false;
    let cancelScheduledBatch: (() => void) | null = null;
    let index = 0;
    const entries: NativeFindTextRecord[] = [];

    setState({ entries: [], key: buildKey });

    const runBatch = (deadline?: IdleDeadline) => {
      cancelScheduledBatch = null;
      if (isCancelled) return;

      let processed = 0;
      while (
        index < chunks.length &&
        shouldBuildNativeFindTextInCurrentBatch(deadline, processed)
      ) {
        const chunk = chunks[index];
        index += 1;
        if (!chunk) continue;
        entries.push({
          chunk,
          text: chunk.nativeFindText,
        });
        processed += 1;
      }

      if (processed > 0) {
        setState({ entries: entries.slice(), key: buildKey });
      }
      if (index < chunks.length) {
        cancelScheduledBatch = scheduleNativeFindTextBatch(runBatch);
      }
    };

    cancelScheduledBatch = scheduleNativeFindTextBatch(runBatch);
    return () => {
      isCancelled = true;
      cancelScheduledBatch?.();
    };
  });

  return state.key === buildKey ? state.entries : [];
}

function shouldBuildNativeFindTextInCurrentBatch(
  deadline: IdleDeadline | undefined,
  processed: number,
) {
  if (processed === 0) return true;
  if (processed >= NATIVE_FIND_TEXT_BATCH_CHUNKS) return false;
  if (!deadline) return true;
  return deadline.timeRemaining() > NATIVE_FIND_TEXT_IDLE_MIN_REMAINING_MS;
}

function scheduleNativeFindTextBatch(
  callback: (deadline?: IdleDeadline) => void,
) {
  if (typeof window === "undefined") return null;
  const browserWindow = window as MarkdownIdleWindow;
  if (browserWindow.requestIdleCallback) {
    const idleId = browserWindow.requestIdleCallback(callback, {
      timeout: 250,
    });
    return () => browserWindow.cancelIdleCallback?.(idleId);
  }

  const timeoutId = browserWindow.setTimeout(() => callback(), 0);
  return () => browserWindow.clearTimeout(timeoutId);
}

function DownloadError({ message }: { message: string }) {
  if (!message) return null;
  return (
    <span
      className="text-destructive max-w-48 truncate text-xs"
      data-slot="markdown-download-error"
      role="status"
    >
      {message}
    </span>
  );
}

function ChunkFrame({
  children,
  chunk,
  frame,
  highlightRange,
  highlighted,
  measurementKey,
  renderedTop,
  onMeasuredHeight,
}: {
  children: React.ReactNode;
  chunk: MarkdownGreenfieldChunk;
  frame: MarkdownGreenfieldChunkFrame;
  highlightRange: { end: number; start: number } | null;
  highlighted: boolean;
  measurementKey: string;
  renderedTop: number;
  onMeasuredHeight: (chunk: MarkdownGreenfieldChunk, height: number) => void;
}) {
  const ref = React.useRef<HTMLDivElement | null>(null);

  useKeyedLayoutEffect(joinEffectKey([chunk, onMeasuredHeight]), () => {
    measure();
    const element = ref.current;
    if (!element || typeof ResizeObserver === "undefined") return;
    const observer = new ResizeObserver(measure);
    observer.observe(element);
    return () => observer.disconnect();

    function measure() {
      const element = ref.current;
      if (!element) return;
      onMeasuredHeight(chunk, element.getBoundingClientRect().height);
    }
  });

  return (
    <section
      ref={ref}
      aria-label={
        highlighted
          ? `Highlighted source lines ${highlightRange?.start}-${highlightRange?.end}`
          : `Markdown lines ${frame.sourceStartLine} to ${frame.sourceEndLine}`
      }
      className={[
        "absolute left-1/2 w-full -translate-x-1/2 px-8 py-1",
      ].join(" ")}
      data-markdown-chunk=""
      data-markdown-highlighted={highlighted ? "" : undefined}
      data-pretext-measured-height-key={measurementKey}
      data-source-highlight-end={highlighted ? highlightRange?.end : undefined}
      data-source-highlight-start={
        highlighted ? highlightRange?.start : undefined
      }
      data-source-end-line={frame.sourceEndLine}
      data-source-start-line={frame.sourceStartLine}
      role={highlighted ? "region" : undefined}
      style={{
        maxWidth: MARKDOWN_GREENFIELD_CHUNK_MAX_INLINE_SIZE,
        top: frame.top - renderedTop,
        ...(highlighted ? MARKDOWN_GREENFIELD_HIGHLIGHT_STYLE : null),
      }}
    >
      {children}
    </section>
  );
}

function resolveScrollBehavior(behavior: ScrollBehavior | undefined) {
  if (behavior) return behavior;
  if (
    typeof window !== "undefined" &&
    window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
  ) {
    return "auto";
  }
  return "smooth";
}

function handleRenderedClick({
  document,
  event,
  scrollToLineRange,
}: {
  document: ReturnType<typeof createMarkdownGreenfieldDocument>;
  event: React.MouseEvent;
  scrollToLineRange: (
    range: ReturnType<typeof normalizeTextLineRange>,
    options?: MarkdownScrollToLineOptions,
  ) => void;
}) {
  const link = (event.target as HTMLElement | null)?.closest("a[href]");
  const href = link?.getAttribute("href");
  if (!href?.startsWith("#")) return;

  const target = findMarkdownGreenfieldFragmentTargetById(document, href);
  if (!target) return;
  const chunk = findMarkdownGreenfieldChunkByBlockId(document, target.blockId);

  event.preventDefault();
  window.history.pushState(null, "", href);
  scrollToLineRange(
    normalizeTextLineRange(
      { end: target.sourceLine, start: target.sourceLine },
      document.lineCount,
    ),
    { behavior: "smooth", preferredChunkId: chunk?.id },
  );
}

function chunkIntersectsLineRange({
  chunkFrame,
  range,
}: {
  chunkFrame: MarkdownGreenfieldChunkFrame;
  range: { end: number; start: number } | null;
}) {
  if (!range) return false;
  return (
    chunkFrame.sourceStartLine <= range.end &&
    chunkFrame.sourceEndLine >= range.start
  );
}

function measuredHeightKey({
  chunk,
  context,
  documentMeasurementId,
}: {
  chunk: MarkdownGreenfieldChunk;
  context: MarkdownGreenfieldMeasurementContext;
  documentMeasurementId: string;
}) {
  return [
    documentMeasurementId,
    chunk.id,
    Math.round(context.width),
    context.fontScale.toFixed(4),
    context.policyVersion,
  ].join(":");
}

function measurementDocumentIdForText(text: string) {
  let hash = 2166136261;
  for (let index = 0; index < text.length; index += 1) {
    hash ^= text.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return `md-${text.length}-${(hash >>> 0).toString(36)}`;
}

function estimateMarkdownWordCount(text: string) {
  return text.trim().split(/\s+/).filter(Boolean).length;
}
registry/new-york-v4/ui/markdown-greenfield-code-highlight.tsx
"use client";

import * as React from "react";

import {
  ensureCodePrismLanguage,
  tokenizeCodeLine as tokenizePrismCodeLine,
} from "./code-viewer-syntax-prism";
import { shouldTokenizeCodeLine } from "./code-viewer-syntax-protocol";
import { markdownCodeTokensToHtml } from "./markdown-greenfield-code-highlight-html";
import {
  MARKDOWN_CODE_HIGHLIGHT_BATCH_SIZE,
  MARKDOWN_CODE_HIGHLIGHT_CACHE_LIMIT,
  MARKDOWN_CODE_HIGHLIGHT_RENDERER_VERSION,
  type MarkdownCodeHighlightLineRequest,
  type MarkdownCodeHighlightLineResult,
  type MarkdownCodeHighlightWorkerRequest,
  type MarkdownCodeHighlightWorkerResponse,
} from "./markdown-greenfield-code-highlight-protocol";

export const MARKDOWN_CODE_HIGHLIGHT_STYLES = `
.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); }
`;

type MarkdownCodeLineHtmlRequest = MarkdownCodeHighlightLineRequest & {
  highlightPattern: string;
  key: string;
  languageId: string;
};

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

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

const resolvedMarkdownCodeLineHtml = new Map<string, string | null>();
const pendingMarkdownCodeLineHtml = new Map<
  string,
  MarkdownCodeLineHtmlRequest
>();
const markdownCodeLineHtmlSubscribers = new Map<string, Set<() => void>>();
const activeWorkerRequests = new Map<
  number,
  readonly MarkdownCodeLineHtmlRequest[]
>();

let markdownCodeHighlightFlushHandle: MarkdownCodeHighlightTaskHandle | null =
  null;
let markdownCodeHighlightWorker: Worker | null = null;
let isMarkdownCodeHighlightWorkerFailed = false;
let markdownCodeHighlightRequestId = 0;

export function isSafeHighlightedCodeLine(line: number) {
  return Number.isInteger(line) && line > 0 && line <= 100_000;
}

export function normalizeCodeLanguage(language: string | null) {
  const value = (language ?? "text").toLowerCase();
  const aliases: Record<string, string> = {
    bash: "shell",
    docker: "dockerfile",
    javascript: "js",
    jsonc: "json",
    md: "markdown",
    patch: "diff",
    rb: "ruby",
    "shell-session": "shell",
    terminal: "shell",
    typescript: "ts",
    yml: "yaml",
  };
  return aliases[value] ?? value;
}

export function diffLineKind(line: string) {
  if (line.startsWith("+") && !line.startsWith("+++")) return "add";
  if (line.startsWith("-") && !line.startsWith("---")) return "remove";
  return null;
}

export function renderCodeLine({
  fallbackLanguage,
  line,
  lineHtml,
  pattern,
}: {
  fallbackLanguage: string;
  line: string;
  lineHtml: string | undefined;
  pattern: string;
}) {
  if (lineHtml !== undefined) {
    return (
      <span
        data-pretext-code-line-html=""
        dangerouslySetInnerHTML={{ __html: lineHtml }}
      />
    );
  }
  if (!pattern) return renderFallbackCodeTokens(line || " ", fallbackLanguage);
  const index = line.indexOf(pattern);
  if (index < 0) return renderFallbackCodeTokens(line || " ", fallbackLanguage);
  return (
    <>
      {renderFallbackCodeTokens(line.slice(0, index), fallbackLanguage)}
      <span data-highlighted-chars="">{pattern}</span>
      {renderFallbackCodeTokens(
        line.slice(index + pattern.length),
        fallbackLanguage,
      )}
    </>
  );
}

export function useMarkdownCodeLineHtml({
  end,
  highlightPattern,
  language,
  sourceLines,
  start,
}: {
  end: number;
  highlightPattern: string;
  language: string;
  sourceLines: readonly string[];
  start: number;
}) {
  const requests = React.useMemo(
    () =>
      createMarkdownCodeLineHtmlRequests({
        end,
        highlightPattern,
        language,
        sourceLines,
        start,
      }),
    [end, highlightPattern, language, sourceLines, start],
  );
  const subscribe = React.useCallback(
    (onStoreChange: () => void) => {
      for (const request of requests) {
        let subscribers = markdownCodeLineHtmlSubscribers.get(request.key);
        if (!subscribers) {
          subscribers = new Set();
          markdownCodeLineHtmlSubscribers.set(request.key, subscribers);
        }
        subscribers.add(onStoreChange);
        ensureMarkdownCodeLineHtml(request);
      }
      return () => {
        for (const request of requests) {
          const subscribers = markdownCodeLineHtmlSubscribers.get(request.key);
          if (!subscribers) continue;
          subscribers.delete(onStoreChange);
          if (!subscribers.size) {
            markdownCodeLineHtmlSubscribers.delete(request.key);
          }
        }
      };
    },
    [requests],
  );
  const getSnapshot = React.useCallback(
    () =>
      requests
        .map((request) =>
          resolvedMarkdownCodeLineHtml.has(request.key) ? "1" : "0",
        )
        .join(""),
    [requests],
  );
  const resolvedVersion = React.useSyncExternalStore(
    subscribe,
    getSnapshot,
    () => "",
  );

  return React.useMemo(() => {
    void resolvedVersion;
    const htmlByIndex = new Map<number, string>();
    for (const request of requests) {
      const html = resolvedMarkdownCodeLineHtml.get(request.key);
      if (typeof html === "string") htmlByIndex.set(request.index, html);
    }
    return htmlByIndex;
  }, [requests, resolvedVersion]);
}

function createMarkdownCodeLineHtmlRequests({
  end,
  highlightPattern,
  language,
  sourceLines,
  start,
}: {
  end: number;
  highlightPattern: string;
  language: string;
  sourceLines: readonly string[];
  start: number;
}) {
  const languageId = prismLanguageForMarkdownCode(language);
  if (!languageId) return [];

  const requests: MarkdownCodeLineHtmlRequest[] = [];
  const boundedStart = Math.max(0, Math.min(start, sourceLines.length));
  const boundedEnd = Math.max(boundedStart, Math.min(end, sourceLines.length));
  for (let index = boundedStart; index < boundedEnd; index += 1) {
    const line = sourceLines[index] ?? "";
    requests.push({
      highlightPattern,
      index,
      key: markdownCodeLineHtmlKey({
        highlightPattern,
        languageId,
        line,
      }),
      languageId,
      line,
    });
  }
  return requests;
}

function ensureMarkdownCodeLineHtml(request: MarkdownCodeLineHtmlRequest) {
  if (
    resolvedMarkdownCodeLineHtml.has(request.key) ||
    pendingMarkdownCodeLineHtml.has(request.key)
  ) {
    return;
  }
  pendingMarkdownCodeLineHtml.set(request.key, request);
  scheduleMarkdownCodeHighlightFlush();
}

function scheduleMarkdownCodeHighlightFlush() {
  if (markdownCodeHighlightFlushHandle) return;
  markdownCodeHighlightFlushHandle = scheduleMarkdownCodeHighlightTask(() => {
    markdownCodeHighlightFlushHandle = null;
    flushPendingMarkdownCodeHighlights();
  });
}

function flushPendingMarkdownCodeHighlights() {
  const firstRequest = pendingMarkdownCodeLineHtml.values().next().value;
  if (!firstRequest) return;

  const requests: MarkdownCodeLineHtmlRequest[] = [];
  for (const request of pendingMarkdownCodeLineHtml.values()) {
    if (
      request.languageId !== firstRequest.languageId ||
      request.highlightPattern !== firstRequest.highlightPattern
    ) {
      continue;
    }
    pendingMarkdownCodeLineHtml.delete(request.key);
    requests.push(request);
    if (requests.length >= MARKDOWN_CODE_HIGHLIGHT_BATCH_SIZE) break;
  }

  if (requests.length === 0) return;
  if (canUseMarkdownCodeHighlightWorker()) {
    requestMarkdownCodeHighlightWorker(requests);
  } else {
    void highlightMarkdownCodeLinesOnMainThread(requests);
  }
  if (pendingMarkdownCodeLineHtml.size > 0)
    scheduleMarkdownCodeHighlightFlush();
}

function canUseMarkdownCodeHighlightWorker() {
  return (
    !isMarkdownCodeHighlightWorkerFailed &&
    typeof Worker !== "undefined" &&
    typeof window !== "undefined"
  );
}

function requestMarkdownCodeHighlightWorker(
  requests: readonly MarkdownCodeLineHtmlRequest[],
) {
  const worker = getMarkdownCodeHighlightWorker();
  if (!worker) {
    void highlightMarkdownCodeLinesOnMainThread(requests);
    return;
  }

  markdownCodeHighlightRequestId += 1;
  const requestId = markdownCodeHighlightRequestId;
  activeWorkerRequests.set(requestId, requests);
  const firstRequest = requests[0]!;
  const request: MarkdownCodeHighlightWorkerRequest = {
    generation: MARKDOWN_CODE_HIGHLIGHT_RENDERER_VERSION,
    highlightPattern: firstRequest.highlightPattern,
    languageId: firstRequest.languageId,
    lines: requests.map(({ index, line }) => ({ index, line })),
    requestId,
    type: "highlight",
  };
  worker.postMessage(request);
}

function getMarkdownCodeHighlightWorker() {
  if (markdownCodeHighlightWorker) return markdownCodeHighlightWorker;
  try {
    markdownCodeHighlightWorker = new Worker(
      new URL(
        "./markdown-greenfield-code-highlight.worker.ts",
        import.meta.url,
      ),
      { type: "module" },
    );
    markdownCodeHighlightWorker.onmessage = (
      event: MessageEvent<MarkdownCodeHighlightWorkerResponse>,
    ) => handleMarkdownCodeHighlightWorkerMessage(event.data);
    markdownCodeHighlightWorker.onerror = () =>
      failMarkdownCodeHighlightWorker();
    markdownCodeHighlightWorker.onmessageerror = () =>
      failMarkdownCodeHighlightWorker();
    return markdownCodeHighlightWorker;
  } catch {
    isMarkdownCodeHighlightWorkerFailed = true;
    return null;
  }
}

function handleMarkdownCodeHighlightWorkerMessage(
  message: MarkdownCodeHighlightWorkerResponse,
) {
  const requests = activeWorkerRequests.get(message.requestId);
  if (
    !requests ||
    message.generation !== MARKDOWN_CODE_HIGHLIGHT_RENDERER_VERSION
  ) {
    return;
  }
  activeWorkerRequests.delete(message.requestId);
  if (message.type === "error") {
    resolveMarkdownCodeLineHtmlRequests(
      requests.map((request) => ({ html: null, index: request.index })),
      requests,
    );
    return;
  }
  resolveMarkdownCodeLineHtmlRequests(message.results, requests);
}

function failMarkdownCodeHighlightWorker() {
  const activeRequests = Array.from(activeWorkerRequests.values()).flat();
  activeWorkerRequests.clear();
  markdownCodeHighlightWorker?.terminate();
  markdownCodeHighlightWorker = null;
  isMarkdownCodeHighlightWorkerFailed = true;
  if (activeRequests.length) {
    void highlightMarkdownCodeLinesOnMainThread(activeRequests);
  }
}

async function highlightMarkdownCodeLinesOnMainThread(
  requests: readonly MarkdownCodeLineHtmlRequest[],
) {
  const firstRequest = requests[0];
  if (!firstRequest) return;
  try {
    await ensureCodePrismLanguage(firstRequest.languageId);
    resolveMarkdownCodeLineHtmlRequests(
      requests.map((request) => ({
        html: shouldTokenizeCodeLine(request.line)
          ? markdownCodeTokensToHtml({
              highlightPattern: request.highlightPattern,
              line: request.line,
              tokens: tokenizePrismCodeLine(request.languageId, request.line),
            })
          : null,
        index: request.index,
      })),
      requests,
    );
  } catch {
    resolveMarkdownCodeLineHtmlRequests(
      requests.map((request) => ({ html: null, index: request.index })),
      requests,
    );
  }
}

function resolveMarkdownCodeLineHtmlRequests(
  results: readonly MarkdownCodeHighlightLineResult[],
  requests: readonly MarkdownCodeLineHtmlRequest[],
) {
  const requestsByIndex = new Map(
    requests.map((request) => [request.index, request] as const),
  );
  const resolvedKeys: string[] = [];
  for (const result of results) {
    const request = requestsByIndex.get(result.index);
    if (!request) continue;
    resolvedMarkdownCodeLineHtml.set(request.key, result.html);
    resolvedKeys.push(request.key);
  }
  trimMarkdownCodeLineHtmlCache();
  notifyMarkdownCodeLineHtmlSubscribers(resolvedKeys);
}

function notifyMarkdownCodeLineHtmlSubscribers(keys: readonly string[]) {
  const subscribers = new Set<() => void>();
  for (const key of keys) {
    for (const subscriber of markdownCodeLineHtmlSubscribers.get(key) ?? []) {
      subscribers.add(subscriber);
    }
  }
  for (const subscriber of subscribers) subscriber();
}

function trimMarkdownCodeLineHtmlCache() {
  while (
    resolvedMarkdownCodeLineHtml.size > MARKDOWN_CODE_HIGHLIGHT_CACHE_LIMIT
  ) {
    const oldestKey = resolvedMarkdownCodeLineHtml.keys().next().value;
    if (oldestKey === undefined) break;
    resolvedMarkdownCodeLineHtml.delete(oldestKey);
  }
}

function scheduleMarkdownCodeHighlightTask(callback: () => void) {
  if (typeof window === "undefined") {
    const id = setTimeout(callback, 0) as unknown as number;
    return { id, kind: "timeout" as const };
  }
  const browserWindow = window as MarkdownCodeHighlightIdleWindow;
  if (browserWindow.requestIdleCallback) {
    return {
      id: browserWindow.requestIdleCallback(callback, { timeout: 120 }),
      kind: "idle" as const,
    };
  }
  return {
    id: browserWindow.setTimeout(callback, 0),
    kind: "timeout" as const,
  };
}

export function resetMarkdownCodeHighlightForTests() {
  resolvedMarkdownCodeLineHtml.clear();
  pendingMarkdownCodeLineHtml.clear();
  markdownCodeLineHtmlSubscribers.clear();
  activeWorkerRequests.clear();
  if (markdownCodeHighlightFlushHandle) {
    cancelMarkdownCodeHighlightTask(markdownCodeHighlightFlushHandle);
  }
  markdownCodeHighlightFlushHandle = null;
  markdownCodeHighlightWorker?.terminate();
  markdownCodeHighlightWorker = null;
  isMarkdownCodeHighlightWorkerFailed = false;
  markdownCodeHighlightRequestId = 0;
}

function cancelMarkdownCodeHighlightTask(
  handle: MarkdownCodeHighlightTaskHandle,
) {
  if (typeof window === "undefined") {
    clearTimeout(handle.id);
    return;
  }
  const browserWindow = window as MarkdownCodeHighlightIdleWindow;
  if (handle.kind === "idle") {
    browserWindow.cancelIdleCallback?.(handle.id);
    return;
  }
  browserWindow.clearTimeout(handle.id);
}

function markdownCodeLineHtmlKey({
  highlightPattern,
  languageId,
  line,
}: {
  highlightPattern: string;
  languageId: string;
  line: string;
}) {
  return [
    MARKDOWN_CODE_HIGHLIGHT_RENDERER_VERSION,
    languageId,
    highlightPattern,
    line,
  ].join("\0");
}

function prismLanguageForMarkdownCode(language: string) {
  const aliases: Record<string, string> = {
    dockerfile: "dockerfile",
    js: "javascript",
    shell: "bash",
    ts: "typescript",
  };
  const languageId = aliases[language] ?? language;
  if (languageId === "text" || languageId === "plaintext") return "";
  return languageId;
}

function renderFallbackCodeTokens(line: string, language: string) {
  const tokens = tokenizeFallbackCodeLine(line, language);
  if (!tokens.length) return " ";
  return tokens.map((token, index) =>
    token.kind === "plain" ? (
      <React.Fragment key={index}>{token.value}</React.Fragment>
    ) : (
      <span
        key={index}
        className={codeTokenClassName(token.kind)}
        data-pretext-code-token={token.kind}
      >
        {token.value}
      </span>
    ),
  );
}

type CodeToken = {
  kind: "comment" | "keyword" | "literal" | "number" | "plain" | "string";
  value: string;
};

function tokenizeFallbackCodeLine(line: string, language: string): CodeToken[] {
  if (!line) return [];
  if (language === "diff") return tokenizeDiffLine(line);
  if (language === "json") return tokenizeJsonLikeLine(line);
  if (language === "yaml") return tokenizeYamlLine(line);
  if (
    language === "js" ||
    language === "jsx" ||
    language === "ts" ||
    language === "tsx"
  ) {
    return tokenizeCStyleLine(line);
  }
  if (
    language === "shell" ||
    language === "bash" ||
    language === "dockerfile"
  ) {
    return tokenizeShellLine(line);
  }
  return [{ kind: "plain", value: line }];
}

function tokenizeCStyleLine(line: string): CodeToken[] {
  const commentIndex = line.indexOf("//");
  const codePart = commentIndex >= 0 ? line.slice(0, commentIndex) : line;
  const commentPart = commentIndex >= 0 ? line.slice(commentIndex) : "";
  return [
    ...tokenizeByPattern(
      codePart,
      /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\b(?:as|async|await|break|case|catch|class|const|continue|default|do|else|export|extends|false|finally|for|from|function|if|import|in|instanceof|interface|let|new|null|of|return|satisfies|switch|throw|true|try|type|typeof|undefined|var|while|yield)\b|\b\d+(?:\.\d+)?\b)/g,
      classifyCStyleToken,
    ),
    ...(commentPart ? [{ kind: "comment" as const, value: commentPart }] : []),
  ];
}

function classifyCStyleToken(value: string): CodeToken["kind"] {
  if (/^["'`]/.test(value)) return "string";
  if (/^\d/.test(value)) return "number";
  if (/^(?:true|false|null|undefined)$/.test(value)) return "literal";
  return "keyword";
}

function tokenizeJsonLikeLine(line: string): CodeToken[] {
  return tokenizeByPattern(
    line,
    /("(?:\\.|[^"\\])*"|\b(?:true|false|null)\b|-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b)/gi,
    (value) => {
      if (/^"/.test(value)) return "string";
      if (/^(?:true|false|null)$/i.test(value)) return "literal";
      return "number";
    },
  );
}

function tokenizeYamlLine(line: string): CodeToken[] {
  const commentIndex = line.indexOf("#");
  const codePart = commentIndex >= 0 ? line.slice(0, commentIndex) : line;
  const commentPart = commentIndex >= 0 ? line.slice(commentIndex) : "";
  return [
    ...tokenizeByPattern(
      codePart,
      /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\b(?:true|false|null)\b|-?\b\d+(?:\.\d+)?\b)/gi,
      (value) => {
        if (/^["']/.test(value)) return "string";
        if (/^(?:true|false|null)$/i.test(value)) return "literal";
        return "number";
      },
    ),
    ...(commentPart ? [{ kind: "comment" as const, value: commentPart }] : []),
  ];
}

function tokenizeShellLine(line: string): CodeToken[] {
  const commentIndex = line.search(/(^|\s)#/);
  const codePart = commentIndex >= 0 ? line.slice(0, commentIndex) : line;
  const commentPart = commentIndex >= 0 ? line.slice(commentIndex) : "";
  return [
    ...tokenizeByPattern(
      codePart,
      /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\b(?:cd|cp|curl|echo|export|git|grep|mkdir|mv|node|npm|pnpm|rm|sed|test|yarn)\b)/g,
      (value) => (/^["']/.test(value) ? "string" : "keyword"),
    ),
    ...(commentPart ? [{ kind: "comment" as const, value: commentPart }] : []),
  ];
}

function tokenizeDiffLine(line: string): CodeToken[] {
  if (line.startsWith("+") && !line.startsWith("+++")) {
    return [{ kind: "literal", value: line }];
  }
  if (line.startsWith("-") && !line.startsWith("---")) {
    return [{ kind: "comment", value: line }];
  }
  return [{ kind: "plain", value: line }];
}

function tokenizeByPattern(
  line: string,
  pattern: RegExp,
  classify: (value: string) => CodeToken["kind"],
): CodeToken[] {
  const tokens: CodeToken[] = [];
  let cursor = 0;
  for (const match of line.matchAll(pattern)) {
    const index = match.index ?? 0;
    if (index > cursor) {
      tokens.push({ kind: "plain", value: line.slice(cursor, index) });
    }
    const value = match[0];
    tokens.push({ kind: classify(value), value });
    cursor = index + value.length;
  }
  if (cursor < line.length) {
    tokens.push({ kind: "plain", value: line.slice(cursor) });
  }
  return tokens;
}

function codeTokenClassName(kind: CodeToken["kind"]) {
  switch (kind) {
    case "comment":
      return "text-muted-foreground italic";
    case "keyword":
      return "font-semibold text-sky-700 dark:text-sky-300";
    case "literal":
      return "text-purple-700 dark:text-purple-300";
    case "number":
      return "text-amber-700 dark:text-amber-300";
    case "string":
      return "text-emerald-700 dark:text-emerald-300";
    default:
      return "";
  }
}
registry/new-york-v4/ui/markdown-greenfield-diagram.tsx
"use client";

import * as React from "react";
import { Check, Clipboard } from "lucide-react";
import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";
import {
  MERMAID_VIEWER_STYLES,
  describeDiagram,
  estimateDiagramBodyHeight,
  readDiagramLimitMessage,
  renderDiagram,
  type DiagramState,
} from "@/components/ui/mermaid-renderer";

export { resetMermaidRendererForTests as resetMarkdownMermaidRendererForTests } from "@/components/ui/mermaid-renderer";

const MERMAID_VIEWPORT_ROOT_MARGIN = "640px 0px";

// A per-instance async store for one diagram's mermaid render. Kept per
// component (not module-global) so instances never share state — no cross-
// render notify fan-out and no shared cache keyed by a render-order id.
type MermaidDiagramStore = {
  key: string;
  result: DiagramState | null;
  listeners: Set<() => void>;
};

export function MarkdownGreenfieldDiagram({
  caption,
  componentName,
  onContentReady,
  source,
  title,
}: {
  caption?: string;
  componentName?: string;
  onContentReady?: () => void;
  source: string;
  title?: string;
}) {
  const limitMessage = React.useMemo(
    () => readDiagramLimitMessage(source),
    [source],
  );
  const diagramId = React.useId().replace(/:/g, "");
  const elementId = `markdown-diagram-${diagramId}`;
  const description = React.useMemo(() => describeDiagram(source), [source]);
  const bodyHeight = React.useMemo(
    () => estimateDiagramBodyHeight(source),
    [source],
  );
  const descriptionId = description
    ? `markdown-diagram-description-${diagramId}`
    : undefined;
  const captionId = caption
    ? `markdown-diagram-caption-${diagramId}`
    : undefined;
  const describedBy =
    [descriptionId, captionId].filter(Boolean).join(" ") || undefined;
  const [figureRef, isRenderEligible] = useMermaidRenderEligibility({
    disabled: Boolean(limitMessage),
    source,
  });

  // Derive the displayed state: a limit message fails immediately; otherwise the
  // state follows this instance's async mermaid store (loading until resolved).
  const storeRef = React.useRef<MermaidDiagramStore | null>(null);
  if (storeRef.current === null) {
    storeRef.current = { key: "", listeners: new Set(), result: null };
  }
  const store = storeRef.current;
  const renderKey =
    limitMessage || !isRenderEligible ? "" : `${elementId}\0${source}`;
  const subscribe = React.useCallback(
    (onStoreChange: () => void) => {
      store.listeners.add(onStoreChange);
      // Kick off (or restart on source change) the render here, in the store
      // subscription, so no effect is needed.
      if (!limitMessage && store.key !== renderKey) {
        store.key = renderKey;
        store.result = null;
        void renderDiagram(source, elementId).then((result) => {
          if (store.key !== renderKey) return;
          store.result = result;
          for (const listener of store.listeners) listener();
        });
      }
      return () => {
        store.listeners.delete(onStoreChange);
      };
    },
    [elementId, limitMessage, renderKey, source, store],
  );
  const getSnapshot = React.useCallback(
    () => (store.key === renderKey ? store.result : null),
    [renderKey, store],
  );
  const resolvedState = React.useSyncExternalStore(
    subscribe,
    getSnapshot,
    () => null,
  );
  const state: DiagramState = limitMessage
    ? { status: "failed", message: limitMessage }
    : (resolvedState ?? { status: "loading" });

  useKeyedLayoutEffect(
    joinEffectKey([bodyHeight, onContentReady, state.status]),
    () => {
      onContentReady?.();
    },
  );

  return (
    <figure
      ref={figureRef}
      aria-describedby={describedBy}
      aria-label={title || "Mermaid diagram"}
      className="group bg-muted/30 my-5 min-h-40 overflow-hidden rounded-md border"
      data-diagram-language="mermaid"
      data-diagram-renderer={
        state.status === "ready" ? state.renderer : undefined
      }
      data-diagram-reserved-height={bodyHeight}
      data-diagram-state={state.status}
      data-pretext-component={componentName}
      role="group"
      style={
        {
          "--pretext-diagram-body-height": `${bodyHeight}px`,
        } as React.CSSProperties
      }
    >
      <div className="bg-muted/60 flex h-9 items-center gap-1 border-b px-3">
        <span className="text-muted-foreground min-w-0 truncate text-xs font-medium">
          {title || "mermaid"}
        </span>
        <DiagramCopyButton
          ariaLabel="Copy diagram source"
          className="ml-auto"
          text={source}
        />
        {state.status === "ready" ? (
          <DiagramCopyButton ariaLabel="Copy diagram SVG" text={state.svg} />
        ) : null}
      </div>
      {description ? (
        <p
          className="sr-only"
          data-pretext-diagram-description=""
          id={descriptionId}
        >
          {description}
        </p>
      ) : null}
      <div
        aria-label="Mermaid diagram body"
        className="h-(--pretext-diagram-body-height) overflow-auto p-4"
        data-pretext-diagram-body=""
        onKeyDown={(event) => {
          const element = event.currentTarget;
          if (event.key === "ArrowRight") {
            element.scrollLeft += 50;
            event.preventDefault();
          } else if (event.key === "ArrowLeft") {
            element.scrollLeft -= 50;
            event.preventDefault();
          } else if (event.key === "End") {
            element.scrollLeft = Math.max(
              0,
              element.scrollWidth - element.clientWidth,
            );
            event.preventDefault();
          } else if (event.key === "Home") {
            element.scrollLeft = 0;
            event.preventDefault();
          }
        }}
        role="region"
        tabIndex={0}
      >
        {state.status === "ready" ? (
          <>
            <style data-pretext-mermaid-styles="">
              {MERMAID_VIEWER_STYLES}
            </style>
            <div
              className="text-foreground"
              data-pretext-mermaid-svg=""
              dangerouslySetInnerHTML={{ __html: state.svg }}
            />
          </>
        ) : (
          <>
            {state.status === "failed" ? (
              <p
                className="border-destructive/25 bg-destructive/10 text-destructive mb-3 rounded border px-3 py-2 text-sm"
                data-pretext-diagram-error=""
                role="alert"
              >
                {state.message}
              </p>
            ) : null}
            <pre
              aria-label="Mermaid diagram source"
              className="text-muted-foreground m-0 overflow-x-auto font-mono text-[0.82em] leading-relaxed"
              tabIndex={0}
            >
              {source}
            </pre>
          </>
        )}
      </div>
      {caption ? (
        <figcaption
          className="bg-muted/30 text-muted-foreground border-t px-3 py-2 text-sm"
          data-pretext-diagram-caption=""
          id={captionId}
        >
          {caption}
        </figcaption>
      ) : null}
    </figure>
  );
}

function useMermaidRenderEligibility({
  disabled,
  source,
}: {
  disabled: boolean;
  source: string;
}) {
  const ref = React.useRef<HTMLElement | null>(null);
  const [isEligible, setIsEligible] = React.useState(
    () => !disabled && typeof IntersectionObserver === "undefined",
  );

  useKeyedLayoutEffect(joinEffectKey([disabled, source]), () => {
    if (disabled) {
      setIsEligible(false);
      return;
    }
    if (typeof IntersectionObserver === "undefined") {
      setIsEligible(true);
      return;
    }
    const element = ref.current;
    if (!element) return;
    const observer = new IntersectionObserver(
      (entries) => {
        const entry = entries[0];
        if (!entry?.isIntersecting && (entry?.intersectionRatio ?? 0) <= 0) {
          return;
        }
        setIsEligible(true);
        observer.disconnect();
      },
      { root: null, rootMargin: MERMAID_VIEWPORT_ROOT_MARGIN },
    );
    observer.observe(element);
    return () => observer.disconnect();
  });

  return [ref, isEligible] as const;
}

function DiagramCopyButton({
  ariaLabel,
  className,
  text,
}: {
  ariaLabel: string;
  className?: string;
  text: string;
}) {
  const [isCopied, setIsCopied] = React.useState(false);

  return (
    <button
      aria-label={isCopied ? "Copied" : ariaLabel}
      className={[
        "text-muted-foreground hover:bg-background hover:text-foreground focus-visible:ring-ring inline-flex size-7 items-center justify-center rounded-md transition focus-visible:ring-2 focus-visible:outline-none",
        className,
      ]
        .filter(Boolean)
        .join(" ")}
      type="button"
      onClick={() => {
        void navigator.clipboard?.writeText(text).then(() => {
          setIsCopied(true);
          window.setTimeout(() => setIsCopied(false), 1200);
        });
      }}
    >
      {isCopied ? (
        <Check aria-hidden="true" className="size-3.5" />
      ) : (
        <Clipboard aria-hidden="true" className="size-3.5" />
      )}
    </button>
  );
}
registry/new-york-v4/ui/markdown-greenfield-document.ts
"use client";

import type {
  MarkdownHastElement,
  MarkdownHastNode,
} from "./markdown-hast-types";
import {
  markdownSourceRangeFromPosition,
  markdownSourceTextForRange,
  type MarkdownSourceRange,
} from "./markdown-source-map";
import {
  createMarkdownUnifiedDocument,
  type MarkdownUnifiedDocument,
} from "./markdown-unified-pipeline";

const TARGET_CHUNK_SOURCE_LINES = 42;
const MAX_CHUNK_SOURCE_LINES = 64;
const HOSTILE_CODE_LINE_COUNT = 400;
const HOSTILE_HAST_NODE_COUNT = 3_000;
const HOSTILE_HAST_DEPTH = 80;
const HOSTILE_TEXT_LENGTH = 20_000;
const HOSTILE_TABLE_CELL_COUNT = 50_000;
const HOSTILE_TABLE_HAST_NODE_COUNT = 120_000;
const HOSTILE_TABLE_TEXT_LENGTH = 1_000_000;
const DOCUMENT_CACHE_LIMIT = 24;

const markdownGreenfieldDocumentCache = new Map<
  string,
  {
    document: MarkdownGreenfieldDocument;
    text: string;
  }
>();

export type MarkdownGreenfieldBlockKind =
  | "blockquote"
  | "code"
  | "component"
  | "diagram"
  | "footnotes"
  | "frontmatter"
  | "heading"
  | "html"
  | "image"
  | "list"
  | "math"
  | "paragraph"
  | "table"
  | "thematicBreak"
  | "unknown";

export type MarkdownGreenfieldDocument = {
  blocks: MarkdownGreenfieldBlock[];
  chunks: MarkdownGreenfieldChunk[];
  fragmentTargets: MarkdownGreenfieldFragmentTarget[];
  headings: MarkdownGreenfieldHeading[];
  lineCount: number;
  text: string;
  unified: MarkdownUnifiedDocument;
  wordCount: number;
};

export type MarkdownGreenfieldHeading = {
  blockId: string;
  id: string;
  sourceLine: number;
  text: string;
};

export type MarkdownGreenfieldFragmentTarget = {
  blockId: string;
  id: string;
  sourceLine: number;
};

export type MarkdownGreenfieldBlock = {
  hastChildren: MarkdownHastNode[];
  id: string;
  index: number;
  isGenerated: boolean;
  isHostile: boolean;
  kind: MarkdownGreenfieldBlockKind;
  sourceLineCount: number;
  sourceLineLengths: readonly number[];
  sourceRange: MarkdownSourceRange | null;
  sourceText: string;
};

export type MarkdownGreenfieldChunk = {
  blockIds: string[];
  hastChildren: MarkdownHastNode[];
  id: string;
  index: number;
  isHostile: boolean;
  nativeFindText: string;
  sourceEndLine: number;
  sourceLineCount: number;
  sourceRange: MarkdownSourceRange | null;
  sourceStartLine: number;
  sourceText: string;
};

export function createMarkdownGreenfieldDocument(
  markdown: string,
): MarkdownGreenfieldDocument {
  const text = markdown.length ? markdown : " ";
  const cacheKey = markdownGreenfieldDocumentTextKey(text);
  const cached = markdownGreenfieldDocumentCache.get(cacheKey);
  if (cached?.text === text) {
    markdownGreenfieldDocumentCache.delete(cacheKey);
    markdownGreenfieldDocumentCache.set(cacheKey, cached);
    return cached.document;
  }

  const document = createUncachedMarkdownGreenfieldDocument(text);
  markdownGreenfieldDocumentCache.set(cacheKey, {
    document,
    text,
  });
  while (markdownGreenfieldDocumentCache.size > DOCUMENT_CACHE_LIMIT) {
    const oldestKey = markdownGreenfieldDocumentCache.keys().next().value;
    if (!oldestKey) break;
    markdownGreenfieldDocumentCache.delete(oldestKey);
  }

  return document;
}

function createUncachedMarkdownGreenfieldDocument(
  text: string,
): MarkdownGreenfieldDocument {
  const unified = createMarkdownUnifiedDocument(text);
  normalizeMarkdownGreenfieldHeadingIds(unified.hast.children, unified);
  normalizeMarkdownGreenfieldTables(unified.hast.children);
  annotateMarkdownGreenfieldSourceMetadata(unified.hast.children, unified);
  const blocks = createMarkdownGreenfieldBlocks({ text, unified });
  const chunks = createMarkdownGreenfieldChunks({ blocks, text });
  const headings = createMarkdownGreenfieldHeadings(blocks);
  const fragmentTargets = createMarkdownGreenfieldFragmentTargets({
    blocks,
    unified,
  });

  return freezeMarkdownGreenfieldDocument({
    blocks,
    chunks,
    fragmentTargets,
    headings,
    lineCount: unified.sourceMap.lineCount,
    text,
    unified,
    wordCount: text.trim().split(/\s+/).filter(Boolean).length,
  });
}

export function findMarkdownGreenfieldHeadingById(
  document: MarkdownGreenfieldDocument,
  headingId: string,
) {
  const normalizedId = headingId.replace(/^#/, "");
  return document.headings.find((heading) => heading.id === normalizedId);
}

export function findMarkdownGreenfieldFragmentTargetById(
  document: MarkdownGreenfieldDocument,
  fragmentId: string,
) {
  const normalizedId = normalizeFragmentTargetId(fragmentId);
  return document.fragmentTargets.find((target) => target.id === normalizedId);
}

export function findMarkdownGreenfieldBlockById(
  document: MarkdownGreenfieldDocument,
  blockId: string,
) {
  return document.blocks.find((block) => block.id === blockId) ?? null;
}

export function findMarkdownGreenfieldChunkByBlockId(
  document: MarkdownGreenfieldDocument,
  blockId: string,
) {
  return (
    document.chunks.find((chunk) => chunk.blockIds.includes(blockId)) ?? null
  );
}

export function findMarkdownGreenfieldBlockBySourceLine(
  document: MarkdownGreenfieldDocument,
  sourceLine: number,
) {
  const line = clampSourceLine(sourceLine, document.lineCount);
  return (
    document.blocks.find((block) => {
      const range = block.sourceRange;
      return range && range.startLine <= line && range.endLine >= line;
    }) ?? null
  );
}

export function findMarkdownGreenfieldChunkBySourceLine(
  document: MarkdownGreenfieldDocument,
  sourceLine: number,
) {
  const block = findMarkdownGreenfieldBlockBySourceLine(document, sourceLine);
  if (block) return findMarkdownGreenfieldChunkByBlockId(document, block.id);

  const line = clampSourceLine(sourceLine, document.lineCount);
  return (
    document.chunks.find(
      (chunk) => chunk.sourceStartLine <= line && chunk.sourceEndLine >= line,
    ) ?? null
  );
}

export function findMarkdownGreenfieldBlockBySourceOffset(
  document: MarkdownGreenfieldDocument,
  sourceOffset: number,
) {
  const offset = clampSourceOffset(sourceOffset, document.text.length);
  return (
    document.blocks.find((block) => {
      const range = block.sourceRange;
      return range && range.startOffset <= offset && range.endOffset > offset;
    }) ?? null
  );
}

export function findMarkdownGreenfieldChunkBySourceOffset(
  document: MarkdownGreenfieldDocument,
  sourceOffset: number,
) {
  const block = findMarkdownGreenfieldBlockBySourceOffset(
    document,
    sourceOffset,
  );
  return block
    ? findMarkdownGreenfieldChunkByBlockId(document, block.id)
    : null;
}

function createMarkdownGreenfieldBlocks({
  text,
  unified,
}: {
  text: string;
  unified: MarkdownUnifiedDocument;
}) {
  const blocks: MarkdownGreenfieldBlock[] = [];

  for (const child of unified.hast.children) {
    if (isWhitespaceText(child)) continue;

    const directSourceRange = markdownSourceRangeFromPosition({
      position: child.position,
      sourceMap: unified.sourceMap,
    });
    const sourceRange =
      directSourceRange ?? markdownSyntheticSourceRangeForNode(child, unified);
    const kind = markdownBlockKindForHastChild(child);
    const sourceText = markdownSourceTextForRange({
      range: sourceRange,
      sourceMap: unified.sourceMap,
    });
    const normalizedSourceText =
      sourceText ||
      (sourceRange
        ? text.slice(sourceRange.startOffset, sourceRange.endOffset)
        : "");
    const sourceMetrics =
      markdownGreenfieldSourceMetricsForText(normalizedSourceText);
    const line = sourceRange?.startLine ?? unified.sourceMap.lineCount;
    const block: MarkdownGreenfieldBlock = {
      hastChildren: [child],
      id: `block-${blocks.length + 1}-${line}-${kind}`,
      index: blocks.length,
      isGenerated: !directSourceRange,
      isHostile: isHostileMarkdownGreenfieldBlock({
        child,
        kind,
        sourceLineCount: sourceMetrics.sourceLineCount,
        sourceText: normalizedSourceText,
      }),
      kind,
      sourceLineCount: sourceMetrics.sourceLineCount,
      sourceLineLengths: sourceMetrics.sourceLineLengths,
      sourceRange,
      sourceText: normalizedSourceText,
    };
    blocks.push(block);
  }

  if (!blocks.length) {
    const sourceMetrics = markdownGreenfieldSourceMetricsForText(text);
    blocks.push({
      hastChildren: [],
      id: "block-1-empty",
      index: 0,
      isGenerated: true,
      isHostile: false,
      kind: "paragraph",
      sourceLineCount: sourceMetrics.sourceLineCount,
      sourceLineLengths: sourceMetrics.sourceLineLengths,
      sourceRange: {
        endLine: 1,
        endOffset: text.length,
        startLine: 1,
        startOffset: 0,
      },
      sourceText: text,
    });
  }

  return blocks;
}

function createMarkdownGreenfieldChunks({
  blocks,
  text,
}: {
  blocks: readonly MarkdownGreenfieldBlock[];
  text: string;
}) {
  const chunks: MarkdownGreenfieldChunk[] = [];
  let current: MarkdownGreenfieldBlock[] = [];

  const flush = () => {
    if (!current.length) return;
    chunks.push(createChunk(current, chunks.length, text));
    current = [];
  };

  for (const block of blocks) {
    if (block.isHostile) {
      flush();
      current.push(block);
      flush();
      continue;
    }

    const nextLineCount = lineCountForBlocks(current, block);
    const startsNewChunk =
      current.length > 0 &&
      block.kind === "heading" &&
      nextLineCount >= TARGET_CHUNK_SOURCE_LINES;
    const exceedsMax =
      current.length > 0 && nextLineCount > MAX_CHUNK_SOURCE_LINES;

    if (startsNewChunk || exceedsMax) flush();
    current.push(block);
  }

  flush();
  return chunks;
}

function createChunk(
  blocks: readonly MarkdownGreenfieldBlock[],
  index: number,
  text: string,
): MarkdownGreenfieldChunk {
  const ranges = blocks
    .map((block) => block.sourceRange)
    .filter((range): range is MarkdownSourceRange => Boolean(range));
  const sourceRange = ranges.length
    ? {
        endLine: Math.max(...ranges.map((range) => range.endLine)),
        endOffset: Math.max(...ranges.map((range) => range.endOffset)),
        startLine: Math.min(...ranges.map((range) => range.startLine)),
        startOffset: Math.min(...ranges.map((range) => range.startOffset)),
      }
    : null;
  const sourceStartLine =
    sourceRange?.startLine ?? blocks[0]?.sourceRange?.startLine ?? 1;
  const sourceEndLine =
    sourceRange?.endLine ??
    blocks[blocks.length - 1]?.sourceRange?.endLine ??
    sourceStartLine;
  const sourceLineCount = sourceRange
    ? sourceEndLine - sourceStartLine + 1
    : Math.max(
        1,
        blocks.reduce((sum, block) => sum + block.sourceLineCount, 0),
      );
  const hastChildren = blocks.flatMap((block) => block.hastChildren);

  return {
    blockIds: blocks.map((block) => block.id),
    hastChildren,
    id: `chunk-${index + 1}-${sourceStartLine}`,
    index,
    isHostile: blocks.some((block) => block.isHostile),
    nativeFindText: nativeFindTextForHastChildren(hastChildren),
    sourceEndLine,
    sourceLineCount,
    sourceRange,
    sourceStartLine,
    sourceText: sourceRange
      ? text.slice(sourceRange.startOffset, sourceRange.endOffset)
      : "",
  };
}

function createMarkdownGreenfieldHeadings(
  blocks: readonly MarkdownGreenfieldBlock[],
) {
  return blocks.flatMap((block) => {
    const element = readHastElement(block.hastChildren[0]);
    if (!element || !/^h[1-6]$/.test(element.tagName)) return [];

    const id = readStringProperty(element.properties?.id);
    if (!id) return [];

    return [
      {
        blockId: block.id,
        id,
        sourceLine: block.sourceRange?.startLine ?? 1,
        text: extractHastText(element).trim(),
      },
    ];
  });
}

function createMarkdownGreenfieldFragmentTargets({
  blocks,
  unified,
}: {
  blocks: readonly MarkdownGreenfieldBlock[];
  unified: MarkdownUnifiedDocument;
}) {
  const targets = new Map<string, MarkdownGreenfieldFragmentTarget>();

  for (const block of blocks) {
    for (const child of block.hastChildren) {
      collectFragmentTargets({
        block,
        node: child,
        targets,
        unified,
      });
    }
  }

  return Array.from(targets.values());
}

function collectFragmentTargets({
  block,
  node,
  targets,
  unified,
}: {
  block: MarkdownGreenfieldBlock;
  node: MarkdownHastNode;
  targets: Map<string, MarkdownGreenfieldFragmentTarget>;
  unified: MarkdownUnifiedDocument;
}) {
  const element = readHastElement(node);
  if (!element) return;

  const id = readStringProperty(element.properties?.id);
  if (id) {
    const sourceRange =
      markdownSourceRangeFromPosition({
        position: element.position,
        sourceMap: unified.sourceMap,
      }) ?? block.sourceRange;
    const target = {
      blockId: block.id,
      id,
      sourceLine: sourceRange?.startLine ?? unified.sourceMap.lineCount,
    };

    for (const alias of fragmentTargetAliases(id)) {
      if (!targets.has(alias)) {
        targets.set(alias, { ...target, id: alias });
      }
    }
  }

  for (const child of element.children) {
    collectFragmentTargets({ block, node: child, targets, unified });
  }
}

function markdownSyntheticSourceRangeForNode(
  node: MarkdownHastNode,
  unified: MarkdownUnifiedDocument,
): MarkdownSourceRange | null {
  const ranges = collectMarkdownSourceRanges(node, unified);
  if (!ranges.length) return null;
  return {
    endLine: Math.max(...ranges.map((range) => range.endLine)),
    endOffset: Math.max(...ranges.map((range) => range.endOffset)),
    startLine: Math.min(...ranges.map((range) => range.startLine)),
    startOffset: Math.min(...ranges.map((range) => range.startOffset)),
  };
}

function collectMarkdownSourceRanges(
  node: MarkdownHastNode,
  unified: MarkdownUnifiedDocument,
): MarkdownSourceRange[] {
  const range = markdownSourceRangeFromPosition({
    position: node.position,
    sourceMap: unified.sourceMap,
  });
  const element = readHastElement(node);
  return [
    ...(range ? [range] : []),
    ...(element?.children ?? []).flatMap((child) =>
      collectMarkdownSourceRanges(child, unified),
    ),
  ];
}

function normalizeMarkdownGreenfieldHeadingIds(
  nodes: readonly MarkdownHastNode[],
  unified: MarkdownUnifiedDocument,
) {
  const usedIds = new Map<string, number>();
  for (const node of nodes) {
    normalizeHeadingIdsInNode(node, usedIds, unified);
  }
}

function normalizeHeadingIdsInNode(
  node: MarkdownHastNode,
  usedIds: Map<string, number>,
  unified: MarkdownUnifiedDocument,
) {
  const element = readHastElement(node);
  if (!element) return;

  if (/^h[1-6]$/.test(element.tagName)) {
    const visibleText = extractHastText(element);
    const markdownText = markdownHeadingTextFromSource(element, unified);
    const baseId = safeHeadingIdForText(
      markdownText.includes("__proto__") ? markdownText : visibleText,
    );
    const duplicateCount = usedIds.get(baseId) ?? 0;
    usedIds.set(baseId, duplicateCount + 1);
    element.properties = {
      ...element.properties,
      id: duplicateCount === 0 ? baseId : `${baseId}-${duplicateCount}`,
    };
  }

  for (const child of element.children) {
    normalizeHeadingIdsInNode(child, usedIds, unified);
  }
}

function markdownHeadingTextFromSource(
  element: MarkdownHastElement,
  unified: MarkdownUnifiedDocument,
) {
  const range = markdownSourceRangeFromPosition({
    position: element.position,
    sourceMap: unified.sourceMap,
  });
  if (!range) return "";
  return markdownSourceTextForRange({
    range,
    sourceMap: unified.sourceMap,
  })
    .replace(/^\s{0,3}#{1,6}[ \t]*/, "")
    .replace(/[ \t]+#*\s*$/, "")
    .trim();
}

function safeHeadingIdForText(text: string) {
  const slug =
    text
      .toLowerCase()
      .normalize("NFKD")
      .replace(/[\u0300-\u036f]/g, "")
      .replace(/[^\w\s-]/g, "")
      .trim()
      .replace(/\s+/g, "-")
      .replace(/-+/g, "-") || "section";

  return isDomClobberingId(slug) ? `section-${slug}` : slug;
}

function normalizeFragmentTargetId(fragmentId: string) {
  return decodeURIComponent(fragmentId.replace(/^#/, ""));
}

function fragmentTargetAliases(id: string) {
  const aliases = new Set<string>([normalizeFragmentTargetId(id)]);
  const withoutRepeatedClobberPrefix = id.replace(
    /^(user-content-)+/,
    "user-content-",
  );
  aliases.add(withoutRepeatedClobberPrefix);
  aliases.add(withoutRepeatedClobberPrefix.replace(/^user-content-/, ""));
  return Array.from(aliases).filter(Boolean);
}

function normalizeMarkdownGreenfieldTables(nodes: readonly MarkdownHastNode[]) {
  let tableIndex = 0;
  for (const node of nodes) {
    tableIndex = normalizeTablesInNode(node, tableIndex);
  }
}

function normalizeTablesInNode(node: MarkdownHastNode, tableIndex: number) {
  const element = readHastElement(node);
  if (!element) return tableIndex;

  if (element.tagName === "table") {
    normalizeTableElement(element, tableIndex);
    tableIndex += 1;
  }

  for (const child of element.children) {
    tableIndex = normalizeTablesInNode(child, tableIndex);
  }
  return tableIndex;
}

function normalizeTableElement(table: MarkdownHastElement, tableIndex: number) {
  const rows = tableRows(table);
  const headerIds = new Map<number, string>();

  rows.forEach((row, rowIndex) => {
    row.properties = {
      ...row.properties,
      ariaRowIndex: rowIndex + 1,
      dataPretextTableRowIndex: rowIndex + 1,
    };

    tableCells(row).forEach((cell, columnIndex) => {
      const column = columnIndex + 1;
      const properties = {
        ...cell.properties,
        ariaColIndex: column,
        dataPretextTableColumnIndex: column,
      };
      if (cell.tagName === "th") {
        const id = `markdown-table-${tableIndex + 1}-column-${column}`;
        headerIds.set(columnIndex, id);
        cell.properties = {
          ...properties,
          id,
          scope: "col",
        };
      } else {
        cell.properties = {
          ...properties,
          headers: headerIds.get(columnIndex),
        };
      }
    });
  });
}

function annotateMarkdownGreenfieldSourceMetadata(
  nodes: readonly MarkdownHastNode[],
  unified: MarkdownUnifiedDocument,
) {
  for (const node of nodes) {
    annotateSourceMetadataInNode(node, unified);
  }
}

function annotateSourceMetadataInNode(
  node: MarkdownHastNode,
  unified: MarkdownUnifiedDocument,
) {
  const element = readHastElement(node);
  if (!element) return;

  const sourceRange = markdownSourceRangeFromPosition({
    position: element.position,
    sourceMap: unified.sourceMap,
  });
  if (sourceRange) {
    element.properties = {
      ...element.properties,
      dataPretextSourceEndLine: sourceRange.endLine,
      dataPretextSourceEndOffset: sourceRange.endOffset,
      dataPretextSourceStartLine: sourceRange.startLine,
      dataPretextSourceStartOffset: sourceRange.startOffset,
    };
  }

  for (const child of element.children) {
    annotateSourceMetadataInNode(child, unified);
  }
}

function tableRows(element: MarkdownHastElement) {
  const rows: MarkdownHastElement[] = [];
  for (const child of element.children) {
    const childElement = readHastElement(child);
    if (!childElement) continue;
    if (childElement.tagName === "tr") {
      rows.push(childElement);
    } else if (
      childElement.tagName === "thead" ||
      childElement.tagName === "tbody" ||
      childElement.tagName === "tfoot"
    ) {
      rows.push(...tableRows(childElement));
    }
  }
  return rows;
}

function tableCells(row: MarkdownHastElement) {
  return row.children
    .map(readHastElement)
    .filter(
      (child): child is MarkdownHastElement =>
        child?.tagName === "td" || child?.tagName === "th",
    );
}

function isDomClobberingId(id: string) {
  return [
    "__proto__",
    "constructor",
    "document",
    "forms",
    "history",
    "location",
    "name",
    "prototype",
    "window",
  ].includes(id);
}

function markdownBlockKindForHastChild(
  child: MarkdownHastNode,
): MarkdownGreenfieldBlockKind {
  const element = readHastElement(child);
  if (!element) return child.type === "text" ? "paragraph" : "unknown";

  if (element.properties?.dataFootnotes != null) return "footnotes";
  if (element.properties?.dataMarkdownFrontmatter != null) return "frontmatter";
  if (/^h[1-6]$/.test(element.tagName)) return "heading";
  if (isMarkdownDiagramElement(element)) return "diagram";
  if (isMarkdownComponentElement(element)) return "component";
  if (isDisplayMathElement(element)) return "math";

  switch (element.tagName) {
    case "blockquote":
      return "blockquote";
    case "hr":
      return "thematicBreak";
    case "ol":
    case "ul":
      return "list";
    case "p":
      if (isDisplayMathElement(element)) return "math";
      return firstElementChild(element)?.tagName === "img"
        ? "image"
        : "paragraph";
    case "pre":
      if (isMermaidCodeElement(element)) return "diagram";
      return "code";
    case "table":
      return "table";
    default:
      return "html";
  }
}

function isMarkdownDiagramElement(element: MarkdownHastElement) {
  return (
    readStringProperty(element.properties?.dataPretextComponentName) ===
    "Diagram"
  );
}

function isMarkdownComponentElement(element: MarkdownHastElement) {
  return (
    element.properties?.dataPretextComponentName != null ||
    element.properties?.dataPretextComponentFallback != null ||
    element.properties?.dataPretextCalloutKind != null
  );
}

function isDisplayMathElement(element: MarkdownHastElement): boolean {
  if (
    hasClassName(element, "katex-display") ||
    hasClassName(element, "math-display")
  )
    return true;
  return element.children
    .map(readHastElement)
    .some((child) => child != null && isDisplayMathElement(child));
}

function isMermaidCodeElement(element: MarkdownHastElement) {
  const code = firstElementChild(element);
  return (
    code?.tagName === "code" &&
    ["language-mermaid", "language-mmd", "language-mermaid-js"].some(
      (className) => hasClassName(code, className),
    )
  );
}

function hasClassName(element: MarkdownHastElement, className: string) {
  const classes = element.properties?.className;
  return Array.isArray(classes)
    ? classes.includes(className)
    : typeof classes === "string" && classes.split(/\s+/).includes(className);
}

function isHostileMarkdownGreenfieldBlock({
  child,
  kind,
  sourceLineCount,
  sourceText,
}: {
  child: MarkdownHastNode;
  kind: MarkdownGreenfieldBlockKind;
  sourceLineCount: number;
  sourceText: string;
}) {
  if (kind === "table") {
    if (sourceText.length > HOSTILE_TABLE_TEXT_LENGTH) return true;
    if (countTableCells(child) > HOSTILE_TABLE_CELL_COUNT) return true;
    if (countHastNodes(child) > HOSTILE_TABLE_HAST_NODE_COUNT) return true;
    if (maxHastDepth(child) > HOSTILE_HAST_DEPTH) return true;
    return false;
  }
  if (sourceText.length > HOSTILE_TEXT_LENGTH) return true;
  if (countHastNodes(child) > HOSTILE_HAST_NODE_COUNT) return true;
  if (maxHastDepth(child) > HOSTILE_HAST_DEPTH) return true;
  if (kind === "code" && sourceLineCount > HOSTILE_CODE_LINE_COUNT) {
    return true;
  }
  return false;
}

function countHastNodes(node: MarkdownHastNode): number {
  const element = readHastElement(node);
  return (
    1 +
    (element?.children ?? []).reduce(
      (sum, child) => sum + countHastNodes(child),
      0,
    )
  );
}

function maxHastDepth(node: MarkdownHastNode): number {
  const element = readHastElement(node);
  if (!element?.children.length) return 1;
  return 1 + Math.max(...element.children.map(maxHastDepth));
}

function lineCountForBlocks(
  blocks: readonly MarkdownGreenfieldBlock[],
  nextBlock: MarkdownGreenfieldBlock,
) {
  let fallbackLineCount = 0;
  let sourceEndLine = 0;
  let sourceStartLine = 0;

  for (const block of blocks) {
    fallbackLineCount += block.sourceLineCount;
    const range = block.sourceRange;
    if (!range) continue;
    sourceStartLine = sourceStartLine
      ? Math.min(sourceStartLine, range.startLine)
      : range.startLine;
    sourceEndLine = Math.max(sourceEndLine, range.endLine);
  }
  fallbackLineCount += nextBlock.sourceLineCount;
  const nextRange = nextBlock.sourceRange;
  if (nextRange) {
    sourceStartLine = sourceStartLine
      ? Math.min(sourceStartLine, nextRange.startLine)
      : nextRange.startLine;
    sourceEndLine = Math.max(sourceEndLine, nextRange.endLine);
  }

  if (!sourceStartLine) return Math.max(1, fallbackLineCount);
  return sourceEndLine - sourceStartLine + 1;
}

function countTableCells(node: MarkdownHastNode): number {
  const element = readHastElement(node);
  if (!element) return 0;
  const self = element.tagName === "td" || element.tagName === "th" ? 1 : 0;
  return (
    self +
    (element.children ?? []).reduce(
      (sum, child) => sum + countTableCells(child),
      0,
    )
  );
}

function markdownGreenfieldSourceMetricsForText(sourceText: string) {
  const sourceLineLengths: number[] = [];
  let sourceLineLength = 0;

  for (let index = 0; index < sourceText.length; index += 1) {
    const charCode = sourceText.charCodeAt(index);
    if (charCode === 13) {
      sourceLineLengths.push(sourceLineLength);
      sourceLineLength = 0;
      if (sourceText.charCodeAt(index + 1) === 10) index += 1;
      continue;
    }
    if (charCode === 10 || charCode === 0x2028 || charCode === 0x2029) {
      sourceLineLengths.push(sourceLineLength);
      sourceLineLength = 0;
      continue;
    }
    sourceLineLength += 1;
  }

  sourceLineLengths.push(sourceLineLength);
  return {
    sourceLineCount: sourceLineLengths.length,
    sourceLineLengths,
  };
}

function readHastElement(node: unknown): MarkdownHastElement | null {
  return node &&
    typeof node === "object" &&
    (node as MarkdownHastElement).type === "element"
    ? (node as MarkdownHastElement)
    : null;
}

function firstElementChild(element: MarkdownHastElement) {
  return element.children.map(readHastElement).find(Boolean) ?? null;
}

function isWhitespaceText(node: MarkdownHastNode) {
  return (
    node.type === "text" &&
    typeof node.value === "string" &&
    node.value.trim() === ""
  );
}

function readStringProperty(value: unknown) {
  if (typeof value === "string") return value;
  if (Array.isArray(value)) return value.filter(Boolean).join(" ");
  return "";
}

function extractHastText(node: MarkdownHastNode): string {
  if (node.type === "text" && typeof node.value === "string") return node.value;
  const element = readHastElement(node);
  if (!element) return "";
  return element.children.map(extractHastText).join("");
}

function nativeFindTextForHastChildren(children: readonly MarkdownHastNode[]) {
  const text: string[] = [];
  const stack = [...children].reverse();
  while (stack.length) {
    const node = stack.pop();
    if (!node) continue;
    if (node.type === "text" && typeof node.value === "string") {
      text.push(node.value);
      continue;
    }

    const element = readHastElement(node);
    if (!element) continue;
    if (element.tagName === "script" || element.tagName === "style") continue;
    for (let index = element.children.length - 1; index >= 0; index -= 1) {
      stack.push(element.children[index]!);
    }
  }
  return text.join(" ").trim();
}

function freezeMarkdownHastNode(node: unknown) {
  if (!node || typeof node !== "object" || Object.isFrozen(node)) return;

  const record = node as Record<string, unknown>;
  for (const value of Object.values(record)) {
    if (Array.isArray(value)) {
      value.forEach(freezeMarkdownHastNode);
      Object.freeze(value);
      continue;
    }
    if (value && typeof value === "object") {
      freezeMarkdownHastNode(value);
    }
  }
  Object.freeze(node);
}

export function freezeMarkdownGreenfieldDocument(
  document: MarkdownGreenfieldDocument,
) {
  freezeMarkdownHastNode(document.unified.hast);
  for (const block of document.blocks) {
    if (block.sourceRange) Object.freeze(block.sourceRange);
    Object.freeze(block.hastChildren);
    Object.freeze(block.sourceLineLengths);
    Object.freeze(block);
  }
  for (const chunk of document.chunks) {
    if (chunk.sourceRange) Object.freeze(chunk.sourceRange);
    Object.freeze(chunk.blockIds);
    Object.freeze(chunk.hastChildren);
    Object.freeze(chunk);
  }
  for (const heading of document.headings) Object.freeze(heading);
  for (const target of document.fragmentTargets) Object.freeze(target);
  Object.freeze(document.blocks);
  Object.freeze(document.chunks);
  Object.freeze(document.headings);
  Object.freeze(document.fragmentTargets);
  return Object.freeze(document);
}

export function markdownGreenfieldDocumentTextKey(text: string) {
  let hash = 2166136261;
  for (let index = 0; index < text.length; index += 1) {
    hash ^= text.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return `${text.length}:${(hash >>> 0).toString(36)}`;
}

function clampSourceLine(line: number, lineCount: number) {
  if (!Number.isFinite(line)) return 1;
  return Math.max(1, Math.min(lineCount, Math.floor(line)));
}

function clampSourceOffset(offset: number, textLength: number) {
  if (!Number.isFinite(offset)) return 0;
  return Math.max(0, Math.min(textLength, Math.floor(offset)));
}
registry/new-york-v4/ui/markdown-greenfield-document-store.ts
"use client";

import * as React from "react";

import {
  createMarkdownGreenfieldDocument,
  freezeMarkdownGreenfieldDocument,
  markdownGreenfieldDocumentTextKey,
  type MarkdownGreenfieldDocument,
} from "./markdown-greenfield-document";

export const MARKDOWN_GREENFIELD_ASYNC_DOCUMENT_MIN_CHARS = 60_000;
export const MARKDOWN_GREENFIELD_DOCUMENT_WORKER_LOCAL_STORAGE_KEY =
  "retab:markdown-document-worker";
export const MARKDOWN_GREENFIELD_DOCUMENT_WORKER_SEARCH_PARAM =
  "markdownDocumentWorker";

const MARKDOWN_GREENFIELD_DOCUMENT_WORKER_READY_TIMEOUT_MS = 800;
const MARKDOWN_GREENFIELD_DOCUMENT_ENTRY_CACHE_LIMIT = 16;

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

type MarkdownDocumentState =
  | { status: "failed"; error: Error }
  | { status: "pending" }
  | { status: "ready"; document: MarkdownGreenfieldDocument };

type MarkdownDocumentEntry = {
  key: string;
  listeners: Set<() => void>;
  started: boolean;
  state: MarkdownDocumentState;
  text: string;
};

type MarkdownDocumentWorkerRequest = {
  id: number;
  text: string;
  type: "parse";
};

type MarkdownDocumentWorkerResponse =
  | {
      type: "ready";
    }
  | {
      document: MarkdownGreenfieldDocument;
      id: number;
      ok: true;
      type: "result";
    }
  | {
      failure: "clone_failed" | "parse_failed";
      id: number;
      message: string;
      ok: false;
      type: "result";
    };

const markdownDocumentEntries = new Map<string, MarkdownDocumentEntry>();
let nextMarkdownDocumentWorkerRequestId = 1;

export function useMarkdownGreenfieldDocument(text: string) {
  const shouldLoadAsync =
    text.length >= MARKDOWN_GREENFIELD_ASYNC_DOCUMENT_MIN_CHARS;
  const syncDocument = React.useMemo(
    () => (shouldLoadAsync ? null : createMarkdownGreenfieldDocument(text)),
    [shouldLoadAsync, text],
  );
  const entry = React.useMemo(
    () => (shouldLoadAsync ? getMarkdownDocumentEntry(text) : null),
    [shouldLoadAsync, text],
  );
  const asyncState = React.useSyncExternalStore(
    React.useCallback(
      (onStoreChange) => {
        if (!entry) return () => {};
        entry.listeners.add(onStoreChange);
        startMarkdownDocumentEntry(entry);
        return () => {
          entry.listeners.delete(onStoreChange);
        };
      },
      [entry],
    ),
    React.useCallback(() => entry?.state ?? null, [entry]),
    () => null,
  );

  if (syncDocument) return syncDocument;
  if (!asyncState || asyncState.status === "pending") return null;
  if (asyncState.status === "failed") throw asyncState.error;
  return asyncState.document;
}

function getMarkdownDocumentEntry(text: string) {
  const key = markdownGreenfieldDocumentTextKey(text);
  const existing = markdownDocumentEntries.get(key);
  if (existing?.text === text) return existing;

  const entry: MarkdownDocumentEntry = {
    key,
    listeners: new Set(),
    started: false,
    state: { status: "pending" },
    text,
  };
  markdownDocumentEntries.set(key, entry);
  evictMarkdownDocumentEntries(key);
  return entry;
}

function evictMarkdownDocumentEntries(currentKey: string) {
  for (const [key, entry] of markdownDocumentEntries) {
    if (
      markdownDocumentEntries.size <=
      MARKDOWN_GREENFIELD_DOCUMENT_ENTRY_CACHE_LIMIT
    ) {
      return;
    }
    if (key === currentKey || entry.listeners.size > 0) continue;
    markdownDocumentEntries.delete(key);
  }
}

function startMarkdownDocumentEntry(entry: MarkdownDocumentEntry) {
  if (entry.started || entry.state.status !== "pending") return;
  entry.started = true;

  if (typeof Worker !== "undefined" && isMarkdownDocumentWorkerEnabled()) {
    try {
      startMarkdownDocumentWorker(entry);
      return;
    } catch {
      startMarkdownDocumentFallback(entry);
      return;
    }
  }

  startMarkdownDocumentFallback(entry);
}

function startMarkdownDocumentWorker(entry: MarkdownDocumentEntry) {
  const worker = new Worker(
    new URL("./markdown-greenfield-document.worker.ts", import.meta.url),
    { type: "module" },
  );
  const id = nextMarkdownDocumentWorkerRequestId++;
  let isSettled = false;

  const fallBackToMainThread = () => {
    if (isSettled) return;
    isSettled = true;
    window.clearTimeout(readyTimeoutId);
    if (!isCurrentMarkdownDocumentEntry(entry)) {
      worker.terminate();
      return;
    }
    worker.terminate();
    startMarkdownDocumentFallback(entry);
  };
  const readyTimeoutId = window.setTimeout(() => {
    fallBackToMainThread();
  }, MARKDOWN_GREENFIELD_DOCUMENT_WORKER_READY_TIMEOUT_MS);

  worker.onmessage = (event: MessageEvent<unknown>) => {
    const message = markdownDocumentWorkerResponseFromValue(event.data);
    if (!message) {
      fallBackToMainThread();
      return;
    }

    if (message.type === "ready") {
      window.clearTimeout(readyTimeoutId);
      try {
        worker.postMessage({
          id,
          text: entry.text,
          type: "parse",
        } satisfies MarkdownDocumentWorkerRequest);
      } catch {
        fallBackToMainThread();
      }
      return;
    }
    if (message.id !== id || isSettled) {
      return;
    }
    isSettled = true;
    window.clearTimeout(readyTimeoutId);
    worker.terminate();
    if (!isCurrentMarkdownDocumentEntry(entry)) return;
    if (message.ok) {
      publishMarkdownDocumentState(entry, {
        document: freezeMarkdownGreenfieldDocument(message.document),
        status: "ready",
      });
      return;
    }
    if (message.failure === "clone_failed") {
      startMarkdownDocumentFallback(entry);
      return;
    }
    publishMarkdownDocumentState(entry, {
      error: new Error(message.message),
      status: "failed",
    });
  };
  worker.onerror = fallBackToMainThread;
  worker.onmessageerror = fallBackToMainThread;
}

function startMarkdownDocumentFallback(entry: MarkdownDocumentEntry) {
  scheduleMarkdownDocumentTask(() => {
    if (!isCurrentMarkdownDocumentEntry(entry)) return;
    try {
      publishMarkdownDocumentState(entry, {
        document: createMarkdownGreenfieldDocument(entry.text),
        status: "ready",
      });
    } catch (error) {
      publishMarkdownDocumentState(entry, {
        error:
          error instanceof Error
            ? error
            : new Error("Could not parse Markdown."),
        status: "failed",
      });
    }
  });
}

function scheduleMarkdownDocumentTask(callback: () => void) {
  if (typeof window === "undefined") return;
  const browserWindow = window as MarkdownIdleWindow;
  const runWhenIdle = () => {
    if (browserWindow.requestIdleCallback) {
      browserWindow.requestIdleCallback(callback, { timeout: 120 });
      return;
    }
    browserWindow.setTimeout(callback, 0);
  };

  if (browserWindow.requestAnimationFrame) {
    browserWindow.requestAnimationFrame(() => {
      browserWindow.setTimeout(runWhenIdle, 0);
    });
    return;
  }
  browserWindow.setTimeout(runWhenIdle, 0);
}

function publishMarkdownDocumentState(
  entry: MarkdownDocumentEntry,
  state: MarkdownDocumentState,
) {
  if (!isCurrentMarkdownDocumentEntry(entry)) return;
  entry.state = state;
  for (const listener of entry.listeners) listener();
}

function isCurrentMarkdownDocumentEntry(entry: MarkdownDocumentEntry) {
  const current = markdownDocumentEntries.get(entry.key);
  return current === entry && current.text === entry.text;
}

function isMarkdownDocumentWorkerEnabled() {
  if (typeof window === "undefined") return false;
  const urlFlag = readMarkdownDocumentWorkerSearchFlag(window.location.search);
  if (urlFlag != null) return urlFlag;
  return readMarkdownDocumentWorkerStorageFlag() ?? true;
}

function readMarkdownDocumentWorkerSearchFlag(search: string) {
  try {
    const value = new URLSearchParams(search).get(
      MARKDOWN_GREENFIELD_DOCUMENT_WORKER_SEARCH_PARAM,
    );
    return markdownDocumentWorkerFlagValue(value);
  } catch {
    return null;
  }
}

function readMarkdownDocumentWorkerStorageFlag() {
  try {
    return markdownDocumentWorkerFlagValue(
      window.localStorage.getItem(
        MARKDOWN_GREENFIELD_DOCUMENT_WORKER_LOCAL_STORAGE_KEY,
      ),
    );
  } catch {
    return null;
  }
}

function markdownDocumentWorkerFlagValue(value: string | null) {
  if (value == null) return null;
  switch (value.trim().toLowerCase()) {
    case "1":
    case "true":
    case "yes":
    case "on":
    case "worker":
      return true;
    case "0":
    case "false":
    case "no":
    case "off":
    case "main":
      return false;
    default:
      return null;
  }
}

function markdownDocumentWorkerResponseFromValue(
  value: unknown,
): MarkdownDocumentWorkerResponse | null {
  if (!value || typeof value !== "object") return null;
  const response = value as Partial<MarkdownDocumentWorkerResponse>;
  if (response.type === "ready") return { type: "ready" };
  if (response.type !== "result" || typeof response.id !== "number") {
    return null;
  }
  if (response.ok === true && response.document) {
    return response as MarkdownDocumentWorkerResponse;
  }
  if (
    response.ok === false &&
    (response.failure === "clone_failed" ||
      response.failure === "parse_failed") &&
    typeof response.message === "string"
  ) {
    return response as MarkdownDocumentWorkerResponse;
  }
  return null;
}
registry/new-york-v4/ui/markdown-greenfield-document.worker.ts
import { createMarkdownGreenfieldDocument } from "./markdown-greenfield-document";

type MarkdownDocumentWorkerRequest = {
  id: number;
  text: string;
  type: "parse";
};

type MarkdownDocumentWorkerResponse =
  | {
      type: "ready";
    }
  | {
      document: ReturnType<typeof createMarkdownGreenfieldDocument>;
      id: number;
      ok: true;
      type: "result";
    }
  | {
      failure: "clone_failed" | "parse_failed";
      id: number;
      message: string;
      ok: false;
      type: "result";
    };

self.postMessage({ type: "ready" } satisfies MarkdownDocumentWorkerResponse);

self.onmessage = (event: MessageEvent<MarkdownDocumentWorkerRequest>) => {
  const { id, text, type } = event.data;
  if (type !== "parse") {
    postMarkdownDocumentWorkerError({
      failure: "parse_failed",
      id,
      message: "Markdown document worker received an invalid request.",
    });
    return;
  }

  let document: ReturnType<typeof createMarkdownGreenfieldDocument>;
  try {
    document = createMarkdownGreenfieldDocument(text);
  } catch (error) {
    postMarkdownDocumentWorkerError({
      failure: "parse_failed",
      id,
      message:
        error instanceof Error ? error.message : "Could not parse Markdown.",
    });
    return;
  }

  try {
    assertStructuredCloneable(document);
    self.postMessage({
      document,
      id,
      ok: true,
      type: "result",
    } satisfies MarkdownDocumentWorkerResponse);
  } catch (error) {
    postMarkdownDocumentWorkerError({
      failure: "clone_failed",
      id,
      message:
        error instanceof Error
          ? error.message
          : "Markdown document worker payload is not structured-clone safe.",
    });
  }
};

function assertStructuredCloneable(value: unknown) {
  if (typeof structuredClone === "function") {
    structuredClone(value);
    return;
  }

  if (typeof MessageChannel === "function") {
    const channel = new MessageChannel();
    try {
      channel.port2.postMessage(value);
    } finally {
      channel.port1.close();
      channel.port2.close();
    }
    return;
  }

  throw new Error("Structured clone probe is unavailable.");
}

function postMarkdownDocumentWorkerError({
  failure,
  id,
  message,
}: {
  failure: "clone_failed" | "parse_failed";
  id: number;
  message: string;
}) {
  self.postMessage({
    failure,
    id,
    message,
    ok: false,
    type: "result",
  } satisfies MarkdownDocumentWorkerResponse);
}
registry/new-york-v4/ui/markdown-greenfield-layout.ts
"use client";

import type {
  MarkdownGreenfieldBlock,
  MarkdownGreenfieldChunk,
  MarkdownGreenfieldDocument,
} from "./markdown-greenfield-document";

// The chunk reading column's max inline size (the old max-w-4xl, 56rem at
// the 16px root). Shared with the surface motion resolver: the align
// translate that hides the close-leg recenter must agree with the markup
// about where the column's margin clamps.
export const MARKDOWN_GREENFIELD_CHUNK_MAX_INLINE_SIZE = 896;
const CHUNK_PADDING_X = 32;
const DOCUMENT_PADDING_TOP = CHUNK_PADDING_X;
const DOCUMENT_PADDING_BOTTOM = CHUNK_PADDING_X;
const MIN_CHUNK_HEIGHT = 64;
// Tuned to the rendered typography (15.5px body at leading-relaxed ≈ 25px,
// 14px mono code at leading-5 = 20px) so estimates track measured heights and
// scroll correction stays minimal.
const BODY_LINE_HEIGHT = 25;
const MONO_LINE_HEIGHT = 20;
const APPROX_BODY_CHAR_WIDTH = 7.9;
const APPROX_MONO_CHAR_WIDTH = 7.8;
export const MARKDOWN_GREENFIELD_LAYOUT_POLICY_VERSION =
  "greenfield-layout-rich-blocks-v7";
const MARKDOWN_TABLE_VIRTUALIZATION_ROW_THRESHOLD = 80;
const MARKDOWN_TABLE_VIRTUALIZED_HEIGHT = 640;
const LAYOUT_CACHE_LIMIT = 64;
const ESTIMATED_HEIGHT_CACHE_LIMIT = 12;

const markdownGreenfieldLayoutDocumentIds = new WeakMap<
  MarkdownGreenfieldDocument,
  number
>();
const markdownGreenfieldLayoutCache = new Map<
  string,
  {
    document: MarkdownGreenfieldDocument;
    frame: MarkdownGreenfieldFrame;
  }
>();
const markdownGreenfieldEstimatedHeightCaches = new WeakMap<
  MarkdownGreenfieldDocument,
  Map<string, readonly number[]>
>();
let nextMarkdownGreenfieldLayoutDocumentId = 1;

export type MarkdownGreenfieldFrame = {
  chunks: MarkdownGreenfieldChunkFrame[];
  totalHeight: number;
  width: number;
};

export type MarkdownGreenfieldChunkFrame = {
  bottom: number;
  height: number;
  id: string;
  index: number;
  measuredHeight: number | null;
  sourceEndLine: number;
  sourceStartLine: number;
  top: number;
};

export type MarkdownGreenfieldMeasuredHeights = {
  /** Compact revision key for the current measured-height snapshot. */
  cacheKey?: string;
  get(
    chunk: MarkdownGreenfieldChunk,
    context: MarkdownGreenfieldMeasurementContext,
  ): number | undefined;
};

export type MarkdownGreenfieldMeasurementContext = {
  fontScale: number;
  policyVersion: string;
  width: number;
};

export function layoutMarkdownGreenfieldDocument({
  contentWidth,
  document,
  fontScale,
  measuredHeights,
}: {
  contentWidth: number;
  document: MarkdownGreenfieldDocument;
  fontScale: number;
  measuredHeights?: MarkdownGreenfieldMeasuredHeights;
}): MarkdownGreenfieldFrame {
  const width = Math.max(1, contentWidth);
  const context = {
    fontScale,
    policyVersion: MARKDOWN_GREENFIELD_LAYOUT_POLICY_VERSION,
    width,
  };
  const measuredHeightsCacheKey = measuredHeights?.cacheKey ?? null;
  const shouldCacheFrame = !measuredHeights || measuredHeightsCacheKey != null;
  const cacheKey = markdownGreenfieldLayoutCacheKey({
    context,
    document,
    measuredHeightsCacheKey,
  });
  if (shouldCacheFrame) {
    const cached = markdownGreenfieldLayoutCache.get(cacheKey);
    if (cached?.document === document) {
      markdownGreenfieldLayoutCache.delete(cacheKey);
      markdownGreenfieldLayoutCache.set(cacheKey, cached);
      return cached.frame;
    }
  }

  const estimatedHeights = readMarkdownGreenfieldEstimatedHeights({
    context,
    document,
  });
  const chunks: MarkdownGreenfieldChunkFrame[] = [];
  let y = DOCUMENT_PADDING_TOP;

  for (const [chunkIndex, chunk] of document.chunks.entries()) {
    const estimatedHeight = estimatedHeights[chunkIndex] ?? MIN_CHUNK_HEIGHT;
    const measuredHeight = readMarkdownGreenfieldMeasuredHeight({
      chunk,
      context,
      measuredHeights,
    });
    const height = Math.max(
      MIN_CHUNK_HEIGHT,
      measuredHeight == null ? estimatedHeight : measuredHeight,
    );
    chunks.push({
      bottom: y + height,
      height,
      id: chunk.id,
      index: chunk.index,
      measuredHeight,
      sourceEndLine: chunk.sourceEndLine,
      sourceStartLine: chunk.sourceStartLine,
      top: y,
    });
    y += height;
  }

  const frame = freezeMarkdownGreenfieldFrame({
    chunks,
    totalHeight: chunks.length ? y + DOCUMENT_PADDING_BOTTOM : 0,
    width,
  });
  if (shouldCacheFrame) {
    markdownGreenfieldLayoutCache.set(cacheKey, { document, frame });
    while (markdownGreenfieldLayoutCache.size > LAYOUT_CACHE_LIMIT) {
      const oldestKey = markdownGreenfieldLayoutCache.keys().next().value;
      if (!oldestKey) break;
      markdownGreenfieldLayoutCache.delete(oldestKey);
    }
  }
  return frame;
}

function readMarkdownGreenfieldEstimatedHeights({
  context,
  document,
}: {
  context: MarkdownGreenfieldMeasurementContext;
  document: MarkdownGreenfieldDocument;
}) {
  const cacheKey = markdownGreenfieldEstimateCacheKey(context);
  const cache = markdownGreenfieldEstimateCacheForDocument(document);
  const cached = cache.get(cacheKey);
  if (cached) {
    cache.delete(cacheKey);
    cache.set(cacheKey, cached);
    return cached;
  }

  const blocksById = new Map(document.blocks.map((block) => [block.id, block]));
  const estimatedHeights = Object.freeze(
    document.chunks.map((chunk) =>
      estimateMarkdownGreenfieldChunkHeight({
        blocks: chunk.blockIds
          .map((blockId) => blocksById.get(blockId))
          .filter((block): block is MarkdownGreenfieldBlock => Boolean(block)),
        fontScale: context.fontScale,
        width: context.width,
      }),
    ),
  );
  cache.set(cacheKey, estimatedHeights);
  while (cache.size > ESTIMATED_HEIGHT_CACHE_LIMIT) {
    const oldestKey = cache.keys().next().value;
    if (!oldestKey) break;
    cache.delete(oldestKey);
  }
  return estimatedHeights;
}

function estimateMarkdownGreenfieldChunkHeight({
  blocks,
  fontScale,
  width,
}: {
  blocks: readonly MarkdownGreenfieldBlock[];
  fontScale: number;
  width: number;
}) {
  const textWidth = Math.max(1, width - CHUNK_PADDING_X * 2);
  const height = blocks.reduce(
    (sum, block) =>
      sum +
      estimateMarkdownGreenfieldBlockHeight({
        block,
        fontScale,
        textWidth,
      }),
    0,
  );
  return Math.max(MIN_CHUNK_HEIGHT, height);
}

function estimateMarkdownGreenfieldBlockHeight({
  block,
  fontScale,
  textWidth,
}: {
  block: MarkdownGreenfieldBlock;
  fontScale: number;
  textWidth: number;
}) {
  if (block.kind === "thematicBreak") return 36 * fontScale;
  if (block.kind === "frontmatter") {
    const lines = Math.max(1, block.sourceLineCount);
    return (72 + lines * MONO_LINE_HEIGHT) * fontScale;
  }
  if (block.kind === "footnotes")
    return estimateTextBlock(block, textWidth, fontScale, 0.9);
  if (block.kind === "heading") {
    return estimateTextBlock(block, textWidth, fontScale, 1.3) + 18 * fontScale;
  }
  if (block.kind === "diagram") return 360 * fontScale;
  if (block.kind === "math") return 120 * fontScale;
  if (block.kind === "component") return 160 * fontScale;
  if (block.kind === "code") {
    const lines = Math.max(1, block.sourceLineCount);
    return 48 * fontScale + lines * MONO_LINE_HEIGHT * fontScale;
  }
  if (block.kind === "table") {
    const rows = Math.max(2, block.sourceLineCount);
    if (rows >= MARKDOWN_TABLE_VIRTUALIZATION_ROW_THRESHOLD) {
      return MARKDOWN_TABLE_VIRTUALIZED_HEIGHT * fontScale;
    }
    return (48 + rows * 36) * fontScale;
  }
  if (block.kind === "image") return 280 * fontScale;
  if (block.isHostile) return 420 * fontScale;
  return estimateTextBlock(block, textWidth, fontScale, 1);
}

function estimateTextBlock(
  block: MarkdownGreenfieldBlock,
  textWidth: number,
  fontScale: number,
  multiplier: number,
) {
  const charWidth =
    block.kind === "code" ? APPROX_MONO_CHAR_WIDTH : APPROX_BODY_CHAR_WIDTH;
  const columns = Math.max(12, Math.floor(textWidth / (charWidth * fontScale)));
  const visualLines = block.sourceLineLengths.reduce(
    (sum, lineLength) => sum + Math.max(1, Math.ceil(lineLength / columns)),
    0,
  );
  return (
    16 * fontScale + visualLines * BODY_LINE_HEIGHT * fontScale * multiplier
  );
}

function readMarkdownGreenfieldMeasuredHeight({
  chunk,
  context,
  measuredHeights,
}: {
  chunk: MarkdownGreenfieldChunk;
  context: MarkdownGreenfieldMeasurementContext;
  measuredHeights?: MarkdownGreenfieldMeasuredHeights;
}) {
  if (!measuredHeights) return null;
  const measuredHeight = measuredHeights.get(chunk, context);
  if (
    typeof measuredHeight === "number" &&
    Number.isFinite(measuredHeight) &&
    measuredHeight > 0
  ) {
    return measuredHeight;
  }
  return null;
}

function markdownGreenfieldLayoutCacheKey({
  context,
  document,
  measuredHeightsCacheKey,
}: {
  context: MarkdownGreenfieldMeasurementContext;
  document: MarkdownGreenfieldDocument;
  measuredHeightsCacheKey: string | null;
}) {
  return [
    markdownGreenfieldLayoutDocumentId(document),
    Math.round(context.width * 100) / 100,
    context.fontScale.toFixed(4),
    context.policyVersion,
    measuredHeightsCacheKey ?? "unmeasured",
  ].join(":");
}

function markdownGreenfieldEstimateCacheKey(
  context: MarkdownGreenfieldMeasurementContext,
) {
  return [
    Math.round(context.width * 100) / 100,
    context.fontScale.toFixed(4),
    context.policyVersion,
  ].join(":");
}

function markdownGreenfieldEstimateCacheForDocument(
  document: MarkdownGreenfieldDocument,
) {
  let cache = markdownGreenfieldEstimatedHeightCaches.get(document);
  if (cache) return cache;
  cache = new Map();
  markdownGreenfieldEstimatedHeightCaches.set(document, cache);
  return cache;
}

function markdownGreenfieldLayoutDocumentId(
  document: MarkdownGreenfieldDocument,
) {
  const existing = markdownGreenfieldLayoutDocumentIds.get(document);
  if (existing) return existing;
  const next = nextMarkdownGreenfieldLayoutDocumentId++;
  markdownGreenfieldLayoutDocumentIds.set(document, next);
  return next;
}

function freezeMarkdownGreenfieldFrame(frame: MarkdownGreenfieldFrame) {
  for (const chunk of frame.chunks) Object.freeze(chunk);
  Object.freeze(frame.chunks);
  return Object.freeze(frame);
}
registry/new-york-v4/ui/markdown-greenfield-renderer.tsx
"use client";

import * as React from "react";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
import {
  BadgeAlert,
  Check,
  CircleAlert,
  Copy,
  ExternalLink,
  Info,
  Lightbulb,
  Link2,
  TriangleAlert,
} from "lucide-react";

import {
  diffLineKind,
  isSafeHighlightedCodeLine,
  MARKDOWN_CODE_HIGHLIGHT_STYLES,
  normalizeCodeLanguage,
  renderCodeLine,
  useMarkdownCodeLineHtml,
} from "./markdown-greenfield-code-highlight";
import { MarkdownGreenfieldDiagram } from "./markdown-greenfield-diagram";
import type { MarkdownGreenfieldChunk } from "./markdown-greenfield-document";
import type {
  MarkdownHastElement,
  MarkdownHastNode,
  MarkdownHastRoot,
} from "./markdown-hast-types";
import {
  sanitizeMarkdownImageUrl,
  sanitizeMarkdownMediaUrl,
  sanitizeMarkdownUrl,
} from "./markdown-url-policy";
import { useKeyedLayoutEffect } from "@/hooks/use-keyed-layout-effect";
import { joinEffectKey } from "@/lib/effect-key";

const MarkdownContentReadyContext = React.createContext<(() => void) | null>(
  null,
);

// The rendered body size at 100% zoom. Every other size (headings, code,
// tables, footnotes) is authored in `em` relative to this, so scaling this one
// value with the zoom `fontScale` resizes the whole document as one system.
export const MARKDOWN_GREENFIELD_BASE_FONT_PX = 15.5;
// Tailwind v4's default spacing unit (0.25rem). Scaling it with the zoom
// fontScale keeps padding/margins proportional to the body text.
export const MARKDOWN_GREENFIELD_BASE_SPACING_REM = 0.25;
const MARKDOWN_CODE_VIRTUALIZATION_LINE_THRESHOLD = 100;
const MARKDOWN_CODE_VIRTUALIZED_OVERSCAN_LINES = 12;
const MARKDOWN_CODE_VIRTUALIZED_VIEWPORT_HEIGHT_PX = 512;
const MARKDOWN_CODE_VIRTUALIZED_LINE_HEIGHT_FALLBACK_PX = 24;
const MARKDOWN_TABLE_VIRTUALIZATION_ROW_THRESHOLD = 80;
const MARKDOWN_TABLE_VIRTUALIZATION_CELL_THRESHOLD = 600;
const MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX = 36;
const MARKDOWN_TABLE_VIRTUALIZED_VIEWPORT_HEIGHT_PX = 560;
const MARKDOWN_TABLE_VIRTUALIZED_OVERSCAN_ROWS = 8;
const MARKDOWN_TABLE_NATIVE_FIND_ROWS_PER_ENTRY = 8;
const MARKDOWN_RENDERED_CHUNK_CACHE_LIMIT = 96;

const markdownRenderedChunkCache = new Map<string, React.ReactNode>();

export const MarkdownGreenfieldChunkRenderer = React.memo(
  function MarkdownGreenfieldChunkRenderer({
    activeMatchOccurrence,
    chunk,
    fontScale = 1,
    onContentReady,
    searchQuery,
    urlFragmentNavigation = true,
  }: {
    activeMatchOccurrence?: number;
    chunk: MarkdownGreenfieldChunk;
    fontScale?: number;
    onContentReady?: () => void;
    searchQuery?: string;
    urlFragmentNavigation?: boolean;
  }) {
    const ref = React.useRef<HTMLDivElement | null>(null);
    const notifyContentReady = React.useCallback(() => {
      onContentReady?.();
    }, [onContentReady]);
    const renderedChildren = React.useMemo(
      () =>
        renderCachedHastChunkChildren(
          chunk,
          searchQuery,
          activeMatchOccurrence,
          { urlFragmentNavigation },
        ),
      [activeMatchOccurrence, chunk, searchQuery, urlFragmentNavigation],
    );

    useKeyedLayoutEffect(joinEffectKey([chunk.id, notifyContentReady]), () => {
      notifyContentReady();
      const element = ref.current;
      if (!element || typeof ResizeObserver === "undefined") return;
      const observer = new ResizeObserver(notifyContentReady);
      observer.observe(element);
      return () => observer.disconnect();
    });

    if (chunk.isHostile) {
      return <MarkdownGreenfieldHostileChunk chunk={chunk} />;
    }

    return (
      <MarkdownContentReadyContext.Provider value={notifyContentReady}>
        <div
          ref={ref}
          className="markdown-greenfield-content text-foreground min-w-0 leading-relaxed"
          data-slot="markdown-greenfield-content"
          // Scale both the font and the spacing scale with zoom so vertical
          // rhythm tracks the type size. Tailwind v4 spacing utilities resolve to
          // calc(var(--spacing) * n), so overriding --spacing here scales every
          // margin/padding/gap inside the document at once.
          style={
            {
              "--spacing": `${(MARKDOWN_GREENFIELD_BASE_SPACING_REM * fontScale).toFixed(5)}rem`,
              fontSize: `${MARKDOWN_GREENFIELD_BASE_FONT_PX * fontScale}px`,
            } as React.CSSProperties
          }
        >
          {renderedChildren}
        </div>
      </MarkdownContentReadyContext.Provider>
    );
  },
);

function renderCachedHastChunkChildren(
  chunk: MarkdownGreenfieldChunk,
  searchQuery?: string,
  activeMatchOccurrence?: number,
  options: { urlFragmentNavigation: boolean } = {
    urlFragmentNavigation: true,
  },
) {
  const normalizedQuery = normalizeMarkdownSearchQuery(searchQuery);
  const cacheKey = joinEffectKey([
    "markdown-rendered-chunk",
    chunk,
    normalizedQuery,
    normalizedQuery ? (activeMatchOccurrence ?? -1) : -1,
    options.urlFragmentNavigation,
  ]);
  const cached = readRenderedChunkCache(cacheKey);
  if (cached !== undefined || markdownRenderedChunkCache.has(cacheKey)) {
    return cached;
  }

  const rendered = renderHastChildrenUncached({
    activeMatchOccurrence,
    children: chunk.hastChildren,
    normalizedQuery,
    urlFragmentNavigation: options.urlFragmentNavigation,
  });
  writeRenderedChunkCache(cacheKey, rendered);
  return rendered;
}

function renderHastChildren(
  children: readonly MarkdownHastNode[],
  searchQuery?: string,
  activeMatchOccurrence?: number,
  options: { urlFragmentNavigation: boolean } = {
    urlFragmentNavigation: true,
  },
) {
  return renderHastChildrenUncached({
    activeMatchOccurrence,
    children,
    normalizedQuery: normalizeMarkdownSearchQuery(searchQuery),
    urlFragmentNavigation: options.urlFragmentNavigation,
  });
}

function renderHastChildrenUncached({
  activeMatchOccurrence,
  children,
  normalizedQuery,
  urlFragmentNavigation,
}: {
  activeMatchOccurrence?: number;
  children: readonly MarkdownHastNode[];
  normalizedQuery: string;
  urlFragmentNavigation: boolean;
}) {
  const root: MarkdownHastRoot = {
    type: "root",
    children: children.map(cloneHastNode),
  };
  if (!urlFragmentNavigation) {
    suppressDomFragmentIds(root.children);
  }

  if (normalizedQuery) {
    // The counter tracks rendered occurrences in document order so the one at
    // activeMatchOccurrence (the chunk-local index of the toolbar's current
    // match) can be marked active and styled distinctly from the rest.
    highlightMarkdownSearchMatches(root.children, normalizedQuery, {
      count: 0,
      active: activeMatchOccurrence ?? -1,
    });
  }

  return toJsxRuntime(root as never, {
    Fragment,
    components: markdownComponents,
    ignoreInvalidStyle: true,
    jsx,
    jsxs,
    passKeys: true,
    passNode: true,
  });
}

function readRenderedChunkCache(key: string) {
  if (!markdownRenderedChunkCache.has(key)) return undefined;
  const rendered = markdownRenderedChunkCache.get(key);
  markdownRenderedChunkCache.delete(key);
  markdownRenderedChunkCache.set(key, rendered);
  return rendered;
}

function writeRenderedChunkCache(key: string, rendered: React.ReactNode) {
  markdownRenderedChunkCache.set(key, rendered);
  while (
    markdownRenderedChunkCache.size > MARKDOWN_RENDERED_CHUNK_CACHE_LIMIT
  ) {
    const oldestKey = markdownRenderedChunkCache.keys().next().value;
    if (!oldestKey) break;
    markdownRenderedChunkCache.delete(oldestKey);
  }
}

function normalizeMarkdownSearchQuery(searchQuery: string | undefined) {
  return searchQuery?.trim().toLowerCase() ?? "";
}

const markdownComponents = {
  a: ({
    children,
    href,
    node: _node,
    rel: _rel,
    target: _target,
    ...props
  }: any) => {
    const safeHref = sanitizeMarkdownUrl(href ?? "");
    if (!safeHref) return <span>{children}</span>;
    const text = reactNodeText(children);
    const kind = linkKindForHref(safeHref);
    const form = linkFormForHref({
      href: safeHref,
      text,
    });
    const external = kind === "external";
    return (
      <a
        {...props}
        className={[
          "text-primary decoration-muted-foreground/50 visited:text-muted-foreground font-medium [overflow-wrap:anywhere] underline underline-offset-4 hover:decoration-current",
          kind === "fragment" ? "decoration-dotted" : "",
          form !== "inline" ? "font-mono" : "",
        ]
          .filter(Boolean)
          .join(" ")}
        data-pretext-link-form={form}
        data-pretext-link-kind={kind}
        href={safeHref}
        aria-label={footnoteLabelForLink({
          href: safeHref,
          label: props["aria-label"],
          text,
        })}
        rel={external ? "noopener noreferrer" : undefined}
        target={external ? "_blank" : undefined}
      >
        {children}
        {external ? (
          <ExternalLink className="ml-1 inline size-3" aria-hidden="true" />
        ) : null}
      </a>
    );
  },
  br: ({ node: _node, ...props }: any) => (
    <br {...props} data-pretext-line-break="soft" />
  ),
  abbr: ({ node: _node, ...props }: any) => (
    <abbr
      {...props}
      className="cursor-help underline decoration-dotted"
      data-pretext-raw-inline=""
    />
  ),
  blockquote: ({ children, node, ...props }: any) => {
    const alertKind = readDataProperty(node, "dataPretextAlertKind");
    const alertTitle = readDataProperty(node, "dataPretextAlertTitle");
    if (alertKind) {
      const label = String(alertTitle || alertKind);
      const Icon = alertIconForKind(alertKind);
      const alertProps = withoutPretextAlertMetadata(props);
      return (
        <aside
          {...alertProps}
          aria-label={label}
          className="bg-muted/35 my-5 rounded-md border px-4 py-3"
          data-pretext-alert-kind={alertKind}
          role="note"
        >
          <div
            className="text-foreground mb-2 flex items-center gap-2 text-[0.9em] font-semibold"
            data-pretext-alert-title=""
          >
            <Icon className="size-[1.15em]" aria-hidden="true" />
            {label}
          </div>
          <div
            className="[&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
            data-pretext-alert-body=""
          >
            {children}
          </div>
        </aside>
      );
    }

    return (
      <blockquote
        {...props}
        className="border-border text-muted-foreground my-4 border-l-2 pl-4 italic [&_blockquote]:my-3 [&_ol]:list-[lower-alpha] [&>ul]:my-2"
      >
        {children}
      </blockquote>
    );
  },
  code: ({ children, className, node: _node, ...props }: any) => (
    <code
      {...props}
      className={[
        "bg-muted rounded px-1 py-0.5 font-mono text-[0.88em]",
        className,
      ]
        .filter(Boolean)
        .join(" ")}
    >
      {children}
    </code>
  ),
  caption: ({ node: _node, ...props }: any) => (
    <caption
      {...props}
      className="text-muted-foreground caption-top px-3 py-2 text-left text-[0.85em] font-medium"
    />
  ),
  del: ({ node: _node, ...props }: any) => (
    <del
      {...props}
      className="text-muted-foreground decoration-muted-foreground/70 decoration-2"
      data-pretext-strikethrough=""
    />
  ),
  details: ({ node: _node, ...props }: any) => (
    <details {...props} className="bg-muted/25 my-4 rounded-md border p-3" />
  ),
  dl: ({ node: _node, ...props }: any) => (
    <dl {...props} className="my-4 space-y-2" data-pretext-definition-list="" />
  ),
  dt: ({ node: _node, ...props }: any) => (
    <dt {...props} className="font-semibold" data-pretext-definition-term="" />
  ),
  dd: ({ node: _node, ...props }: any) => (
    <dd
      {...props}
      className="text-muted-foreground ml-4"
      data-pretext-definition-description=""
    />
  ),
  div: ({ children, node, ...props }: any) => {
    const calloutKind = readTrustedDataProperty(node, "dataPretextCalloutKind");
    if (calloutKind) {
      const title =
        readTrustedDataProperty(node, "dataPretextCalloutTitle") ||
        calloutTitle(calloutKind);
      return (
        <aside
          aria-label={title}
          className="bg-muted/35 my-5 rounded-md border px-4 py-3"
          data-pretext-callout-kind={calloutKind}
          role="note"
        >
          <div className="mb-2 text-[0.9em] font-semibold">{title}</div>
          <div className="[&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
            {children}
          </div>
        </aside>
      );
    }

    const componentName = readDataProperty(node, "dataPretextComponentName");
    const trusted = isTrustedPretextComponentNode(node);
    if (trusted && hasDataProperty(node, "dataPretextComponentFallback")) {
      return (
        <div
          data-pretext-component-fallback=""
          data-pretext-component-fallback-name={readDataProperty(
            node,
            "dataPretextComponentFallbackName",
          )}
          data-pretext-component-fallback-reason={readDataProperty(
            node,
            "dataPretextComponentFallbackReason",
          )}
          data-pretext-component-fallback-source={readDataProperty(
            node,
            "dataPretextComponentFallbackSource",
          )}
        >
          {children}
        </div>
      );
    }
    if (trusted && componentName === "Metric") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      return (
        <div
          className="bg-muted/25 my-4 w-fit max-w-full min-w-0 rounded-md border px-4 py-3"
          data-pretext-component="Metric"
        >
          <div className="text-muted-foreground text-[0.9em] [overflow-wrap:anywhere]">
            {readOptionalString(componentProps.label)}
          </div>
          <div className="text-[1.55em] font-semibold [overflow-wrap:anywhere]">
            {readOptionalString(componentProps.value)}
          </div>
        </div>
      );
    }
    if (trusted && componentName === "Badge") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      return (
        <span
          className="bg-muted/35 inline-flex max-w-full items-center rounded-md border px-2 py-0.5 text-[0.9em] font-medium [overflow-wrap:anywhere]"
          data-pretext-component="Badge"
        >
          {readOptionalString(componentProps.label)}
        </span>
      );
    }
    if (trusted && componentName === "Callout") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      const kind = readOptionalString(componentProps.kind) ?? "note";
      const title =
        readOptionalString(componentProps.title) ?? calloutTitle(kind);
      return (
        <aside
          aria-label={title}
          className="bg-muted/35 my-5 rounded-md border px-4 py-3"
          data-pretext-callout-kind={kind}
          data-pretext-component="Callout"
          role="note"
        >
          <div className="mb-2 text-[0.9em] font-semibold">{title}</div>
          <div className="[&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
            {children}
          </div>
        </aside>
      );
    }
    if (trusted && componentName === "Accordion") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      return (
        <details
          className="bg-muted/25 my-4 rounded-md border p-3"
          data-pretext-component="Accordion"
        >
          <summary className="cursor-pointer font-medium">
            {readOptionalString(componentProps.title)}
          </summary>
          <div className="mt-3 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
            {children}
          </div>
        </details>
      );
    }
    if (trusted && componentName === "Tabs") {
      return <MarkdownTabs node={readHastElement(node)} />;
    }
    if (trusted && componentName === "Image") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      return (
        <MarkdownImageSurface
          alt={readOptionalString(componentProps.alt) ?? ""}
          componentName="Image"
          height={readOptionalNumber(componentProps.height)}
          src={readOptionalString(componentProps.src) ?? ""}
          title={readOptionalString(componentProps.title)}
          width={readOptionalNumber(componentProps.width)}
        />
      );
    }
    if (trusted && componentName === "Video") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      return (
        <MarkdownVideoSurface
          controls={readOptionalBoolean(componentProps.controls) ?? true}
          label={readOptionalString(componentProps.label) ?? "Video"}
          loop={readOptionalBoolean(componentProps.loop) ?? false}
          muted={readOptionalBoolean(componentProps.muted) ?? false}
          src={readOptionalString(componentProps.src) ?? ""}
          title={readOptionalString(componentProps.title)}
        />
      );
    }
    if (trusted && componentName === "Diagram") {
      const componentProps = readComponentProps(
        readDataProperty(node, "dataPretextComponentProps"),
      );
      if (
        componentProps.type === "mermaid" &&
        typeof componentProps.source === "string"
      ) {
        return (
          <MarkdownMeasuredDiagram
            caption={readOptionalString(componentProps.caption)}
            componentName="Diagram"
            source={componentProps.source}
            title={readOptionalString(componentProps.title)}
          />
        );
      }
    }

    return <div {...withoutInternalPretextMetadata(props)}>{children}</div>;
  },
  h1: headingComponent(
    "h1",
    "mt-6 mb-3 first:mt-0",
    "text-[1.55em] leading-tight font-semibold tracking-tight",
  ),
  h2: headingComponent(
    "h2",
    "mt-7 mb-3 first:mt-0",
    "text-[1.3em] leading-snug font-semibold tracking-tight",
  ),
  h3: headingComponent(
    "h3",
    "mt-5 mb-2 first:mt-0",
    "text-[1.1em] leading-snug font-semibold",
  ),
  h4: headingComponent(
    "h4",
    "mt-4 mb-2 first:mt-0",
    "text-[1em] leading-snug font-semibold",
  ),
  h5: headingComponent(
    "h5",
    "mt-4 mb-1.5 first:mt-0",
    "text-[0.95em] leading-snug font-semibold",
  ),
  h6: headingComponent(
    "h6",
    "mt-4 mb-1.5 first:mt-0",
    "text-[0.9em] leading-snug font-semibold text-muted-foreground",
  ),
  hr: ({ node: _node, ...props }: any) => (
    <hr
      {...props}
      className="border-border my-10 border-0 border-t"
      data-pretext-thematic-break=""
    />
  ),
  kbd: ({ node: _node, ...props }: any) => (
    <kbd
      {...props}
      className="bg-muted rounded border px-1.5 py-0.5 font-mono text-[0.85em]"
      data-pretext-raw-inline=""
    />
  ),
  mark: ({ node: _node, ...props }: any) => {
    const isActiveMatch = "data-pretext-search-match-active" in props;
    return (
      <mark
        {...props}
        aria-current={isActiveMatch ? "true" : undefined}
        className={[
          "text-foreground rounded px-1",
          isActiveMatch
            ? "bg-amber-400 ring-1 ring-amber-500/70 dark:bg-amber-500/70"
            : "bg-yellow-200/70 dark:bg-yellow-400/30",
        ].join(" ")}
        data-pretext-raw-inline=""
      />
    );
  },
  img: ({ alt, height, node, src, title, width }: any) => {
    if (!hasDataProperty(node, "dataPretextMarkdownImage")) return null;
    return (
      <MarkdownImageSurface
        alt={alt ?? ""}
        height={readOptionalNumber(height)}
        src={src ?? ""}
        title={title}
        width={readOptionalNumber(width)}
      />
    );
  },
  input: ({ checked, node: _node, type, ...props }: any) => {
    if (type !== "checkbox") return null;
    return (
      <input
        {...props}
        aria-label={checked ? "Completed task" : "Incomplete task"}
        aria-readonly="true"
        checked={checked}
        className="border-border accent-primary mr-2 size-3.5 rounded align-[-0.15em]"
        data-pretext-task-checkbox={checked ? "checked" : "unchecked"}
        disabled
        readOnly
        type="checkbox"
      />
    );
  },
  li: ({ children, className, node, ...props }: any) => {
    const isTask = hasDescendantElement(readHastElement(node), "input");
    return (
      <li
        {...props}
        className={[
          "leading-relaxed",
          isTask ? "list-none pl-0" : "",
          "[&>p]:my-1",
          className,
        ]
          .filter(Boolean)
          .join(" ")}
        data-pretext-task-list-item={isTask ? "" : undefined}
      >
        {children}
      </li>
    );
  },
  ol: ({ className, node: _node, ...props }: any) => (
    <ol
      {...props}
      className={[
        "my-3 ml-5 list-decimal space-y-1 [&_ol]:list-[lower-alpha]",
        className,
      ]
        .filter(Boolean)
        .join(" ")}
    />
  ),
  p: ({ node, ...props }: any) => {
    const element = readHastElement(node);
    const onlyImage =
      element?.children.filter((child) => !isWhitespaceText(child)).length ===
        1 &&
      readHastElement(
        element.children.find((child) => !isWhitespaceText(child)),
      )?.tagName === "img";
    if (onlyImage) {
      return (
        <div
          {...props}
          className="my-3 min-w-0 leading-relaxed [overflow-wrap:anywhere]"
        />
      );
    }
    return (
      <p
        {...props}
        className="my-4 min-w-0 leading-7 [overflow-wrap:anywhere]"
      />
    );
  },
  span: ({ className, node: _node, ...props }: any) => {
    const classes = String(className ?? "");
    if (classes.includes("katex-display")) {
      return (
        <span
          {...props}
          aria-label="Math block"
          className={[classes, "block overflow-x-auto"]
            .filter(Boolean)
            .join(" ")}
          data-pretext-math-block=""
          role="region"
          tabIndex={0}
          onKeyDown={handleHorizontalScrollKeyDown}
        />
      );
    }
    return (
      <span
        {...props}
        className={classes || undefined}
        data-pretext-math-inline={classes.includes("katex") ? "" : undefined}
      />
    );
  },
  pre: ({ children, node, ...props }: any) => {
    const code = readPreCodeElement(node);
    if (
      hasDataProperty(node, "dataMarkdownFrontmatterSource") ||
      hasDataProperty(code, "dataMarkdownFrontmatterSource")
    ) {
      return (
        <pre
          {...props}
          className="bg-muted/25 my-4 overflow-x-auto rounded-md border p-4 font-mono text-[0.9em] leading-[1.7] whitespace-pre"
          data-markdown-frontmatter-source=""
          role="region"
          tabIndex={0}
          onKeyDown={handleHorizontalScrollKeyDown}
        >
          <code>{extractHastNodeText(code).replace(/\n$/, "")}</code>
        </pre>
      );
    }

    const language = normalizeCodeLanguage(readCodeLanguage(code));
    if (language === "mermaid") {
      const metadata = readCodeMetadata(code);
      return (
        <MarkdownMeasuredDiagram
          caption={metadata.caption}
          source={extractHastNodeText(code).replace(/\n$/, "")}
          title={metadata.title}
        />
      );
    }

    return (
      <MarkdownCodeBlock
        language={language ?? "text"}
        metadata={readCodeMetadata(code)}
        source={extractHastNodeText(code).replace(/\n$/, "")}
      >
        {children}
      </MarkdownCodeBlock>
    );
  },
  section: ({ children, className, node, ...props }: any) => {
    const isFootnotes = hasDataProperty(node, "dataFootnotes");
    return (
      <section
        {...props}
        aria-label={isFootnotes ? "Footnotes" : props["aria-label"]}
        className={[
          isFootnotes
            ? "text-muted-foreground mt-10 border-t pt-5 text-[0.9em]"
            : "my-5",
          className,
        ]
          .filter(Boolean)
          .join(" ")}
      >
        {children}
      </section>
    );
  },
  strong: ({ node: _node, ...props }: any) => (
    <strong {...props} className="font-semibold" />
  ),
  summary: ({ node: _node, ...props }: any) => (
    <summary {...props} className="cursor-pointer font-medium" />
  ),
  q: ({ cite, node: _node, ...props }: any) => (
    <q
      {...props}
      cite={sanitizeMarkdownUrl(cite ?? "") || undefined}
      className="italic"
      data-pretext-raw-inline=""
    />
  ),
  ins: ({ cite, node: _node, ...props }: any) => (
    <ins
      {...props}
      cite={sanitizeMarkdownUrl(cite ?? "") || undefined}
      className="underline decoration-green-600/60"
      data-pretext-raw-inline=""
    />
  ),
  cite: ({ node: _node, ...props }: any) => (
    <cite {...props} className="italic" data-pretext-raw-inline="" />
  ),
  dfn: ({ node: _node, ...props }: any) => (
    <dfn {...props} className="italic" data-pretext-raw-inline="" />
  ),
  samp: ({ node: _node, ...props }: any) => (
    <samp {...props} className="font-mono" data-pretext-raw-inline="" />
  ),
  small: ({ node: _node, ...props }: any) => (
    <small
      {...props}
      className="text-muted-foreground text-[0.85em]"
      data-pretext-raw-inline=""
    />
  ),
  sub: ({ node: _node, ...props }: any) => (
    <sub {...props} className="align-sub" data-pretext-raw-inline="" />
  ),
  sup: ({ node: _node, ...props }: any) => (
    <sup {...props} className="align-super" data-pretext-raw-inline="" />
  ),
  time: ({ node: _node, ...props }: any) => (
    <time {...props} data-pretext-raw-inline="" />
  ),
  var: ({ node: _node, ...props }: any) => (
    <var {...props} className="font-mono italic" data-pretext-raw-inline="" />
  ),
  table: MarkdownTable,
  tbody: ({ node: _node, ...props }: any) => <tbody {...props} />,
  td: ({ align, node, ...props }: any) => {
    const resolvedAlign = align ?? readHastElement(node)?.properties?.align;
    return (
      <td
        {...props}
        align={typeof resolvedAlign === "string" ? resolvedAlign : undefined}
        className="border-border border-t px-3 py-1.5 align-top [overflow-wrap:break-word] [&[align=center]]:text-center [&[align=right]]:text-right [&[align=right]]:tabular-nums"
      />
    );
  },
  th: ({ align, node, ...props }: any) => {
    const resolvedAlign = align ?? readHastElement(node)?.properties?.align;
    return (
      <th
        {...props}
        align={typeof resolvedAlign === "string" ? resolvedAlign : undefined}
        className="border-border bg-muted/55 border-b px-3 py-1.5 text-left align-top font-medium [overflow-wrap:break-word] [&[align=center]]:text-center [&[align=right]]:text-right [&[align=right]]:tabular-nums"
        scope="col"
      />
    );
  },
  thead: ({ node: _node, ...props }: any) => <thead {...props} />,
  tr: ({ node: _node, ...props }: any) => <tr {...props} />,
  ul: ({ className, node: _node, ...props }: any) => (
    <ul
      {...props}
      className={[
        "my-3 ml-5 list-disc space-y-1 [&_ul]:list-[circle]",
        className,
      ]
        .filter(Boolean)
        .join(" ")}
    />
  ),
};

function headingComponent(
  Tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6",
  blockClassName: string,
  textClassName: string,
) {
  return function Heading({ children, node, ...props }: any) {
    const id =
      typeof props.id === "string"
        ? props.id
        : readDataProperty(node, "dataPretextFragmentId");
    const text =
      extractHastText(readHastElement(node)) || reactNodeText(children);
    delete props.dataPretextFragmentId;
    delete props["data-pretext-fragment-id"];
    if (!id) {
      return (
        <Tag {...props} className={`${blockClassName} ${textClassName}`}>
          {children}
        </Tag>
      );
    }
    return (
      <div className={`group/heading relative ${blockClassName}`}>
        <Tag {...props} className={textClassName}>
          {children}
        </Tag>
        <HeadingAnchor id={id} text={text} />
      </div>
    );
  };
}

// A GitHub-style anchor that appears in the left gutter on hover/focus and
// copies a deep link to the heading. Kept as a sibling of the heading (not a
// child) so it never leaks into the heading's accessible name.
function HeadingAnchor({ id, text }: { id: string; text: string }) {
  const [copied, setCopied] = React.useState(false);
  return (
    <button
      aria-label={`Copy link to ${text}`}
      className="text-muted-foreground hover:text-foreground absolute top-1/2 -left-7 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded opacity-0 transition-opacity group-hover/heading:opacity-100 focus-visible:opacity-100"
      type="button"
      onClick={() => {
        copyHeadingLink(id);
        setCopied(true);
        window.setTimeout(() => setCopied(false), 1200);
      }}
    >
      {copied ? (
        <Check className="size-4" aria-hidden="true" />
      ) : (
        <Link2 className="size-4" aria-hidden="true" />
      )}
    </button>
  );
}

function MarkdownGreenfieldHostileChunk({
  chunk,
}: {
  chunk: MarkdownGreenfieldChunk;
}) {
  const sourceLines = React.useMemo(
    () => chunk.sourceText.split(/\r\n|[\n\r\u2028\u2029]/),
    [chunk.sourceText],
  );
  const [scrollTop, setScrollTop] = React.useState(0);
  const lineHeight = 24;
  const viewportHeight = 576;
  const start = Math.max(0, Math.floor(scrollTop / lineHeight) - 12);
  const end = Math.min(
    sourceLines.length,
    Math.ceil((scrollTop + viewportHeight) / lineHeight) + 12,
  );
  const mountedLines = sourceLines.slice(start, end);
  const omittedLines = Math.max(0, sourceLines.length - mountedLines.length);

  return (
    <section
      aria-label="Large Markdown block"
      className="bg-muted/25 text-muted-foreground overflow-hidden rounded-md border text-sm"
      data-markdown-hostile-fallback=""
      data-markdown-hostile-line-count={sourceLines.length}
      data-markdown-hostile-mounted-lines={mountedLines.length}
      data-markdown-hostile-omitted-lines={omittedLines}
      data-markdown-hostile-virtualized=""
    >
      <div className="bg-muted/55 text-foreground flex items-center justify-between gap-3 border-b px-3 py-2 font-medium">
        Large Markdown block
        <button
          aria-label="Copy large Markdown block source"
          className="text-muted-foreground text-xs underline underline-offset-4"
          type="button"
          onClick={() => void navigator.clipboard?.writeText(chunk.sourceText)}
        >
          Copy
        </button>
      </div>
      <pre
        aria-label="Large Markdown source preview"
        className="bg-background/60 max-h-[36rem] overflow-auto font-mono text-[13px] whitespace-pre"
        data-markdown-hostile-preview=""
        role="region"
        tabIndex={0}
        onScroll={(event) => setScrollTop(event.currentTarget.scrollTop)}
      >
        <code
          className="relative block min-w-max"
          data-markdown-hostile-scroll-canvas=""
          style={{ height: Math.max(sourceLines.length, 1) * lineHeight }}
        >
          {mountedLines.map((line, index) => {
            const lineNumber = start + index + 1;
            return (
              <span
                key={lineNumber}
                className="absolute inset-x-0 grid grid-cols-[4rem_minmax(0,1fr)] px-4"
                data-markdown-hostile-line={lineNumber}
                style={{
                  height: lineHeight,
                  lineHeight: `${lineHeight}px`,
                  top: (lineNumber - 1) * lineHeight,
                }}
              >
                <span
                  aria-hidden="true"
                  className="text-muted-foreground pr-4 text-right select-none"
                >
                  {lineNumber}
                </span>
                <span>{line || " "}</span>
              </span>
            );
          })}
        </code>
      </pre>
    </section>
  );
}

function MarkdownCodeBlock({
  children: _children,
  language,
  metadata,
  source,
}: {
  children: React.ReactNode;
  language: string;
  metadata: ReturnType<typeof readCodeMetadata>;
  source: string;
}) {
  const [copyFailed, setCopyFailed] = React.useState(false);
  const title = metadata.title || language;
  const sourceLines = React.useMemo(() => source.split("\n"), [source]);
  const lineNumberStart = metadata.lineNumberStart ?? 1;
  const lineNumberMaxDigits = String(
    lineNumberStart + Math.max(0, sourceLines.length - 1),
  ).length;

  const copy = React.useCallback(
    async (selectedOnly: boolean) => {
      const selection = window.getSelection();
      const selectedText =
        selectedOnly && selection?.rangeCount
          ? selection.toString().trimEnd()
          : "";
      try {
        await navigator.clipboard?.writeText(selectedText || source);
        setCopyFailed(false);
      } catch {
        setCopyFailed(true);
      }
    },
    [source],
  );

  return (
    <figure
      aria-label={`${title} code block`}
      // text-[1em] keeps the code block on the document's scaling em cascade:
      // a host stylesheet (e.g. the demo's rehype-pretty-code rule) may pin
      // [data-rehype-pretty-code-figure] to a fixed font-size, which would stop
      // the block from following the zoom control. A utility-layer size wins.
      className="bg-muted/25 my-5 overflow-hidden rounded-md border text-[1em]"
      data-pretext-code-language={language}
      data-rehype-pretty-code-figure=""
      role="group"
    >
      <figcaption className="bg-muted/55 flex items-center justify-between gap-3 border-b px-3 py-2 text-sm">
        <span
          className="text-muted-foreground font-mono text-xs font-medium"
          data-pretext-code-title={metadata.title || undefined}
        >
          {title}
        </span>
        <span className="flex items-center gap-2">
          {copyFailed ? (
            <span aria-label="Copy failed" className="text-destructive text-xs">
              Copy failed
            </span>
          ) : null}
          <button
            aria-label="Copy code block"
            className="text-muted-foreground text-xs underline underline-offset-4"
            type="button"
            onClick={() => void copy(false)}
          >
            Copy
          </button>
          <button
            aria-label="Copy selected code or block"
            className="sr-only"
            type="button"
            onClick={() => void copy(true)}
          />
        </span>
      </figcaption>
      <MarkdownCodeSource
        language={language}
        lineNumberMaxDigits={lineNumberMaxDigits}
        lineNumberStart={lineNumberStart}
        metadata={metadata}
        sourceLines={sourceLines}
      />
      {metadata.caption ? (
        <figcaption
          className="text-muted-foreground border-t px-3 py-2 text-[0.9em]"
          data-pretext-code-caption=""
        >
          {metadata.caption}
        </figcaption>
      ) : null}
    </figure>
  );
}

type MarkdownCodeMetadata = ReturnType<typeof readCodeMetadata>;

const MarkdownCodeSource = React.memo(function MarkdownCodeSource({
  language,
  lineNumberMaxDigits,
  lineNumberStart,
  metadata,
  sourceLines,
}: {
  language: string;
  lineNumberMaxDigits: number;
  lineNumberStart: number;
  metadata: MarkdownCodeMetadata;
  sourceLines: readonly string[];
}) {
  const isVirtualized =
    sourceLines.length > MARKDOWN_CODE_VIRTUALIZATION_LINE_THRESHOLD;
  const lineHtmlByIndex = useMarkdownCodeLineHtml({
    end: isVirtualized ? 0 : sourceLines.length,
    highlightPattern: metadata.highlightPattern,
    language,
    sourceLines,
    start: 0,
  });

  if (isVirtualized) {
    return (
      <MarkdownVirtualizedCodeSource
        language={language}
        lineNumberMaxDigits={lineNumberMaxDigits}
        lineNumberStart={lineNumberStart}
        metadata={metadata}
        sourceLines={sourceLines}
      />
    );
  }

  return (
    <>
      <style data-pretext-code-highlight-styles="">
        {MARKDOWN_CODE_HIGHLIGHT_STYLES}
      </style>
      <pre
        aria-label={`${language} code source`}
        className="overflow-x-auto p-3 [overflow-wrap:normal] [&_code]:min-w-max"
        data-pretext-code-source=""
        role="region"
        tabIndex={0}
        onKeyDown={handleHorizontalScrollKeyDown}
      >
        <code
          {...markdownCodeElementProps({
            language,
            lineNumberMaxDigits,
            lineNumberStart,
            metadata,
          })}
        >
          {sourceLines.map((line, index) => (
            <MarkdownCodeLine
              key={index}
              index={index}
              language={language}
              line={line}
              lineHtml={lineHtmlByIndex.get(index)}
              lineNumberStart={lineNumberStart}
              metadata={metadata}
            />
          ))}
        </code>
      </pre>
    </>
  );
});

function MarkdownVirtualizedCodeSource({
  language,
  lineNumberMaxDigits,
  lineNumberStart,
  metadata,
  sourceLines,
}: {
  language: string;
  lineNumberMaxDigits: number;
  lineNumberStart: number;
  metadata: MarkdownCodeMetadata;
  sourceLines: readonly string[];
}) {
  const preRef = React.useRef<HTMLPreElement | null>(null);
  const codeRef = React.useRef<HTMLElement | null>(null);
  const [scrollTop, setScrollTop] = React.useState(0);
  const [lineHeight, setLineHeight] = React.useState(
    MARKDOWN_CODE_VIRTUALIZED_LINE_HEIGHT_FALLBACK_PX,
  );
  const [viewportHeight, setViewportHeight] = React.useState(
    MARKDOWN_CODE_VIRTUALIZED_VIEWPORT_HEIGHT_PX,
  );
  const start = Math.max(
    0,
    Math.floor(scrollTop / lineHeight) -
      MARKDOWN_CODE_VIRTUALIZED_OVERSCAN_LINES,
  );
  const end = Math.min(
    sourceLines.length,
    Math.ceil((scrollTop + viewportHeight) / lineHeight) +
      MARKDOWN_CODE_VIRTUALIZED_OVERSCAN_LINES,
  );
  const mountedLines = sourceLines.slice(start, end);
  const lineWidthCh = React.useMemo(
    () => widestMarkdownCodeLineWidthCh(sourceLines),
    [sourceLines],
  );
  const lineHtmlByIndex = useMarkdownCodeLineHtml({
    end,
    highlightPattern: metadata.highlightPattern,
    language,
    sourceLines,
    start,
  });

  useKeyedLayoutEffect(joinEffectKey([language, sourceLines.length]), () => {
    const updateMetrics = () => {
      const measuredLineHeight = measureMarkdownCodeLineHeight(codeRef.current);
      if (measuredLineHeight > 0) {
        setLineHeight((current) =>
          Math.abs(current - measuredLineHeight) > 0.5
            ? measuredLineHeight
            : current,
        );
      }
      const measuredViewportHeight = preRef.current?.clientHeight ?? 0;
      if (measuredViewportHeight > 0) {
        setViewportHeight((current) =>
          Math.abs(current - measuredViewportHeight) > 0.5
            ? measuredViewportHeight
            : current,
        );
      }
    };

    updateMetrics();
    if (typeof ResizeObserver === "undefined") return;
    const observer = new ResizeObserver(updateMetrics);
    if (preRef.current) observer.observe(preRef.current);
    if (codeRef.current) observer.observe(codeRef.current);
    return () => observer.disconnect();
  });

  return (
    <>
      <style data-pretext-code-highlight-styles="">
        {MARKDOWN_CODE_HIGHLIGHT_STYLES}
      </style>
      <pre
        ref={preRef}
        aria-label={`${language} code source`}
        className="max-h-[32rem] overflow-auto overflow-x-auto p-3 [overflow-wrap:normal] [&_code]:min-w-max"
        data-pretext-code-line-count={sourceLines.length}
        data-pretext-code-mounted-lines={mountedLines.length}
        data-pretext-code-source=""
        data-pretext-code-virtualized=""
        role="region"
        tabIndex={0}
        onKeyDown={handleHorizontalScrollKeyDown}
        onScroll={(event) => setScrollTop(event.currentTarget.scrollTop)}
      >
        <code
          ref={codeRef}
          {...markdownCodeElementProps({
            language,
            lineNumberMaxDigits,
            lineNumberStart,
            metadata,
            start,
            virtualized: true,
          })}
          style={markdownVirtualizedCodeCanvasStyle({
            language,
            lineNumberStart,
            lineWidthCh,
            metadata,
            start,
            totalLines: sourceLines.length,
          })}
        >
          {mountedLines.map((line, offset) => {
            const index = start + offset;
            return (
              <MarkdownCodeLine
                key={index}
                index={index}
                language={language}
                line={line}
                lineHtml={lineHtmlByIndex.get(index)}
                lineNumberStart={lineNumberStart}
                metadata={metadata}
                totalLines={sourceLines.length}
                virtualized
              />
            );
          })}
        </code>
      </pre>
    </>
  );
}

function MarkdownCodeLine({
  index,
  language,
  line,
  lineHtml,
  lineNumberStart,
  metadata,
  totalLines,
  virtualized = false,
}: {
  index: number;
  language: string;
  line: string;
  lineHtml: string | undefined;
  lineNumberStart: number;
  metadata: MarkdownCodeMetadata;
  totalLines?: number;
  virtualized?: boolean;
}) {
  const lineNumber = lineNumberStart + index;
  const diffKind = diffLineKind(line);
  return (
    <span
      aria-label={metadata.showLineNumbers ? `Line ${lineNumber}` : undefined}
      aria-posinset={
        metadata.showLineNumbers && virtualized ? index + 1 : undefined
      }
      aria-setsize={
        metadata.showLineNumbers && virtualized ? totalLines : undefined
      }
      className={[
        markdownCodeLineClassName(diffKind),
        virtualized ? "absolute right-0 left-0" : "",
      ]
        .filter(Boolean)
        .join(" ")}
      data-highlighted-line={
        metadata.highlightedLines.has(index + 1) ? "" : undefined
      }
      data-line=""
      data-pretext-code-diff-line={diffKind ?? undefined}
      data-pretext-code-line-number={
        metadata.showLineNumbers ? lineNumber : undefined
      }
      role={metadata.showLineNumbers ? "listitem" : undefined}
      style={
        virtualized
          ? {
              height: "var(--markdown-code-line-height)",
              top: `calc(${index} * var(--markdown-code-line-height))`,
            }
          : undefined
      }
    >
      {renderCodeLine({
        fallbackLanguage: language,
        line,
        lineHtml,
        pattern: metadata.highlightPattern,
      })}
    </span>
  );
}

function markdownCodeElementProps({
  language,
  lineNumberMaxDigits,
  lineNumberStart,
  metadata,
  start = 0,
  virtualized = false,
}: {
  language: string;
  lineNumberMaxDigits: number;
  lineNumberStart: number;
  metadata: MarkdownCodeMetadata;
  start?: number;
  virtualized?: boolean;
}) {
  return {
    "aria-label": metadata.showLineNumbers
      ? `${language} numbered code lines`
      : undefined,
    className: [
      "block font-mono text-[0.9em] leading-[1.45]",
      virtualized ? "relative" : "",
      metadata.showLineNumbers
        ? "[counter-reset:line] before:content-[counter(line)]"
        : "",
      metadata.highlightedLines.size
        ? "[&>[data-highlighted-line]]:bg-primary/10"
        : "",
      metadata.highlightPattern
        ? "[&_[data-highlighted-chars]]:bg-primary/20 [&_[data-highlighted-chars]]:rounded"
        : "",
    ]
      .filter(Boolean)
      .join(" "),
    "data-language": language,
    "data-line-numbers": metadata.showLineNumbers ? "" : undefined,
    "data-line-numbers-max-digits": metadata.showLineNumbers
      ? lineNumberMaxDigits
      : undefined,
    "data-pretext-code-virtualized": virtualized ? "" : undefined,
    role: metadata.showLineNumbers ? "list" : undefined,
    style: metadata.showLineNumbers
      ? { counterSet: `line ${lineNumberStart + start - 1}` }
      : undefined,
  };
}

function markdownVirtualizedCodeCanvasStyle({
  language,
  lineNumberStart,
  lineWidthCh,
  metadata,
  start,
  totalLines,
}: {
  language: string;
  lineNumberStart: number;
  lineWidthCh: number;
  metadata: MarkdownCodeMetadata;
  start: number;
  totalLines: number;
}): React.CSSProperties {
  return {
    "--markdown-code-line-height": markdownCodeLineHeightCss(language),
    counterSet: metadata.showLineNumbers
      ? `line ${lineNumberStart + start - 1}`
      : undefined,
    height: `calc(${Math.max(totalLines, 1)} * var(--markdown-code-line-height))`,
    minWidth: markdownCodeMinWidth(lineWidthCh, metadata.showLineNumbers),
  } as React.CSSProperties;
}

function markdownCodeLineClassName(diffKind: ReturnType<typeof diffLineKind>) {
  return [
    "block min-h-5 box-border whitespace-pre",
    diffKind === "add" ? "bg-emerald-500/10" : "",
    diffKind === "remove" ? "bg-red-500/10" : "",
  ]
    .filter(Boolean)
    .join(" ");
}

function markdownCodeLineHeightCss(language: string) {
  return language === "text" || language === "plaintext"
    ? "0.95em"
    : "calc(1.45em + var(--spacing) * 1)";
}

function markdownCodeMinWidth(lineWidthCh: number, showLineNumbers: boolean) {
  const contentWidth = `${Math.max(1, lineWidthCh)}ch`;
  return showLineNumbers
    ? `max(100%, calc(${contentWidth} + var(--spacing) * 22))`
    : `max(100%, ${contentWidth})`;
}

function widestMarkdownCodeLineWidthCh(lines: readonly string[]) {
  return lines.reduce(
    (widest, line) => Math.max(widest, markdownCodeLineWidthCh(line)),
    1,
  );
}

function markdownCodeLineWidthCh(line: string) {
  let width = 0;
  for (const character of line) {
    if (character === "\t") {
      width += 4 - (width % 4);
    } else {
      width += 1;
    }
  }
  return width;
}

function measureMarkdownCodeLineHeight(code: HTMLElement | null) {
  if (!code) return 0;
  const sample = document.createElement("span");
  sample.className = markdownCodeLineClassName(null);
  sample.dataset.line = "";
  sample.style.position = "absolute";
  sample.style.visibility = "hidden";
  sample.textContent = " ";
  code.appendChild(sample);
  const height = sample.getBoundingClientRect().height || sample.offsetHeight;
  sample.remove();
  return height;
}

function MarkdownMeasuredDiagram({
  caption,
  componentName,
  source,
  title,
}: {
  caption?: string;
  componentName?: string;
  source: string;
  title?: string;
}) {
  const notifyContentReady = React.useContext(MarkdownContentReadyContext);
  return (
    <MarkdownGreenfieldDiagram
      caption={caption}
      componentName={componentName}
      onContentReady={notifyContentReady ?? undefined}
      source={source}
      title={title}
    />
  );
}

function MarkdownImageSurface({
  alt,
  componentName,
  height,
  src,
  title,
  width,
}: {
  alt: string;
  componentName?: string;
  height?: number;
  src: string;
  title?: string;
  width?: number;
}) {
  const notifyContentReady = React.useContext(MarkdownContentReadyContext);
  const safeSrc = sanitizeMarkdownImageUrl(src);
  const explicitAspectRatio =
    width && height ? `${width} / ${height}` : undefined;
  const [state, setState] = React.useState<
    "blocked" | "failed" | "loading" | "ready"
  >(safeSrc ? "loading" : "blocked");
  const [aspectRatio, setAspectRatio] = React.useState(
    () => explicitAspectRatio ?? "",
  );
  const captionId = React.useId();

  // Reset load state when the source/aspect-ratio inputs change by adjusting
  // state during render (React's prop-change pattern) instead of in an effect.
  const sourceResetKey = `${safeSrc}::${explicitAspectRatio ?? ""}`;
  const [prevSourceResetKey, setPrevSourceResetKey] =
    React.useState(sourceResetKey);
  if (sourceResetKey !== prevSourceResetKey) {
    setPrevSourceResetKey(sourceResetKey);
    setState(safeSrc ? "loading" : "blocked");
    setAspectRatio(explicitAspectRatio ?? "");
  }

  useKeyedLayoutEffect(
    joinEffectKey([aspectRatio, notifyContentReady, state]),
    () => {
      notifyContentReady?.();
    },
  );

  return (
    <figure
      aria-label={
        state === "failed" ? `Image failed: ${alt}` : alt || title || "Image"
      }
      className="my-5 w-fit max-w-full"
      data-pretext-component={componentName}
      data-pretext-image-height={height}
      data-pretext-image-state={state}
      data-pretext-image-src={safeSrc || undefined}
      data-pretext-image-width={width}
      role="group"
      style={aspectRatio ? { aspectRatio } : undefined}
    >
      {safeSrc ? (
        <div
          className="bg-muted/25 relative flex min-h-48 max-w-full items-center justify-center overflow-hidden rounded-md border"
          data-pretext-image-frame=""
          style={aspectRatio ? { aspectRatio } : undefined}
        >
          {state === "loading" ? (
            <span className="text-muted-foreground absolute inset-x-4 top-1/2 -translate-y-1/2 text-center text-sm">
              Loading image
            </span>
          ) : null}
          {state === "failed" ? (
            <div
              className="text-muted-foreground absolute inset-0 flex items-center justify-center px-4 text-center text-sm"
              role="alert"
            >
              Could not load image{alt ? `: ${alt}` : ""}
            </div>
          ) : null}
          {state === "failed" ? (
            <div
              aria-label={alt || "Image"}
              className="text-muted-foreground text-sm"
              data-pretext-image-state="failed"
              role="img"
            >
              Image failed to load: {alt}
              <button
                className="ml-2 text-xs underline underline-offset-4"
                type="button"
                onClick={() => setState("loading")}
              >
                Retry image
              </button>
            </div>
          ) : (
            <img
              alt={alt}
              aria-describedby={title ? captionId : undefined}
              className={[
                "block max-h-[70vh] max-w-full object-contain transition-opacity",
                state === "loading" ? "opacity-0" : "",
              ]
                .filter(Boolean)
                .join(" ")}
              decoding="async"
              loading="lazy"
              src={safeSrc}
              title={title}
              onError={(event) => {
                event.currentTarget.setAttribute(
                  "data-pretext-image-state",
                  "failed",
                );
                setState("failed");
              }}
              onLoad={(event) => {
                const image = event.currentTarget;
                if (image.naturalWidth && image.naturalHeight) {
                  setAspectRatio(
                    `${image.naturalWidth} / ${image.naturalHeight}`,
                  );
                }
                setState("ready");
              }}
            />
          )}
        </div>
      ) : (
        <span
          aria-label={alt || "Blocked image"}
          className="bg-muted/35 text-muted-foreground flex min-h-24 items-center rounded-md border border-dashed px-4 text-sm"
          role="img"
        >
          {alt || "Blocked image"}
        </span>
      )}
      {title ? (
        <figcaption
          id={captionId}
          className="text-muted-foreground mt-2 text-[0.9em]"
          data-pretext-image-caption=""
        >
          {title}
        </figcaption>
      ) : null}
    </figure>
  );
}

function MarkdownVideoSurface({
  controls = true,
  label,
  loop = false,
  muted = false,
  src,
  title,
}: {
  controls?: boolean;
  label: string;
  loop?: boolean;
  muted?: boolean;
  src: string;
  title?: string;
}) {
  const notifyContentReady = React.useContext(MarkdownContentReadyContext);
  const safeSrc = sanitizeMarkdownMediaUrl(src);
  const [failed, setFailed] = React.useState(false);
  useKeyedLayoutEffect(
    joinEffectKey([failed, notifyContentReady, safeSrc]),
    () => {
      notifyContentReady?.();
    },
  );
  if (!safeSrc) {
    return (
      <div
        aria-label={`Video blocked: ${label}`}
        className="bg-muted/35 text-muted-foreground my-5 rounded-md border border-dashed p-4 text-sm"
        data-pretext-component="Video"
        data-pretext-video-state="blocked"
        role="group"
      >
        {label}
      </div>
    );
  }
  return (
    <figure
      aria-label={failed ? `Video failed to load: ${label}` : label}
      className="my-5 max-w-full"
      data-pretext-component="Video"
      data-pretext-video-state={failed ? "failed" : "ready"}
      role="group"
    >
      <video
        className="bg-muted block max-h-[70vh] max-w-full rounded-md border"
        controls={controls}
        loop={loop}
        muted={muted}
        preload="metadata"
        src={safeSrc}
        title={title}
        onError={() => setFailed(true)}
      />
      {title ? (
        <figcaption className="text-muted-foreground mt-2 text-[0.9em]">
          {title}
        </figcaption>
      ) : null}
    </figure>
  );
}

function MarkdownTabs({ node }: { node: MarkdownHastElement | null }) {
  const props = readComponentProps(
    readDataProperty(node, "dataPretextComponentProps"),
  );
  const tabs = (node?.children ?? [])
    .map(readHastElement)
    .filter((child): child is MarkdownHastElement => {
      return (
        child?.tagName === "div" &&
        readDataProperty(child, "dataPretextComponentName") === "Tab"
      );
    });
  const [selected, setSelected] = React.useState(0);
  const baseId = React.useId();
  const label = readOptionalString(props.label) ?? "Tabs";

  const select = (index: number) =>
    setSelected((index + tabs.length) % tabs.length);

  return (
    <div className="my-5" data-pretext-component="Tabs">
      <div aria-label={label} className="flex gap-1 border-b" role="tablist">
        {tabs.map((tab, index) => {
          const tabProps = readComponentProps(
            readDataProperty(tab, "dataPretextComponentProps"),
          );
          const title =
            readOptionalString(tabProps.title) ?? `Tab ${index + 1}`;
          const active = selected === index;
          return (
            <button
              key={index}
              aria-controls={`${baseId}-panel-${index}`}
              aria-selected={active}
              className="px-3 py-2 text-sm font-medium"
              id={`${baseId}-tab-${index}`}
              role="tab"
              tabIndex={active ? 0 : -1}
              type="button"
              onClick={() => select(index)}
              onKeyDown={(event) => {
                if (event.key === "ArrowRight") {
                  event.preventDefault();
                  const nextIndex = (index + 1) % tabs.length;
                  select(nextIndex);
                  document
                    .getElementById(`${baseId}-tab-${nextIndex}`)
                    ?.focus();
                } else if (event.key === "End") {
                  event.preventDefault();
                  select(tabs.length - 1);
                  document
                    .getElementById(`${baseId}-tab-${tabs.length - 1}`)
                    ?.focus();
                } else if (event.key === "Home") {
                  event.preventDefault();
                  select(0);
                  document.getElementById(`${baseId}-tab-0`)?.focus();
                }
              }}
            >
              {title}
            </button>
          );
        })}
      </div>
      {tabs.map((tab, index) => (
        <div
          key={index}
          aria-labelledby={`${baseId}-tab-${index}`}
          hidden={selected !== index}
          id={`${baseId}-panel-${index}`}
          className="pt-3"
          role="tabpanel"
        >
          {renderHastChildren(tab.children)}
        </div>
      ))}
    </div>
  );
}

// Tags whose text is verbatim source (code) or already a highlight; wrapping a
// match inside them would corrupt the rendered output, so they are skipped.
const MARKDOWN_SEARCH_SKIP_TAGS = new Set([
  "code",
  "pre",
  "mark",
  "script",
  "style",
  "textarea",
]);

// Browser-find-style highlighting for the in-app search: wraps every case-
// insensitive occurrence of the active query in a <mark> so matches are visible
// where the search navigates. Mutates the freshly cloned chunk tree in place,
// matching the same trimmed-substring semantics as the toolbar match count.
type MarkdownSearchHighlightContext = { active: number; count: number };

function highlightMarkdownSearchMatches(
  nodes: MarkdownHastNode[],
  lowerQuery: string,
  context: MarkdownSearchHighlightContext,
) {
  for (let index = 0; index < nodes.length; index += 1) {
    const node = nodes[index];
    if (node.type === "text" && typeof node.value === "string") {
      const replacement = splitMarkdownTextForSearch(
        node.value,
        lowerQuery,
        context,
      );
      if (replacement) {
        nodes.splice(index, 1, ...replacement);
        index += replacement.length - 1;
      }
      continue;
    }
    if (node.type === "element" && Array.isArray(node.children)) {
      const tagName = (node as MarkdownHastElement).tagName.toLowerCase();
      if (MARKDOWN_SEARCH_SKIP_TAGS.has(tagName)) continue;
      highlightMarkdownSearchMatches(node.children, lowerQuery, context);
    }
  }
}

function splitMarkdownTextForSearch(
  value: string,
  lowerQuery: string,
  context: MarkdownSearchHighlightContext,
): MarkdownHastNode[] | null {
  const lowerValue = value.toLowerCase();
  let matchStart = lowerValue.indexOf(lowerQuery);
  if (matchStart === -1) return null;

  const out: MarkdownHastNode[] = [];
  let cursor = 0;
  while (matchStart !== -1) {
    if (matchStart > cursor) {
      out.push({ type: "text", value: value.slice(cursor, matchStart) });
    }
    const matchEnd = matchStart + lowerQuery.length;
    const isActive = context.count === context.active;
    context.count += 1;
    out.push({
      type: "element",
      tagName: "mark",
      properties: isActive
        ? { dataPretextSearchMatch: "", dataPretextSearchMatchActive: "" }
        : { dataPretextSearchMatch: "" },
      children: [{ type: "text", value: value.slice(matchStart, matchEnd) }],
    });
    cursor = matchEnd;
    matchStart = lowerValue.indexOf(lowerQuery, cursor);
  }
  if (cursor < value.length) {
    out.push({ type: "text", value: value.slice(cursor) });
  }
  return out;
}

function cloneHastNode<T extends MarkdownHastNode>(node: T): T {
  if (!("children" in node) || !Array.isArray(node.children)) {
    return { ...node };
  }

  return {
    ...node,
    children: node.children.map((child) =>
      cloneHastNode(child as MarkdownHastNode),
    ),
    properties: readHastElement(node)?.properties
      ? { ...readHastElement(node)!.properties }
      : undefined,
  } as T;
}

function suppressDomFragmentIds(nodes: MarkdownHastNode[]) {
  for (const node of nodes) {
    const element = readHastElement(node);
    if (!element) continue;

    const id = element.properties?.id;
    if (
      typeof id === "string" &&
      id &&
      shouldSuppressDomFragmentId(element, id)
    ) {
      element.properties = {
        ...element.properties,
        dataPretextFragmentId: id,
      };
      delete element.properties.id;
    }

    suppressDomFragmentIds(element.children);
  }
}

function shouldSuppressDomFragmentId(element: MarkdownHastElement, id: string) {
  return /^h[1-6]$/.test(element.tagName) || /^user-content-fn/.test(id);
}

function readDataProperty(node: unknown, property: string) {
  const element = readHastElement(node);
  const value =
    element?.properties?.[property] ?? element?.properties?.[toKebab(property)];
  return typeof value === "string" ? value : "";
}

function readStringProperty(value: unknown) {
  return typeof value === "string" ? value : null;
}

function readTrustedDataProperty(node: unknown, property: string) {
  if (!isTrustedPretextComponentNode(node)) return "";
  return readDataProperty(node, property);
}

function isTrustedPretextComponentNode(node: unknown) {
  return readHastElement(node)?.properties?.pretextComponentTrusted === true;
}

function hasDataProperty(node: unknown, property: string) {
  const element = readHastElement(node);
  return (
    element?.properties != null &&
    (Object.hasOwn(element.properties, property) ||
      Object.hasOwn(element.properties, toKebab(property)))
  );
}

function withoutPretextAlertMetadata(props: Record<string, unknown>) {
  const next = { ...props };
  delete next.dataPretextAlertKind;
  delete next.dataPretextAlertTitle;
  delete next["data-pretext-alert-kind"];
  delete next["data-pretext-alert-title"];
  return next;
}

function withoutInternalPretextMetadata(props: Record<string, unknown>) {
  const next = { ...props };
  for (const key of Object.keys(next)) {
    if (/^dataPretext(?:Component|Callout|Heading)/.test(key)) {
      delete next[key];
    }
    if (/^data-pretext-(?:component|callout|heading)/.test(key)) {
      delete next[key];
    }
    if (key === "pretextComponentTrusted") {
      delete next[key];
    }
  }
  return next;
}

function readHastElement(node: unknown): MarkdownHastElement | null {
  return node &&
    typeof node === "object" &&
    (node as MarkdownHastElement).type === "element"
    ? (node as MarkdownHastElement)
    : null;
}

function readPreCodeElement(node: unknown): MarkdownHastElement | null {
  const pre = readHastElement(node);
  if (pre?.tagName !== "pre") return null;
  const code =
    pre.children
      .map(readHastElement)
      .find((child): child is MarkdownHastElement =>
        Boolean(child && child.tagName === "code"),
      ) ?? null;
  return code;
}

function readCodeLanguage(code: MarkdownHastElement | null) {
  const className = code?.properties?.className;
  const classes = Array.isArray(className) ? className : [className];
  const languageClass = classes.find(
    (value): value is string =>
      typeof value === "string" && value.startsWith("language-"),
  );
  const language = languageClass?.slice("language-".length).toLowerCase();
  if (language === "mmd" || language === "mermaid-js") return "mermaid";
  return language ?? null;
}

function readCodeMetadata(code: MarkdownHastElement | null) {
  return parseCodeMetadata(readDataProperty(code, "dataPretextCodeMeta"));
}

function parseCodeMetadata(meta: string) {
  const result: {
    caption?: string;
    highlightedLines: Set<number>;
    highlightPattern: string;
    lineNumberStart?: number;
    showLineNumbers: boolean;
    title?: string;
  } = {
    highlightedLines: new Set<number>(),
    highlightPattern: "",
    showLineNumbers: false,
  };
  for (const match of meta.matchAll(
    /(?:^|\s)(title|caption)=(?:"([^"]*)"|'([^']*)'|([^\s]+))/g,
  )) {
    const key = match[1] as "caption" | "title";
    result[key] = match[2] ?? match[3] ?? match[4] ?? "";
  }
  const lineNumbers = /(?:^|\s)showLineNumbers(?:\{(\d+)\})?(?=\s|$)/i.exec(
    meta,
  );
  if (lineNumbers) {
    result.showLineNumbers = true;
    result.lineNumberStart = lineNumbers[1] ? Number(lineNumbers[1]) : 1;
  }
  for (const match of meta.matchAll(/\{(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*)\}/g)) {
    for (const value of match[1]!.split(",")) {
      addHighlightedCodeLineSpec(result.highlightedLines, value);
    }
  }
  const highlightPattern = /\/([^/\n]+)\//.exec(meta);
  result.highlightPattern = highlightPattern?.[1] ?? "";
  return result;
}

function addHighlightedCodeLineSpec(lines: Set<number>, spec: string) {
  const range = /^(\d+)-(\d+)$/.exec(spec);
  if (range) {
    const start = Number(range[1]);
    const end = Number(range[2]);
    if (!isSafeHighlightedCodeLine(start) || !isSafeHighlightedCodeLine(end)) {
      return;
    }
    if (end < start || end - start > 500) return;
    for (let line = start; line <= end; line += 1) lines.add(line);
    return;
  }

  const line = Number(spec);
  if (isSafeHighlightedCodeLine(line)) lines.add(line);
}

function toKebab(value: string) {
  return value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
}

function alertIconForKind(kind: string) {
  switch (kind) {
    case "note":
      return Info;
    case "tip":
      return Lightbulb;
    case "warning":
      return TriangleAlert;
    case "caution":
      return CircleAlert;
    default:
      return BadgeAlert;
  }
}

function calloutTitle(kind: string) {
  const titles: Record<string, string> = {
    caution: "Caution",
    important: "Important",
    note: "Note",
    tip: "Tip",
    warning: "Warning",
  };
  return titles[kind] ?? "Note";
}

function linkKindForHref(href: string) {
  if (href.startsWith("#")) return "fragment";
  if (href.startsWith("/")) return "root";
  if (/^mailto:/i.test(href)) return "email";
  if (/^(?:https?:)?\/\//i.test(href)) return "external";
  return "relative";
}

function linkFormForHref({ href, text }: { href: string; text: string }) {
  if (/^mailto:/i.test(href) && href.slice("mailto:".length) === text) {
    return "email-autolink";
  }
  if (
    /^https?:\/\/www\./i.test(href) &&
    href.replace(/^https?:\/\//i, "") === text
  ) {
    return "autolink";
  }
  if (href === text && /^(?:https?:)?\/\//i.test(href)) return "autolink";
  return "inline";
}

function footnoteLabelForLink({
  href,
  label,
  text,
}: {
  href: string;
  label: unknown;
  text: string;
}) {
  const display = footnoteDisplayForHref(href, text);
  if (/^#(?:user-content-)?fnref-/i.test(href)) {
    return "Back to footnote reference ↩";
  }
  if (/^#(?:user-content-)?fn-/i.test(href)) {
    return `Footnote${display ? ` ${display}` : ""}`;
  }
  if (typeof label === "string" && label) return label;
  return undefined;
}

function footnoteDisplayForHref(href: string, text: string) {
  const visibleText = text.trim().replace(/^\[|\]$/g, "");
  if (/^\d+[a-z]?$/i.test(visibleText)) return visibleText;

  const match = /^#(?:user-content-)?fn(?:ref)?-([A-Za-z0-9_-]+)/i.exec(href);
  if (!match) return "";
  return match[1]!.replace(/-/g, " ");
}

function reactNodeText(node: React.ReactNode): string {
  if (typeof node === "string" || typeof node === "number") return String(node);
  if (Array.isArray(node)) return node.map(reactNodeText).join("");
  return "";
}

function hasDescendantElement(
  element: MarkdownHastElement | null,
  tagName: string,
): boolean {
  if (!element) return false;
  return element.children.some((child) => {
    const childElement = readHastElement(child);
    return (
      childElement?.tagName === tagName ||
      hasDescendantElement(childElement, tagName)
    );
  });
}

function isWhitespaceText(node: MarkdownHastNode) {
  return (
    node.type === "text" &&
    typeof node.value === "string" &&
    node.value.trim() === ""
  );
}

function extractHastText(element: MarkdownHastElement | null): string {
  return extractHastNodeText(element);
}

function extractHastNodeText(
  node: MarkdownHastNode | null | undefined,
): string {
  if (!node) return "";
  if (node.type === "text" && typeof node.value === "string") return node.value;
  const element = readHastElement(node);
  if (!element) return "";
  return element.children.map(extractHastNodeText).join("");
}

type MarkdownTableModel = {
  caption: MarkdownHastElement | null;
  captionText: string;
  cellCount: number;
  columnCount: number;
  rows: MarkdownTableRowModel[];
  tsv: string;
};

type MarkdownTableRowModel = {
  cells: MarkdownTableCellModel[];
  key: string;
  rowIndex: number;
};

type MarkdownTableCellModel = {
  align: MarkdownTableCellAlign | null;
  children: readonly MarkdownHastNode[];
  columnIndex: number;
  headerId: string | null;
  headers: string | null;
  isHeader: boolean;
  key: string;
  text: string;
};

type MarkdownTableCellAlign = "center" | "char" | "justify" | "left" | "right";

type MarkdownTableFindEntry = {
  key: string;
  rowIndex: number;
  text: string;
};

function MarkdownTable({ node, style, ...props }: any) {
  const table = readHastElement(node);
  const model = React.useMemo(() => readMarkdownTableModel(table), [table]);
  if (model && shouldVirtualizeMarkdownTable(model)) {
    return <MarkdownVirtualTable model={model} style={style} />;
  }

  const ariaColumnCount = model?.columnCount ?? tableColumnCount(table);
  const columnCount = ariaColumnCount ?? 0;
  return (
    <div
      aria-label="Markdown table"
      className="group relative my-4 overflow-hidden rounded-lg border"
      data-markdown-table-region=""
      role="region"
      tabIndex={0}
      onKeyDown={handleHorizontalScrollKeyDown}
    >
      <TableCopyButton copyText={model?.tsv} />
      <div className="overflow-x-auto" data-markdown-table-scroll="">
        <table
          {...props}
          aria-colcount={ariaColumnCount}
          aria-rowcount={model?.rows.length ?? tableRowCount(table)}
          className="w-full border-collapse text-[0.85em]"
          data-markdown-table=""
          style={{
            ...style,
            minWidth: markdownTableMinWidth(columnCount, style?.minWidth),
          }}
        />
      </div>
    </div>
  );
}

function MarkdownVirtualTable({
  model,
  style,
}: {
  model: MarkdownTableModel;
  style?: React.CSSProperties;
}) {
  const scrollRef = React.useRef<HTMLDivElement | null>(null);
  const scrollFrameRef = React.useRef<number | null>(null);
  const [viewport, setViewport] = React.useState({
    height: MARKDOWN_TABLE_VIRTUALIZED_VIEWPORT_HEIGHT_PX,
    scrollTop: 0,
  });
  const visibleWindow = React.useMemo(
    () =>
      markdownVirtualTableVisibleWindow({
        rowCount: model.rows.length,
        scrollTop: viewport.scrollTop,
        viewportHeight: viewport.height,
      }),
    [model.rows.length, viewport.height, viewport.scrollTop],
  );
  const visibleRows = React.useMemo(
    () => model.rows.slice(visibleWindow.start, visibleWindow.end),
    [model.rows, visibleWindow.end, visibleWindow.start],
  );
  const findEntries = React.useMemo(
    () => markdownTableFindEntries(model.rows),
    [model.rows],
  );
  const updateViewport = React.useCallback(() => {
    const element = scrollRef.current;
    if (!element) return;
    setViewport({
      height:
        element.clientHeight || MARKDOWN_TABLE_VIRTUALIZED_VIEWPORT_HEIGHT_PX,
      scrollTop: element.scrollTop,
    });
  }, []);
  const handleScroll = React.useCallback(() => {
    if (scrollFrameRef.current != null) return;
    scrollFrameRef.current = requestAnimationFrame(() => {
      scrollFrameRef.current = null;
      updateViewport();
    });
  }, [updateViewport]);
  const revealRow = React.useCallback((rowIndex: number) => {
    const top = rowIndex * MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX;
    scrollRef.current?.scrollTo?.({ behavior: "auto", top });
    if (scrollRef.current) scrollRef.current.scrollTop = top;
    setViewport((current) => ({
      height: scrollRef.current?.clientHeight || current.height,
      scrollTop: top,
    }));
  }, []);

  useKeyedLayoutEffect(
    joinEffectKey(["markdown-virtual-table-viewport", model.rows.length]),
    () => {
      updateViewport();
      return () => {
        if (scrollFrameRef.current != null) {
          cancelAnimationFrame(scrollFrameRef.current);
        }
      };
    },
  );

  return (
    <div
      aria-label="Markdown table"
      className="group relative my-4 overflow-hidden rounded-lg border"
      data-markdown-table-region=""
      data-markdown-table-virtualized-region=""
      role="region"
      tabIndex={0}
      onKeyDown={handleHorizontalScrollKeyDown}
    >
      <TableCopyButton copyText={model.tsv} />
      <div
        ref={scrollRef}
        className="max-h-[560px] overflow-auto"
        data-markdown-table-scroll=""
        onScroll={handleScroll}
      >
        <table
          aria-colcount={model.columnCount}
          aria-rowcount={model.rows.length}
          className="w-full border-collapse text-[0.85em]"
          data-markdown-table=""
          data-markdown-table-mounted-rows={visibleRows.length}
          data-markdown-table-total-rows={model.rows.length}
          data-markdown-table-virtualized=""
          style={{
            ...style,
            minWidth: markdownTableMinWidth(model.columnCount, style?.minWidth),
          }}
        >
          {model.caption ? (
            <caption className="text-muted-foreground bg-background sticky top-0 z-20 caption-top px-3 py-2 text-left font-medium">
              {renderHastChildren(model.caption.children)}
            </caption>
          ) : null}
          <tbody>
            {visibleWindow.start > 0 ? (
              <MarkdownVirtualTableSpacerRow
                columnCount={model.columnCount}
                height={
                  visibleWindow.start * MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX
                }
              />
            ) : null}
            {visibleRows.map((row) => (
              <MarkdownVirtualTableRow key={row.key} row={row} />
            ))}
            {visibleWindow.end < model.rows.length ? (
              <MarkdownVirtualTableSpacerRow
                columnCount={model.columnCount}
                height={
                  (model.rows.length - visibleWindow.end) *
                  MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX
                }
              />
            ) : null}
          </tbody>
        </table>
        <MarkdownVirtualTableNativeFindIndex
          entries={findEntries}
          revealRow={revealRow}
        />
      </div>
    </div>
  );
}

function MarkdownVirtualTableRow({ row }: { row: MarkdownTableRowModel }) {
  return (
    <tr
      aria-rowindex={row.rowIndex + 1}
      className="border-b"
      data-markdown-table-row=""
      data-pretext-table-row-index={row.rowIndex + 1}
      style={{
        height: MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX,
      }}
    >
      {row.cells.map((cell) => (
        <MarkdownVirtualTableCell key={cell.key} cell={cell} />
      ))}
    </tr>
  );
}

function MarkdownVirtualTableCell({ cell }: { cell: MarkdownTableCellModel }) {
  const Cell = cell.isHeader ? "th" : "td";
  return (
    <Cell
      align={cell.align ?? undefined}
      aria-colindex={cell.columnIndex + 1}
      className={markdownTableCellClassName({
        align: cell.align,
        isHeader: cell.isHeader,
      })}
      data-markdown-table-cell=""
      data-pretext-table-column-index={cell.columnIndex + 1}
      headers={cell.isHeader ? undefined : (cell.headers ?? undefined)}
      id={cell.headerId ?? undefined}
      scope={cell.isHeader ? "col" : undefined}
    >
      <span className="block truncate">
        {renderHastChildren(cell.children)}
      </span>
    </Cell>
  );
}

function MarkdownVirtualTableSpacerRow({
  columnCount,
  height,
}: {
  columnCount: number;
  height: number;
}) {
  return (
    <tr aria-hidden="true" data-markdown-table-spacer-row="">
      <td colSpan={columnCount} style={{ height, padding: 0 }} />
    </tr>
  );
}

function MarkdownVirtualTableNativeFindIndex({
  entries,
  revealRow,
}: {
  entries: readonly MarkdownTableFindEntry[];
  revealRow: (rowIndex: number) => void;
}) {
  return (
    <div
      aria-hidden="true"
      className="pointer-events-none absolute top-0 left-0 h-px w-px overflow-hidden opacity-0"
      data-markdown-table-native-find-index=""
      data-markdown-table-native-find-entries={entries.length}
    >
      {entries.map((entry) => (
        <MarkdownVirtualTableNativeFindEntry
          key={entry.key}
          entry={entry}
          revealRow={revealRow}
        />
      ))}
    </div>
  );
}

function MarkdownVirtualTableNativeFindEntry({
  entry,
  revealRow,
}: {
  entry: MarkdownTableFindEntry;
  revealRow: (rowIndex: number) => void;
}) {
  const ref = React.useRef<HTMLSpanElement | null>(null);

  useKeyedLayoutEffect(joinEffectKey([entry.key, entry.rowIndex]), () => {
    const element = ref.current;
    if (!element) return;
    element.setAttribute("hidden", "until-found");

    const handleBeforeMatch = () => {
      revealRow(entry.rowIndex);
      requestAnimationFrame(() => {
        element.setAttribute("hidden", "until-found");
      });
    };

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

  return (
    <span
      ref={ref}
      className="absolute top-0 left-0 block h-px w-px overflow-hidden whitespace-pre"
      data-markdown-table-native-find-entry=""
      data-markdown-table-native-find-row={entry.rowIndex + 1}
    >
      {entry.text || " "}
    </span>
  );
}

function readMarkdownTableModel(
  table: MarkdownHastElement | null,
): MarkdownTableModel | null {
  if (!table) return null;
  const caption = table.children
    .map(readHastElement)
    .find(
      (child): child is MarkdownHastElement => child?.tagName === "caption",
    );
  const rows = tableRows(table).map((row, rowIndex) =>
    readMarkdownTableRowModel(row, rowIndex),
  );
  if (!rows.length) return null;

  const columnCount = rows.reduce(
    (max, row) => Math.max(max, row.cells.length),
    0,
  );
  const cellCount = rows.reduce((sum, row) => sum + row.cells.length, 0);
  return {
    caption: caption ?? null,
    captionText: normalizeTableCellText(extractHastNodeText(caption)),
    cellCount,
    columnCount,
    rows,
    tsv: serializeMarkdownTableModelAsTsv(rows),
  };
}

function readMarkdownTableRowModel(
  row: MarkdownHastElement,
  rowIndex: number,
): MarkdownTableRowModel {
  return {
    cells: tableCells(row).map((cell, columnIndex) =>
      readMarkdownTableCellModel(cell, rowIndex, columnIndex),
    ),
    key: `row-${rowIndex}`,
    rowIndex,
  };
}

function readMarkdownTableCellModel(
  cell: MarkdownHastElement,
  rowIndex: number,
  columnIndex: number,
): MarkdownTableCellModel {
  return {
    align: readMarkdownTableCellAlign(cell.properties?.align),
    children: cell.children,
    columnIndex,
    headerId: readStringProperty(cell.properties?.id),
    headers: readStringProperty(cell.properties?.headers),
    isHeader: cell.tagName === "th",
    key: `cell-${rowIndex}-${columnIndex}`,
    text: normalizeTableCellText(extractHastNodeText(cell)),
  };
}

function readMarkdownTableCellAlign(
  value: unknown,
): MarkdownTableCellAlign | null {
  return value === "center" ||
    value === "char" ||
    value === "justify" ||
    value === "left" ||
    value === "right"
    ? value
    : null;
}

function shouldVirtualizeMarkdownTable(model: MarkdownTableModel) {
  return (
    model.rows.length >= MARKDOWN_TABLE_VIRTUALIZATION_ROW_THRESHOLD ||
    model.cellCount >= MARKDOWN_TABLE_VIRTUALIZATION_CELL_THRESHOLD
  );
}

function markdownVirtualTableVisibleWindow({
  rowCount,
  scrollTop,
  viewportHeight,
}: {
  rowCount: number;
  scrollTop: number;
  viewportHeight: number;
}) {
  const visibleStart = Math.max(
    0,
    Math.floor(scrollTop / MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX) -
      MARKDOWN_TABLE_VIRTUALIZED_OVERSCAN_ROWS,
  );
  const visibleCount =
    Math.ceil(viewportHeight / MARKDOWN_TABLE_VIRTUALIZED_ROW_HEIGHT_PX) +
    MARKDOWN_TABLE_VIRTUALIZED_OVERSCAN_ROWS * 2;
  return {
    end: Math.min(rowCount, visibleStart + Math.max(1, visibleCount)),
    start: visibleStart,
  };
}

function markdownTableFindEntries(
  rows: readonly MarkdownTableRowModel[],
): MarkdownTableFindEntry[] {
  const entries: MarkdownTableFindEntry[] = [];
  for (
    let rowIndex = 0;
    rowIndex < rows.length;
    rowIndex += MARKDOWN_TABLE_NATIVE_FIND_ROWS_PER_ENTRY
  ) {
    const chunkRows = rows.slice(
      rowIndex,
      rowIndex + MARKDOWN_TABLE_NATIVE_FIND_ROWS_PER_ENTRY,
    );
    entries.push({
      key: `table-find-${rowIndex}`,
      rowIndex,
      text: chunkRows
        .map((row) => row.cells.map((cell) => cell.text).join("\t"))
        .join("\n"),
    });
  }
  return entries;
}

function serializeMarkdownTableModelAsTsv(
  rows: readonly MarkdownTableRowModel[],
) {
  return rows
    .map((row) => row.cells.map((cell) => cell.text).join("\t"))
    .join("\n");
}

function markdownTableMinWidth(
  columnCount: number,
  fallback: React.CSSProperties["minWidth"],
) {
  return columnCount >= 4 ? `${Math.max(640, columnCount * 160)}px` : fallback;
}

function markdownTableCellClassName({
  align,
  isHeader,
}: {
  align: string | null;
  isHeader: boolean;
}) {
  return [
    "min-w-0 border-r px-3 py-1.5 last:border-r-0",
    isHeader
      ? "border-b bg-muted/55 text-left font-medium"
      : "border-t align-top",
    align === "center" ? "text-center" : "",
    align === "right" ? "text-right tabular-nums" : "",
  ]
    .filter(Boolean)
    .join(" ");
}

function handleHorizontalScrollKeyDown(
  event: React.KeyboardEvent<HTMLElement>,
) {
  const element =
    event.currentTarget.querySelector<HTMLElement>(
      "[data-markdown-table-scroll]",
    ) ?? event.currentTarget;
  if (event.key === "ArrowRight") {
    element.scrollLeft += 50;
    event.preventDefault();
  } else if (event.key === "ArrowLeft") {
    element.scrollLeft -= 50;
    event.preventDefault();
  } else if (event.key === "End") {
    element.scrollLeft = Math.max(0, element.scrollWidth - element.clientWidth);
    event.preventDefault();
  } else if (event.key === "Home") {
    element.scrollLeft = 0;
    event.preventDefault();
  }
}

function tableRowCount(table: MarkdownHastElement | null) {
  if (!table) return undefined;
  return countDescendantElements(table, "tr");
}

function tableColumnCount(table: MarkdownHastElement | null) {
  const firstRow = findDescendantElement(table, "tr");
  if (!firstRow) return undefined;
  return firstRow.children.filter((child) => {
    const element = readHastElement(child);
    return element?.tagName === "td" || element?.tagName === "th";
  }).length;
}

function tableRows(element: MarkdownHastElement) {
  const rows: MarkdownHastElement[] = [];
  for (const child of element.children) {
    const childElement = readHastElement(child);
    if (!childElement) continue;
    if (childElement.tagName === "tr") {
      rows.push(childElement);
    } else if (
      childElement.tagName === "thead" ||
      childElement.tagName === "tbody" ||
      childElement.tagName === "tfoot"
    ) {
      rows.push(...tableRows(childElement));
    }
  }
  return rows;
}

function tableCells(row: MarkdownHastElement) {
  return row.children
    .map(readHastElement)
    .filter(
      (child): child is MarkdownHastElement =>
        child?.tagName === "td" || child?.tagName === "th",
    );
}

function countDescendantElements(
  element: MarkdownHastElement,
  tagName: string,
): number {
  return element.children.reduce((sum, child) => {
    const childElement = readHastElement(child);
    if (!childElement) return sum;
    return (
      sum +
      (childElement.tagName === tagName ? 1 : 0) +
      countDescendantElements(childElement, tagName)
    );
  }, 0);
}

function findDescendantElement(
  element: MarkdownHastElement | null,
  tagName: string,
): MarkdownHastElement | null {
  if (!element) return null;
  for (const child of element.children) {
    const childElement = readHastElement(child);
    if (!childElement) continue;
    if (childElement.tagName === tagName) return childElement;
    const found = findDescendantElement(childElement, tagName);
    if (found) return found;
  }
  return null;
}

// A subtle hover copy affordance in the table's top-right corner, replacing the
// persistent chrome bar so the table reads as a clean document table.
function TableCopyButton({ copyText }: { copyText?: string }) {
  const [copied, setCopied] = React.useState(false);
  return (
    <button
      aria-label="Copy table as TSV"
      className="bg-background/90 text-muted-foreground hover:text-foreground absolute top-2 right-2 z-10 inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs font-medium opacity-0 shadow-sm backdrop-blur-sm transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
      type="button"
      onClick={(event) => {
        copyTable(event.currentTarget, copyText);
        setCopied(true);
        window.setTimeout(() => setCopied(false), 1200);
      }}
    >
      {copied ? (
        <Check className="size-3.5" aria-hidden="true" />
      ) : (
        <Copy className="size-3.5" aria-hidden="true" />
      )}
      {copied ? "Copied" : "Copy"}
    </button>
  );
}

function copyTable(button: HTMLButtonElement, copyText?: string) {
  const region = button.closest('[role="region"]');

  const selection = window.getSelection();
  const selectedText =
    selection &&
    selection.rangeCount > 0 &&
    region?.contains(selection.anchorNode)
      ? selection.toString()
      : "";
  if (selectedText.trim()) {
    void navigator.clipboard?.writeText(selectedText.trim());
    return;
  }

  if (copyText != null) {
    void navigator.clipboard?.writeText(copyText);
    return;
  }

  const table = region?.querySelector("table");
  if (!table) return;
  void navigator.clipboard?.writeText(serializeTableAsTsv(table));
}

function serializeTableAsTsv(table: HTMLTableElement) {
  return Array.from(table.querySelectorAll("tr"))
    .map((row) =>
      Array.from(row.querySelectorAll("th,td"))
        .map((cell) => normalizeTableCellText(cell.textContent ?? ""))
        .join("\t"),
    )
    .join("\n");
}

function normalizeTableCellText(value: string) {
  return value.trim().replace(/[\t\r\n ]+/g, " ");
}

function copyHeadingLink(id: string) {
  const base = `${window.location.origin}${window.location.pathname}${window.location.search}`;
  void navigator.clipboard?.writeText(`${base}#${id}`);
}

function readComponentProps(value: string): Record<string, unknown> {
  try {
    const parsed = JSON.parse(value);
    return parsed && typeof parsed === "object"
      ? (parsed as Record<string, unknown>)
      : {};
  } catch {
    return {};
  }
}

function readOptionalString(value: unknown) {
  return typeof value === "string" && value ? value : undefined;
}

function readOptionalNumber(value: unknown) {
  const parsed =
    typeof value === "number"
      ? value
      : typeof value === "string"
        ? Number(value)
        : NaN;
  return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
}

function readOptionalBoolean(value: unknown) {
  if (typeof value === "boolean") return value;
  if (typeof value !== "string") return undefined;
  if (value === "true") return true;
  if (value === "false") return false;
  return undefined;
}
registry/new-york-v4/ui/markdown-greenfield-virtualizer.ts
"use client";

import type { MarkdownGreenfieldChunk } from "./markdown-greenfield-document";
import type { MarkdownGreenfieldChunkFrame } from "./markdown-greenfield-layout";

export type MarkdownGreenfieldScrollAnchor = {
  chunkId: string;
  /** Negative when the viewport is in document padding before the chunk. */
  offsetWithinChunkPx: number;
  chunkHeightPx: number;
};

export type MarkdownGreenfieldVisibleRange = {
  start: number;
  end: number;
};

export type MarkdownGreenfieldVisibleProjection = {
  frameIds: readonly string[];
  range: MarkdownGreenfieldVisibleRange;
};

export function getMarkdownGreenfieldVisibleRange({
  frames,
  overscanPx,
  scrollTop,
  viewportHeight,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  overscanPx: number;
  scrollTop: number;
  viewportHeight: number;
}): MarkdownGreenfieldVisibleRange {
  if (!frames.length) return { start: 0, end: 0 };
  const start = firstFrameWithBottomAfter(
    frames,
    Math.max(0, scrollTop - overscanPx),
  );
  const end = firstFrameWithTopAtOrAfter(
    frames,
    scrollTop + viewportHeight + overscanPx,
  );
  return { start, end: Math.max(start + 1, end) };
}

export function createMarkdownGreenfieldVisibleProjection({
  frames,
  range,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  range: MarkdownGreenfieldVisibleRange;
}): MarkdownGreenfieldVisibleProjection {
  return {
    frameIds: frames.slice(range.start, range.end).map((frame) => frame.id),
    range,
  };
}

export function getMarkdownGreenfieldVisibleProjection({
  frames,
  overscanPx,
  scrollTop,
  viewportHeight,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  overscanPx: number;
  scrollTop: number;
  viewportHeight: number;
}): MarkdownGreenfieldVisibleProjection {
  return createMarkdownGreenfieldVisibleProjection({
    frames,
    range: getMarkdownGreenfieldVisibleRange({
      frames,
      overscanPx,
      scrollTop,
      viewportHeight,
    }),
  });
}

export function getMarkdownGreenfieldProjectedVisibleFrames({
  frames,
  projection,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  projection: MarkdownGreenfieldVisibleProjection;
}) {
  const rangedFrames = frames.slice(
    projection.range.start,
    projection.range.end,
  );
  if (frameIdsEqual(rangedFrames, projection.frameIds)) return rangedFrames;

  const framesById = new Map(frames.map((frame) => [frame.id, frame]));
  return projection.frameIds.flatMap((frameId) => {
    const frame = framesById.get(frameId);
    return frame ? [frame] : [];
  });
}

export function isMarkdownGreenfieldVisibleProjectionSameWindow({
  frames,
  projection,
  range,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  projection: MarkdownGreenfieldVisibleProjection;
  range: MarkdownGreenfieldVisibleRange;
}) {
  const nextLength = range.end - range.start;
  if (projection.frameIds.length !== nextLength) return false;
  for (let offset = 0; offset < nextLength; offset += 1) {
    if (frames[range.start + offset]?.id !== projection.frameIds[offset]) {
      return false;
    }
  }
  return true;
}

export function getMarkdownGreenfieldVisibleFrames({
  frames,
  overscanPx,
  scrollTop,
  viewportHeight,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  overscanPx: number;
  scrollTop: number;
  viewportHeight: number;
}) {
  const range = getMarkdownGreenfieldVisibleRange({
    frames,
    overscanPx,
    scrollTop,
    viewportHeight,
  });
  return frames.slice(range.start, range.end);
}

export function getMarkdownGreenfieldScrollAnchor({
  frames,
  scrollTop,
}: {
  frames: readonly MarkdownGreenfieldChunkFrame[];
  scrollTop: number;
}): MarkdownGreenfieldScrollAnchor | null {
  if (!frames.length) return null;
  const frame =
    frames[
      Math.min(firstFrameWithBottomAfter(frames, scrollTop), frames.length - 1)
    ];
  if (!frame) return null;

  return {
    chunkId: frame.id,
    chunkHeightPx: frame.height,
    offsetWithinChunkPx: scrollTop - frame.top,
  };
}

export function resolveMarkdownGreenfieldScrollAnchor({
  anchor,
  frames,
}: {
  anchor: MarkdownGreenfieldScrollAnchor;
  frames: readonly MarkdownGreenfieldChunkFrame[];
}) {
  const frame = frames.find((item) => item.id === anchor.chunkId);
  if (!frame) return null;
  if (anchor.offsetWithinChunkPx < 0) {
    return Math.max(0, frame.top + anchor.offsetWithinChunkPx);
  }
  // Preserve the position *within* the anchored chunk proportionally. When the
  // chunk keeps its height this reduces to the original pixel offset; when an
  // over-estimated chunk shrinks after measurement, the viewport stays at the
  // same relative content instead of snapping toward the chunk's top (which is
  // what `min(offset, height - 1)` did, yanking the reader upward).
  const fraction =
    anchor.chunkHeightPx > 0
      ? Math.min(
          1,
          Math.max(0, anchor.offsetWithinChunkPx / anchor.chunkHeightPx),
        )
      : 0;
  return frame.top + fraction * frame.height;
}

export function getMarkdownGreenfieldScrollTopForLineRange({
  chunks,
  frames,
  preferredChunkId,
  range,
  viewportHeight,
}: {
  chunks: readonly Pick<
    MarkdownGreenfieldChunk,
    "id" | "index" | "sourceEndLine" | "sourceStartLine"
  >[];
  frames: readonly MarkdownGreenfieldChunkFrame[];
  preferredChunkId?: string | null;
  range: { end: number; start: number } | null;
  viewportHeight: number;
}) {
  if (!range) return null;
  const preferredChunk =
    preferredChunkId == null
      ? null
      : chunks.find((item) => item.id === preferredChunkId);
  const chunk =
    preferredChunk ??
    chunks.find(
      (item) =>
        item.sourceStartLine <= range.start &&
        item.sourceEndLine >= range.start,
    ) ??
    chunks[0];
  const frame = chunk
    ? frames.find((candidate) => candidate.index === chunk.index)
    : null;
  if (!frame) return null;

  const lineCount = Math.max(
    1,
    frame.sourceEndLine - frame.sourceStartLine + 1,
  );
  const lineOffset =
    (Math.max(0, range.start - frame.sourceStartLine) / lineCount) *
      frame.height || 0;
  return Math.max(0, frame.top + lineOffset - viewportHeight * 0.25);
}

export function getMarkdownGreenfieldSourceLineForScrollTop({
  chunks,
  frames,
  scrollTop,
}: {
  chunks: readonly Pick<
    MarkdownGreenfieldChunk,
    "index" | "sourceEndLine" | "sourceStartLine"
  >[];
  frames: readonly MarkdownGreenfieldChunkFrame[];
  scrollTop: number;
}) {
  if (!frames.length) return 1;
  const frame =
    frames[
      Math.min(firstFrameWithBottomAfter(frames, scrollTop), frames.length - 1)
    ];
  if (!frame) return 1;
  const chunk = chunks.find((item) => item.index === frame.index);
  if (!chunk) return frame.sourceStartLine;
  const lineCount = Math.max(
    1,
    chunk.sourceEndLine - chunk.sourceStartLine + 1,
  );
  const ratio = Math.max(
    0,
    Math.min(1, (scrollTop - frame.top) / frame.height),
  );
  return (
    chunk.sourceStartLine +
    Math.min(lineCount - 1, Math.floor(ratio * lineCount))
  );
}

function firstFrameWithBottomAfter(
  frames: readonly MarkdownGreenfieldChunkFrame[],
  y: number,
) {
  let low = 0;
  let high = frames.length;
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if (frames[mid]!.bottom > y) high = mid;
    else low = mid + 1;
  }
  return low;
}

function firstFrameWithTopAtOrAfter(
  frames: readonly MarkdownGreenfieldChunkFrame[],
  y: number,
) {
  let low = 0;
  let high = frames.length;
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if (frames[mid]!.top >= y) high = mid;
    else low = mid + 1;
  }
  return low;
}

function frameIdsEqual(
  frames: readonly MarkdownGreenfieldChunkFrame[],
  frameIds: readonly string[],
) {
  if (frames.length !== frameIds.length) return false;
  for (let index = 0; index < frames.length; index += 1) {
    if (frames[index]?.id !== frameIds[index]) return false;
  }
  return true;
}
registry/new-york-v4/ui/markdown-hast-types.ts
"use client";

export type MarkdownPoint = {
  column?: number;
  line?: number;
  offset?: number;
};

export type MarkdownPosition = {
  end?: MarkdownPoint;
  start?: MarkdownPoint;
};

export type MarkdownHastText = {
  position?: MarkdownPosition;
  type: "text";
  value: string;
};

export type MarkdownHastElement = {
  children: MarkdownHastNode[];
  position?: MarkdownPosition;
  properties?: Record<string, unknown>;
  tagName: string;
  type: "element";
};

export type MarkdownHastRoot = {
  children: MarkdownHastNode[];
  type: "root";
};

export type MarkdownHastNode =
  | MarkdownHastElement
  | MarkdownHastText
  | {
      children?: MarkdownHastNode[];
      position?: MarkdownPosition;
      type: string;
      value?: unknown;
    };
registry/new-york-v4/ui/markdown-source-map.ts
"use client";

import type { MarkdownPosition } from "./markdown-hast-types";

export interface MarkdownSourceRange {
  endLine: number;
  endOffset: number;
  startLine: number;
  startOffset: number;
}

export interface MarkdownSourceMap {
  lineCount: number;
  lineStarts: readonly number[];
  text: string;
}

export function createMarkdownSourceMap(text: string): MarkdownSourceMap {
  const lineStarts = [0];
  for (const match of text.matchAll(/\r\n|[\n\r\u2028\u2029]/g)) {
    lineStarts.push(match.index + match[0].length);
  }

  return {
    lineCount: Math.max(1, lineStarts.length),
    lineStarts,
    text,
  };
}

export function markdownSourceRangeFromPosition({
  position,
  sourceMap,
}: {
  position: MarkdownPosition | null | undefined;
  sourceMap: MarkdownSourceMap;
}): MarkdownSourceRange | null {
  if (!position?.start || !position.end) return null;

  const startLine = clampLine(position.start.line, sourceMap);
  const endLine = clampLine(position.end.line, sourceMap);
  const startOffset =
    typeof position.start.offset === "number"
      ? clampOffset(position.start.offset, sourceMap.text.length)
      : offsetFromLineColumn({
          column: position.start.column,
          line: startLine,
          sourceMap,
        });
  const endOffset =
    typeof position.end.offset === "number"
      ? clampOffset(position.end.offset, sourceMap.text.length)
      : offsetFromLineColumn({
          column: position.end.column,
          line: endLine,
          sourceMap,
        });

  return {
    endLine: Math.max(startLine, endLine),
    endOffset: Math.max(startOffset, endOffset),
    startLine,
    startOffset,
  };
}

export function markdownSourceTextForRange({
  range,
  sourceMap,
}: {
  range: MarkdownSourceRange | null;
  sourceMap: MarkdownSourceMap;
}) {
  if (!range) return "";
  return sourceMap.text.slice(range.startOffset, range.endOffset);
}

export function markdownRangesIntersect({
  a,
  b,
}: {
  a: MarkdownSourceRange | null;
  b: MarkdownSourceRange | null;
}) {
  if (!a || !b) return false;
  return a.startOffset < b.endOffset && a.endOffset > b.startOffset;
}

export function markdownLineRangeIntersects({
  endLine,
  range,
  startLine,
}: {
  endLine: number;
  range: { end: number; start: number } | null;
  startLine: number;
}) {
  if (!range) return false;
  return startLine <= range.end && endLine >= range.start;
}

function offsetFromLineColumn({
  column,
  line,
  sourceMap,
}: {
  column: number | undefined;
  line: number;
  sourceMap: MarkdownSourceMap;
}) {
  const lineStart = sourceMap.lineStarts[line - 1] ?? 0;
  return clampOffset(
    lineStart + Math.max(0, (column ?? 1) - 1),
    sourceMap.text.length,
  );
}

function clampLine(line: number | undefined, sourceMap: MarkdownSourceMap) {
  if (!Number.isFinite(line)) return 1;
  return Math.max(1, Math.min(sourceMap.lineCount, Math.trunc(line ?? 1)));
}

function clampOffset(offset: number, textLength: number) {
  return Math.max(0, Math.min(textLength, offset));
}
registry/new-york-v4/ui/markdown-unified-pipeline.ts
"use client";

import rehypeKatex from "rehype-katex";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeSlug from "rehype-slug";
import remarkBreaks from "remark-breaks";
import remarkDirective from "remark-directive";
import remarkGemoji from "remark-gemoji";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkSmartypants from "remark-smartypants";
import { unified } from "unified";
import { VFile } from "vfile";

import type {
  MarkdownHastElement,
  MarkdownHastNode,
  MarkdownHastRoot,
} from "./markdown-hast-types";
import { createMarkdownSourceMap } from "./markdown-source-map";

export type MarkdownUnifiedDocument = {
  hast: MarkdownHastRoot;
  mdast: MarkdownMdastRoot;
  messages: MarkdownUnifiedMessage[];
  sourceMap: ReturnType<typeof createMarkdownSourceMap>;
};

export type MarkdownUnifiedMessage = {
  column?: number;
  fatal?: boolean | null;
  line?: number;
  reason: string;
  ruleId?: string | null;
  source?: string | null;
};

export type MarkdownUnifiedOptions = {
  gfm?: MarkdownGfmOptions;
};

export const MARKDOWN_REMARK_PLUGINS = [
  "remark-parse",
  "remark-directive",
  "remark-gfm",
  "remark-breaks",
  "remark-math",
  "remark-gemoji",
  "remark-markdown-prose-transforms",
  "remark-smartypants",
  "remark-markdown-github-alerts",
  "remark-markdown-definition-lists",
  "remark-markdown-components",
  "remark-markdown-code-metadata",
  "remark-markdown-trusted-images",
] as const;

export const MARKDOWN_REHYPE_PLUGINS = [
  "remark-rehype",
  "rehype-raw",
  "rehype-slug",
  "rehype-sanitize",
  "rehype-markdown-trusted-metadata",
  "rehype-markdown-safe-inputs",
  "rehype-katex",
] as const;

type MarkdownGfmOptions = {
  singleTilde?: boolean;
  stringLength?: (value: string) => number;
  tableCellPadding?: boolean;
  tablePipeAlign?: boolean;
};

type MarkdownMdastNode = {
  children?: MarkdownMdastNode[];
  data?: {
    hProperties?: Record<string, unknown>;
    [key: string]: unknown;
  };
  lang?: string;
  meta?: string;
  position?: {
    end?: {
      column?: number;
      line?: number;
      offset?: number;
    };
    start?: {
      column?: number;
      line?: number;
      offset?: number;
    };
  };
  type: string;
  value?: string;
};

type MarkdownMdastRoot = MarkdownMdastNode & {
  children: MarkdownMdastNode[];
  type: "root";
};

type MarkdownMdastParagraph = MarkdownMdastNode & {
  children: MarkdownMdastNode[];
  type: "paragraph";
};

type MarkdownComponent = {
  children?: MarkdownMdastNode[];
  name: string;
  props: Record<string, string>;
};

const GITHUB_ALERT_LABELS = {
  caution: "Caution",
  important: "Important",
  note: "Note",
  tip: "Tip",
  warning: "Warning",
} as const;

const MARKDOWN_KATEX_OPTIONS = {
  maxExpand: 1000,
  maxSize: 10,
  strict: "ignore",
  trust: false,
} as const;
const MARKDOWN_KATEX_RENDER_CACHE_LIMIT = 512;
const MARKDOWN_KATEX_RENDER_CACHE_RENDERER = "rehype-katex@7.0.1";
const MARKDOWN_KATEX_RENDER_CACHE_CONFIG = markdownKatexConfigKey(
  MARKDOWN_KATEX_OPTIONS,
);

type MarkdownHastParent = MarkdownHastElement | MarkdownHastRoot;

type MarkdownKatexCacheMode = "display" | "inline";

type MarkdownKatexCacheEntry = {
  message?: MarkdownKatexCacheMessage;
  nodes: MarkdownHastNode[];
};

type MarkdownKatexCacheMessage = {
  reason: string;
  ruleId?: string | null;
  source?: string | null;
};

type MarkdownKatexMatch = {
  displayMode: boolean;
  messagePosition?: MarkdownHastElement["position"];
  mode: MarkdownKatexCacheMode;
  scope: MarkdownHastElement;
  source: string;
};

type MarkdownKatexRenderMiss = {
  endMarkerId: string;
  key: string;
  messagePosition?: MarkdownHastElement["position"];
  startMarkerId: string;
};

type MarkdownKatexRenderPlaceholder = {
  key: string;
  messagePosition?: MarkdownHastElement["position"];
  placeholderId: string;
};

type MarkdownKatexCachedMessageReplay = {
  message: MarkdownKatexCacheMessage;
  messagePosition?: MarkdownHastElement["position"];
};

type MarkdownKatexRenderState = {
  cachedMessages: MarkdownKatexCachedMessageReplay[];
  firstMissByKey: Map<string, MarkdownKatexRenderMiss>;
  firstMisses: MarkdownKatexRenderMiss[];
  nextMarkerId: number;
  placeholders: MarkdownKatexRenderPlaceholder[];
  renderedByKey: Map<string, MarkdownKatexCacheEntry>;
};

type MarkdownMdastProcessor = ReturnType<typeof createMarkdownMdastProcessor>;
type MarkdownHastProcessor = ReturnType<typeof createMarkdownHastProcessor>;

let defaultMarkdownMdastProcessor: MarkdownMdastProcessor | null = null;
let defaultMarkdownHastProcessor: MarkdownHastProcessor | null = null;
let markdownUnifiedSanitizeSchema: ReturnType<
  typeof createUncachedMarkdownUnifiedSanitizeSchema
> | null = null;
const markdownKatexRenderCache = new Map<string, MarkdownKatexCacheEntry>();
const markdownKatexRenderCacheStats = {
  hits: 0,
  misses: 0,
  sameDocumentHits: 0,
  writes: 0,
};

export function createMarkdownUnifiedDocument(
  markdown: string,
  options: MarkdownUnifiedOptions = {},
): MarkdownUnifiedDocument {
  const file = new VFile({ value: markdown });
  const mdastProcessor = getMarkdownMdastProcessor(options);
  const parsedMdast = mdastProcessor.parse(file) as MarkdownMdastRoot;
  const mdast = mdastProcessor.runSync(
    parsedMdast as never,
    file,
  ) as MarkdownMdastRoot;
  const hastProcessor = getMarkdownHastProcessor();
  const hast = hastProcessor.runSync(mdast as never, file) as MarkdownHastRoot;
  const sourceMap = createMarkdownSourceMap(markdown);
  injectMarkdownFrontmatter(hast, sourceMap.text);

  return {
    hast,
    mdast,
    messages: file.messages.map(markdownUnifiedMessageFromVFileMessage),
    sourceMap,
  };
}

function getMarkdownMdastProcessor(
  options: MarkdownUnifiedOptions,
): MarkdownMdastProcessor {
  if (options.gfm) return createMarkdownMdastProcessor(options);
  if (!defaultMarkdownMdastProcessor) {
    defaultMarkdownMdastProcessor = createMarkdownMdastProcessor({});
  }
  return defaultMarkdownMdastProcessor;
}

function createMarkdownMdastProcessor(options: MarkdownUnifiedOptions) {
  return unified()
    .use(remarkParse)
    .use(remarkDirective)
    .use(remarkGfm, options.gfm)
    .use(remarkBreaks)
    .use(remarkMath)
    .use(remarkGemoji)
    .use(remarkMarkdownProseTransforms)
    .use(remarkSmartypants)
    .use(remarkMarkdownGithubAlerts)
    .use(remarkMarkdownDefinitionLists)
    .use(remarkMarkdownComponents)
    .use(remarkMarkdownCodeMetadata)
    .use(remarkMarkdownTrustedImages);
}

function getMarkdownHastProcessor(): MarkdownHastProcessor {
  if (!defaultMarkdownHastProcessor) {
    defaultMarkdownHastProcessor = createMarkdownHastProcessor();
  }
  return defaultMarkdownHastProcessor;
}

function createMarkdownHastProcessor() {
  return unified()
    .use(remarkRehype, { allowDangerousHtml: true })
    .use(rehypeRaw)
    .use(rehypeSlug)
    .use(rehypeSanitize, getMarkdownUnifiedSanitizeSchema())
    .use(rehypeMarkdownTrustedMetadata)
    .use(rehypeMarkdownSafeInputs)
    .use(rehypeMarkdownCachedKatex, MARKDOWN_KATEX_OPTIONS);
}

function getMarkdownUnifiedSanitizeSchema() {
  if (!markdownUnifiedSanitizeSchema) {
    markdownUnifiedSanitizeSchema =
      createUncachedMarkdownUnifiedSanitizeSchema();
  }
  return markdownUnifiedSanitizeSchema;
}

function createUncachedMarkdownUnifiedSanitizeSchema() {
  return {
    ...defaultSchema,
    clobberPrefix: "user-content-",
    attributes: {
      ...defaultSchema.attributes,
      "*": [
        ...(defaultSchema.attributes?.["*"] ?? []),
        "ariaDescribedBy",
        "ariaHidden",
        "ariaLabel",
        "ariaLabelledBy",
        "className",
        "dataFootnoteBackref",
        "dataFootnoteRef",
        "dataFootnotes",
        "dataPretextComponentFallback",
        "dataPretextComponentFallbackName",
        "dataPretextComponentFallbackReason",
        "dataPretextComponentFallbackSource",
        "dataPretextComponentName",
        "dataPretextComponentProps",
        "dataPretextMarkdownImage",
        "dataPretextAlertKind",
        "dataPretextAlertTitle",
        "dataPretextCalloutKind",
        "dataPretextCalloutTitle",
        "id",
      ],
      a: [
        ...(defaultSchema.attributes?.a ?? []),
        "ariaDescribedBy",
        "dataFootnoteBackref",
        "dataFootnoteRef",
        "href",
        "id",
        "title",
      ],
      code: [
        ...(defaultSchema.attributes?.code ?? []),
        "className",
        "dataPretextCodeMeta",
      ],
      img: [
        ...(defaultSchema.attributes?.img ?? []),
        "dataPretextMarkdownImage",
      ],
      ins: [...(defaultSchema.attributes?.ins ?? []), "cite"],
      input: ["checked", "disabled", "type"],
      li: [...(defaultSchema.attributes?.li ?? []), "className"],
      ol: [...(defaultSchema.attributes?.ol ?? []), "start"],
      section: [
        ...(defaultSchema.attributes?.section ?? []),
        "className",
        "dataFootnotes",
      ],
      time: [...(defaultSchema.attributes?.time ?? []), "dateTime"],
      sup: [...(defaultSchema.attributes?.sup ?? []), "id"],
      td: [...(defaultSchema.attributes?.td ?? []), "align"],
      th: [...(defaultSchema.attributes?.th ?? []), "align"],
      q: [...(defaultSchema.attributes?.q ?? []), "cite"],
    },
    tagNames: [
      ...(defaultSchema.tagNames ?? []),
      "abbr",
      "caption",
      "cite",
      "dd",
      "details",
      "dfn",
      "dl",
      "dt",
      "figcaption",
      "figure",
      "input",
      "ins",
      "kbd",
      "mark",
      "q",
      "samp",
      "section",
      "small",
      "summary",
      "time",
      "var",
    ],
  };
}

function markdownUnifiedMessageFromVFileMessage(message: {
  column?: number;
  fatal?: boolean | null;
  line?: number;
  reason: string;
  ruleId?: string | null;
  source?: string | null;
}): MarkdownUnifiedMessage {
  return {
    column: message.column,
    fatal: message.fatal,
    line: message.line,
    reason: message.reason,
    ruleId: message.ruleId,
    source: message.source,
  };
}

function injectMarkdownFrontmatter(hast: MarkdownHastRoot, markdown: string) {
  const frontmatter = readMarkdownFrontmatter(markdown);
  if (!frontmatter) return;
  hast.children = [
    createFrontmatterElement(frontmatter),
    ...hast.children.filter((child) => {
      const line = child.position?.start?.line ?? Number.POSITIVE_INFINITY;
      return line > frontmatter.endLine;
    }),
  ];
}

function readMarkdownFrontmatter(markdown: string) {
  const lines = markdown.split(/\r\n|[\n\r\u2028\u2029]/);
  const first = lines[0]?.trim();
  const kind = first === "---" ? "yaml" : first === "+++" ? "toml" : "";
  if (!kind) return null;
  const closeIndex = lines.findIndex(
    (line, index) => index > 0 && line.trim() === first,
  );
  if (closeIndex <= 0) return null;
  const body = lines.slice(1, closeIndex);
  if (!body.some((line) => line.trim())) return null;
  return {
    body,
    endLine: closeIndex + 1,
    kind,
    raw: lines.slice(0, closeIndex + 1).join("\n"),
  };
}

function createFrontmatterElement(frontmatter: {
  body: string[];
  endLine: number;
  kind: string;
  raw: string;
}): MarkdownHastElement {
  const lastLine =
    frontmatter.raw.split(/\r\n|[\n\r\u2028\u2029]/).at(-1) ?? "";
  return {
    type: "element",
    tagName: "div",
    position: {
      start: { line: 1, column: 1, offset: 0 },
      end: {
        line: frontmatter.endLine,
        column: lastLine.length + 1,
        offset: frontmatter.raw.length,
      },
    },
    properties: {
      dataMarkdownFrontmatter: frontmatter.kind,
    },
    children: [
      {
        type: "element",
        tagName: "pre",
        properties: { dataMarkdownFrontmatterSource: "" },
        children: [
          {
            type: "element",
            tagName: "code",
            properties: { dataMarkdownFrontmatterSource: "" },
            children: [{ type: "text", value: frontmatter.raw }],
          },
        ],
      },
      {
        type: "element",
        tagName: "dl",
        properties: { dataMarkdownFrontmatterMetadata: "" },
        children: frontmatterEntries(frontmatter).flatMap(
          ([key, value, kind]) => [
            {
              type: "element" as const,
              tagName: "dt",
              properties: {},
              children: [{ type: "text" as const, value: key }],
            },
            {
              type: "element" as const,
              tagName: "dd",
              properties: { dataFrontmatterValueKind: kind },
              children: [{ type: "text" as const, value }],
            },
          ],
        ),
      },
    ],
  };
}

function frontmatterEntries(frontmatter: { body: string[]; kind: string }) {
  return frontmatter.kind === "toml"
    ? tomlFrontmatterEntries(frontmatter.body)
    : yamlFrontmatterEntries(frontmatter.body);
}

function yamlFrontmatterEntries(
  lines: string[],
): Array<[string, string, string]> {
  const entries: Array<[string, string, string]> = [];
  for (let index = 0; index < lines.length; index += 1) {
    const line = lines[index]!;
    const match = /^([A-Za-z0-9_.-]+):\s*(.*)$/.exec(line);
    if (!match) continue;
    const key = match[1]!;
    const value = match[2]!.trim();
    if (value.startsWith("{") || value.startsWith("[{")) continue;
    if (/^\[.*\]$/.test(value)) {
      entries.push([
        key,
        value
          .slice(1, -1)
          .split(",")
          .map((item) => item.trim())
          .join(", "),
        "list",
      ]);
    } else if (value) {
      entries.push([key, value.replace(/^["']|["']$/g, ""), "scalar"]);
    } else {
      const items: string[] = [];
      while (/^\s+-\s+/.test(lines[index + 1] ?? "")) {
        index += 1;
        items.push((lines[index] ?? "").replace(/^\s+-\s+/, "").trim());
      }
      if (items.length) entries.push([key, items.join(", "), "list"]);
    }
  }
  return entries;
}

function tomlFrontmatterEntries(
  lines: string[],
): Array<[string, string, string]> {
  const entries: Array<[string, string, string]> = [];
  let section = "";
  for (const line of lines) {
    const sectionMatch = /^\[([^\]]+)\]$/.exec(line.trim());
    if (sectionMatch) {
      section = `${sectionMatch[1]}.`;
      continue;
    }
    const match = /^([A-Za-z0-9_.-]+)\s*=\s*(.*)$/.exec(line.trim());
    if (!match) continue;
    const key = `${section}${match[1]}`;
    const value = match[2]!.trim();
    if (/^\[.*\]$/.test(value)) {
      entries.push([
        key,
        value
          .slice(1, -1)
          .split(",")
          .map((item) => item.trim().replace(/^["']|["']$/g, ""))
          .join(", "),
        "list",
      ]);
    } else {
      entries.push([key, value.replace(/^["']|["']$/g, ""), "scalar"]);
    }
  }
  return entries;
}

function remarkMarkdownGithubAlerts() {
  return function transform(tree: unknown) {
    for (const node of (tree as MarkdownMdastRoot).children) {
      transformGithubAlertBlockquote(node);
    }
  };
}

function remarkMarkdownProseTransforms() {
  return function transform(tree: unknown) {
    visitMarkdownMdastNodes(tree as MarkdownMdastRoot, (node) => {
      if (node.type !== "text" || typeof node.value !== "string") return;
      node.value = node.value
        .replace(/--/g, "—")
        .replace(/\.\.\./g, "…")
        .replace(/->/g, "→")
        .replace(/\b1\/2\b/g, "½");
    });
  };
}

function transformGithubAlertBlockquote(node: MarkdownMdastNode) {
  if (node.type !== "blockquote") return;

  const blockquote = node;
  const first = blockquote.children?.[0];
  if (!first || first.type !== "paragraph") return;

  const alert = readGithubAlertMarker(first as MarkdownMdastParagraph);
  if (!alert) return;

  blockquote.data = {
    ...blockquote.data,
    hProperties: {
      ...(blockquote.data?.hProperties as Record<string, unknown> | undefined),
      dataPretextAlertKind: alert.kind,
      dataPretextAlertTitle: alert.title,
    },
  };
}

function remarkMarkdownCodeMetadata() {
  return function transform(tree: unknown) {
    visitMarkdownMdastNodes(tree as MarkdownMdastRoot, (node) => {
      if (node.type !== "code" || !node.meta) return;
      node.data = {
        ...node.data,
        hProperties: {
          ...(node.data?.hProperties as Record<string, unknown> | undefined),
          dataPretextCodeMeta: node.meta,
        },
      };
    });
  };
}

function remarkMarkdownTrustedImages() {
  return function transform(tree: unknown) {
    visitMarkdownMdastNodes(tree as MarkdownMdastRoot, (node) => {
      if (node.type !== "image" && node.type !== "imageReference") return;
      node.data = {
        ...node.data,
        hProperties: {
          ...(node.data?.hProperties as Record<string, unknown> | undefined),
          dataPretextMarkdownImage: "",
        },
      };
    });
  };
}

function visitMarkdownMdastNodes(
  node: MarkdownMdastNode,
  visitor: (node: MarkdownMdastNode) => void,
) {
  visitor(node);
  for (const child of node.children ?? []) {
    visitMarkdownMdastNodes(child, visitor);
  }
}

function rehypeMarkdownCachedKatex(options: typeof MARKDOWN_KATEX_OPTIONS) {
  const renderKatex = rehypeKatex(options);

  return function transform(tree: MarkdownHastRoot, file: VFile) {
    const state: MarkdownKatexRenderState = {
      cachedMessages: [],
      firstMissByKey: new Map(),
      firstMisses: [],
      nextMarkerId: 1,
      placeholders: [],
      renderedByKey: new Map(),
    };
    prepareMarkdownKatexRenderCache(tree, state);
    const firstKatexMessageIndex = file.messages.length;
    renderKatex(tree as never, file);
    finishMarkdownKatexRenderCache(tree, file, state, firstKatexMessageIndex);
  };
}

function prepareMarkdownKatexRenderCache(
  parent: MarkdownHastParent,
  state: MarkdownKatexRenderState,
) {
  let index = 0;
  while (index < parent.children.length) {
    const child = parent.children[index]!;
    const element = readHastElement(child);
    if (!element) {
      index += 1;
      continue;
    }

    const match = readMarkdownKatexMatch(element);
    if (!match) {
      prepareMarkdownKatexRenderCache(element, state);
      index += 1;
      continue;
    }

    const key = markdownKatexRenderCacheKey(match);
    const cached = readMarkdownKatexRenderCache(key);
    if (cached) {
      parent.children.splice(index, 1, ...cloneMarkdownHastNodes(cached.nodes));
      if (cached.message) {
        state.cachedMessages.push({
          message: cached.message,
          messagePosition: match.messagePosition,
        });
      }
      markdownKatexRenderCacheStats.hits += 1;
      index += cached.nodes.length;
      continue;
    }

    if (state.firstMissByKey.has(key)) {
      const placeholder = createMarkdownKatexPlaceholder(state);
      parent.children[index] = placeholder;
      state.placeholders.push({
        key,
        messagePosition: match.messagePosition,
        placeholderId: readStringProperty(
          placeholder.properties?.dataPretextKatexCachePlaceholder,
        ),
      });
      markdownKatexRenderCacheStats.sameDocumentHits += 1;
      index += 1;
      continue;
    }

    const startMarker = createMarkdownKatexMarker(state, "start");
    const endMarker = createMarkdownKatexMarker(state, "end");
    const miss: MarkdownKatexRenderMiss = {
      endMarkerId: readStringProperty(
        endMarker.properties?.dataPretextKatexCacheMarker,
      ),
      key,
      messagePosition: match.messagePosition,
      startMarkerId: readStringProperty(
        startMarker.properties?.dataPretextKatexCacheMarker,
      ),
    };
    state.firstMissByKey.set(key, miss);
    state.firstMisses.push(miss);
    markdownKatexRenderCacheStats.misses += 1;
    parent.children.splice(index, 1, startMarker, match.scope, endMarker);
    index += 3;
  }
}

function finishMarkdownKatexRenderCache(
  tree: MarkdownHastRoot,
  file: VFile,
  state: MarkdownKatexRenderState,
  firstKatexMessageIndex: number,
) {
  const katexMessages = file.messages
    .slice(firstKatexMessageIndex)
    .filter((message) => message.source === "rehype-katex");
  const usedMessageIndexes = new Set<number>();

  for (const miss of state.firstMisses) {
    const renderedNodes = readRenderedMarkdownKatexNodes(tree, miss);
    if (!renderedNodes) continue;
    const message = readMarkdownKatexMessageForMiss({
      messages: katexMessages,
      miss,
      usedMessageIndexes,
    });
    const entry = {
      message,
      nodes: cloneMarkdownHastNodes(renderedNodes),
    };
    state.renderedByKey.set(miss.key, entry);
    writeMarkdownKatexRenderCache(miss.key, entry);
  }

  for (const placeholder of state.placeholders) {
    const cached =
      state.renderedByKey.get(placeholder.key) ??
      readMarkdownKatexRenderCache(placeholder.key);
    if (!cached) continue;
    replaceMarkdownKatexPlaceholder(
      tree,
      placeholder.placeholderId,
      cloneMarkdownHastNodes(cached.nodes),
    );
    replayMarkdownKatexCacheMessage(file, placeholder, cached.message);
  }

  for (const cachedMessage of state.cachedMessages) {
    replayMarkdownKatexCacheMessage(file, cachedMessage, cachedMessage.message);
  }
}

function readMarkdownKatexMatch(
  element: MarkdownHastElement,
): MarkdownKatexMatch | null {
  if (element.tagName === "pre") {
    const code = element.children.map(readHastElement).find((child) => {
      return (
        child?.tagName === "code" && hasArrayClassName(child, "language-math")
      );
    });
    if (code) {
      return {
        displayMode: true,
        messagePosition: code.position,
        mode: "display",
        scope: element,
        source: extractMarkdownKatexText(element),
      };
    }
  }

  const languageMath = hasArrayClassName(element, "language-math");
  const mathDisplay = hasArrayClassName(element, "math-display");
  const mathInline = hasArrayClassName(element, "math-inline");
  if (!languageMath && !mathDisplay && !mathInline) return null;

  return {
    displayMode: mathDisplay,
    messagePosition: element.position,
    mode: mathDisplay ? "display" : "inline",
    scope: element,
    source: extractMarkdownKatexText(element),
  };
}

function createMarkdownKatexMarker(
  state: MarkdownKatexRenderState,
  side: "end" | "start",
): MarkdownHastElement {
  const id = `${side}-${state.nextMarkerId}`;
  state.nextMarkerId += 1;
  return {
    type: "element",
    tagName: "span",
    properties: {
      dataPretextKatexCacheMarker: id,
      hidden: true,
    },
    children: [],
  };
}

function createMarkdownKatexPlaceholder(
  state: MarkdownKatexRenderState,
): MarkdownHastElement {
  const id = `placeholder-${state.nextMarkerId}`;
  state.nextMarkerId += 1;
  return {
    type: "element",
    tagName: "span",
    properties: {
      dataPretextKatexCachePlaceholder: id,
      hidden: true,
    },
    children: [],
  };
}

function readRenderedMarkdownKatexNodes(
  parent: MarkdownHastParent,
  miss: MarkdownKatexRenderMiss,
): MarkdownHastNode[] | null {
  const startIndex = parent.children.findIndex((child) =>
    isMarkdownKatexMarker(child, miss.startMarkerId),
  );
  if (startIndex >= 0) {
    const endIndex = parent.children.findIndex((child, index) => {
      return (
        index > startIndex && isMarkdownKatexMarker(child, miss.endMarkerId)
      );
    });
    if (endIndex < 0) return null;
    const renderedNodes = parent.children.slice(startIndex + 1, endIndex);
    parent.children.splice(
      startIndex,
      endIndex - startIndex + 1,
      ...renderedNodes,
    );
    return renderedNodes;
  }

  for (const child of parent.children) {
    const element = readHastElement(child);
    if (!element) continue;
    const renderedNodes = readRenderedMarkdownKatexNodes(element, miss);
    if (renderedNodes) return renderedNodes;
  }
  return null;
}

function replaceMarkdownKatexPlaceholder(
  parent: MarkdownHastParent,
  placeholderId: string,
  nodes: MarkdownHastNode[],
): boolean {
  const index = parent.children.findIndex((child) =>
    isMarkdownKatexPlaceholder(child, placeholderId),
  );
  if (index >= 0) {
    parent.children.splice(index, 1, ...nodes);
    return true;
  }

  for (const child of parent.children) {
    const element = readHastElement(child);
    if (
      element &&
      replaceMarkdownKatexPlaceholder(element, placeholderId, nodes)
    ) {
      return true;
    }
  }
  return false;
}

function readMarkdownKatexMessageForMiss({
  messages,
  miss,
  usedMessageIndexes,
}: {
  messages: readonly VFile["messages"][number][];
  miss: MarkdownKatexRenderMiss;
  usedMessageIndexes: Set<number>;
}): MarkdownKatexCacheMessage | undefined {
  const start = miss.messagePosition?.start;
  const exactIndex = messages.findIndex((message, index) => {
    return (
      !usedMessageIndexes.has(index) &&
      message.line === start?.line &&
      (start?.column == null || message.column === start.column)
    );
  });
  const hasPosition = start?.line != null || start?.column != null;
  if (exactIndex < 0 && hasPosition) return undefined;
  const fallbackIndex =
    exactIndex >= 0
      ? exactIndex
      : messages.findIndex((_, index) => !usedMessageIndexes.has(index));
  if (fallbackIndex < 0) return undefined;

  usedMessageIndexes.add(fallbackIndex);
  const message = messages[fallbackIndex]!;
  return {
    reason: message.reason,
    ruleId: message.ruleId,
    source: message.source,
  };
}

function replayMarkdownKatexCacheMessage(
  file: VFile,
  target: { messagePosition?: MarkdownHastElement["position"] },
  message: MarkdownKatexCacheMessage | undefined,
) {
  if (!message) return;
  file.message(message.reason, {
    place: target.messagePosition as never,
    ruleId: message.ruleId ?? undefined,
    source: message.source ?? undefined,
  });
}

function isMarkdownKatexMarker(node: MarkdownHastNode, id: string) {
  return (
    readStringProperty(
      readHastElement(node)?.properties?.dataPretextKatexCacheMarker,
    ) === id
  );
}

function isMarkdownKatexPlaceholder(node: MarkdownHastNode, id: string) {
  return (
    readStringProperty(
      readHastElement(node)?.properties?.dataPretextKatexCachePlaceholder,
    ) === id
  );
}

function readMarkdownKatexRenderCache(key: string) {
  const entry = markdownKatexRenderCache.get(key);
  if (!entry) return null;
  markdownKatexRenderCache.delete(key);
  markdownKatexRenderCache.set(key, entry);
  return entry;
}

function writeMarkdownKatexRenderCache(
  key: string,
  entry: MarkdownKatexCacheEntry,
) {
  markdownKatexRenderCache.set(key, entry);
  markdownKatexRenderCacheStats.writes += 1;
  while (markdownKatexRenderCache.size > MARKDOWN_KATEX_RENDER_CACHE_LIMIT) {
    const oldestKey = markdownKatexRenderCache.keys().next().value;
    if (!oldestKey) break;
    markdownKatexRenderCache.delete(oldestKey);
  }
}

function markdownKatexRenderCacheKey(match: MarkdownKatexMatch) {
  return JSON.stringify([
    MARKDOWN_KATEX_RENDER_CACHE_RENDERER,
    MARKDOWN_KATEX_RENDER_CACHE_CONFIG,
    match.mode,
    match.displayMode,
    match.source,
  ]);
}

function markdownKatexConfigKey(options: Record<string, unknown>) {
  return JSON.stringify(
    Object.fromEntries(
      Object.entries(options).sort(([left], [right]) =>
        left.localeCompare(right),
      ),
    ),
  );
}

function extractMarkdownKatexText(node: MarkdownHastNode): string {
  if (node.type === "text" && typeof node.value === "string") return node.value;
  const element = readHastElement(node);
  if (!element) return "";
  return element.children.map(extractMarkdownKatexText).join("");
}

function cloneMarkdownHastNodes(nodes: readonly MarkdownHastNode[]) {
  return nodes.map(cloneMarkdownHastNode);
}

function cloneMarkdownHastNode(node: MarkdownHastNode): MarkdownHastNode {
  if (node.type === "text") {
    return {
      ...node,
      position: node.position
        ? cloneMarkdownPosition(node.position)
        : undefined,
    };
  }
  const element = readHastElement(node);
  if (element) {
    return {
      ...element,
      children: cloneMarkdownHastNodes(element.children),
      position: element.position
        ? cloneMarkdownPosition(element.position)
        : undefined,
      properties: element.properties
        ? cloneMarkdownProperties(element.properties)
        : undefined,
    };
  }
  const children = "children" in node ? node.children : undefined;
  return {
    ...node,
    children: children ? cloneMarkdownHastNodes(children) : undefined,
    position: node.position ? cloneMarkdownPosition(node.position) : undefined,
  };
}

function cloneMarkdownPosition(
  position: NonNullable<MarkdownHastNode["position"]>,
) {
  return {
    end: position.end ? { ...position.end } : undefined,
    start: position.start ? { ...position.start } : undefined,
  };
}

function cloneMarkdownProperties(properties: Record<string, unknown>) {
  const next: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(properties)) {
    next[key] = Array.isArray(value) ? [...value] : value;
  }
  return next;
}

function readHastElement(node: unknown): MarkdownHastElement | null {
  return node &&
    typeof node === "object" &&
    (node as MarkdownHastElement).type === "element"
    ? (node as MarkdownHastElement)
    : null;
}

function hasArrayClassName(element: MarkdownHastElement, className: string) {
  const value = element.properties?.className;
  return Array.isArray(value) && value.includes(className);
}

function readStringProperty(value: unknown) {
  if (typeof value === "string") return value;
  if (Array.isArray(value)) return value.filter(Boolean).join(" ");
  return "";
}

export function resetMarkdownMathRenderCacheForTests() {
  markdownKatexRenderCache.clear();
  markdownKatexRenderCacheStats.hits = 0;
  markdownKatexRenderCacheStats.misses = 0;
  markdownKatexRenderCacheStats.sameDocumentHits = 0;
  markdownKatexRenderCacheStats.writes = 0;
}

export function getMarkdownMathRenderCacheStatsForTests() {
  return {
    ...markdownKatexRenderCacheStats,
    size: markdownKatexRenderCache.size,
  };
}

function readGithubAlertMarker(paragraph: MarkdownMdastParagraph) {
  const first = paragraph.children[0];
  if (!first || first.type !== "text") return null;

  const match =
    /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][\t ]*(?:\r?\n)?/i.exec(
      first.value ?? "",
    );
  if (!match) return null;

  const kind = match[1]!.toLowerCase() as keyof typeof GITHUB_ALERT_LABELS;
  first.value = (first.value ?? "").slice(match[0].length);
  if (!first.value) {
    paragraph.children.shift();
  }

  return {
    kind,
    title: GITHUB_ALERT_LABELS[kind],
  };
}

function remarkMarkdownComponents() {
  return function transform(tree: unknown, file: VFile) {
    transformMarkdownComponentChildren(tree as MarkdownMdastRoot, file);
  };
}

function remarkMarkdownDefinitionLists() {
  return function transform(tree: unknown) {
    const root = tree as MarkdownMdastRoot;
    const nextChildren: MarkdownMdastNode[] = [];
    for (const child of root.children) {
      const definitionList = markdownDefinitionListFromParagraph(child);
      nextChildren.push(definitionList ?? child);
    }
    root.children = nextChildren;
  };
}

function markdownDefinitionListFromParagraph(
  node: MarkdownMdastNode,
): MarkdownMdastNode | null {
  if (node.type !== "paragraph" || !node.children?.length) return null;

  const lines = splitMdastParagraphLines(node.children);
  if (lines.length < 2) return null;
  const term = trimMdastLine(lines[0] ?? []);
  const definitions = lines
    .slice(1)
    .map(trimDefinitionLine)
    .filter(
      (definition): definition is MarkdownMdastNode[] => definition != null,
    );
  if (!term.length || !definitions.length) return null;

  return {
    type: "list",
    data: {
      hName: "dl",
      hProperties: {
        dataPretextDefinitionList: "",
      },
    },
    children: [
      {
        type: "listItem",
        data: {
          hName: "dt",
          hProperties: {
            dataPretextDefinitionTerm: "",
          },
        },
        children: term,
      },
      ...definitions.map((definition) => ({
        type: "listItem",
        data: {
          hName: "dd",
          hProperties: {
            dataPretextDefinitionDescription: "",
          },
        },
        children: definition,
      })),
    ],
  };
}

function splitMdastParagraphLines(children: MarkdownMdastNode[]) {
  const lines: MarkdownMdastNode[][] = [[]];
  for (const child of children) {
    if (child.type === "break") {
      lines.push([]);
      continue;
    }
    if (child.type === "text" && typeof child.value === "string") {
      const parts = child.value.split(/\r?\n/);
      parts.forEach((part, index) => {
        if (index > 0) lines.push([]);
        if (part) lines[lines.length - 1]!.push({ ...child, value: part });
      });
      continue;
    }
    lines[lines.length - 1]!.push(child);
  }
  return lines;
}

function trimMdastLine(line: MarkdownMdastNode[]) {
  return trimMdastLineEnd(trimMdastLineStart(line));
}

function trimDefinitionLine(line: MarkdownMdastNode[]) {
  const trimmed = trimMdastLine(line);
  const first = trimmed[0];
  if (
    !first ||
    first.type !== "text" ||
    typeof first.value !== "string" ||
    !first.value.startsWith(":")
  ) {
    return null;
  }
  first.value = first.value.replace(/^:\s*/, "");
  return trimMdastLine(trimmed);
}

function trimMdastLineStart(line: MarkdownMdastNode[]) {
  const next = line.map((node) => ({ ...node }));
  while (
    next[0]?.type === "text" &&
    typeof next[0].value === "string" &&
    !next[0].value.trim()
  ) {
    next.shift();
  }
  if (next[0]?.type === "text" && typeof next[0].value === "string") {
    next[0].value = next[0].value.replace(/^\s+/, "");
    if (!next[0].value) next.shift();
  }
  return next;
}

function trimMdastLineEnd(line: MarkdownMdastNode[]) {
  const next = line.map((node) => ({ ...node }));
  while (
    next.at(-1)?.type === "text" &&
    typeof next.at(-1)?.value === "string" &&
    !String(next.at(-1)?.value).trim()
  ) {
    next.pop();
  }
  const last = next.at(-1);
  if (last?.type === "text" && typeof last.value === "string") {
    last.value = last.value.replace(/\s+$/, "");
    if (!last.value) next.pop();
  }
  return next;
}

function rehypeMarkdownSafeInputs() {
  return function transform(tree: MarkdownHastRoot) {
    removeUnsafeInputChildren(tree);
  };
}

function rehypeMarkdownTrustedMetadata() {
  return function transform(tree: MarkdownHastRoot) {
    trustGeneratedMarkdownMetadata(tree);
  };
}

function trustGeneratedMarkdownMetadata(
  node: MarkdownHastElement | MarkdownHastRoot,
) {
  for (const child of node.children) {
    const element = child as MarkdownHastElement;
    if (!element || element.type !== "element") continue;

    if (hasMarkdownInternalMetadata(element)) {
      if (element.position) {
        stripMarkdownInternalMetadata(element);
      } else if (hasMarkdownTrustedComponentMetadata(element)) {
        element.properties ??= {};
        element.properties.pretextComponentTrusted = true;
      }
    }

    trustGeneratedMarkdownMetadata(element);
  }
}

function hasMarkdownInternalMetadata(element: MarkdownHastElement) {
  return Object.keys(element.properties ?? {}).some((key) =>
    /^(?:dataPretextComponent|dataPretextCallout|dataFootnotes|dataFootnoteBackref|pretextComponentTrusted)/.test(
      key,
    ),
  );
}

function hasMarkdownTrustedComponentMetadata(element: MarkdownHastElement) {
  return Object.keys(element.properties ?? {}).some((key) =>
    /^(?:dataPretextComponent|dataPretextCallout|pretextComponentTrusted)/.test(
      key,
    ),
  );
}

function stripMarkdownInternalMetadata(element: MarkdownHastElement) {
  for (const key of Object.keys(element.properties ?? {})) {
    if (
      /^(?:dataPretextComponent|data-pretext-component|dataPretextCallout|data-pretext-callout|dataFootnotes|data-footnotes|dataFootnoteBackref|data-footnote-backref|pretextComponentTrusted)/.test(
        key,
      )
    ) {
      delete element.properties?.[key];
    }
  }
}

function removeUnsafeInputChildren(
  parent: MarkdownHastElement | MarkdownHastRoot,
) {
  if (!Array.isArray(parent.children)) return;
  parent.children = parent.children.filter((child) => {
    const element = child as MarkdownHastElement;
    if (!element || element.type !== "element") return true;
    if (element.tagName !== "input") return true;
    return (
      isHastElement(parent) &&
      parent.tagName === "li" &&
      hasClassName(parent, "task-list-item") &&
      element.properties?.type === "checkbox"
    );
  });
  for (const child of parent.children) {
    const element = child as MarkdownHastElement;
    if (element?.type === "element") removeUnsafeInputChildren(element);
  }
}

function isHastElement(
  node: MarkdownHastElement | MarkdownHastRoot,
): node is MarkdownHastElement {
  return node.type === "element";
}

function hasClassName(element: MarkdownHastElement, className: string) {
  const value = element.properties?.className;
  return Array.isArray(value) && value.includes(className);
}

function transformMarkdownComponentChildren(
  parent: MarkdownMdastNode,
  file: VFile,
) {
  const children = parent.children;
  if (!children) return;
  transformMarkdownHtmlContainers(parent, file);

  for (let index = 0; index < children.length; index += 1) {
    const child = children[index]!;

    if (child.type === "html" && typeof child.value === "string") {
      const component = parseMarkdownComponentHtml(child.value);
      if (component) {
        children[index] = createMarkdownComponentNode(component);
        continue;
      }
      // CommonMark merges consecutive component tags (no blank line between)
      // into one HTML block; split it so each tag renders as its own component
      // (or its own fallback) instead of the whole run falling through to text.
      const multiple = splitMarkdownComponentHtml(child.value, file);
      if (multiple) {
        children.splice(index, 1, ...multiple);
        index += multiple.length - 1;
        continue;
      }
      if (isMarkdownComponentHtml(child.value)) {
        const reason = fallbackReasonForHtml(child.value);
        emitMarkdownComponentFallbackMessage({
          file,
          node: child,
          reason,
        });
        children[index] = createMarkdownComponentFallbackNode({
          name: componentNameFromHtml(child.value) ?? "Component",
          reason,
          source: child.value.trim(),
        });
        continue;
      }
    }

    const paragraphComponentSource = componentSourceFromParagraph(child, file);
    if (paragraphComponentSource) {
      const reason = fallbackReasonForHtml(paragraphComponentSource);
      emitMarkdownComponentFallbackMessage({
        file,
        node: child,
        reason,
      });
      children[index] = createMarkdownComponentFallbackNode({
        name: componentNameFromHtml(paragraphComponentSource) ?? "Component",
        reason,
        source: paragraphComponentSource,
      });
      continue;
    }

    if (
      child.type === "containerDirective" &&
      isMarkdownContainerComponentName(readDirectiveName(child))
    ) {
      transformMarkdownComponentChildren(child, file);
      const component = parseMarkdownDirectiveComponent(child);
      if (component) {
        children[index] = createMarkdownComponentNode(component);
        continue;
      }
      if (isMarkdownContainerComponentName(readDirectiveName(child))) {
        const reason = "Unsupported component directive props";
        emitMarkdownComponentFallbackMessage({
          file,
          node: child,
          reason,
        });
        children[index] = createMarkdownComponentFallbackNode({
          name:
            componentNameForDirective(readDirectiveName(child)) ?? "Component",
          reason,
          source: directiveSourceForUnsafeComponent(child),
          children: child.children ?? [],
        });
        continue;
      }
    }

    if (
      child.type === "containerDirective" &&
      isCalloutKind(readDirectiveName(child))
    ) {
      children[index] = createMarkdownCalloutNode(child);
      continue;
    }

    if (child.type === "leafDirective" || child.type === "textDirective") {
      const component = parseMarkdownDirectiveComponent(child);
      if (component) {
        children[index] = createMarkdownComponentNode(component);
        continue;
      }
      if (componentNameForDirective(readDirectiveName(child))) {
        const reason = "Unsupported component directive props";
        emitMarkdownComponentFallbackMessage({
          file,
          node: child,
          reason,
        });
        children[index] = createMarkdownComponentFallbackNode({
          name:
            componentNameForDirective(readDirectiveName(child)) ?? "Component",
          reason,
          source: directiveSourceForUnsafeComponent(child),
          children:
            child.type === "textDirective" ? (child.children ?? []) : [],
        });
        continue;
      }
    }

    transformMarkdownComponentChildren(child, file);
  }
}

function componentSourceFromParagraph(node: MarkdownMdastNode, file: VFile) {
  if (node.type !== "paragraph") return null;
  const nonWhitespaceChildren = (node.children ?? []).filter(
    (child) => !(child.type === "text" && !String(child.value ?? "").trim()),
  );
  if (nonWhitespaceChildren.length !== 1) return null;
  const onlyChild = nonWhitespaceChildren[0]!;
  if (onlyChild.type !== "text" || typeof onlyChild.value !== "string") {
    return null;
  }

  const source = sourceTextForMdastNode(node, file).trim() || onlyChild.value;
  return isMarkdownComponentHtml(source) ? source : null;
}

function transformMarkdownHtmlContainers(
  parent: MarkdownMdastNode,
  file: VFile,
) {
  const children = parent.children;
  if (!children) return;
  for (let index = 0; index < children.length; index += 1) {
    const child = children[index]!;
    if (child.type !== "html" || typeof child.value !== "string") continue;
    const start = /^<([A-Z][A-Za-z0-9]*)\b([^>]*)>$/.exec(child.value.trim());
    if (!start || !["Accordion", "Callout"].includes(start[1]!)) continue;
    const name = start[1]!;
    const closeIndex = children.findIndex(
      (candidate, candidateIndex) =>
        candidateIndex > index &&
        candidate.type === "html" &&
        typeof candidate.value === "string" &&
        candidate.value.trim() === `</${name}>`,
    );
    if (closeIndex < 0) continue;
    const propsText = start[2]!;
    const source = child.value.trim();
    const inner = children.slice(index + 1, closeIndex);
    const component = hasEventHandlerAttribute(propsText)
      ? null
      : parseMarkdownComponentProps(
          name,
          parseComponentAttributes(propsText),
          inner,
        );
    if (!component) {
      emitMarkdownComponentFallbackMessage({
        file,
        node: child,
        reason: fallbackReasonForHtml(source),
      });
    }
    children.splice(
      index,
      closeIndex - index + 1,
      component
        ? createMarkdownComponentNode(component)
        : createMarkdownComponentFallbackNode({
            name,
            reason: fallbackReasonForHtml(source),
            source,
            children: inner,
          }),
    );
  }
}

function emitMarkdownComponentFallbackMessage({
  file,
  node,
  reason,
}: {
  file: VFile;
  node: MarkdownMdastNode;
  reason: string;
}) {
  file.message(
    new Error(reason),
    markdownMessagePoint(node),
    "markdown:component-fallback",
  );
}

function markdownMessagePoint(node: MarkdownMdastNode) {
  const point = node.position?.start;
  return typeof point?.line === "number" && typeof point.column === "number"
    ? { column: point.column, line: point.line }
    : null;
}

function createMarkdownComponentNode(
  component: MarkdownComponent,
): MarkdownMdastNode {
  return {
    type: "pretextComponent",
    data: {
      hName: "div",
      hProperties: {
        dataPretextComponentName: component.name,
        dataPretextComponentProps: JSON.stringify(component.props),
      },
    },
    children: component.children ?? [],
  };
}

function createMarkdownComponentFallbackNode({
  children = [],
  name,
  reason,
  source,
}: {
  children?: MarkdownMdastNode[];
  name: string;
  reason: string;
  source: string;
}): MarkdownMdastNode {
  return {
    type: "pretextComponentFallback",
    data: {
      hName: "div",
      hProperties: {
        dataPretextComponentFallback: "",
        dataPretextComponentFallbackName: name,
        dataPretextComponentFallbackReason: reason,
        dataPretextComponentFallbackSource: source,
      },
    },
    children: [{ type: "text", value: source }, ...children],
  };
}

function createMarkdownCalloutNode(node: MarkdownMdastNode): MarkdownMdastNode {
  const kind = calloutKind(readDirectiveName(node));
  const attrs = readDirectiveAttributes(node) ?? {};
  const title =
    typeof attrs.title === "string" && attrs.title
      ? attrs.title
      : calloutTitle(kind);
  return {
    type: "pretextCallout",
    data: {
      hName: "div",
      hProperties: {
        dataPretextCalloutKind: kind,
        dataPretextCalloutTitle: title,
      },
    },
    children: node.children ?? [],
  };
}

// Splits an HTML block holding several self-closing component tags (only
// whitespace between them) into one node per tag: a component node when the tag
// parses, otherwise a fallback node — so one invalid tag never blanks the whole
// run. Returns null only when the block isn't entirely component tags (then
// normal HTML handling applies).
function splitMarkdownComponentHtml(value: string, file: VFile) {
  const trimmed = value.trim();
  // Quote-aware so `>` inside an attribute value doesn't end a tag early.
  const tagPattern = /<[A-Z][A-Za-z0-9]*\b(?:[^>"']|"[^"]*"|'[^']*')*\/>/g;
  const matches = [...trimmed.matchAll(tagPattern)];
  if (matches.length < 2) return null;

  let cursor = 0;
  for (const match of matches) {
    if (trimmed.slice(cursor, match.index).trim() !== "") return null;
    cursor = match.index + match[0].length;
  }
  if (trimmed.slice(cursor).trim() !== "") return null;

  // Only treat the block as components when every tag names a known component.
  if (
    !matches.every((match) => {
      const name = componentNameFromHtml(match[0]);
      return name !== null && isMarkdownLeafComponentName(name);
    })
  ) {
    return null;
  }

  return matches.map((match) => {
    const tag = match[0];
    const component = parseMarkdownComponentHtml(tag);
    if (component) return createMarkdownComponentNode(component);
    const reason = fallbackReasonForHtml(tag);
    emitMarkdownComponentFallbackMessage({
      file,
      node: { type: "html", value: tag } as MarkdownMdastNode,
      reason,
    });
    return createMarkdownComponentFallbackNode({
      name: componentNameFromHtml(tag) ?? "Component",
      reason,
      source: tag,
    });
  });
}

function parseMarkdownComponentHtml(value: string) {
  // Match exactly one self-closing tag: attribute chars are either non-quote/
  // non-`>` or fully-quoted strings, so `>` inside an attribute (e.g. a mermaid
  // `source="graph TD; A-->B"`) is allowed, while a multi-tag HTML block fails
  // here and is handled by splitMarkdownComponentHtml.
  const match = /^<([A-Z][A-Za-z0-9]*)\b((?:[^>"']|"[^"]*"|'[^']*')*)\/>$/.exec(
    value.trim(),
  );
  if (!match) return null;
  const name = match[1]!;
  const propsText = match[2]!;
  if (!isMarkdownLeafComponentName(name)) {
    return null;
  }
  return parseMarkdownComponentProps(
    name,
    parseComponentAttributes(propsText),
    [],
  );
}

function parseMarkdownDirectiveComponent(node: MarkdownMdastNode) {
  const name = componentNameForDirective(readDirectiveName(node));
  if (!name) return null;
  return parseMarkdownComponentProps(
    name,
    readDirectiveAttributes(node),
    node.children ?? [],
  );
}

function parseMarkdownComponentProps(
  name: string,
  props: Record<string, unknown> | null,
  children: MarkdownMdastNode[],
): MarkdownComponent | null {
  if (!props) return null;
  if (Object.keys(props).some((key) => /^on/i.test(key))) return null;

  if (name === "Diagram") {
    if (props.type !== "mermaid" || typeof props.source !== "string")
      return null;
    return {
      name,
      props: {
        caption: readPropString(props.caption),
        source: normalizeMarkdownDiagramSource(props.source),
        title: readPropString(props.title),
        type: "mermaid",
      },
    };
  }
  if (name === "Metric") {
    if (!props.label || !props.value || props.tone) return null;
    return {
      name,
      props: {
        label: readPropString(props.label),
        value: readPropString(props.value),
      },
    };
  }
  if (name === "Badge") {
    const label = readPropString(props.label) || mdastText(children);
    const tone = readPropString(props.tone);
    if (!label || (tone && !["default", "success", "warning"].includes(tone))) {
      return null;
    }
    return { name, props: { label, tone } };
  }
  if (name === "Image") {
    if (!props.src || !props.alt) return null;
    return {
      name,
      props: {
        alt: readPropString(props.alt),
        height: readPropString(props.height),
        src: readPropString(props.src),
        title: readPropString(props.title),
        width: readPropString(props.width),
      },
    };
  }
  if (name === "Video") {
    if (!props.src || !props.label) return null;
    return {
      name,
      props: {
        controls: readPropString(props.controls),
        label: readPropString(props.label),
        loop: readPropString(props.loop),
        muted: readPropString(props.muted),
        src: readPropString(props.src),
        title: readPropString(props.title),
      },
    };
  }
  if (name === "Accordion") {
    if (!props.title) return null;
    return { name, props: { title: readPropString(props.title) }, children };
  }
  if (name === "Callout") {
    const kind = calloutKind(readPropString(props.kind) || "note");
    return {
      name,
      props: {
        kind,
        title: readPropString(props.title) || calloutTitle(kind),
      },
      children,
    };
  }
  if (name === "Tabs" || name === "Tab") {
    return {
      name,
      props: {
        label: readPropString(props.label),
        title: readPropString(props.title),
      },
      children,
    };
  }
  return null;
}

function normalizeMarkdownDiagramSource(source: string) {
  return source
    .split(/;\s*/)
    .map((line) => line.trim())
    .filter(Boolean)
    .join("\n");
}

function readDirectiveName(node: MarkdownMdastNode) {
  const directive = node as unknown as { name?: unknown };
  return typeof directive.name === "string" ? directive.name : "";
}

function readDirectiveAttributes(node: MarkdownMdastNode) {
  const attributes = (node as { attributes?: unknown }).attributes;
  return attributes && typeof attributes === "object"
    ? (attributes as Record<string, unknown>)
    : null;
}

function parseQuotedAttributes(value: string): Record<string, string> {
  return Object.fromEntries(
    Array.from(value.matchAll(/\s*([A-Za-z][A-Za-z0-9_]*)="([^"]*)"/g)).map(
      (item) => [item[1]!, item[2]!],
    ),
  );
}

function parseComponentAttributes(value: string) {
  if (/\{\s*\.\.\./.test(value)) return null;
  const attributes: Record<string, string> = {};
  const consumed: Array<[number, number]> = [];
  for (const match of value.matchAll(
    /\s+([A-Za-z][A-Za-z0-9_]*)=(?:"([^"]*)"|\{(\d+(?:\.\d+)?|true|false)\})/g,
  )) {
    attributes[match[1]!] = match[2] ?? match[3] ?? "";
    consumed.push([match.index ?? 0, (match.index ?? 0) + match[0].length]);
  }
  for (const match of value.matchAll(/\s+([A-Za-z][A-Za-z0-9_]*)(?=\s|$)/g)) {
    const index = match.index ?? 0;
    if (consumed.some(([start, end]) => index >= start && index < end)) {
      continue;
    }
    attributes[match[1]!] = "true";
    consumed.push([index, index + match[0].length]);
  }
  if (removeRanges(value, consumed).trim()) return null;
  return attributes;
}

function removeRanges(value: string, ranges: Array<[number, number]>) {
  let result = "";
  let offset = 0;
  for (const [start, end] of ranges.sort((a, b) => a[0] - b[0])) {
    result += value.slice(offset, start);
    offset = end;
  }
  return result + value.slice(offset);
}

function readPropString(value: unknown) {
  return typeof value === "string" ? value : "";
}

function mdastText(nodes: readonly MarkdownMdastNode[]): string {
  return nodes
    .map((node) =>
      typeof node.value === "string"
        ? node.value
        : mdastText(node.children ?? []),
    )
    .join("");
}

function componentNameForDirective(name: string) {
  const normalized = name.toLowerCase();
  const names: Record<string, string> = {
    accordion: "Accordion",
    badge: "Badge",
    callout: "Callout",
    diagram: "Diagram",
    image: "Image",
    metric: "Metric",
    tab: "Tab",
    tabs: "Tabs",
    video: "Video",
  };
  return names[normalized] ?? null;
}

function isMarkdownLeafComponentName(name: string) {
  return ["Badge", "Diagram", "Image", "Metric", "Video"].includes(name);
}

function isMarkdownContainerComponentName(name: string) {
  return Boolean(componentNameForDirective(name));
}

function isMarkdownComponentHtml(value: string) {
  return /^<\/?[A-Z][A-Za-z0-9.]*(?:\b|\.)/.test(value.trim());
}

function componentNameFromHtml(value: string) {
  return /^<\/?([A-Z][A-Za-z0-9.]*)/.exec(value.trim())?.[1] ?? null;
}

function hasEventHandlerAttribute(value: string) {
  return /\son[A-Za-z]+\s*=/.test(value);
}

function fallbackReasonForHtml(value: string) {
  const name = componentNameFromHtml(value);
  if (name?.includes(".")) {
    return "Remote or namespaced components are not supported";
  }
  if (!name || !componentNameForDirective(name)) return "Unsupported component";
  if (hasEventHandlerAttribute(value))
    return "Event handler props are not supported";
  if (value.includes("{")) return "Component props must be literal values";
  return "Unsupported component";
}

function sourceTextForMdastNode(node: MarkdownMdastNode, file: VFile) {
  const value = String(file.value ?? "");
  const start = node.position?.start?.offset;
  const end = node.position?.end?.offset;
  if (
    typeof start !== "number" ||
    typeof end !== "number" ||
    start < 0 ||
    end < start
  ) {
    return "";
  }
  return value.slice(start, end);
}

function isCalloutKind(name: string) {
  return ["caution", "important", "note", "success", "tip", "warning"].includes(
    name.toLowerCase(),
  );
}

function calloutKind(value: string) {
  const normalized = value.toLowerCase();
  if (normalized === "success") return "tip";
  if (isCalloutKind(normalized)) return normalized;
  return "note";
}

function calloutTitle(kind: string) {
  const titles: Record<string, string> = {
    caution: "Caution",
    important: "Important",
    note: "Note",
    tip: "Tip",
    warning: "Warning",
  };
  return titles[kind] ?? "Note";
}

function directiveSourceForUnsafeComponent(node: MarkdownMdastNode) {
  const name = readDirectiveName(node);
  const attrs = readDirectiveAttributes(node) ?? {};
  const attrSource = Object.entries(attrs)
    .map(([key, value]) => `${key}="${String(value)}"`)
    .join(" ");
  const marker = node.type === "textDirective" ? ":" : "::";
  return `${marker}${name}${attrSource ? `{${attrSource}}` : ""}`;
}
registry/new-york-v4/ui/markdown-url-policy.ts
"use client";

const URL_CONTROL_CHARACTER_PATTERN = /[\u0000-\u001F\u007F]/;
const URL_SCHEME_PATTERN = /^([A-Za-z][A-Za-z0-9+.-]*):/;
const URL_BACKSLASH_PATTERN = /[\\\uFE68\uFF3C\u2216]/;
const URL_CONFUSABLE_DELIMITER_PATTERN =
  /[\u02D0\u0589\u05C3\u0703\u0704\u16EC\u1803\u1809\u205A\u2236\uA789\uFE13\uFE55\uFF1A\u2044\u2215\u2571\u27CB\u29F8\uFF0F]/;
const ALLOWED_LINK_PROTOCOLS = new Set(["http", "https", "mailto"]);

export function sanitizeMarkdownUrl(value: string) {
  const trimmed = value.trim();
  if (!trimmed) return "";
  if (URL_CONTROL_CHARACTER_PATTERN.test(trimmed)) return "";
  if (URL_BACKSLASH_PATTERN.test(trimmed)) return "";
  if (URL_CONFUSABLE_DELIMITER_PATTERN.test(trimmed)) return "";

  const decoded = decodeMarkdownUrl(trimmed).trim();
  if (!decoded || URL_CONTROL_CHARACTER_PATTERN.test(decoded)) return "";
  if (URL_BACKSLASH_PATTERN.test(decoded)) return "";
  if (URL_CONFUSABLE_DELIMITER_PATTERN.test(decoded)) return "";

  const decodedScheme = getMarkdownUrlScheme(decoded);
  const rawScheme = getMarkdownUrlScheme(trimmed);
  if (decodedScheme && decodedScheme !== rawScheme) return "";
  if (decodedScheme && !ALLOWED_LINK_PROTOCOLS.has(decodedScheme)) return "";

  if (trimmed.startsWith("#")) return trimmed;
  if (trimmed.startsWith("/")) return trimmed.startsWith("//") ? "" : trimmed;

  try {
    const url = new URL(trimmed, "https://retab.local");
    if (ALLOWED_LINK_PROTOCOLS.has(url.protocol.slice(0, -1))) {
      return rawScheme ? url.href : trimmed;
    }
  } catch {
    return "";
  }

  return "";
}

export function sanitizeMarkdownImageUrl(value: string) {
  const safeUrl = sanitizeMarkdownUrl(value);
  if (!safeUrl || safeUrl.startsWith("mailto:") || safeUrl.startsWith("#")) {
    return "";
  }
  if (isMarkdownSvgResourceUrl(safeUrl)) return "";
  return safeUrl;
}

export function sanitizeMarkdownMediaUrl(value: string) {
  // Image URLs already reject SVG resources, so media inherits that policy.
  return sanitizeMarkdownImageUrl(value);
}

function decodeMarkdownUrl(value: string) {
  try {
    return decodeURIComponent(value);
  } catch {
    return value;
  }
}

function getMarkdownUrlScheme(value: string) {
  return URL_SCHEME_PATTERN.exec(value)?.[1]?.toLowerCase() ?? null;
}

export function isMarkdownSvgResourceUrl(value: string) {
  const decoded = decodeMarkdownUrl(value).trim();

  try {
    const url = new URL(decoded, "https://retab.local");
    return /\.(?:svg|svgz)$/i.test(url.pathname);
  } catch {
    const pathname = decoded.split(/[?#]/, 1)[0] ?? decoded;
    return /\.(?:svg|svgz)$/i.test(pathname);
  }
}
registry/new-york-v4/ui/text-viewer-virtualization.ts
"use client";

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

const DEFAULT_VIEWPORT_HEIGHT = 600;
const DEFAULT_VIEWPORT_WIDTH = 800;
const DEFAULT_OVERSCAN = 6;
const MAX_VIRTUAL_ITEMS = 500;

export interface TextVirtualItem {
  index: number;
  key: React.Key;
  start: number;
  size: number;
  end: number;
}

export interface TextVirtualOffsets {
  starts: number[];
  totalSize: number;
}

export interface TextVirtualViewport {
  scrollTop: number;
  clientHeight: number;
  clientWidth: number;
}

export interface TextScrollAnchor {
  index: number;
  offsetWithinLine: number;
}

export interface TextFrameVirtualItem {
  end: number;
  index: number;
  key: React.Key;
  size: number;
  start: number;
}

export interface TextFrameGeometry {
  bottom: number;
  height: number;
  top: number;
}

export interface TextFrameScrollAnchor {
  index: number;
  offsetWithinFrame: number;
}

export interface TextInverseStickyWindow {
  afterHeight: number;
  beforeHeight: number;
  renderedBottom: number;
  renderedHeight: number;
  renderedTop: number;
  stickyOffset: number;
}

export function buildTextVirtualOffsets({
  itemSizes,
  paddingStart = 0,
  paddingEnd = 0,
}: {
  itemSizes: readonly number[];
  paddingStart?: number;
  paddingEnd?: number;
}): TextVirtualOffsets {
  const starts: number[] = [];
  let offset = safeSize(paddingStart);

  for (const itemSize of itemSizes) {
    starts.push(offset);
    offset += safeSize(itemSize);
  }

  return {
    starts,
    totalSize: offset + safeSize(paddingEnd),
  };
}

export function getTextVirtualItems({
  itemSizes,
  offsets,
  scrollTop,
  viewportHeight,
  overscan = DEFAULT_OVERSCAN,
}: {
  itemSizes: readonly number[];
  offsets: TextVirtualOffsets;
  scrollTop: number;
  viewportHeight: number;
  overscan?: number;
}): TextVirtualItem[] {
  const count = itemSizes.length;
  if (count === 0) return [];

  const safeScrollTop = safeOffset(scrollTop);
  const safeViewportHeight = Math.max(1, safeSize(viewportHeight));
  const safeOverscan = safeCount(overscan);
  const visibleStartIndex = findFirstItemEndingAfter({
    itemSizes,
    starts: offsets.starts,
    offset: safeScrollTop,
  });
  const visibleEndExclusive = findFirstItemStartingAtOrAfter({
    starts: offsets.starts,
    offset: safeScrollTop + safeViewportHeight,
  });
  const start = Math.max(0, visibleStartIndex - safeOverscan);
  const end = Math.min(
    count,
    Math.max(visibleStartIndex + 1, visibleEndExclusive) + safeOverscan,
  );
  const cappedEnd = Math.min(count, start + MAX_VIRTUAL_ITEMS);

  return Array.from(
    { length: Math.min(end, cappedEnd) - start },
    (_, localIndex) => {
      const index = start + localIndex;
      const size = safeSize(itemSizes[index]);
      const itemStart = offsets.starts[index] ?? 0;
      return {
        index,
        key: index,
        start: itemStart,
        size,
        end: itemStart + size,
      };
    },
  );
}

export function textScrollTopForItem({
  itemIndex,
  itemSizes,
  offsets,
  viewportHeight,
  align = "center",
}: {
  itemIndex: number;
  itemSizes: readonly number[];
  offsets: TextVirtualOffsets;
  viewportHeight: number;
  align?: "start" | "center" | "end";
}) {
  if (!Number.isSafeInteger(itemIndex) || itemIndex < 0) return 0;
  const itemStart = offsets.starts[itemIndex];
  if (itemStart == null) return 0;

  const itemSize = safeSize(itemSizes[itemIndex]);
  const safeViewportHeight = safeSize(viewportHeight);
  if (align === "end") {
    return Math.max(0, itemStart - safeViewportHeight + itemSize);
  }
  if (align === "center") {
    return Math.max(0, itemStart - safeViewportHeight / 2 + itemSize / 2);
  }
  return Math.max(0, itemStart);
}

export function getTextScrollAnchor({
  itemSizes,
  offsets,
  scrollTop,
}: {
  itemSizes: readonly number[];
  offsets: Pick<TextVirtualOffsets, "starts">;
  scrollTop: number;
}): TextScrollAnchor | null {
  if (itemSizes.length === 0) return null;

  const safeScrollTop = safeOffset(scrollTop);
  let low = 0;
  let high = itemSizes.length - 1;
  let index = 0;

  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    const start = offsets.starts[mid] ?? 0;
    const end = start + safeSize(itemSizes[mid]);
    if (end > safeScrollTop) {
      index = mid;
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }

  return {
    index,
    offsetWithinLine: Math.max(0, safeScrollTop - (offsets.starts[index] ?? 0)),
  };
}

export function useTextVariableVirtualizer({
  itemSizes,
  overscan = DEFAULT_OVERSCAN,
  paddingStart = 0,
  paddingEnd = 0,
  scrollRef,
}: {
  itemSizes: readonly number[];
  overscan?: number;
  paddingStart?: number;
  paddingEnd?: number;
  scrollRef: React.RefObject<HTMLElement | null>;
}) {
  const viewport = useTextVirtualViewport(scrollRef);
  const offsets = React.useMemo(
    () => buildTextVirtualOffsets({ itemSizes, paddingStart, paddingEnd }),
    [itemSizes, paddingEnd, paddingStart],
  );
  const viewportHeight = viewport.clientHeight || DEFAULT_VIEWPORT_HEIGHT;
  const viewportWidth = viewport.clientWidth || DEFAULT_VIEWPORT_WIDTH;
  const virtualItems = React.useMemo(
    () =>
      getTextVirtualItems({
        itemSizes,
        offsets,
        overscan,
        scrollTop: viewport.scrollTop,
        viewportHeight,
      }),
    [itemSizes, offsets, overscan, viewport.scrollTop, viewportHeight],
  );
  return {
    offsets,
    totalSize: offsets.totalSize,
    viewportHeight,
    viewportWidth,
    virtualItems,
  };
}

export function getTextFrameVirtualItems({
  frames,
  maxItems = MAX_VIRTUAL_ITEMS,
  overscanPx = 0,
  scrollTop,
  viewportHeight,
}: {
  frames: readonly TextFrameGeometry[];
  maxItems?: number;
  overscanPx?: number;
  scrollTop: number;
  viewportHeight: number;
}): TextFrameVirtualItem[] {
  if (frames.length === 0) return [];

  const safeScrollTop = safeOffset(scrollTop);
  const safeViewportHeight = Math.max(1, safeSize(viewportHeight));
  const safeOverscanPx = safeSize(overscanPx);
  const visibleStart = findFirstFrameEndingAfter(frames, safeScrollTop);
  const visibleEnd = Math.max(
    visibleStart + 1,
    findFirstFrameStartingAtOrAfter(
      frames,
      safeScrollTop + safeViewportHeight,
      visibleStart,
    ),
  );
  const start = findFirstFrameEndingAfter(
    frames,
    Math.max(0, safeScrollTop - safeOverscanPx),
  );
  const end = Math.max(
    visibleEnd,
    findFirstFrameStartingAtOrAfter(
      frames,
      safeScrollTop + safeViewportHeight + safeOverscanPx,
      visibleStart,
    ),
  );
  const safeMaxItems = Math.max(visibleEnd - start, safeCount(maxItems));
  const cappedEnd = Math.min(frames.length, end, start + safeMaxItems);

  return Array.from({ length: cappedEnd - start }, (_, localIndex) => {
    const index = start + localIndex;
    const frame = frames[index]!;
    return {
      end: frame.bottom,
      index,
      key: index,
      size: safeSize(frame.height),
      start: frame.top,
    };
  });
}

export function getTextInverseStickyWindow({
  renderedBottom,
  renderedTop,
  totalHeight,
  viewportHeight,
}: {
  renderedBottom: number;
  renderedTop: number;
  totalHeight: number;
  viewportHeight: number;
}): TextInverseStickyWindow {
  const safeTotalHeight = safeOffset(totalHeight);
  const safeViewportHeight = Math.max(1, safeSize(viewportHeight));
  const top = clamp(safeOffset(renderedTop), 0, safeTotalHeight);
  const bottom = clamp(
    Math.max(top, safeOffset(renderedBottom)),
    top,
    safeTotalHeight,
  );
  const renderedHeight = bottom - top;

  return {
    afterHeight: Math.max(0, safeTotalHeight - bottom),
    beforeHeight: top,
    renderedBottom: bottom,
    renderedHeight,
    renderedTop: top,
    stickyOffset: -Math.max(0, renderedHeight - safeViewportHeight),
  };
}

export function getTextFrameScrollAnchor({
  frames,
  scrollTop,
}: {
  frames: readonly TextFrameGeometry[];
  scrollTop: number;
}): TextFrameScrollAnchor | null {
  if (frames.length === 0) return null;

  const safeScrollTop = safeOffset(scrollTop);
  const index = findFirstFrameEndingAfter(frames, safeScrollTop);
  const frame = frames[index] ?? frames[frames.length - 1];
  if (!frame) return null;

  return {
    index,
    offsetWithinFrame: Math.max(0, safeScrollTop - frame.top),
  };
}

export function useTextVirtualViewport(
  scrollRef: React.RefObject<HTMLElement | null>,
): TextVirtualViewport {
  const [viewport, setViewport] = React.useState<TextVirtualViewport>({
    scrollTop: 0,
    clientHeight: 0,
    clientWidth: 0,
  });

  useKeyedLayoutEffect(joinEffectKey([scrollRef]), () => {
    const scrollElement = scrollRef.current;
    if (!scrollElement) return;

    let frame = 0;
    const readViewport = () => {
      frame = 0;
      const next = {
        scrollTop: safeOffset(scrollElement.scrollTop),
        clientHeight: safeSize(scrollElement.clientHeight),
        clientWidth: safeSize(scrollElement.clientWidth),
      };
      setViewport((current) =>
        current.scrollTop === next.scrollTop &&
        current.clientHeight === next.clientHeight &&
        current.clientWidth === next.clientWidth
          ? current
          : next,
      );
    };
    const scheduleRead = () => {
      if (frame) return;
      frame = requestAnimationFrame(readViewport);
    };

    readViewport();
    scrollElement.addEventListener("scroll", scheduleRead, { passive: true });
    const observer =
      typeof ResizeObserver !== "undefined"
        ? new ResizeObserver(scheduleRead)
        : null;
    observer?.observe(scrollElement);

    return () => {
      if (frame) cancelAnimationFrame(frame);
      scrollElement.removeEventListener("scroll", scheduleRead);
      observer?.disconnect();
    };
  });

  return viewport;
}

function findFirstFrameEndingAfter(
  frames: readonly TextFrameGeometry[],
  offset: number,
) {
  let low = 0;
  let high = frames.length - 1;
  let result = frames.length - 1;

  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    if ((frames[mid]?.bottom ?? 0) > offset) {
      result = mid;
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }

  return result;
}

function findFirstFrameStartingAtOrAfter(
  frames: readonly TextFrameGeometry[],
  offset: number,
  start: number,
) {
  let low = Math.max(0, start);
  let high = frames.length - 1;
  let result = frames.length;

  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    if ((frames[mid]?.top ?? 0) >= offset) {
      result = mid;
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }

  return result;
}

function findFirstItemEndingAfter({
  itemSizes,
  starts,
  offset,
}: {
  itemSizes: readonly number[];
  starts: readonly number[];
  offset: number;
}) {
  let low = 0;
  let high = itemSizes.length - 1;
  let result = itemSizes.length - 1;

  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    const end = (starts[mid] ?? 0) + safeSize(itemSizes[mid]);
    if (end > offset) {
      result = mid;
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }

  return result;
}

function findFirstItemStartingAtOrAfter({
  starts,
  offset,
}: {
  starts: readonly number[];
  offset: number;
}) {
  let low = 0;
  let high = starts.length - 1;
  let result = starts.length;

  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    if ((starts[mid] ?? 0) >= offset) {
      result = mid;
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }

  return result;
}

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

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

function safeSize(value: number | undefined) {
  return Number.isFinite(value) && value != null && 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/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);
}
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-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/markdown-greenfield-code-highlight-html.ts
import type { CodeTokenLeaf } from "./code-viewer-syntax-protocol";

export function markdownCodeTokensToHtml({
  highlightPattern,
  line,
  tokens,
}: {
  highlightPattern: string;
  line: string;
  tokens: readonly CodeTokenLeaf[] | null;
}) {
  if (!tokens) return null;
  if (!tokens.length) return " ";
  const highlightStart = highlightPattern ? line.indexOf(highlightPattern) : -1;
  const highlightEnd =
    highlightStart >= 0 ? highlightStart + highlightPattern.length : -1;
  let cursor = 0;
  let html = "";

  for (const token of tokens) {
    const tokenStart = cursor;
    const tokenEnd = cursor + token.text.length;
    cursor = tokenEnd;
    html += tokenToHtml({
      highlightEnd,
      highlightStart,
      token,
      tokenEnd,
      tokenStart,
    });
  }

  return html || " ";
}

function tokenToHtml({
  highlightEnd,
  highlightStart,
  token,
  tokenEnd,
  tokenStart,
}: {
  highlightEnd: number;
  highlightStart: number;
  token: CodeTokenLeaf;
  tokenEnd: number;
  tokenStart: number;
}) {
  const inner = tokenTextToHtml({
    highlightEnd,
    highlightStart,
    text: token.text,
    textEnd: tokenEnd,
    textStart: tokenStart,
  });
  if (!token.kind || !/^[a-z-]+$/.test(token.kind)) return inner;
  return `<span class="cv-token-${token.kind}" data-pretext-code-token="${token.kind}">${inner}</span>`;
}

function tokenTextToHtml({
  highlightEnd,
  highlightStart,
  text,
  textEnd,
  textStart,
}: {
  highlightEnd: number;
  highlightStart: number;
  text: string;
  textEnd: number;
  textStart: number;
}) {
  if (
    highlightStart < 0 ||
    textEnd <= highlightStart ||
    textStart >= highlightEnd
  ) {
    return escapeHtml(text);
  }

  const before = text.slice(0, Math.max(0, highlightStart - textStart));
  const highlighted = text.slice(
    Math.max(0, highlightStart - textStart),
    Math.min(text.length, highlightEnd - textStart),
  );
  const after = text.slice(Math.min(text.length, highlightEnd - textStart));
  return [
    escapeHtml(before),
    highlighted
      ? `<span data-highlighted-chars="">${escapeHtml(highlighted)}</span>`
      : "",
    escapeHtml(after),
  ].join("");
}

function escapeHtml(value: string) {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;");
}
registry/new-york-v4/ui/markdown-greenfield-code-highlight-protocol.ts
export const MARKDOWN_CODE_HIGHLIGHT_BATCH_SIZE = 64;
export const MARKDOWN_CODE_HIGHLIGHT_CACHE_LIMIT = 4096;
export const MARKDOWN_CODE_HIGHLIGHT_RENDERER_VERSION = 1;

export type MarkdownCodeHighlightLineRequest = {
  index: number;
  line: string;
};

export type MarkdownCodeHighlightLineResult = {
  html: string | null;
  index: number;
};

export type MarkdownCodeHighlightWorkerRequest = {
  generation: number;
  highlightPattern: string;
  languageId: string;
  lines: MarkdownCodeHighlightLineRequest[];
  requestId: number;
  type: "highlight";
};

export type MarkdownCodeHighlightWorkerResponse =
  | {
      generation: number;
      languageId: string;
      requestId: number;
      results: MarkdownCodeHighlightLineResult[];
      type: "highlighted";
    }
  | {
      generation: number;
      languageId: string;
      message: string;
      requestId: number;
      type: "error";
    };
registry/new-york-v4/ui/markdown-greenfield-code-highlight.worker.ts
import { shouldTokenizeCodeLine } from "./code-viewer-syntax-protocol";
import {
  ensureCodePrismLanguage,
  tokenizeCodeLine,
} from "./code-viewer-syntax-prism";
import { markdownCodeTokensToHtml } from "./markdown-greenfield-code-highlight-html";
import type {
  MarkdownCodeHighlightWorkerRequest,
  MarkdownCodeHighlightWorkerResponse,
} from "./markdown-greenfield-code-highlight-protocol";

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

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

async function highlightInWorker(request: MarkdownCodeHighlightWorkerRequest) {
  await ensureCodePrismLanguage(request.languageId);
  post({
    generation: request.generation,
    languageId: request.languageId,
    requestId: request.requestId,
    results: request.lines.map(({ index, line }) => ({
      html: shouldTokenizeCodeLine(line)
        ? markdownCodeTokensToHtml({
            highlightPattern: request.highlightPattern,
            line,
            tokens: tokenizeCodeLine(request.languageId, line),
          })
        : null,
      index,
    })),
    type: "highlighted",
  });
}

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

  void highlightInWorker(request).catch((error) => {
    post({
      generation: request.generation,
      languageId: request.languageId,
      message: error instanceof Error ? error.message : String(error),
      requestId: request.requestId,
      type: "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/mermaid-renderer.ts
const MERMAID_MAX_LINES = 160;
const MERMAID_MAX_SOURCE_LENGTH = 12_000;
const MERMAID_CACHE_LIMIT = 64;
const MERMAID_SCOPED_CACHE_LIMIT = 128;
const MMDR_VERSION = "0.2.2";
const MMDR_WASM_URL = "/vendor/mmdr/typst_mmdr.wasm";
const MMDR_BASE_THEME = "modern";
const MMDR_LAYOUT = "";

const MMDR_THEME = {
  background: "transparent",
  cluster_bkg: "transparent",
  cluster_border: "var(--mmdr-border)",
  edge_label_background: "transparent",
  font_family: "Inter, ui-sans-serif, system-ui, sans-serif",
  font_size: 16,
  line_color: "var(--mmdr-line)",
  primary_border_color: "var(--mmdr-border)",
  primary_color: "var(--mmdr-node-fill)",
  primary_text_color: "var(--mmdr-text)",
  secondary_color: "var(--mmdr-node-fill)",
  tertiary_color: "var(--mmdr-node-fill)",
} as const;
const MMDR_THEME_JSON = JSON.stringify(MMDR_THEME);

const MERMAID_CONFIG = {
  flowchart: {
    htmlLabels: false,
    useMaxWidth: true,
  },
  securityLevel: "strict",
  sequence: {
    useMaxWidth: true,
  },
  startOnLoad: false,
  suppressErrorRendering: true,
  theme: "base",
  themeVariables: {
    background: "transparent",
    fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif",
    lineColor: "#64748b",
    primaryBorderColor: "#94a3b8",
    primaryColor: "#f8fafc",
    primaryTextColor: "#0f172a",
    secondaryColor: "#ecfeff",
    tertiaryColor: "#f0fdf4",
  },
} as const;

const DIAGRAM_CACHE_CONFIG_KEY = JSON.stringify({
  mermaid: MERMAID_CONFIG,
  mmdr: {
    baseTheme: MMDR_BASE_THEME,
    theme: MMDR_THEME,
    version: MMDR_VERSION,
  },
});
const MERMAID_CACHE_CONFIG_KEY = JSON.stringify(MERMAID_CONFIG);

export const MERMAID_VIEWER_STYLES = `
[data-pretext-mermaid-svg] {
  --mmdr-border: var(--border);
  --mmdr-line: var(--muted-foreground);
  --mmdr-node-fill: color-mix(in srgb, var(--background) 88%, var(--foreground) 12%);
  --mmdr-text: var(--foreground);
}
[data-pretext-mermaid-svg] svg {
  color: var(--foreground);
  display: block;
  height: auto;
  margin-inline: auto;
  max-width: 100%;
}
[data-pretext-mermaid-svg] rect.background {
  fill: transparent;
  stroke: none;
}
[data-pretext-mermaid-svg] .node rect,
[data-pretext-mermaid-svg] .node polygon,
[data-pretext-mermaid-svg] .node circle,
[data-pretext-mermaid-svg] .node ellipse,
[data-pretext-mermaid-svg] .node path,
[data-pretext-mermaid-svg] .basic.label-container,
[data-pretext-mermaid-svg] .label-container,
[data-pretext-mermaid-svg] .actor,
[data-pretext-mermaid-svg] .entityBox,
[data-pretext-mermaid-svg] .state,
[data-pretext-mermaid-svg] .classGroup rect,
[data-pretext-mermaid-svg] .requirement,
[data-pretext-mermaid-svg] .requirementBox,
[data-pretext-mermaid-svg] .element,
[data-pretext-mermaid-svg] .cluster rect,
[data-pretext-mermaid-svg] .note,
[data-pretext-mermaid-svg] .task {
  fill: color-mix(in srgb, var(--background) 88%, var(--foreground) 12%);
  stroke: var(--border);
  stroke-width: 1px;
}
[data-pretext-mermaid-svg] .cluster rect,
[data-pretext-mermaid-svg] .section,
[data-pretext-mermaid-svg] .grid .tick line {
  fill: color-mix(in srgb, var(--background) 94%, var(--foreground) 6%);
  stroke: var(--border);
}
[data-pretext-mermaid-svg] text,
[data-pretext-mermaid-svg] tspan,
[data-pretext-mermaid-svg] .label,
[data-pretext-mermaid-svg] .label text,
[data-pretext-mermaid-svg] .nodeLabel,
[data-pretext-mermaid-svg] .edgeLabel,
[data-pretext-mermaid-svg] .actor,
[data-pretext-mermaid-svg] .messageText,
[data-pretext-mermaid-svg] .noteText,
[data-pretext-mermaid-svg] .taskText,
[data-pretext-mermaid-svg] .sectionTitle,
[data-pretext-mermaid-svg] .titleText {
  color: var(--foreground);
  fill: var(--foreground);
}
[data-pretext-mermaid-svg] .edgePath path,
[data-pretext-mermaid-svg] .flowchart-link,
[data-pretext-mermaid-svg] .messageLine0,
[data-pretext-mermaid-svg] .messageLine1,
[data-pretext-mermaid-svg] .actor-line,
[data-pretext-mermaid-svg] .relation,
[data-pretext-mermaid-svg] .transition,
[data-pretext-mermaid-svg] line {
  fill: none;
  stroke: var(--muted-foreground);
}
[data-pretext-mermaid-svg] marker path,
[data-pretext-mermaid-svg] marker polygon,
[data-pretext-mermaid-svg] marker circle,
[data-pretext-mermaid-svg] .marker,
[data-pretext-mermaid-svg] .arrowMarkerPath {
  fill: var(--muted-foreground);
  stroke: var(--muted-foreground);
}
`;

const mermaidDiagramCache = new Map<string, Promise<DiagramState>>();
const mermaidScopedSvgCache = new Map<string, string>();

type MermaidApi = {
  initialize?: (config: typeof MERMAID_CONFIG) => void;
  render: (id: string, source: string) => Promise<{ svg: string }>;
};

type MmdrApi = {
  render: (source: string) => string;
};

type MmdrExports = {
  memory: WebAssembly.Memory;
  render: (
    sourceLength: number,
    baseThemeLength: number,
    themeLength: number,
    layoutLength: number,
  ) => number;
};

type MermaidRenderJob = {
  reject: (reason: unknown) => void;
  resolve: (state: DiagramState) => void;
  run: () => Promise<DiagramState>;
};

type MermaidIdleWindow = Window &
  typeof globalThis & {
    requestIdleCallback?: (
      callback: () => void,
      options?: { timeout?: number },
    ) => number;
  };

let mermaidApiPromise: Promise<MermaidApi> | null = null;
let mmdrApiPromise: Promise<MmdrApi> | null = null;
let isMmdrUnavailable = false;
let mermaidInitializedConfigKey = "";
let isMermaidRenderQueueRunning = false;
const mermaidRenderQueue: MermaidRenderJob[] = [];

export type DiagramRenderer = "basic" | "mermaid" | "mmdr";
export type DiagramState =
  | { status: "failed"; message: string }
  | { status: "loading" }
  | { renderer: DiagramRenderer; status: "ready"; svg: string };
type ReadyDiagramState = Extract<DiagramState, { status: "ready" }>;

export function resetMermaidRendererForTests() {
  mermaidDiagramCache.clear();
  mermaidScopedSvgCache.clear();
  mermaidRenderQueue.length = 0;
  mermaidApiPromise = null;
  mmdrApiPromise = null;
  isMmdrUnavailable = false;
  mermaidInitializedConfigKey = "";
  isMermaidRenderQueueRunning = false;
}

export async function renderDiagram(
  source: string,
  id: string,
): Promise<DiagramState> {
  const state = await getCachedDiagram(source);
  if (state.status !== "ready") return state;
  const scopedKey = `${DIAGRAM_CACHE_CONFIG_KEY}\0${state.renderer}\0${id}\0${source}`;
  let svg = mermaidScopedSvgCache.get(scopedKey);
  if (!svg) {
    svg = scopeCachedMermaidSvg(state.svg, id);
    mermaidScopedSvgCache.set(scopedKey, svg);
    trimMapToLimit(mermaidScopedSvgCache, MERMAID_SCOPED_CACHE_LIMIT);
  }
  return { renderer: state.renderer, status: "ready", svg };
}

function getCachedDiagram(source: string) {
  const key = `${DIAGRAM_CACHE_CONFIG_KEY}\0${source}`;
  let cached = mermaidDiagramCache.get(key);
  if (!cached) {
    cached = enqueueMermaidRender(() => loadDiagram(source));
    mermaidDiagramCache.set(key, cached);
    trimMapToLimit(mermaidDiagramCache, MERMAID_CACHE_LIMIT);
  }
  return cached;
}

function enqueueMermaidRender(run: () => Promise<DiagramState>) {
  return new Promise<DiagramState>((resolve, reject) => {
    mermaidRenderQueue.push({ reject, resolve, run });
    void drainMermaidRenderQueue();
  });
}

async function drainMermaidRenderQueue() {
  if (isMermaidRenderQueueRunning) return;
  isMermaidRenderQueueRunning = true;
  try {
    for (;;) {
      const job = mermaidRenderQueue.shift();
      if (!job) return;
      try {
        await waitForMermaidRenderSlot();
        job.resolve(await job.run());
      } catch (error) {
        job.reject(error);
      }
    }
  } finally {
    isMermaidRenderQueueRunning = false;
  }
}

function waitForMermaidRenderSlot() {
  if (typeof window === "undefined") return Promise.resolve();
  const idleWindow = window as MermaidIdleWindow;
  if (typeof idleWindow.requestIdleCallback === "function") {
    return new Promise<void>((resolve) => {
      idleWindow.requestIdleCallback?.(() => resolve(), { timeout: 250 });
    });
  }
  if (typeof window.requestAnimationFrame === "function") {
    return new Promise<void>((resolve) => {
      window.requestAnimationFrame(() => resolve());
    });
  }
  return new Promise<void>((resolve) => {
    window.setTimeout(resolve, 0);
  });
}

async function loadDiagram(source: string): Promise<DiagramState> {
  const mmdrState = await loadMmdrDiagram(source);
  if (mmdrState.status === "ready") return mmdrState;
  return loadMermaidDiagram(source);
}

async function loadMmdrDiagram(source: string): Promise<DiagramState> {
  try {
    const mmdr = await loadMmdrApi();
    return {
      renderer: "mmdr",
      status: "ready",
      svg: sanitizeSvg(mmdr.render(source)),
    };
  } catch {
    return { status: "failed", message: "mmdr unavailable" };
  }
}

async function loadMermaidDiagram(source: string): Promise<DiagramState> {
  try {
    const mermaid = await loadMermaidApi();
    const result = await mermaid.render(
      `markdown-diagram-cache-${hashMermaidSource(source)}`,
      source,
    );
    return {
      renderer: "mermaid",
      status: "ready",
      svg: sanitizeSvg(result.svg),
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : "Invalid diagram";
    if (!/getBBox|layout|force-basic-fallback/i.test(message)) {
      return { status: "failed", message };
    }
    return renderBasicMermaidDiagram(source);
  }
}

async function loadMmdrApi() {
  if (!canUseMmdrRenderer()) {
    throw new Error("mmdr is unavailable in this runtime");
  }
  if (isMmdrUnavailable) {
    throw new Error("mmdr failed to load");
  }
  mmdrApiPromise ??= instantiateMmdrApi().catch((error) => {
    isMmdrUnavailable = true;
    throw error;
  });
  return mmdrApiPromise;
}

async function loadMermaidApi() {
  mermaidApiPromise ??= import("mermaid").then(
    (mermaidModule) => mermaidModule.default as MermaidApi,
  );
  const mermaid = await mermaidApiPromise;
  if (mermaidInitializedConfigKey !== MERMAID_CACHE_CONFIG_KEY) {
    mermaid.initialize?.(MERMAID_CONFIG);
    mermaidInitializedConfigKey = MERMAID_CACHE_CONFIG_KEY;
  }
  return mermaid;
}

function canUseMmdrRenderer() {
  return (
    typeof window !== "undefined" &&
    typeof window.fetch === "function" &&
    typeof WebAssembly !== "undefined" &&
    typeof TextDecoder !== "undefined" &&
    typeof TextEncoder !== "undefined"
  );
}

async function instantiateMmdrApi(): Promise<MmdrApi> {
  let instance: WebAssembly.Instance | null = null;
  let currentArgs: Uint8Array[] = [];
  let currentResult: Uint8Array | null = null;
  const imports = {
    typst_env: {
      wasm_minimal_protocol_send_result_to_host(
        pointer: number,
        length: number,
      ) {
        if (!instance) return;
        const memory = new Uint8Array(
          (instance.exports as unknown as MmdrExports).memory.buffer,
        );
        currentResult = memory.slice(pointer, pointer + length);
      },
      wasm_minimal_protocol_write_args_to_buffer(pointer: number) {
        if (!instance) return;
        const memory = new Uint8Array(
          (instance.exports as unknown as MmdrExports).memory.buffer,
        );
        let offset = pointer;
        for (const arg of currentArgs) {
          memory.set(arg, offset);
          offset += arg.length;
        }
      },
    },
  };
  const response = await window.fetch(MMDR_WASM_URL);
  if (!response.ok) {
    throw new Error(`Failed to load mmdr renderer: ${response.status}`);
  }
  const wasm = await WebAssembly.instantiate(
    await response.arrayBuffer(),
    imports,
  );
  instance = wasm.instance;
  const exports = instance.exports as unknown as MmdrExports;
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();

  return {
    render(source: string) {
      currentArgs = [
        encoder.encode(source),
        encoder.encode(MMDR_BASE_THEME),
        encoder.encode(MMDR_THEME_JSON),
        encoder.encode(MMDR_LAYOUT),
      ];
      currentResult = null;
      const status = exports.render(
        currentArgs[0]?.length ?? 0,
        currentArgs[1]?.length ?? 0,
        currentArgs[2]?.length ?? 0,
        currentArgs[3]?.length ?? 0,
      );
      const result = decoder.decode(currentResult ?? new Uint8Array());
      currentArgs = [];
      currentResult = null;
      if (status !== 0) {
        throw new Error(result || "mmdr failed to render the diagram");
      }
      return result;
    },
  };
}

function scopeCachedMermaidSvg(svg: string, idPrefix: string) {
  if (typeof DOMParser === "undefined") return svg;
  const document = new DOMParser().parseFromString(svg, "image/svg+xml");
  const root = document.documentElement;
  if (!root || root.tagName.toLowerCase() !== "svg") return svg;

  const ids = new Map<string, string>();
  for (const element of Array.from(root.querySelectorAll("[id]"))) {
    const id = element.getAttribute("id");
    if (!id) continue;
    const nextId = `${idPrefix}-${id}`;
    ids.set(id, nextId);
    element.setAttribute("id", nextId);
  }
  if (!ids.size) return new XMLSerializer().serializeToString(root);

  for (const element of Array.from(root.querySelectorAll("*"))) {
    for (const attribute of Array.from(element.attributes)) {
      const value = attribute.value;
      let nextValue = value;
      for (const [oldId, nextId] of ids) {
        nextValue = nextValue
          .replaceAll(`url(#${oldId})`, `url(#${nextId})`)
          .replaceAll(`#${oldId}`, `#${nextId}`);
      }
      if (nextValue !== value) element.setAttribute(attribute.name, nextValue);
    }
  }

  return new XMLSerializer().serializeToString(root);
}

function hashMermaidSource(source: string) {
  let hash = 2166136261;
  for (let index = 0; index < source.length; index += 1) {
    hash ^= source.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(36);
}

function renderBasicMermaidDiagram(source: string): ReadyDiagramState {
  const kind = diagramKind(source);
  const lines = readableDiagramLines(source);
  const escaped = escapeHtml(lines.join(" | ") || describeDiagram(source));
  const piePaths =
    kind === "pie"
      ? '<path d="M20 20h20v20H20z"/><path d="M44 20h20v20H44z"/><path d="M68 20h20v20H68z"/>'
      : "";
  return {
    renderer: "basic",
    status: "ready",
    svg: `<svg role="img" aria-label="Mermaid diagram" data-pretext-basic-mermaid="${kind}" viewBox="0 0 720 160" width="100%" height="160" xmlns="http://www.w3.org/2000/svg"><rect width="720" height="160" rx="8" fill="currentColor" opacity="0.05"/>${piePaths}<text x="24" y="82" fill="currentColor" font-family="monospace" font-size="16">${escaped}</text></svg>`,
  };
}

function sanitizeSvg(svg: string) {
  if (typeof DOMParser === "undefined") return svg;

  const document = new DOMParser().parseFromString(svg, "image/svg+xml");
  const root = document.documentElement;
  if (!root || root.tagName.toLowerCase() !== "svg") {
    const fallback = renderBasicMermaidDiagram("");
    return fallback.status === "ready" ? fallback.svg : "";
  }

  root.setAttribute("role", "img");
  if (!root.getAttribute("aria-label")) {
    root.setAttribute("aria-label", "Mermaid diagram");
  }
  root.setAttribute("data-pretext-sanitized-mermaid", "");

  for (const element of Array.from(root.querySelectorAll("*"))) {
    const tagName = element.tagName.toLowerCase();
    if (
      tagName === "script" ||
      tagName === "a" ||
      tagName === "animate" ||
      tagName === "foreignobject" ||
      tagName === "image" ||
      tagName === "iframe" ||
      tagName === "object" ||
      tagName === "embed" ||
      tagName === "style" ||
      tagName === "use"
    ) {
      element.remove();
      continue;
    }

    for (const attribute of Array.from(element.attributes)) {
      const name = attribute.name.toLowerCase();
      const value = attribute.value.trim();
      if (name.startsWith("on") || name === "style") {
        element.removeAttribute(attribute.name);
        continue;
      }
      if ((name === "id" || name === "name") && isDomClobberingId(value)) {
        element.setAttribute(attribute.name, `user-content-${value}`);
        continue;
      }
      if (
        (name === "href" || name.endsWith(":href") || name === "src") &&
        !isSafeSvgReference(value)
      ) {
        element.removeAttribute(attribute.name);
      }
    }
  }

  for (const attribute of Array.from(root.attributes)) {
    const name = attribute.name.toLowerCase();
    if (name.startsWith("on") || name === "style") {
      root.removeAttribute(attribute.name);
      continue;
    }
    if (
      (name === "id" || name === "name") &&
      isDomClobberingId(attribute.value)
    ) {
      root.setAttribute(attribute.name, `user-content-${attribute.value}`);
    }
  }

  return new XMLSerializer()
    .serializeToString(root)
    .replace(/^<svg\b([^>]*)\brole="([^"]*)"([^>]*)>/, '<svg role="$2"$1$3>');
}

function trimMapToLimit<K, V>(map: Map<K, V>, limit: number) {
  while (map.size > limit) {
    const oldestKey = map.keys().next().value;
    if (oldestKey === undefined) break;
    map.delete(oldestKey);
  }
}

function isSafeSvgReference(value: string) {
  return (
    value === "" ||
    value.startsWith("#") ||
    value.startsWith("data:image/") ||
    /^https?:\/\//i.test(value)
  );
}

export function readDiagramLimitMessage(source: string) {
  if (source.length > MERMAID_MAX_SOURCE_LENGTH) {
    return "Mermaid diagram too large to render safely. Copy the source and render it in a dedicated diagram tool.";
  }
  if (source.split(/\r\n|[\n\r\u2028\u2029]/).length > MERMAID_MAX_LINES) {
    return "Mermaid diagram has too many lines to render safely. Copy the source and render it in a dedicated diagram tool.";
  }
  return null;
}

export function describeDiagram(source: string) {
  const lines = semanticDiagramLines(source);
  const kind = diagramKind(source);
  if (kind === "graph") {
    const direction = lines[0]
      ?.match(/\b(?:graph|flowchart)\s+([A-Z]{2})\b/i)?.[1]
      ?.toUpperCase();
    const directionText = direction === "LR" ? "left to right" : "top down";
    const edges = lines.filter((line) => /-->|---|-.->|==>/.test(line)).length;
    const nodes = new Set<string>();
    for (const line of lines) {
      for (const match of line.matchAll(/\b([A-Za-z][\w-]*)\b/g)) {
        const value = match[1]!;
        if (
          !["graph", "flowchart", "TD", "LR", "TB", "BT", "RL"].includes(value)
        ) {
          nodes.add(value);
        }
      }
    }
    return `Mermaid graph diagram flowing ${directionText}, with ${nodes.size} nodes and ${edges} edge${edges === 1 ? "" : "s"}.`;
  }
  if (kind === "sequence") {
    const messages = lines.filter((line) =>
      /->>|-->>|->|-->/.test(line),
    ).length;
    const participants = new Set<string>();
    for (const line of lines) {
      const participant = /^participant\s+\S+(?:\s+as\s+(.+))?/i.exec(line);
      if (participant)
        participants.add(participant[1] ?? line.split(/\s+/)[1]!);
      for (const side of line.split(/->>|-->>|->|-->/)) {
        const name = side.split(":")[0]?.trim();
        if (name && !/^sequenceDiagram/i.test(name)) participants.add(name);
      }
    }
    return `Mermaid sequence diagram with ${participants.size} participants and ${messages} message${messages === 1 ? "" : "s"}.`;
  }
  if (kind === "state") {
    const transitions = lines.filter((line) => /-->/.test(line)).length;
    const states = new Set<string>();
    for (const line of lines) {
      const stateAlias = /^state\s+"([^"]+)"\s+as\s+(\w+)/.exec(line);
      if (stateAlias) {
        states.add(stateAlias[1]!);
        continue;
      }
      if (!/-->/.test(line)) continue;
      for (const side of line.split(/-->/)) {
        const name = side
          .split(":")[0]!
          .replace(/\[\*\]/g, "")
          .trim();
        if (name && !/^stateDiagram/.test(name)) {
          states.add(name);
        }
      }
    }
    return `Mermaid state diagram with ${states.size} states and ${transitions} transitions.`;
  }
  if (kind === "class") {
    const relationships = lines.filter((line) =>
      /<\|--|--|<--|\.\./.test(line),
    ).length;
    const classes = new Set<string>();
    for (const line of lines) {
      for (const match of line.matchAll(/\b([A-Z][A-Za-z0-9_]*)\b/g))
        classes.add(match[1]!);
    }
    return `Mermaid class diagram with ${classes.size} classes and ${relationships} relationship${relationships === 1 ? "" : "s"}.`;
  }
  if (kind === "er") {
    const relationships = lines.filter((line) =>
      /\|\||o\{|\|\{/.test(line),
    ).length;
    const entities = new Set<string>();
    for (const line of lines) {
      for (const match of line.matchAll(/\b([A-Z][A-Z0-9_]*)\b/g)) {
        if (!["ERDIAGRAM"].includes(match[1]!)) entities.add(match[1]!);
      }
    }
    return `Mermaid entity relationship diagram with ${entities.size} entities and ${relationships} relationships.`;
  }
  if (kind === "journey")
    return `Mermaid journey diagram with ${countLinesStarting(lines, "section ")} sections and ${lines.filter((line) => /:\s*\d+\s*:/.test(line)).length} tasks.`;
  if (kind === "gantt")
    return `Mermaid Gantt chart with ${countLinesStarting(lines, "section ")} sections and ${lines.filter((line) => /:/.test(line) && !/^dateFormat/i.test(line) && !/^title\b/i.test(line)).length} tasks.`;
  if (kind === "gitGraph")
    return `Mermaid Git graph with ${countLinesStarting(lines, "branch ")} branch, ${countExact(lines, "commit")} commits, and ${countLinesStarting(lines, "merge ")} merge.`;
  if (kind === "timeline")
    return `Mermaid timeline with ${countLinesStarting(lines, "section ")} sections and ${lines.filter((line) => /^\d/.test(line)).length} events.`;
  if (kind === "mindmap")
    return `Mermaid mind map with ${Math.max(0, lines.length - 1)} nodes.`;
  if (kind === "quadrantChart")
    return `Mermaid quadrant chart with ${lines.filter((line) => /:\s*\[[^\]]+\]/.test(line)).length} points.`;
  if (kind === "requirementDiagram")
    return `Mermaid requirement diagram with ${countLinesStarting(lines, "requirement ")} requirement, ${countLinesStarting(lines, "element ")} element, and ${lines.filter((line) => /-\s*\w+\s*->/.test(line)).length} relationship.`;
  if (kind === "xychart") {
    const series = lines.filter((line) => /^(bar|line)\s+\[/.test(line));
    const values = series.reduce(
      (sum, line) => sum + (line.match(/-?\d+(?:\.\d+)?/g)?.length ?? 0),
      0,
    );
    return `Mermaid XY chart with ${series.length} series and ${values} values.`;
  }
  if (kind === "sankey") {
    const flows = lines.filter((line) => line.includes(","));
    const nodes = new Set(
      flows.flatMap((line) =>
        line
          .split(",")
          .slice(0, 2)
          .map((item) => item.trim()),
      ),
    );
    return `Mermaid Sankey diagram with ${nodes.size} nodes and ${flows.length} flows.`;
  }
  if (kind === "c4")
    return `Mermaid C4 diagram with ${lines.filter((line) => /^(Person|System)\(/.test(line)).length} nodes and ${countLinesStarting(lines, "Rel(")} relationship.`;
  if (kind === "pie") {
    const values = lines
      .flatMap((line) => line.match(/:\s*(\d+(?:\.\d+)?)/)?.[1] ?? [])
      .map(Number);
    const total = values.reduce((sum, value) => sum + value, 0);
    return `Mermaid pie chart with ${values.length} slices and total value ${total}.`;
  }
  return "Mermaid diagram";
}

export function estimateDiagramBodyHeight(source: string) {
  switch (diagramKind(source)) {
    case "graph":
      return 286;
    case "sequence":
      return 160;
    case "state":
      return 238;
    case "class":
      return 220;
    case "er":
      return 292;
    case "journey":
      return 270;
    case "gantt":
      return 300;
    case "gitGraph":
      return 212;
    case "timeline":
      return 270;
    case "mindmap":
      return 232;
    case "quadrantChart":
      return 296;
    case "requirementDiagram":
      return 230;
    case "xychart":
      return 334;
    case "sankey":
      return 310;
    case "c4":
      return 236;
    case "pie":
      return 224;
    default:
      return 180;
  }
}

function diagramKind(source: string) {
  const first = semanticDiagramLines(source)[0] ?? "";
  if (/^(?:graph|flowchart)\b/i.test(first)) return "graph";
  if (/^sequenceDiagram\b/i.test(first)) return "sequence";
  if (/^stateDiagram/i.test(first)) return "state";
  if (/^classDiagram\b/i.test(first)) return "class";
  if (/^erDiagram\b/i.test(first)) return "er";
  if (/^journey\b/i.test(first)) return "journey";
  if (/^gantt\b/i.test(first)) return "gantt";
  if (/^gitGraph\b/i.test(first)) return "gitGraph";
  if (/^timeline\b/i.test(first)) return "timeline";
  if (/^mindmap\b/i.test(first)) return "mindmap";
  if (/^quadrantChart\b/i.test(first)) return "quadrantChart";
  if (/^requirementDiagram\b/i.test(first)) return "requirementDiagram";
  if (/^xychart/i.test(first)) return "xychart";
  if (/^sankey/i.test(first)) return "sankey";
  if (/^C4/i.test(first)) return "c4";
  if (/^pie\b/i.test(first)) return "pie";
  return "source";
}

function semanticDiagramLines(source: string) {
  return stripMermaidFrontmatter(source)
    .split(/\r\n|[\n\r\u2028\u2029]/)
    .map((line) => line.trim())
    .filter((line) => line && !line.startsWith("%%"));
}

function readableDiagramLines(source: string) {
  const lines = semanticDiagramLines(source);
  if (diagramKind(source) === "pie") {
    return lines.map((line) => {
      const match = /"([^"]+)"\s*:\s*(\d+(?:\.\d+)?)/.exec(line);
      return match ? `${match[1]} ${match[2]} (${match[2]}%)` : line;
    });
  }
  return lines;
}

function stripMermaidFrontmatter(source: string) {
  const lines = source.split(/\r\n|[\n\r\u2028\u2029]/);
  if (lines[0]?.trim() !== "---") return source;
  const end = lines.findIndex(
    (line, index) => index > 0 && line.trim() === "---",
  );
  return end === -1 ? source : lines.slice(end + 1).join("\n");
}

function countLinesStarting(lines: readonly string[], prefix: string) {
  return lines.filter((line) => line.startsWith(prefix)).length;
}

function countExact(lines: readonly string[], value: string) {
  return lines.filter((line) => line === value).length;
}

function isDomClobberingId(id: string) {
  return ["constructor", "forms", "images", "location", "__proto__"].includes(
    id,
  );
}

function escapeHtml(value: string) {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}
registry/new-york-v4/ui/file-viewer-context.tsx
"use client";

import * as React from "react";

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

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

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

export const DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH = "10rem";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return shellContext;
}
registry/new-york-v4/ui/file-viewer-elements.ts
"use client";

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

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

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

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

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

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

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

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

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

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

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

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

const FILE_VIEWER_FLIGHT_RECORD_LIMIT = 8;
const FILE_VIEWER_FLIGHT_TICK_LIMIT = 240;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    notify();
  };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    documentSurfaceCustomProperties = nextNames;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return rendererFrame.layoutInlineSize ?? fallbackSize;
}

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

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

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

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

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

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

import * as React from "react";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

export const FILE_VIEWER_MOTION_EPSILON = 0.5;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

function areFileViewerMotionNumbersEqual(previous: number, next: number) {
  return Math.abs(previous - next) <= 0.001;
}
registry/new-york-v4/ui/markdown-greenfield-renderer-frame.ts
"use client";

import * as React from "react";

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

import { FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT } from "./file-viewer-elements";
import { createFileViewerAlignTranslateSurfaceMotionResolver } from "./file-viewer-fit-width-motion";
import {
  resolveFileViewerRendererLayoutInlineSize,
  type FileViewerDocumentAlign,
} from "./file-viewer-renderer-contract";
import {
  useOptionalFileViewerRendererEnvironment,
  useOptionalFileViewerRendererFrame,
} from "./file-viewer-renderer-frame";
import { MARKDOWN_GREENFIELD_CHUNK_MAX_INLINE_SIZE } from "./markdown-greenfield-layout";

export type MarkdownGreenfieldRendererFrame = {
  setDocumentSurfaceElement: React.RefCallback<HTMLDivElement>;
  transformOrigin: string;
  usesShellGeometry: boolean;
  viewportInlineSize: number;
};

export function useMarkdownGreenfieldRendererFrame({
  fallbackViewportInlineSize,
  onBeforeLayoutMotion,
}: {
  fallbackViewportInlineSize: number;
  onBeforeLayoutMotion: () => void;
}): MarkdownGreenfieldRendererFrame {
  const { registerDocumentSurface, usesShellGeometry } =
    useOptionalFileViewerRendererEnvironment();
  const rendererFrame = useOptionalFileViewerRendererFrame({
    fallbackInlineSize: fallbackViewportInlineSize,
  });
  const viewportInlineSize =
    resolveFileViewerRendererLayoutInlineSize({
      fallbackInlineSize: fallbackViewportInlineSize,
      rendererFrame,
    }) ?? fallbackViewportInlineSize;
  const surfaceRef = React.useRef<HTMLDivElement | null>(null);
  const [surfaceElement, setSurfaceElement] =
    React.useState<HTMLDivElement | null>(null);
  const handleBeforeLayoutMotion = React.useCallback(() => {
    onBeforeLayoutMotion();
  }, [onBeforeLayoutMotion]);
  const setDocumentSurfaceElement = React.useCallback(
    (element: HTMLDivElement | null) => {
      const previousElement = surfaceRef.current;
      if (previousElement === element) return;
      previousElement?.removeEventListener(
        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,
        handleBeforeLayoutMotion,
      );
      surfaceRef.current = element;
      setSurfaceElement((previous) =>
        previous === element ? previous : element,
      );
      if (!element) return;
      element.addEventListener(
        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,
        handleBeforeLayoutMotion,
      );
    },
    [handleBeforeLayoutMotion],
  );
  // Commit-then-relax's relax half. The canvas commits the motion's TARGET
  // width via minWidth from the first sliding frame, so on a widening pane
  // the centered chunks would recenter synchronously with the click; the
  // resolver translates them back to the live width's align margin and eases
  // to identity. The chunk column is CLAMPED (max inline size), not
  // fit-width — its content width does not scale with the pane — so the
  // reprojection is translate-only, never a scale.
  //
  // The align is the CHUNK COLUMN's, not rendererFrame.align: the shell
  // declares the document frame "start" (inert — the frame is w-full), while
  // the column centers itself inside the canvas by its own markup (left-1/2
  // -translate-x-1/2, physical and direction-independent). Deriving from the
  // shell align here made the resolver a silent no-op and the close-leg snap
  // survived it.
  const resolveSurfaceMotionStyle = React.useMemo(
    () =>
      createFileViewerAlignTranslateSurfaceMotionResolver({
        align: "center",
        direction: rendererFrame.direction,
        maxStageInlineSize: MARKDOWN_GREENFIELD_CHUNK_MAX_INLINE_SIZE,
      }),
    [rendererFrame.direction],
  );
  const documentSurfaceKey = surfaceElement
    ? joinEffectKey([
        "markdown-document-surface",
        surfaceElement,
        registerDocumentSurface,
        resolveSurfaceMotionStyle,
      ])
    : null;
  useKeyedLayoutEffect(documentSurfaceKey, () => {
    if (!surfaceElement) return;
    return registerDocumentSurface({
      element: surfaceElement,
      resolveMotionStyle: resolveSurfaceMotionStyle,
    });
  });

  return React.useMemo(
    () => ({
      setDocumentSurfaceElement,
      transformOrigin: getMarkdownGreenfieldDocumentTransformOrigin(
        rendererFrame.align,
      ),
      usesShellGeometry,
      viewportInlineSize,
    }),
    [
      rendererFrame.align,
      setDocumentSurfaceElement,
      usesShellGeometry,
      viewportInlineSize,
    ],
  );
}

function getMarkdownGreenfieldDocumentTransformOrigin(
  align: FileViewerDocumentAlign,
) {
  switch (align) {
    case "start":
      return "left top";
    case "end":
      return "right top";
    case "center":
      return "center top";
  }
}
registry/new-york-v4/ui/viewer-types.ts
import type * as React from "react";

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

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

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

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

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

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

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

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

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

export type ViewerDocumentScrollMapper = {
  getLogicalScrollTop: (input: {
    blockSize: number;
    physicalScrollTop: number;
    scrollPageOffset: number;
    viewportBlockSize: number;
  }) => number;
  getPhysicalScrollSize: (input: {
    blockSize: number;
    viewportBlockSize: number;
  }) => number;
  resolvePhysicalScrollPosition: (input: {
    blockSize: number;
    logicalScrollTop: number;
    scrollPageOffset: number;
    viewportBlockSize: number;
  }) => ViewerDocumentPhysicalScrollPosition;
};

export type ViewerDocumentScrollMetrics = {
  physicalScrollSize: number;
  physicalScrollTop: number;
  scrollPageOffset: number;
  scrollTop: number;
  viewportBlockSize: number;
};

export type ViewerDocumentScrollTargetResolver<Anchor, Target> = (input: {
  layout: ViewerDocumentLayoutModel<Anchor>;
  scrollTop: number;
  target: Target;
  viewportElement: HTMLDivElement;
}) => ViewerDocumentResolvedScrollTarget | null;

// A zoom step is the one geometry change whose intent is "zoom the camera",
// not "keep my reading position": it re-anchors the viewport CENTER on both
// axes and relaxes a FLIP about that fixed point. `capture` runs in the zoom
// gesture's own task against the pre-zoom layout and painted DOM;
// `resolveScrollTarget` and `play` run inside the geometry commit against the
// post-zoom layout (commit-then-relax).
export type ViewerDocumentZoomMotionBypassReason =
  | "resolve-failed"
  | "shell-transition"
  | "stale-intent";

export type ViewerDocumentZoomMotionController<Transaction = unknown> = {
  capture: (input: {
    scrollTop: number;
    viewportElement: HTMLDivElement;
  }) => Transaction | null;
  /**
   * Telemetry tap: a captured zoom intent reached a geometry commit but the
   * zoom lane declined it. Without this the bypass is invisible — the commit
   * falls back to the reading-anchor restore and no flight is recorded.
   */
  noteBypass?: (reason: ViewerDocumentZoomMotionBypassReason) => void;
  resolveScrollTarget: (input: {
    transaction: Transaction;
    viewportElement: HTMLDivElement;
  }) => ViewerDocumentResolvedScrollTarget | null;
  play: (input: {
    transaction: Transaction;
    viewportElement: HTMLDivElement;
  }) => (() => void) | null;
};

export type ViewerGeometrySnapshot = {
  bodyInlineSize: number;
  documentInlineSize: number;
  hasMeasuredBody: boolean;
  isTransitioning: boolean;
  mode: ViewerSidebarMode;
  open: boolean;
  progress: number;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarInlineSize: number;
  sidebarWidth: number;
  side: ViewerSidebarSide;
  state: ViewerSidebarState;
  transitionPhase: ViewerGeometryTransitionPhase;
};

export type ViewerGeometryStore = {
  getSnapshot: () => ViewerGeometrySnapshot;
  setTarget: (target: ViewerGeometryTarget) => void;
  subscribe: (listener: () => void) => () => void;
};

export type ViewerGeometryTarget = {
  bodyElement: HTMLElement | null;
  mode: ViewerSidebarMode;
  open: boolean;
  rootElement: HTMLElement | null;
  sidebarElement: HTMLElement | null;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarWidth: number;
  side: ViewerSidebarSide;
  state: ViewerSidebarState;
};

export type ViewerSidebarStateValue = {
  state: ViewerSidebarState;
  open: boolean;
  setOpen: (value: boolean | ((open: boolean) => boolean)) => void;
  toggleSidebar: () => void;
  canToggleSidebar: boolean;
  mode: ViewerSidebarMode;
  side: ViewerSidebarSide;
};

export type ViewerRootProps = React.ComponentProps<"div"> & {
  defaultOpen?: boolean;
  inlineBreakpoint?: number;
  mode?: ViewerSidebarRequestedMode;
  onOpenChange?: (open: boolean) => void;
  open?: boolean;
  sidebarCollapsible?: ViewerSidebarCollapsible;
  sidebarGapTransition?: ViewerSidebarGapTransition;
  sidebarSide?: ViewerSidebarSide;
  stateNamespace?: ViewerStateAttributeNamespace;
};

export type ViewerFrameProps = React.ComponentProps<"div">;
export type ViewerHeaderProps = React.ComponentProps<"div">;
export type ViewerBodyProps = React.ComponentProps<"div">;
export type ViewerSurfaceProps = React.ComponentProps<"div">;
export type ViewerViewportProps = React.ComponentProps<"div">;
export type ViewerDocumentFrameProps = React.ComponentProps<"div"> & {
  align?: ViewerDocumentFrameAlign;
  maxInlineSize?: React.CSSProperties["maxInlineSize"];
};

export type ViewerStateAttributeNamespace = {
  prefix: string;
  slots?: {
    body?: boolean;
    root?: boolean;
    sidebar?: boolean;
  };
};

export type ViewerSidebarRegistration = {
  collapsible: ViewerSidebarCollapsible;
  element: HTMLElement;
  id: string;
  instanceId: string;
  side: ViewerSidebarSide;
  width: string;
  widthPixels: number;
};

export type ViewerPortalContainmentAttributes = {
  "data-viewer-portal-root-id": string;
};

export type ViewerSidebarRegistrationState = {
  defaultSidebarCollapsible: ViewerSidebarCollapsible;
  defaultSidebarSide: ViewerSidebarSide;
  geometryStore: ViewerGeometryStore;
  getRootElement: () => HTMLElement | null;
  hasSidebar: boolean;
  registerBody: (element: HTMLElement) => () => void;
  registerSidebar: (registration: ViewerSidebarRegistration) => () => void;
  rootId: string;
  sidebarId: string;
  sidebarGapTransition: ViewerSidebarGapTransition;
  sidebarSide: ViewerSidebarSide;
  setLastTriggerElement: (element: HTMLElement | null) => void;
  stateNamespace?: ViewerStateAttributeNamespace;
};

export type ViewerRootDiagnostics = {
  getRootElement: () => HTMLElement | null;
  layoutSignature: string;
  rootId: string;
};

export type ViewerSurfaceMeasurement = {
  hasMeasured: boolean;
  setViewportElement: React.RefCallback<HTMLDivElement>;
  viewportElement: HTMLDivElement | null;
  viewportHeight: number | null;
  viewportWidth: number | null;
};

export type ViewerSidebarSlotNames = {
  container?: string;
  gap?: string;
  inner?: string;
};

export type ViewerStateAttributeSlot = "body" | "root" | "sidebar";
export type ViewerStateAttributeValues = {
  hasSidebar?: boolean;
  sidebarCollapsible?: ViewerSidebarCollapsible;
  sidebarMode?: ViewerSidebarMode;
  sidebarOpen?: boolean;
  sidebarSide?: ViewerSidebarSide;
  sidebarState?: ViewerSidebarState;
};
export type ViewerDataAttributes = Record<`data-${string}`, string | undefined>;

export type ViewerSidebarProps = React.ComponentProps<"aside"> &
  ViewerDataAttributes & {
    side?: ViewerSidebarSide;
    collapsible?: ViewerSidebarCollapsible;
    innerClassName?: string;
    namespacedSlot?: string;
    namespacedSlotNames?: ViewerSidebarSlotNames;
    slotNames?: ViewerSidebarSlotNames;
    width?: string;
  };
registry/new-york-v4/ui/viewer-measurement.ts
"use client";

import * as React from "react";

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

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

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

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

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

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

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

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

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

  return direction;
}

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

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

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

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

  return { height, width };
}

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

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

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

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

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

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

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

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

      observer.observe(element);

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

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

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

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

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

  return resolvedLength;
}

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

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

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

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

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

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

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

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

import type { FileViewerDocumentSurfaceMotionResolver } from "./file-viewer-motion-kernel";
import type { FileViewerMotionFrame } from "./file-viewer-motion-plan";
import type {
  FileViewerDocumentAlign,
  FileViewerInlineDirection,
} from "./file-viewer-renderer-contract";

export const FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY =
  "--file-viewer-fit-width-anchor-block";

// Commit-then-relax: the renderer lays out at the motion's TARGET width from
// the first commit, and this resolver reprojects that settled layout to the
// in-flight visual width with one uniform transform. The transform terminates
// on identity, so settle removes a no-op style instead of committing layout.
//
// The anchor custom property is the reading line's block offset in the settled
// stage's own coordinates (post-rebase scrollTop + marker offset). It is read
// live via var(), so the renderer writes it once per motion (in a layout
// effect after the slide-start scroll rebase) without re-entering the kernel.
export function createFileViewerFitWidthSurfaceMotionResolver({
  align,
  anchorBlockProperty = FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY,
  direction = "ltr",
  isFitWidth,
  stageInlineSize,
  stageOuterInlinePadding = 0,
  stageInlinePadding = 0,
  stageInlineSlope = 1,
  stageBlockSlope = stageInlineSlope,
}: {
  align: FileViewerDocumentAlign;
  anchorBlockProperty?: string;
  direction?: FileViewerInlineDirection;
  isFitWidth: boolean;
  stageInlineSize: number;
  /** Constant symmetric padding around the transformed stage. */
  stageOuterInlinePadding?: number;
  /** Constant symmetric inline padding inside the transformed stage. */
  stageInlinePadding?: number;
  stageInlineSlope?: number;
  /**
   * Slope for the BLOCK axis when it differs from the inline one. A stage
   * whose inline box carries constant padding while its block stack scales
   * with the content (the image viewer: fit subtracts the horizontal
   * padding, vertical gaps/padding scale) has two different affine models —
   * X tracks the pane 1:1 while Y scales by the content ratio — and a
   * uniform scale cannot land both axes exactly. Defaults to the inline
   * slope (uniform scale) for fully proportional stages like the PDF.
   */
  stageBlockSlope?: number;
}): FileViewerDocumentSurfaceMotionResolver {
  return (frame) => {
    if (!isFitWidth || frame.phase !== "sliding") {
      return {
        transform: "",
        transformOrigin: "",
        willChange: "",
      };
    }

    return {
      transform: getFileViewerFitWidthSurfaceMotionTransform({
        align,
        anchorBlockProperty,
        direction,
        frame,
        stageInlineSize,
        stageOuterInlinePadding,
        stageInlinePadding,
        stageInlineSlope,
        stageBlockSlope,
      }),
      transformOrigin: "0px 0px",
      willChange: "transform",
    };
  };
}

// Commit-then-relax for a CLAMPED reading column rather than a fit-width
// stage: the stage's inline size is min(canvas, maxStageInlineSize), so it
// does not scale with the pane — the only thing a width change moves is the
// align margin. The canvas commits the motion's TARGET width from the first
// sliding frame (minWidth under layoutPolicy "target"), which means a
// widening pane's chunks land at the settled margin synchronously with the
// click; this resolver reprojects them back to the live width's margin with
// a translate that terminates on identity. A narrowing pane never engages it
// (the canvas tracks the live width above its minWidth, so live and settled
// margins agree) — exactly the leg that already glides on layout.
export function createFileViewerAlignTranslateSurfaceMotionResolver({
  align,
  direction = "ltr",
  maxStageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  direction?: FileViewerInlineDirection;
  /** The column's max inline size (the chunk's max-width, in px). */
  maxStageInlineSize: number;
}): FileViewerDocumentSurfaceMotionResolver {
  return (frame) => {
    if (frame.phase !== "sliding") {
      return {
        transform: "",
        transformOrigin: "",
        willChange: "",
      };
    }

    return {
      transform: getFileViewerAlignTranslateSurfaceMotionTransform({
        align,
        direction,
        frame,
        maxStageInlineSize,
      }),
      transformOrigin: "0px 0px",
      willChange: "transform",
    };
  };
}

function getFileViewerAlignTranslateSurfaceMotionTransform({
  align,
  direction,
  frame,
  maxStageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  direction: FileViewerInlineDirection;
  frame: FileViewerMotionFrame;
  maxStageInlineSize: number;
}) {
  if (
    !Number.isFinite(maxStageInlineSize) ||
    maxStageInlineSize <= 0 ||
    frame.layoutInlineSize <= 0 ||
    frame.toInlineSize <= 0
  ) {
    return "";
  }

  // The canvas lays out at max(live, target): minWidth holds the committed
  // target under a still-narrow pane, and a pane wider than the target just
  // fills. The stage (reading column) centers/aligns INSIDE the canvas, and
  // an overflowing canvas itself pins to the pane's start edge — left in
  // LTR, right in RTL — so the stage's pane-space position carries the
  // canvas offset too.
  const canvasInlineSize = Math.max(frame.layoutInlineSize, frame.toInlineSize);
  const stageInlineSize = Math.min(canvasInlineSize, maxStageInlineSize);
  const canvasInlineOffset =
    direction === "rtl"
      ? Math.min(0, frame.layoutInlineSize - canvasInlineSize)
      : 0;
  const settledStageLeft =
    canvasInlineOffset +
    getFileViewerStageInlineMargin({
      align,
      availableInlineSize: canvasInlineSize,
      direction,
      stageInlineSize,
    });
  const liveStageLeft = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: frame.layoutInlineSize,
    direction,
    stageInlineSize,
  });
  const translateX = liveStageLeft - settledStageLeft;

  if (Math.abs(translateX) <= 0.001) return "";

  return `translate3d(${formatFileViewerMotionPixel(translateX)}px, 0px, 0)`;
}

export function getFileViewerFitWidthScale({
  availableInlineSize,
  contentInlineSize,
  stageInlinePadding = 0,
}: {
  availableInlineSize: number;
  contentInlineSize: number;
  stageInlinePadding?: number;
}) {
  if (availableInlineSize <= 0 || contentInlineSize <= 0) return 1;

  const contentAvailableInlineSize = Math.max(
    1,
    availableInlineSize - stageInlinePadding,
  );
  return contentAvailableInlineSize / contentInlineSize;
}

// The visual scale the resolver renders for a given live width — the same
// affine reprojection as the transform itself. Renderers use it to reason
// about the on-screen state (anchor capture/solve) without duplicating the
// formula.
//
// stageInlineSlope is how many stage pixels the settled stage grows per pane
// pixel. It is 1 whenever the stage IS the fit-width content (image, docx,
// pptx, uniform-width PDFs: stage = pane − constant padding), but a stage
// that is WIDER than its fit basis grows faster than the pane — a PDF fits
// its FIRST page while the stage spans its WIDEST page, so a mixed-width
// document has slope maxBase/fitBase > 1. A unit-slope assumption there
// under-scales the first frame by (slope − 1)·delta/stage — measured as a
// ~7px content step at the anchor-hold frame of a 355-page prospectus.
export function getFileViewerFitWidthVisualScale({
  liveInlineSize,
  stageInlineSize,
  stageInlineSlope = 1,
  targetInlineSize,
}: {
  liveInlineSize: number;
  stageInlineSize: number;
  stageInlineSlope?: number;
  targetInlineSize: number;
}) {
  if (
    stageInlineSize <= 0 ||
    !Number.isFinite(liveInlineSize) ||
    !Number.isFinite(targetInlineSize)
  ) {
    return 1;
  }
  const slope =
    Number.isFinite(stageInlineSlope) && stageInlineSlope > 0
      ? stageInlineSlope
      : 1;
  return (
    Math.max(1, stageInlineSize + slope * (liveInlineSize - targetInlineSize)) /
    stageInlineSize
  );
}

// Capture side of the motion anchor: the probe content line's on-screen block
// offset relative to the scroll box, taken just before a motion (or retarget)
// commits. When a motion is already in flight the DOM is the settled layout
// PLUS the live transform, so the capture applies that transform — otherwise
// a retarget would solve continuity against a picture the reader never saw.
export function captureFileViewerFitWidthAnchorScreenOffset({
  lastAnchorBlock,
  liveFrame,
  probeStageOffset,
  scrollTop,
  stageInlineSize,
  stageInlinePadding = 0,
  stageBlockSlope = 1,
}: {
  lastAnchorBlock: number | null;
  liveFrame: FileViewerMotionFrame | null;
  probeStageOffset: number;
  scrollTop: number;
  stageInlineSize: number;
  stageInlinePadding?: number;
  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */
  stageBlockSlope?: number;
}) {
  const untransformed = probeStageOffset - scrollTop;
  if (!liveFrame || liveFrame.phase !== "sliding") return untransformed;

  const liveScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: liveFrame.layoutInlineSize,
    stageInlineSize: getFileViewerFitWidthContentInlineSize({
      stageInlinePadding,
      stageInlineSize,
    }),
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: liveFrame.toInlineSize,
  });
  if (Math.abs(1 - liveScale) <= 0.001) return untransformed;

  return (
    liveScale * probeStageOffset +
    (1 - liveScale) * (lastAnchorBlock ?? 0) -
    scrollTop
  );
}

// Solve side: the anchor block offset that puts the probe content line back on
// its captured screen position under the NEW layout model at the motion's
// first-frame scale. Exact regardless of how the rebase clamped or how the
// old/new layout models relate (measured page sizes, constant gaps/padding).
// Returns null when the motion is degenerate (caller falls back to the live
// reading marker).
export function resolveFileViewerFitWidthMotionAnchorBlock({
  fromInlineSize,
  probeScreenOffset,
  probeStageOffset,
  scrollTop,
  stageInlineSize,
  stageInlinePadding = 0,
  stageBlockSlope = 1,
  toInlineSize,
}: {
  fromInlineSize: number | null;
  probeScreenOffset: number;
  probeStageOffset: number;
  scrollTop: number;
  stageInlineSize: number;
  stageInlinePadding?: number;
  /** The BLOCK-axis slope — anchor capture/solve is block-axis math. */
  stageBlockSlope?: number;
  toInlineSize: number | null;
}) {
  if (fromInlineSize == null || toInlineSize == null) return null;

  const startScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: fromInlineSize,
    stageInlineSize: getFileViewerFitWidthContentInlineSize({
      stageInlinePadding,
      stageInlineSize,
    }),
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: toInlineSize,
  });
  if (!Number.isFinite(startScale) || Math.abs(1 - startScale) <= 0.001) {
    return null;
  }

  return (
    (probeScreenOffset + scrollTop - startScale * probeStageOffset) /
    (1 - startScale)
  );
}

function getFileViewerFitWidthSurfaceMotionTransform({
  align,
  anchorBlockProperty,
  direction,
  frame,
  stageInlineSize,
  stageOuterInlinePadding,
  stageInlinePadding,
  stageInlineSlope,
  stageBlockSlope,
}: {
  align: FileViewerDocumentAlign;
  anchorBlockProperty: string;
  direction: FileViewerInlineDirection;
  frame: FileViewerMotionFrame;
  stageInlineSize: number;
  stageOuterInlinePadding: number;
  stageInlinePadding: number;
  stageInlineSlope: number;
  stageBlockSlope: number;
}) {
  if (
    stageInlineSize <= 0 ||
    frame.layoutInlineSize <= 0 ||
    frame.toInlineSize <= 0
  ) {
    return "";
  }

  // Fit-width renderers size their stage as an affine function of the
  // available width (stage = slope × width − constant padding), so the
  // in-flight visual stage is the settled stage plus the scaled live width
  // delta. At the first frame this resolves to exactly the pre-toggle stage
  // size, and at the last frame to the settled stage — identity. Each axis
  // carries its own slope: they differ when the stage's inline box holds
  // constant padding while its block stack scales with the content.
  const contentInlineSize = getFileViewerFitWidthContentInlineSize({
    stageInlinePadding,
    stageInlineSize,
  });
  const inlineScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: frame.layoutInlineSize,
    stageInlineSize: contentInlineSize,
    stageInlineSlope,
    targetInlineSize: frame.toInlineSize,
  });
  const blockScale = getFileViewerFitWidthVisualScale({
    liveInlineSize: frame.layoutInlineSize,
    stageInlineSize: contentInlineSize,
    stageInlineSlope: stageBlockSlope,
    targetInlineSize: frame.toInlineSize,
  });
  const visualStageInlineSize =
    inlineScale * contentInlineSize + stageInlinePadding;
  const availableStageInlineSize = Math.max(
    1,
    frame.layoutInlineSize - Math.max(0, stageOuterInlinePadding),
  );
  const settledMargin = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: availableStageInlineSize,
    direction,
    stageInlineSize,
  });
  const visualMargin = getFileViewerStageInlineMargin({
    align,
    availableInlineSize: availableStageInlineSize,
    direction,
    stageInlineSize: visualStageInlineSize,
  });
  // The padding is constant in both endpoint layouts. Scaling the outer stage
  // would scale that inset too, making the visible page briefly too wide or
  // narrow on the first frame. Rebase the symmetric start inset so the inner
  // content edge, not the transparent wrapper edge, is pixel-continuous.
  const inlinePaddingStart = stageInlinePadding / 2;
  const translateX =
    visualMargin - settledMargin + (1 - inlineScale) * inlinePaddingStart;

  if (Math.abs(frame.layoutInlineSize - frame.toInlineSize) <= 0.001) {
    return "";
  }

  const formattedInlineScale = formatFileViewerMotionScale(inlineScale);
  const formattedBlockScale = formatFileViewerMotionScale(blockScale);
  const formattedTranslateX = formatFileViewerMotionPixel(translateX);
  // Scale about the stage origin; the anchor term keeps the reading line
  // fixed on the block axis: y' = s·y + (1 − s)·anchor equals y at
  // y = anchor.
  const translateY = `calc((1 - ${formattedBlockScale}) * var(${anchorBlockProperty}, 0px))`;
  const formattedScale =
    formattedInlineScale === formattedBlockScale
      ? formattedInlineScale
      : `${formattedInlineScale}, ${formattedBlockScale}`;

  return `translate3d(${formattedTranslateX}px, ${translateY}, 0) scale(${formattedScale})`;
}

function getFileViewerFitWidthContentInlineSize({
  stageInlinePadding,
  stageInlineSize,
}: {
  stageInlinePadding: number;
  stageInlineSize: number;
}) {
  const padding = Number.isFinite(stageInlinePadding)
    ? Math.max(0, stageInlinePadding)
    : 0;
  return Math.max(1, stageInlineSize - padding);
}

// Physical LEFT offset of the stage box inside the available inline size —
// translateX shifts along the physical X axis, so the model must speak
// physical-left in both directions. Stages align with physical auto margins
// (mx-auto for center, ml-auto for end, plain flow for start), so:
// - free space ≥ 0: center splits it; end pins right in both directions
//   (ml-auto is physical); start follows flow (left in LTR, right in RTL).
// - free space < 0 (the settled stage overflows the live container — the
//   close leg's early frames): auto margins collapse to 0 and CSS resolves
//   the over-constraint against the direction's end edge, pinning the box to
//   the start edge — left edge at 0 in LTR, at the negative free space in
//   RTL. The old unconditional max(0, …) clamp encoded only the LTR half and
//   made the RTL close leg overshoot by the overflow amount.
function getFileViewerStageInlineMargin({
  align,
  availableInlineSize,
  direction,
  stageInlineSize,
}: {
  align: FileViewerDocumentAlign;
  availableInlineSize: number;
  direction: FileViewerInlineDirection;
  stageInlineSize: number;
}) {
  const freeInlineSize = availableInlineSize - stageInlineSize;
  if (freeInlineSize < 0) return direction === "rtl" ? freeInlineSize : 0;

  switch (align) {
    case "start":
      return direction === "rtl" ? freeInlineSize : 0;
    case "end":
      return freeInlineSize;
    case "center":
      return freeInlineSize / 2;
  }
}

function formatFileViewerMotionPixel(value: number) {
  return Number.isFinite(value) ? Number(value.toFixed(3)) : 0;
}

function formatFileViewerMotionScale(value: number) {
  return Number.isFinite(value) ? String(Number(value.toFixed(6))) : "1";
}

More from retab

Browse all components in this library →