// The viewer context the drawing overlay reads: pointer to page fractions, mark drags, the page size. import { createContext, useContext, type PointerEvent as ReactPointerEvent } from "react"; import type { Frac } from "../lib/geometry"; /** The bitmap: at most 2.2 times the PDF size and at most 4200 px wide. */ export function renderScale(pdfWidth: number): number { return Math.min(2.2, 4200 / Math.max(1, pdfWidth)); } /** A pointer that moved less than this is a tap. */ export const TAP_PX = 6; /** A mark drag starts after this. */ export const DRAG_PX = 5; export interface DragHandlers { /** The pointer is at f (page fractions, not clamped). */ onMove: (f: Frac) => void; /** The drag ended at f, or null when it never moved (a tap). */ onEnd: (f: Frac | null) => void; } export interface ViewerApi { /** A client point as page fractions, and whether it lies inside the viewer frame. */ toFrac: (clientX: number, clientY: number) => Frac & { inside: boolean }; /** Starts a mark drag from a pointerdown. Moves and ends follow the window. */ startDrag: (e: ReactPointerEvent, h: DragHandlers) => void; /** True for a short while after a drag ended, so the click that follows it is ignored. */ justDragged: () => boolean; } export interface ViewerInfo extends ViewerApi { /** Page size in bitmap pixels; 0 until the page is drawn. */ w: number; h: number; aspect: number; } export const ViewerCtx = createContext(null); /** The viewer the overlay lives in. Marks use it to turn pointers into page fractions. */ export function useViewer(): ViewerInfo { const v = useContext(ViewerCtx); if (!v) throw new Error("useViewer must be used inside MapViewer"); return v; }