export const DOCS_RULER_MIN_MARGIN_PX = 12 export const DOCS_RULER_MIN_BODY_PX = 48 export type DocsRulerMarginSide = "left" | "right" | "top" | "bottom" export type DocsPageMarginsPx = { top: number right: number bottom: number left: number } export function clampPageMarginPx( side: DocsRulerMarginSide, valuePx: number, margins: DocsPageMarginsPx, pageWidth: number, pageHeight: number ): number { const min = DOCS_RULER_MIN_MARGIN_PX const bodyMin = DOCS_RULER_MIN_BODY_PX switch (side) { case "left": return Math.round( Math.max(min, Math.min(valuePx, pageWidth - margins.right - bodyMin)) ) case "right": { const right = Math.round( Math.max(min, Math.min(valuePx, pageWidth - margins.left - bodyMin)) ) return right } case "top": return Math.round( Math.max(min, Math.min(valuePx, pageHeight - margins.bottom - bodyMin)) ) case "bottom": { const bottom = Math.round( Math.max(min, Math.min(valuePx, pageHeight - margins.top - bodyMin)) ) return bottom } } } /** Horizontal ruler X → left margin px. */ export function pagePxFromHorizontalPointer( pointerX: number, rulerLeft: number, scale: number ): number { if (scale <= 0) return 0 return (pointerX - rulerLeft) / scale } /** Vertical ruler Y → distance from page top px. */ export function pagePxFromVerticalPointer( pointerY: number, rulerTop: number, scale: number ): number { if (scale <= 0) return 0 return (pointerY - rulerTop) / scale } export function mergeMarginPreview( margins: DocsPageMarginsPx, preview: Partial | null | undefined ): DocsPageMarginsPx { if (!preview) return margins return { ...margins, ...preview } }