@magicpages/kalotyp-core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/events/event-bus.ts","../src/canvas/load-image.ts","../src/canvas/viewport.ts","../src/canvas/viewport-controller.ts","../src/canvas/bake-canvas.ts","../src/geometry/rect.ts","../src/state/store.ts","../src/history/history.ts","../src/plugins/crop/aspect-ratio.ts","../src/plugins/crop/resize.ts","../src/plugins/crop/preset-filter.ts","../src/plugins/crop/state.ts","../src/plugins/crop/bake.ts","../src/output/state.ts","../src/pipeline/exif.ts","../src/pipeline/encode.ts","../src/pipeline/run-chain.ts","../src/plugins/flip/state.ts","../src/plugins/flip/bake.ts","../src/plugins/rotate/state.ts","../src/plugins/rotate/inscribe.ts","../src/plugins/rotate/bake.ts","../src/plugins/resize/state.ts","../src/plugins/resize/bake.ts","../src/plugins/finetune/state.ts","../src/plugins/finetune/math.ts","../src/plugins/finetune/bake.ts","../src/plugins/filter/presets.ts","../src/plugins/annotate/state.ts","../src/plugins/annotate/geometry.ts","../src/plugins/annotate/hit-test.ts","../src/plugins/annotate/smooth.ts","../src/plugins/annotate/bake.ts","../src/plugins/redact/state.ts","../src/plugins/redact/bake.ts","../src/plugins/frame/state.ts","../src/plugins/frame/bake.ts"],"sourcesContent":["export type EventListener<T> = (payload: T) => void;\nexport type Unsubscribe = () => void;\n\nexport class EventBus<TEvents extends Record<string, unknown>> {\n private readonly listeners = new Map<keyof TEvents, Set<EventListener<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, listener: EventListener<TEvents[K]>): Unsubscribe {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as EventListener<unknown>);\n return () => {\n set?.delete(listener as EventListener<unknown>);\n };\n }\n\n off<K extends keyof TEvents>(event: K, listener: EventListener<TEvents[K]>): void {\n this.listeners.get(event)?.delete(listener as EventListener<unknown>);\n }\n\n emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of [...set]) {\n try {\n (listener as EventListener<TEvents[K]>)(payload);\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","export interface LoadedImage {\n readonly element: ImageBitmap | HTMLImageElement;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Load a source image and decode it for canvas use.\n *\n * Modern path uses `createImageBitmap(blob, { imageOrientation: 'from-image' })`\n * so EXIF orientation is baked into the pixels — without this, phone photos\n * load sideways because canvas `drawImage` ignores the EXIF flag.\n * Fallback to `HTMLImageElement` when `createImageBitmap` isn't usable;\n * EXIF orientation is not applied on that path.\n */\nexport async function loadImage(src: string | Blob | File): Promise<LoadedImage> {\n if (typeof createImageBitmap === 'function') {\n const blob = await toBlob(src);\n if (blob) {\n try {\n const bitmap = await createImageBitmap(blob, { imageOrientation: 'from-image' });\n return { element: bitmap, width: bitmap.width, height: bitmap.height };\n } catch {\n // Older Safari rejects the options object; fall through.\n }\n }\n }\n\n return loadViaImageElement(src);\n}\n\nasync function toBlob(src: string | Blob | File): Promise<Blob | null> {\n if (src instanceof Blob) return src;\n if (typeof fetch !== 'function') return null;\n try {\n const response = await fetch(src, { credentials: 'omit' });\n if (!response.ok) return null;\n return await response.blob();\n } catch {\n return null;\n }\n}\n\nasync function loadViaImageElement(src: string | Blob | File): Promise<LoadedImage> {\n const url = typeof src === 'string' ? src : URL.createObjectURL(src);\n const ownsObjectUrl = typeof src !== 'string';\n\n try {\n const element = await new Promise<HTMLImageElement>((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load image: ${url}`));\n img.src = url;\n });\n return {\n element,\n width: element.naturalWidth,\n height: element.naturalHeight,\n };\n } finally {\n if (ownsObjectUrl) URL.revokeObjectURL(url);\n }\n}\n","import type { Point, Rect, Size } from '../geometry/rect.js';\n\nexport interface StageDimensions {\n /** Stage width in CSS pixels. */\n readonly width: number;\n /** Stage height in CSS pixels. */\n readonly height: number;\n /** Padding on each side around the image, in CSS pixels. */\n readonly padding: number;\n}\n\n/**\n * User-driven zoom and pan applied on top of the fit-to-screen letterbox.\n * `computeViewport` folds the transform into `displayRect` and `scale` so\n * plugin-level draw calls stay zoom-agnostic. Pan is in stage CSS pixels\n * at the current zoom and is clamped at viewport emission.\n */\nexport interface ViewportTransform {\n /** 1 at fit; > 1 zoomed in; < 1 zoomed out. */\n readonly zoom: number;\n /** CSS pixels of pan offset, applied after the centered-fit baseline. */\n readonly panX: number;\n readonly panY: number;\n}\n\nexport const IDENTITY_VIEWPORT_TRANSFORM: ViewportTransform = Object.freeze({\n zoom: 1,\n panX: 0,\n panY: 0,\n});\n\nexport interface Viewport {\n /** Where the image is drawn in stage CSS pixels (post-zoom, post-pan). */\n readonly displayRect: Rect;\n /** Display CSS pixels per 1 image pixel (uniform on both axes, post-zoom). */\n readonly scale: number;\n}\n\n/**\n * Compute the post-zoom, post-pan display rect for an image inside the stage.\n *\n * At identity, the image is fit-scaled inside the stage minus padding and\n * centered. With a non-identity transform the fit scale is multiplied by\n * `zoom`, then the pan offset is added and clamped so at least 1 image\n * pixel remains inside the inner stage area on each axis.\n */\nexport function computeViewport(\n stage: StageDimensions,\n image: Size,\n transform: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM,\n): Viewport {\n const innerWidth = Math.max(0, stage.width - stage.padding * 2);\n const innerHeight = Math.max(0, stage.height - stage.padding * 2);\n\n if (image.width <= 0 || image.height <= 0 || innerWidth <= 0 || innerHeight <= 0) {\n return {\n displayRect: { x: stage.padding, y: stage.padding, width: 0, height: 0 },\n scale: 0,\n };\n }\n\n const fitScale = Math.min(innerWidth / image.width, innerHeight / image.height);\n const zoom = Math.max(0, transform.zoom || 0);\n const scale = fitScale * zoom;\n\n const width = image.width * scale;\n const height = image.height * scale;\n\n // Centered baseline inside the inner stage area, then add the pan.\n const baselineX = stage.padding + (innerWidth - width) / 2;\n const baselineY = stage.padding + (innerHeight - height) / 2;\n\n const x = baselineX + transform.panX;\n const y = baselineY + transform.panY;\n\n const clampedX = clampAxis(x, baselineX, width, innerWidth, stage.padding);\n const clampedY = clampAxis(y, baselineY, height, innerHeight, stage.padding);\n\n return {\n displayRect: { x: clampedX, y: clampedY, width, height },\n scale,\n };\n}\n\nfunction clampAxis(\n rawPos: number,\n baseline: number,\n size: number,\n innerSize: number,\n padding: number,\n): number {\n // Image narrower than the inner stage: no off-center pan, baseline is centered.\n if (size <= innerSize) return baseline;\n\n // At least 1 image pixel always remains inside the inner stage.\n const innerStart = padding;\n const innerEnd = padding + innerSize;\n const minLeft = innerStart + 1 - size;\n const maxLeft = innerEnd - 1;\n if (rawPos < minLeft) return minLeft;\n if (rawPos > maxLeft) return maxLeft;\n return rawPos;\n}\n\nexport function pointImageToDisplay(point: Point, viewport: Viewport): Point {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nexport function pointDisplayToImage(point: Point, viewport: Viewport): Point {\n if (viewport.scale === 0) return { x: 0, y: 0 };\n return {\n x: (point.x - viewport.displayRect.x) / viewport.scale,\n y: (point.y - viewport.displayRect.y) / viewport.scale,\n };\n}\n\nexport function rectImageToDisplay(rect: Rect, viewport: Viewport): Rect {\n return {\n x: viewport.displayRect.x + rect.x * viewport.scale,\n y: viewport.displayRect.y + rect.y * viewport.scale,\n width: rect.width * viewport.scale,\n height: rect.height * viewport.scale,\n };\n}\n\nexport function rectDisplayToImage(rect: Rect, viewport: Viewport): Rect {\n if (viewport.scale === 0) return { x: 0, y: 0, width: 0, height: 0 };\n return {\n x: (rect.x - viewport.displayRect.x) / viewport.scale,\n y: (rect.y - viewport.displayRect.y) / viewport.scale,\n width: rect.width / viewport.scale,\n height: rect.height / viewport.scale,\n };\n}\n","import type { Point, Size } from '../geometry/rect.js';\nimport {\n IDENTITY_VIEWPORT_TRANSFORM,\n type StageDimensions,\n type Viewport,\n type ViewportTransform,\n computeViewport,\n} from './viewport.js';\n\nexport const MAX_ZOOM = 8;\nexport const MIN_ZOOM = 0.25;\n\nexport interface ViewportControllerSnapshot {\n readonly transform: ViewportTransform;\n readonly pinching: boolean;\n}\n\nexport type ViewportControllerListener = (snapshot: ViewportControllerSnapshot) => void;\n\n/** Editor-level zoom + pan state, shared with every plugin's `UtilityContext`. */\nexport class ViewportController {\n private current: ViewportTransform;\n private isPinching: boolean;\n private readonly listeners: Set<ViewportControllerListener>;\n\n constructor(initial: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM) {\n this.current = clampTransform(initial);\n this.isPinching = false;\n this.listeners = new Set();\n }\n\n getTransform(): ViewportTransform {\n return this.current;\n }\n\n getSnapshot(): ViewportControllerSnapshot {\n return { transform: this.current, pinching: this.isPinching };\n }\n\n setTransform(transform: ViewportTransform): void {\n const next = clampTransform(transform);\n if (transformsEqual(this.current, next)) return;\n this.current = next;\n this.emit();\n }\n\n /**\n * Apply a multiplicative zoom delta anchored on a stage CSS-pixel point.\n * The point under `anchor` stays under `anchor` after the zoom.\n *\n * Pan is the displaced image-center's offset from the stage center, so the\n * dolly-zoom reduces to `pan_new = ratio · pan_old + (1 − ratio) · (anchor − stageCenter)`,\n * independent of the active plugin's image intrinsic.\n */\n zoomAt(deltaZoom: number, anchor: Point, stageCenter: Point): ViewportTransform {\n if (!Number.isFinite(deltaZoom) || deltaZoom <= 0) return this.current;\n const oldZoom = this.current.zoom;\n const requested = oldZoom * deltaZoom;\n const newZoom = clamp(requested, MIN_ZOOM, MAX_ZOOM);\n if (newZoom === oldZoom) return this.current;\n\n const ratio = newZoom / oldZoom;\n const anchorRelX = anchor.x - stageCenter.x;\n const anchorRelY = anchor.y - stageCenter.y;\n const newPanX = ratio * this.current.panX + (1 - ratio) * anchorRelX;\n const newPanY = ratio * this.current.panY + (1 - ratio) * anchorRelY;\n\n this.current = { zoom: newZoom, panX: newPanX, panY: newPanY };\n this.emit();\n return this.current;\n }\n\n /**\n * Translate pan by `(dx, dy)` in stage CSS pixels at the current zoom.\n * Pan is stored raw; `computeViewport` clamps at emission time so the\n * gesture handler's math stays stable when the user pans past the clamp.\n */\n panBy(dx: number, dy: number): void {\n if (dx === 0 && dy === 0) return;\n this.current = {\n zoom: this.current.zoom,\n panX: this.current.panX + dx,\n panY: this.current.panY + dy,\n };\n this.emit();\n }\n\n resetToFit(): void {\n if (transformsEqual(this.current, IDENTITY_VIEWPORT_TRANSFORM)) return;\n this.current = IDENTITY_VIEWPORT_TRANSFORM;\n this.emit();\n }\n\n /** Reset only the pan, preserving zoom. Used on plugin switch. */\n resetPan(): void {\n if (this.current.panX === 0 && this.current.panY === 0) return;\n this.current = { zoom: this.current.zoom, panX: 0, panY: 0 };\n this.emit();\n }\n\n /** True while a multi-pointer gesture is active. Heavy plugins read this. */\n getPinching(): boolean {\n return this.isPinching;\n }\n\n setPinching(value: boolean): void {\n if (this.isPinching === value) return;\n this.isPinching = value;\n this.emit();\n }\n\n computeViewport(stage: StageDimensions, image: Size): Viewport {\n return computeViewport(stage, image, this.current);\n }\n\n /** Subscribe to transform / pinching changes. Handler runs synchronously after every change. */\n subscribe(listener: ViewportControllerListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n private emit(): void {\n const snapshot = this.getSnapshot();\n for (const listener of this.listeners) listener(snapshot);\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nfunction clampTransform(transform: ViewportTransform): ViewportTransform {\n const zoom = clamp(Number.isFinite(transform.zoom) ? transform.zoom : 1, MIN_ZOOM, MAX_ZOOM);\n const panX = Number.isFinite(transform.panX) ? transform.panX : 0;\n const panY = Number.isFinite(transform.panY) ? transform.panY : 0;\n return { zoom, panX, panY };\n}\n\nfunction transformsEqual(a: ViewportTransform, b: ViewportTransform): boolean {\n return a.zoom === b.zoom && a.panX === b.panX && a.panY === b.panY;\n}\n","/**\n * Allocate a canvas suitable for an off-screen bake operation.\n *\n * The two return shapes are a discriminated union because the `toBlob`\n * vs `convertToBlob` signatures differ — callers pass through whichever\n * they got.\n */\nexport type BakeCanvas =\n | { readonly kind: 'offscreen'; readonly canvas: OffscreenCanvas }\n | { readonly kind: 'html'; readonly canvas: HTMLCanvasElement };\n\nexport function createBakeCanvas(width: number, height: number): BakeCanvas {\n if (canUseOffscreenForBlobs()) {\n const canvas = new OffscreenCanvas(width, height);\n return { kind: 'offscreen', canvas };\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return { kind: 'html', canvas };\n}\n\n/**\n * Get a 2D rendering context from either canvas shape with a single\n * narrowed type. Branching on `bake.kind` first lets TS narrow each\n * canvas's `getContext('2d')` correctly; calling it on the un-discriminated\n * union collapses the return to the broader `RenderingContext`.\n */\nexport function getBakeContext2D(\n bake: BakeCanvas,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n}\n\nexport async function bakeCanvasToBlob(\n bake: BakeCanvas,\n mimeType: string,\n quality: number,\n): Promise<Blob> {\n if (bake.kind === 'offscreen') {\n return bake.canvas.convertToBlob({ type: mimeType, quality });\n }\n return new Promise<Blob>((resolve, reject) => {\n bake.canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob produced null'));\n },\n mimeType,\n quality,\n );\n });\n}\n\nfunction canUseOffscreenForBlobs(): boolean {\n if (typeof OffscreenCanvas === 'undefined') return false;\n // WebKit historically shipped OffscreenCanvas without convertToBlob,\n // so test the actual capability rather than the constructor.\n return typeof OffscreenCanvas.prototype.convertToBlob === 'function';\n}\n\n/** Probe whether the runtime canvas can encode `mimeType` to a non-empty blob. Cached per-mime. */\nconst mimeSupportCache = new Map<string, Promise<boolean>>();\n\nexport function canEncodeMime(mimeType: string): Promise<boolean> {\n const cached = mimeSupportCache.get(mimeType);\n if (cached) return cached;\n const probe = (async () => {\n try {\n const bake = createBakeCanvas(1, 1);\n const blob = await bakeCanvasToBlob(bake, mimeType, 0.5);\n // `toBlob` will silently fall back to PNG on unsupported types,\n // so verify the result advertises the requested mime.\n return blob.type === mimeType && blob.size > 0;\n } catch {\n return false;\n }\n })();\n mimeSupportCache.set(mimeType, probe);\n return probe;\n}\n","export interface Point {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface Rect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface Size {\n readonly width: number;\n readonly height: number;\n}\n\nexport function rectFromPoints(a: Point, b: Point): Rect {\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n const width = Math.abs(a.x - b.x);\n const height = Math.abs(a.y - b.y);\n return { x, y, width, height };\n}\n\nexport function rectRight(rect: Rect): number {\n return rect.x + rect.width;\n}\n\nexport function rectBottom(rect: Rect): number {\n return rect.y + rect.height;\n}\n\nexport function rectCenter(rect: Rect): Point {\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };\n}\n\nexport function pointInRect(point: Point, rect: Rect): boolean {\n return (\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height\n );\n}\n\nexport function rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n/**\n * Translate a rect by `(dx, dy)`, then clamp it inside `bounds` so that the\n * full rect remains inside (clamping translates further if needed; size is\n * preserved). If the rect is larger than the bounds in either axis, that axis\n * is left at the bounds origin.\n */\nexport function translateClampedRect(rect: Rect, dx: number, dy: number, bounds: Rect): Rect {\n const moved: Rect = { x: rect.x + dx, y: rect.y + dy, width: rect.width, height: rect.height };\n return clampRectInside(moved, bounds);\n}\n\n/**\n * Clamp a rect so it fits entirely inside `bounds`. If the rect is larger\n * than the bounds in either axis, the rect's extent in that axis is shrunk\n * to fit (preserving the upper-left anchor of `bounds`).\n */\nexport function clampRectInside(rect: Rect, bounds: Rect): Rect {\n let { x, y, width, height } = rect;\n\n if (width > bounds.width) width = bounds.width;\n if (height > bounds.height) height = bounds.height;\n\n if (x < bounds.x) x = bounds.x;\n if (y < bounds.y) y = bounds.y;\n if (x + width > bounds.x + bounds.width) x = bounds.x + bounds.width - width;\n if (y + height > bounds.y + bounds.height) y = bounds.y + bounds.height - height;\n\n return { x, y, width, height };\n}\n\nexport function roundRect(rect: Rect): Rect {\n return {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n}\n","/**\n * Minimal observable store. Subscribers fire after every `set`/`update`;\n * the store does no equality check — subscribers diff if they care.\n */\nexport type StoreListener<T> = (state: T, previous: T) => void;\nexport type Unsubscribe = () => void;\n\nexport interface Store<T> {\n get(): T;\n set(next: Partial<T>): void;\n update(updater: (state: T) => Partial<T>): void;\n subscribe(listener: StoreListener<T>): Unsubscribe;\n}\n\nexport function createStore<T extends object>(initial: T): Store<T> {\n let state = initial;\n const listeners = new Set<StoreListener<T>>();\n\n function notify(previous: T): void {\n for (const listener of [...listeners]) {\n try {\n listener(state, previous);\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n return {\n get(): T {\n return state;\n },\n set(next: Partial<T>): void {\n const previous = state;\n state = { ...state, ...next };\n notify(previous);\n },\n update(updater: (state: T) => Partial<T>): void {\n const previous = state;\n state = { ...state, ...updater(state) };\n notify(previous);\n },\n subscribe(listener: StoreListener<T>): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n","import type { UtilityId } from '../plugins/utility.js';\n\n/**\n * Immutable snapshot of every registered plugin's state at a single moment.\n * `History` deep-clones on capture so live store writes cannot bleed in.\n */\nexport type SessionSnapshot = ReadonlyMap<UtilityId, unknown>;\n\nexport const HISTORY_MAX_ENTRIES = 50;\n\nexport interface UndoResult {\n readonly snapshot: SessionSnapshot;\n /** Utility ids whose state changed between the prior and restored snapshot. */\n readonly changed: ReadonlySet<UtilityId>;\n}\n\n/** Editor-wide undo/redo store using a snapshot stack. State-only; the editor decides when to commit. */\nexport class History {\n private shadow: SessionSnapshot;\n private undoStack: SessionSnapshot[] = [];\n private redoStack: SessionSnapshot[] = [];\n\n constructor(initial: SessionSnapshot) {\n this.shadow = cloneSnapshot(initial);\n }\n\n /**\n * Capture a commit. Previous shadow → undo stack; `current` → shadow.\n * Redo is cleared because a new commit branches the history. A commit\n * structurally equal to the shadow is a no-op so duplicates (clicking\n * the same preset twice) don't pollute the undo stack.\n */\n commit(current: SessionSnapshot): void {\n if (snapshotsEqual(current, this.shadow)) return;\n this.undoStack.push(this.shadow);\n if (this.undoStack.length > HISTORY_MAX_ENTRIES) {\n this.undoStack.shift();\n }\n this.shadow = cloneSnapshot(current);\n this.redoStack.length = 0;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n /** Pop the most recent prior snapshot; `current` goes onto the redo stack. Returns null if undo is empty. */\n undo(current: SessionSnapshot): UndoResult | null {\n const previous = this.undoStack.pop();\n if (!previous) return null;\n this.redoStack.push(cloneSnapshot(current));\n this.shadow = previous;\n return { snapshot: previous, changed: diffSnapshots(current, previous) };\n }\n\n /** Pop the most recent redo entry; `current` goes onto the undo stack. Returns null if redo is empty. */\n redo(current: SessionSnapshot): UndoResult | null {\n const next = this.redoStack.pop();\n if (!next) return null;\n this.undoStack.push(cloneSnapshot(current));\n if (this.undoStack.length > HISTORY_MAX_ENTRIES) {\n this.undoStack.shift();\n }\n this.shadow = next;\n return { snapshot: next, changed: diffSnapshots(current, next) };\n }\n\n /** Test/debug helpers; not part of the editor's runtime API. */\n size(): { undo: number; redo: number } {\n return { undo: this.undoStack.length, redo: this.redoStack.length };\n }\n}\n\nfunction snapshotsEqual(a: SessionSnapshot, b: SessionSnapshot): boolean {\n if (a.size !== b.size) return false;\n for (const [id, value] of a) {\n if (!b.has(id)) return false;\n if (stableJson(value) !== stableJson(b.get(id))) return false;\n }\n return true;\n}\n\nfunction diffSnapshots(prev: SessionSnapshot, next: SessionSnapshot): Set<UtilityId> {\n const changed = new Set<UtilityId>();\n for (const [id, value] of next) {\n if (stableJson(value) !== stableJson(prev.get(id))) changed.add(id);\n }\n for (const id of prev.keys()) {\n if (!next.has(id)) changed.add(id);\n }\n return changed;\n}\n\nfunction cloneSnapshot(snapshot: SessionSnapshot): SessionSnapshot {\n const next = new Map<UtilityId, unknown>();\n for (const [id, value] of snapshot) {\n next.set(id, structuredClone(value));\n }\n return next;\n}\n\n/** Key-sorted JSON for structural equality regardless of insertion order. */\nfunction stableJson(value: unknown): string {\n return JSON.stringify(value, (_key, v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n const sorted: Record<string, unknown> = {};\n for (const k of Object.keys(v as Record<string, unknown>).sort()) {\n sorted[k] = (v as Record<string, unknown>)[k];\n }\n return sorted;\n }\n return v;\n });\n}\n","import { type Rect, clampRectInside } from '../../geometry/rect.js';\n\n/**\n * Compute the largest axis-aligned rectangle of `targetRatio` (= w/h) that\n * fits inside `bounds`, centered. Used to seed the crop rectangle when the\n * user picks an aspect-ratio preset before any drag.\n */\nexport function fitRectToBoundsWithRatio(bounds: Rect, targetRatio: number): Rect {\n if (targetRatio <= 0 || bounds.width <= 0 || bounds.height <= 0) {\n return { x: bounds.x, y: bounds.y, width: 0, height: 0 };\n }\n\n const boundsRatio = bounds.width / bounds.height;\n let width: number;\n let height: number;\n if (targetRatio >= boundsRatio) {\n width = bounds.width;\n height = width / targetRatio;\n } else {\n height = bounds.height;\n width = height * targetRatio;\n }\n\n return {\n x: bounds.x + (bounds.width - width) / 2,\n y: bounds.y + (bounds.height - height) / 2,\n width,\n height,\n };\n}\n\n/**\n * Reshape `rect` to `targetRatio`, anchored at `anchor`, clamped inside\n * `bounds`. If clamping breaks the ratio, falls back to the largest same-ratio\n * sub-rect that fits, anchored identically.\n */\nexport function applyAspectRatio(\n rect: Rect,\n targetRatio: number,\n anchor: AspectAnchor,\n bounds: Rect,\n): Rect {\n if (targetRatio <= 0) return rect;\n if (rect.width <= 0 || rect.height <= 0) return fitRectToBoundsWithRatio(bounds, targetRatio);\n\n const currentRatio = rect.width / rect.height;\n let width: number;\n let height: number;\n if (currentRatio > targetRatio) {\n height = rect.height;\n width = height * targetRatio;\n } else {\n width = rect.width;\n height = width / targetRatio;\n }\n\n const reshaped = anchorRect(rect, width, height, anchor);\n const clamped = clampRectInside(reshaped, bounds);\n\n const clampedRatio = clamped.height === 0 ? 0 : clamped.width / clamped.height;\n if (Math.abs(clampedRatio - targetRatio) <= RATIO_TOLERANCE) {\n return clamped;\n }\n return fitInsideAtAnchor(clamped, targetRatio, anchor);\n}\n\nconst RATIO_TOLERANCE = 1e-6;\n\nexport type AspectAnchor = 'tl' | 'tr' | 'bl' | 'br' | 'center';\n\nfunction anchorRect(rect: Rect, width: number, height: number, anchor: AspectAnchor): Rect {\n switch (anchor) {\n case 'tl':\n return { x: rect.x, y: rect.y, width, height };\n case 'tr':\n return { x: rect.x + rect.width - width, y: rect.y, width, height };\n case 'bl':\n return { x: rect.x, y: rect.y + rect.height - height, width, height };\n case 'br':\n return {\n x: rect.x + rect.width - width,\n y: rect.y + rect.height - height,\n width,\n height,\n };\n case 'center':\n return {\n x: rect.x + (rect.width - width) / 2,\n y: rect.y + (rect.height - height) / 2,\n width,\n height,\n };\n }\n}\n\nfunction fitInsideAtAnchor(bounds: Rect, targetRatio: number, anchor: AspectAnchor): Rect {\n const fitted = fitRectToBoundsWithRatio(bounds, targetRatio);\n return anchorRect(bounds, fitted.width, fitted.height, anchor);\n}\n","import { type Point, type Rect, clampRectInside } from '../../geometry/rect.js';\nimport { type AspectAnchor, applyAspectRatio } from './aspect-ratio.js';\n\nexport type CornerHandle = 'tl' | 'tr' | 'bl' | 'br';\nexport type EdgeHandle = 't' | 'r' | 'b' | 'l';\nexport type HandleDirection = CornerHandle | EdgeHandle;\n\nexport interface ResizeOptions {\n /** Image-space bounds the rect must stay inside. */\n readonly bounds: Rect;\n /** Aspect ratio to enforce, or `undefined` for free crop. */\n readonly aspectRatio?: number;\n /** Minimum size on either axis, in image-space units. Defaults to 1. */\n readonly minSize?: number;\n}\n\n/**\n * Resize a rect from one of its eight handles to `pointer`. Opposite\n * corner/edge anchors; result clamped to `bounds` and reshaped to\n * `aspectRatio` (anchored at the same opposite corner) when supplied.\n */\nexport function resizeRectFromHandle(\n rect: Rect,\n handle: HandleDirection,\n pointer: Point,\n options: ResizeOptions,\n): Rect {\n const minSize = options.minSize ?? 1;\n const left = rect.x;\n const top = rect.y;\n const right = rect.x + rect.width;\n const bottom = rect.y + rect.height;\n\n // Pointer may swap sides (drag through the anchor); recompute from anchor + live edge.\n let newLeft = left;\n let newTop = top;\n let newRight = right;\n let newBottom = bottom;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n newLeft = pointer.x;\n }\n if (handle === 'tr' || handle === 'r' || handle === 'br') {\n newRight = pointer.x;\n }\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n newTop = pointer.y;\n }\n if (handle === 'bl' || handle === 'b' || handle === 'br') {\n newBottom = pointer.y;\n }\n\n if (handle === 'l' || handle === 'r') {\n newTop = top;\n newBottom = bottom;\n }\n if (handle === 't' || handle === 'b') {\n newLeft = left;\n newRight = right;\n }\n\n let nx = Math.min(newLeft, newRight);\n let ny = Math.min(newTop, newBottom);\n let nw = Math.abs(newRight - newLeft);\n let nh = Math.abs(newBottom - newTop);\n\n if (nw < minSize) {\n nw = minSize;\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n nx = right - minSize;\n } else if (handle === 'tr' || handle === 'r' || handle === 'br') {\n nx = left;\n }\n }\n if (nh < minSize) {\n nh = minSize;\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n ny = bottom - minSize;\n } else if (handle === 'bl' || handle === 'b' || handle === 'br') {\n ny = top;\n }\n }\n\n let resized: Rect = { x: nx, y: ny, width: nw, height: nh };\n resized = clampRectInside(resized, options.bounds);\n\n if (options.aspectRatio !== undefined && options.aspectRatio > 0) {\n resized = applyAspectRatio(resized, options.aspectRatio, anchorFor(handle), options.bounds);\n }\n\n return resized;\n}\n\nfunction anchorFor(handle: HandleDirection): AspectAnchor {\n switch (handle) {\n case 'tl':\n return 'br';\n case 'tr':\n return 'bl';\n case 'bl':\n return 'tr';\n case 'br':\n return 'tl';\n case 't':\n return 'bl';\n case 'b':\n return 'tl';\n case 'l':\n return 'tr';\n case 'r':\n return 'tl';\n }\n}\n","/**\n * Filters crop presets by aspect-ratio relative to 1. `landscape` keeps\n * ratio ≥ 1, `portrait` keeps ratio < 1; `undefined` ratios (Custom) and\n * unknown tokens stay visible.\n */\nexport type CropPresetFilter = 'landscape' | 'portrait';\nexport type CropPreset = readonly [number | undefined, string];\n\nexport function isPresetVisible(preset: CropPreset, filter: CropPresetFilter | undefined): boolean {\n const [ratio] = preset;\n if (ratio === undefined) return true;\n if (filter === undefined) return true;\n if (filter === 'landscape') return ratio >= 1;\n if (filter === 'portrait') return ratio < 1;\n return true;\n}\n\nexport function filterPresets(\n presets: readonly CropPreset[],\n filter: CropPresetFilter | undefined,\n): readonly CropPreset[] {\n return presets.filter((preset) => isPresetVisible(preset, filter));\n}\n","import type { Rect, Size } from '../../geometry/rect.js';\nimport { fitRectToBoundsWithRatio } from './aspect-ratio.js';\nimport type { CropPreset, CropPresetFilter } from './preset-filter.js';\n\nexport interface CropState {\n /** Crop rectangle in image-space pixels. */\n readonly rect: Rect;\n /** Active aspect-ratio constraint (image w/h), or `undefined` for free. */\n readonly aspectRatio: number | undefined;\n /** Index into `presets`, or `-1` if no preset is active. */\n readonly activePresetIndex: number;\n /** Visible presets after applying `cropSelectPresetFilter`. */\n readonly presets: readonly CropPreset[];\n /** Image dimensions, in pixels. The bounds the crop can move within. */\n readonly imageSize: Size;\n}\n\nexport interface InitialCropStateInput {\n readonly imageSize: Size;\n readonly presets: readonly CropPreset[];\n readonly filter: CropPresetFilter | undefined;\n}\n\n/** Full-frame crop, \"Custom\" preset active. */\nexport function initialCropState(input: InitialCropStateInput): CropState {\n const bounds: Rect = { x: 0, y: 0, width: input.imageSize.width, height: input.imageSize.height };\n return {\n rect: bounds,\n aspectRatio: undefined,\n activePresetIndex: findCustomIndex(input.presets),\n presets: input.presets,\n imageSize: input.imageSize,\n };\n}\n\nexport function applyPresetByIndex(state: CropState, presetIndex: number): CropState {\n const preset = state.presets[presetIndex];\n if (!preset) return state;\n const [ratio] = preset;\n if (ratio === undefined) {\n return { ...state, aspectRatio: undefined, activePresetIndex: presetIndex };\n }\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: state.imageSize.width,\n height: state.imageSize.height,\n };\n const fitted = fitRectToBoundsWithRatio(bounds, ratio);\n return { ...state, rect: fitted, aspectRatio: ratio, activePresetIndex: presetIndex };\n}\n\nfunction findCustomIndex(presets: readonly CropPreset[]): number {\n return presets.findIndex(([ratio]) => ratio === undefined);\n}\n","import { createBakeCanvas } from '../../canvas/bake-canvas.js';\nimport { type Rect, roundRect } from '../../geometry/rect.js';\nimport type { SourceImage } from '../utility.js';\n\nexport interface CropBakeInput {\n /** The cropped region, in image-space pixels. */\n readonly rect: Rect;\n}\n\n/**\n * Apply a crop and return a SourceImage at the crop's pixel size. The\n * rect is rounded and clamped against the source so an oversized rect\n * doesn't crash — we draw what fits.\n */\nexport function bakeCrop(source: SourceImage, input: CropBakeInput): SourceImage {\n const rounded = roundRect(input.rect);\n const x = clamp(rounded.x, 0, source.width);\n const y = clamp(rounded.y, 0, source.height);\n const w = clamp(rounded.width, 1, source.width - x);\n const h = clamp(rounded.height, 1, source.height - y);\n\n const bake = createBakeCanvas(w, h);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","/**\n * `'auto'` resolves to the smallest format that preserves alpha on the\n * current runtime (WebP on evergreens, PNG fallback). AVIF never auto-\n * resolves; the user must pick it explicitly.\n */\nexport type OutputMimeChoice = 'auto' | 'image/png' | 'image/jpeg' | 'image/webp' | 'image/avif';\n\nexport interface OutputState {\n readonly mimeChoice: OutputMimeChoice;\n /** 0.0 – 1.0. Ignored for PNG (lossless). */\n readonly quality: number;\n /**\n * Strip EXIF / GPS / camera metadata on save. Canvas `convertToBlob`\n * already strips EXIF; when `false`, we attempt to preserve the source\n * EXIF segment, which only works for JPEG → JPEG. Other combinations\n * strip regardless — the toggle is a hint, not a guarantee.\n */\n readonly stripMetadata: boolean;\n}\n\nexport const DEFAULT_OUTPUT_STATE: OutputState = {\n mimeChoice: 'auto',\n quality: 0.85,\n stripMetadata: true,\n};\n\n/** The four concrete mime types the user can pick from in the popover. */\nexport const ENCODABLE_MIMES: ReadonlyArray<Exclude<OutputMimeChoice, 'auto'>> = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/avif',\n];\n\n/** Clamp a quality value to [0, 1]; non-finite inputs return the default. */\nexport function clampQuality(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_OUTPUT_STATE.quality;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nexport function setOutputMime(state: OutputState, mimeChoice: OutputMimeChoice): OutputState {\n if (state.mimeChoice === mimeChoice) return state;\n return { ...state, mimeChoice };\n}\n\nexport function setOutputQuality(state: OutputState, quality: number): OutputState {\n const clamped = clampQuality(quality);\n if (state.quality === clamped) return state;\n return { ...state, quality: clamped };\n}\n\nexport function setStripMetadata(state: OutputState, stripMetadata: boolean): OutputState {\n if (state.stripMetadata === stripMetadata) return state;\n return { ...state, stripMetadata };\n}\n","/**\n * Minimal JPEG EXIF segment copier. Extracts the EXIF APP1 segment from\n * the source JPEG and splices it into the output JPEG right after the\n * SOI marker. JPEG → JPEG only; any other combination returns the output\n * untouched. Canvas-encoded JPEGs never carry EXIF, so no duplicate-APP1\n * risk.\n */\n\nconst SOI = [0xff, 0xd8];\nconst APP1_MARKER = [0xff, 0xe1];\nconst EXIF_HEADER = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; // \"Exif\\0\\0\"\n\nexport interface CopyJpegExifOptions {\n /** The source JPEG bytes (with EXIF). */\n readonly source: Blob;\n /** The freshly-encoded canvas output (no EXIF). */\n readonly output: Blob;\n}\n\n/** Splice the source's EXIF APP1 segment into the output JPEG. Returns the output unchanged on any mismatch. */\nexport async function copyJpegExif(options: CopyJpegExifOptions): Promise<Blob> {\n if (options.output.type && options.output.type !== 'image/jpeg') return options.output;\n\n const sourceBytes = await readBlobBytes(options.source);\n if (!startsWith(sourceBytes, SOI)) return options.output;\n\n const exifSegment = findExifApp1(sourceBytes);\n if (!exifSegment) return options.output;\n\n const outputBytes = await readBlobBytes(options.output);\n if (!startsWith(outputBytes, SOI)) return options.output;\n\n // [SOI][EXIF segment][rest of output after SOI]\n const merged = new Uint8Array(outputBytes.length + exifSegment.length);\n merged.set(outputBytes.subarray(0, 2), 0);\n merged.set(exifSegment, 2);\n merged.set(outputBytes.subarray(2), 2 + exifSegment.length);\n\n return new Blob([merged], { type: 'image/jpeg' });\n}\n\n/** `Blob.arrayBuffer()` path; falls back to `FileReader` for jsdom which lacks `arrayBuffer`. */\nasync function readBlobBytes(blob: Blob): Promise<Uint8Array> {\n if (typeof blob.arrayBuffer === 'function') {\n return new Uint8Array(await blob.arrayBuffer());\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result;\n if (result instanceof ArrayBuffer) {\n resolve(new Uint8Array(result));\n } else {\n reject(new Error('FileReader returned a non-ArrayBuffer result'));\n }\n };\n reader.onerror = () => reject(reader.error ?? new Error('FileReader failed'));\n reader.readAsArrayBuffer(blob);\n });\n}\n\nfunction startsWith(bytes: Uint8Array, prefix: ReadonlyArray<number>): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\n/** Walk JPEG segments and return the EXIF APP1 segment (FF E1 + length + payload), or undefined. */\nfunction findExifApp1(bytes: Uint8Array): Uint8Array | undefined {\n let i = 2; // skip SOI\n while (i + 4 <= bytes.length) {\n if (bytes[i] !== 0xff) return undefined;\n const marker = bytes[i + 1];\n if (marker === undefined) return undefined;\n // SOS (FF DA) starts compressed data; EOI (FF D9) ends the stream.\n if (marker === 0xda) return undefined;\n if (marker === 0xd9) return undefined;\n const length =\n bytes[i + 2] !== undefined && bytes[i + 3] !== undefined\n ? (bytes[i + 2] as number) * 256 + (bytes[i + 3] as number)\n : 0;\n if (length < 2) return undefined;\n const segmentEnd = i + 2 + length;\n if (segmentEnd > bytes.length) return undefined;\n\n if (\n bytes[i] === APP1_MARKER[0] &&\n bytes[i + 1] === APP1_MARKER[1] &&\n hasExifHeader(bytes, i + 4)\n ) {\n return bytes.slice(i, segmentEnd);\n }\n\n i = segmentEnd;\n }\n return undefined;\n}\n\nfunction hasExifHeader(bytes: Uint8Array, offset: number): boolean {\n for (let j = 0; j < EXIF_HEADER.length; j++) {\n if (bytes[offset + j] !== EXIF_HEADER[j]) return false;\n }\n return true;\n}\n","import { bakeCanvasToBlob, canEncodeMime, createBakeCanvas } from '../canvas/bake-canvas.js';\nimport { DEFAULT_OUTPUT_STATE, type OutputState, clampQuality } from '../output/state.js';\nimport type { SourceImage } from '../plugins/utility.js';\nimport { copyJpegExif } from './exif.js';\n\nconst FALLBACK_MIME = 'image/png';\n\nconst ALPHA_CARRYING_SOURCE_MIMES = new Set(['image/png', 'image/webp', 'image/avif']);\n\nexport interface EncodeOptions {\n /** Original source URL or filename, if any — used to derive the output name. */\n readonly sourceName?: string;\n /** Output format + quality. Defaults to the auto-resolved mime at default quality. */\n readonly output?: OutputState;\n /**\n * Original source blob, retained so EXIF can be preserved on JPEG → JPEG\n * when the user opts out of stripping. Optional; when missing, EXIF is\n * always stripped (the canvas re-encode strips unconditionally).\n */\n readonly sourceBlob?: Blob;\n}\n\n/**\n * Resolve the concrete output mime from `OutputState` against runtime support.\n * Explicit choices fall back to WebP then PNG; `'auto'` prefers WebP, then\n * JPEG for non-alpha sources, then PNG.\n */\nexport async function resolveOutputMime(state: OutputState, source: SourceImage): Promise<string> {\n if (state.mimeChoice !== 'auto') {\n if (await canEncodeMime(state.mimeChoice)) return state.mimeChoice;\n if (await canEncodeMime('image/webp')) return 'image/webp';\n return FALLBACK_MIME;\n }\n if (await canEncodeMime('image/webp')) return 'image/webp';\n const sourceHasAlpha = ALPHA_CARRYING_SOURCE_MIMES.has(source.mimeType);\n if (!sourceHasAlpha && (await canEncodeMime('image/jpeg'))) return 'image/jpeg';\n return FALLBACK_MIME;\n}\n\n/**\n * Derive the output filename. `name.ext` → `name.<ext-of-mime>`,\n * otherwise `kalotyp-image.<ext>`.\n */\nexport function deriveOutputName(sourceName: string | undefined, mimeType: string): string {\n const ext = extensionForMime(mimeType);\n if (!sourceName) return `kalotyp-image.${ext}`;\n const basename = lastPathSegment(sourceName);\n if (!basename) return `kalotyp-image.${ext}`;\n const stem = stripExtension(basename);\n if (!stem) return `kalotyp-image.${ext}`;\n return `${stem}.${ext}`;\n}\n\nexport async function encodeSourceImage(\n source: SourceImage,\n options: EncodeOptions = {},\n): Promise<File> {\n const outputState = options.output ?? DEFAULT_OUTPUT_STATE;\n const mimeType = await resolveOutputMime(outputState, source);\n const quality = clampQuality(outputState.quality);\n const name = deriveOutputName(options.sourceName, mimeType);\n const bake = createBakeCanvas(source.width, source.height);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, 0, 0);\n } else {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, 0, 0);\n }\n const baseBlob = await bakeCanvasToBlob(bake, mimeType, quality);\n // EXIF can only be re-attached on JPEG → JPEG; canvas re-encoding strips\n // unconditionally, so this is the only place metadata can survive.\n const shouldPreserveMetadata =\n options.output?.stripMetadata === false &&\n mimeType === 'image/jpeg' &&\n source.mimeType === 'image/jpeg' &&\n options.sourceBlob !== undefined;\n const blob = shouldPreserveMetadata\n ? await copyJpegExif({ source: options.sourceBlob as Blob, output: baseBlob })\n : baseBlob;\n return new File([blob], name, { type: mimeType });\n}\n\nfunction extensionForMime(mime: string): string {\n if (mime === 'image/jpeg') return 'jpg';\n if (mime === 'image/png') return 'png';\n if (mime === 'image/webp') return 'webp';\n if (mime === 'image/avif') return 'avif';\n const subtype = mime.split('/')[1];\n return subtype && subtype.length > 0 ? subtype : 'bin';\n}\n\nfunction lastPathSegment(name: string): string | undefined {\n const trimmed = stripQuery(name);\n const segments = trimmed.split(/[/\\\\]/);\n return segments[segments.length - 1];\n}\n\nfunction stripQuery(name: string): string {\n const q = name.indexOf('?');\n return q === -1 ? name : name.slice(0, q);\n}\n\nfunction stripExtension(basename: string): string {\n const dot = basename.lastIndexOf('.');\n if (dot <= 0) return basename;\n return basename.slice(0, dot);\n}\n","import type { SourceImage, UtilityId, UtilityPlugin } from '../plugins/utility.js';\n\n/** One link in the editor's transform pipeline, with the plugin state at Save time. */\nexport interface ChainLink<TState extends object = object> {\n readonly id: UtilityId;\n readonly plugin: UtilityPlugin<TState>;\n readonly state: TState;\n}\n\n/** Apply each plugin's `bake` in sequence; result of each link feeds the next. */\nexport async function runUtilityChain(\n links: readonly ChainLink[],\n source: SourceImage,\n): Promise<SourceImage> {\n let baked = source;\n for (const link of links) {\n baked = await link.plugin.bake(link.state, baked);\n }\n return baked;\n}\n","/** Two independent axis flips; their composition equals a 180° rotation. */\nexport interface FlipState {\n readonly horizontal: boolean;\n readonly vertical: boolean;\n}\n\nexport function initialFlipState(): FlipState {\n return { horizontal: false, vertical: false };\n}\n\nexport function toggleFlip(state: FlipState, axis: 'horizontal' | 'vertical'): FlipState {\n return axis === 'horizontal'\n ? { ...state, horizontal: !state.horizontal }\n : { ...state, vertical: !state.vertical };\n}\n\nexport function isFlipNoOp(state: FlipState): boolean {\n return !state.horizontal && !state.vertical;\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FlipState, isFlipNoOp } from './state.js';\n\n/** Apply horizontal / vertical flips via a sign-flipped scale on `drawImage`. */\nexport async function bakeFlip(state: FlipState, source: SourceImage): Promise<SourceImage> {\n if (isFlipNoOp(state)) return source;\n\n const { width, height } = source;\n const bake = createBakeCanvas(width, height);\n const ctx = getBakeContext2D(bake);\n\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n const tx = state.horizontal ? width : 0;\n const ty = state.vertical ? height : 0;\n\n ctx.setTransform(sx, 0, 0, sy, tx, ty);\n ctx.drawImage(source.bitmap, 0, 0);\n\n return {\n bitmap: bake.canvas,\n width,\n height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Independent quarter-turns (lossless 90° CW) and free angle (±45°).\n * Bake applies `quarterTurns * 90° + freeAngle` so a press of \"rotate 90°\"\n * doesn't reset a straighten correction and vice versa.\n */\nexport interface RotateState {\n readonly quarterTurns: 0 | 1 | 2 | 3;\n /** Free-angle offset in degrees. Range: [-45, 45]. */\n readonly freeAngle: number;\n}\n\nexport const FREE_ANGLE_MIN = -45;\nexport const FREE_ANGLE_MAX = 45;\nexport const FREE_ANGLE_STEP = 0.1;\n\nexport function initialRotateState(): RotateState {\n return { quarterTurns: 0, freeAngle: 0 };\n}\n\nexport function rotateClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 1) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function rotateCounterClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 3) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function setFreeAngle(state: RotateState, angleDeg: number): RotateState {\n const clamped = clamp(angleDeg, FREE_ANGLE_MIN, FREE_ANGLE_MAX);\n // Snap to 0.1° to match the slider step and avoid sub-step float noise.\n const snapped = Math.round(clamped * 10) / 10;\n return { ...state, freeAngle: snapped };\n}\n\nexport function isRotateNoOp(state: RotateState): boolean {\n return state.quarterTurns === 0 && Math.abs(state.freeAngle) < 1e-6;\n}\n\n/** Effective rotation applied during bake, in degrees clockwise. */\nexport function effectiveAngleDeg(state: RotateState): number {\n return state.quarterTurns * 90 + state.freeAngle;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","import type { Size } from '../../geometry/rect.js';\n\n/**\n * Largest axis-aligned rect of the source's aspect ratio that fits\n * inside the source rotated by `angleRad`. Used by free-angle rotation\n * to avoid transparent corners.\n *\n * The two binding constraints reduce to\n * `2x ≤ W² / (W|cosθ| + H|sinθ|)`\n * `2x ≤ W·H / (W|sinθ| + H|cosθ|)`\n * — the inscribed half-width is the smaller bound. Works on absolute\n * sin/cos so the result is symmetric in the sign of the angle.\n */\nexport function largestInscribedRect(source: Size, angleRad: number): Size {\n const width = source.width;\n const height = source.height;\n if (width <= 0 || height <= 0) return { width: 0, height: 0 };\n\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n\n const denomA = width * c + height * s;\n const denomD = width * s + height * c;\n const capA = denomA > EPSILON ? (width * width) / denomA : Number.POSITIVE_INFINITY;\n const capD = denomD > EPSILON ? (width * height) / denomD : Number.POSITIVE_INFINITY;\n\n const outWidth = Math.min(capA, capD);\n const outHeight = (outWidth * height) / width;\n return { width: outWidth, height: outHeight };\n}\n\nconst EPSILON = 1e-9;\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { largestInscribedRect } from './inscribe.js';\nimport { type RotateState, effectiveAngleDeg, isRotateNoOp } from './state.js';\n\n/**\n * Apply rotation = quarter-turns + free-angle in one `drawImage`. Free\n * angles auto-crop to the largest same-aspect rect inside the rotated\n * bounding box so transparent corners stay out of the output.\n */\nexport async function bakeRotate(state: RotateState, source: SourceImage): Promise<SourceImage> {\n if (isRotateNoOp(state)) return source;\n\n const angleDeg = effectiveAngleDeg(state);\n const angleRad = (angleDeg * Math.PI) / 180;\n\n const sub90Deg = angleDeg - state.quarterTurns * 90; // ∈ [-45, 45]\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n let outWidth: number;\n let outHeight: number;\n\n if (isQuarterOnly) {\n if (state.quarterTurns === 1 || state.quarterTurns === 3) {\n outWidth = source.height;\n outHeight = source.width;\n } else {\n outWidth = source.width;\n outHeight = source.height;\n }\n } else {\n const inscribed = largestInscribedRect(source, angleRad);\n outWidth = Math.max(1, Math.round(inscribed.width));\n outHeight = Math.max(1, Math.round(inscribed.height));\n }\n\n const bake = createBakeCanvas(outWidth, outHeight);\n const ctx = getBakeContext2D(bake);\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.translate(outWidth / 2, outHeight / 2);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -source.width / 2, -source.height / 2);\n\n return {\n bitmap: bake.canvas,\n width: outWidth,\n height: outHeight,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Per-axis scale factors. `lockAspect` makes the editor keep `scaleX === scaleY`.\n * Output pixels are computed at bake time as `round(upstream * scale)`,\n * clamped to `[MIN_DIMENSION, MAX_DIMENSION]`.\n */\nexport interface ResizeState {\n readonly scaleX: number;\n readonly scaleY: number;\n readonly lockAspect: boolean;\n}\n\nexport const MAX_DIMENSION = 8000;\nexport const MIN_DIMENSION = 1;\n\nexport function initialResizeState(): ResizeState {\n return { scaleX: 1, scaleY: 1, lockAspect: true };\n}\n\nexport function isResizeNoOp(state: ResizeState): boolean {\n return Math.abs(state.scaleX - 1) < 1e-9 && Math.abs(state.scaleY - 1) < 1e-9;\n}\n\n/** Integer output dimensions for an upstream image, clamped per axis. */\nexport function resolveOutputSize(\n state: ResizeState,\n upstream: { readonly width: number; readonly height: number },\n): { width: number; height: number } {\n const width = clampInt(Math.round(upstream.width * state.scaleX));\n const height = clampInt(Math.round(upstream.height * state.scaleY));\n return { width, height };\n}\n\n/** Set width via a pixel value; with `lockAspect` the vertical scale follows. */\nexport function setWidthPx(\n state: ResizeState,\n widthPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.width <= 0) return state;\n const target = clampInt(Math.round(widthPx));\n const scaleX = target / upstream.width;\n const scaleY = state.lockAspect ? scaleX : state.scaleY;\n return { ...state, scaleX, scaleY };\n}\n\nexport function setHeightPx(\n state: ResizeState,\n heightPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.height <= 0) return state;\n const target = clampInt(Math.round(heightPx));\n const scaleY = target / upstream.height;\n const scaleX = state.lockAspect ? scaleY : state.scaleX;\n return { ...state, scaleX, scaleY };\n}\n\n/** Uniform percentage change; always touches both axes regardless of `lockAspect`. */\nexport function setPercent(state: ResizeState, percent: number): ResizeState {\n const scale = clampScaleForPercent(percent / 100);\n return { ...state, scaleX: scale, scaleY: scale };\n}\n\nexport function setLockAspect(state: ResizeState, locked: boolean): ResizeState {\n if (state.lockAspect === locked) return state;\n if (!locked) return { ...state, lockAspect: false };\n // Average the two scales so neither axis arbitrarily wins on lock.\n const merged = (state.scaleX + state.scaleY) / 2;\n return { scaleX: merged, scaleY: merged, lockAspect: true };\n}\n\n/** Percent display value; when axes differ, returns the larger scale. */\nexport function effectivePercent(state: ResizeState): number {\n const max = Math.max(state.scaleX, state.scaleY);\n return Math.round(max * 1000) / 10;\n}\n\nfunction clampInt(n: number): number {\n if (!Number.isFinite(n)) return MIN_DIMENSION;\n return Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, Math.trunc(n)));\n}\n\nfunction clampScaleForPercent(scale: number): number {\n if (!Number.isFinite(scale) || scale <= 0) return 0.01;\n return Math.max(0.01, Math.min(scale, MAX_DIMENSION));\n}\n","import { type BakeCanvas, createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type ResizeState, isResizeNoOp, resolveOutputSize } from './state.js';\n\n/**\n * Resize `source` to the dimensions implied by `state`. Downscales > 2×\n * run a halving pyramid first so the bilinear-ish `drawImage` scaler\n * doesn't alias.\n */\nexport async function bakeResize(state: ResizeState, source: SourceImage): Promise<SourceImage> {\n if (isResizeNoOp(state)) return source;\n const { width: targetW, height: targetH } = resolveOutputSize(state, source);\n if (targetW <= 0 || targetH <= 0) return source;\n\n const halvings = countHalvingsNeeded(source.width, source.height, targetW, targetH);\n\n let current: { bitmap: CanvasImageSource; width: number; height: number } = {\n bitmap: source.bitmap,\n width: source.width,\n height: source.height,\n };\n\n // Each halving writes into its own canvas so the source is never reused as destination.\n const intermediates: BakeCanvas[] = [];\n for (let i = 0; i < halvings; i++) {\n const stepW = Math.max(targetW, Math.floor(current.width / 2));\n const stepH = Math.max(targetH, Math.floor(current.height / 2));\n const step = drawScaled(current, stepW, stepH);\n intermediates.push(step);\n current = { bitmap: step.canvas, width: stepW, height: stepH };\n }\n\n const final = drawScaled(current, targetW, targetH);\n\n intermediates.length = 0;\n\n return {\n bitmap: final.canvas,\n width: targetW,\n height: targetH,\n mimeType: source.mimeType,\n };\n}\n\nfunction countHalvingsNeeded(srcW: number, srcH: number, targetW: number, targetH: number): number {\n let w = srcW;\n let h = srcH;\n let count = 0;\n // Defensive cap so a malformed input can't spin forever.\n while ((w / 2 > targetW || h / 2 > targetH) && count < 16) {\n w = Math.floor(w / 2);\n h = Math.floor(h / 2);\n count += 1;\n }\n return count;\n}\n\nfunction drawScaled(\n current: { bitmap: CanvasImageSource; width: number; height: number },\n outW: number,\n outH: number,\n): BakeCanvas {\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(current.bitmap, 0, 0, current.width, current.height, 0, 0, outW, outH);\n return bake;\n}\n","/**\n * Six tone adjustments stored as slider values in [-100, +100]; math\n * constants (gamma exponent, contrast multiplier, etc.) are computed\n * from these in `math.ts` at LUT-build time.\n */\nexport interface FinetuneState {\n readonly brightness: number;\n readonly contrast: number;\n readonly saturation: number;\n readonly exposure: number;\n readonly clarity: number;\n readonly gamma: number;\n}\n\nexport const FINETUNE_MIN = -100;\nexport const FINETUNE_MAX = 100;\nexport const FINETUNE_STEP = 1;\n\nexport const DEFAULT_FINETUNE_STATE: FinetuneState = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n};\n\nexport type FinetuneKey = keyof FinetuneState;\n\nexport const FINETUNE_ADJUSTMENTS: readonly {\n readonly key: FinetuneKey;\n readonly label: string;\n}[] = [\n { key: 'brightness', label: 'Brightness' },\n { key: 'contrast', label: 'Contrast' },\n { key: 'saturation', label: 'Saturation' },\n { key: 'exposure', label: 'Exposure' },\n { key: 'clarity', label: 'Clarity' },\n { key: 'gamma', label: 'Gamma' },\n];\n\nexport function initialFinetuneState(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nexport function isFinetuneNoOp(state: FinetuneState): boolean {\n return (\n state.brightness === 0 &&\n state.contrast === 0 &&\n state.saturation === 0 &&\n state.exposure === 0 &&\n state.clarity === 0 &&\n state.gamma === 0\n );\n}\n\n/** Update a single adjustment, clamped to the legal range. */\nexport function setFinetune(state: FinetuneState, key: FinetuneKey, value: number): FinetuneState {\n const next = clampSliderValue(value);\n if (state[key] === next) return state;\n return { ...state, [key]: next };\n}\n\n/** Reset a single adjustment to its default of 0. */\nexport function resetFinetune(state: FinetuneState, key: FinetuneKey): FinetuneState {\n if (state[key] === 0) return state;\n return { ...state, [key]: 0 };\n}\n\n/** Reset every adjustment to its default. */\nexport function resetAllFinetune(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nfunction clampSliderValue(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value <= FINETUNE_MIN) return FINETUNE_MIN;\n if (value >= FINETUNE_MAX) return FINETUNE_MAX;\n // Snap to slider step so programmatic setters round-trip through the input.\n return Math.round(value / FINETUNE_STEP) * FINETUNE_STEP;\n}\n","/**\n * Pure math for the finetune adjustments. Slider values arrive in\n * [-100, +100] from `state.ts`. Shared by the bake (full-resolution)\n * and the live preview (display-resolution).\n */\n\nimport type { FinetuneState } from './state.js';\n\n/**\n * 256-entry LUT collapsing brightness + contrast + exposure + gamma into\n * one per-byte mapping. Saturation and clarity run in separate passes.\n */\nexport function buildFinetuneLut(state: FinetuneState): Uint8ClampedArray {\n const lut = new Uint8ClampedArray(256);\n\n // brightness: -100 → -0.5, +100 → +0.5 on the normalised value\n const brightnessOffset = state.brightness / 200;\n // contrast multiplier around mid-gray: -100 → 0×, +100 → 2×\n const contrastFactor = 1 + state.contrast / 100;\n // exposure multiplier: -100 → 0.5×, +100 → 1.5×\n const exposureFactor = 1 + state.exposure / 200;\n // gamma exponent: -100 → 2.0, 0 → 1.0, +100 → 0.5\n const gammaExponent = gammaExponentFor(state.gamma);\n\n for (let v = 0; v < 256; v++) {\n let x = v / 255;\n x = x * exposureFactor;\n x = (x - 0.5) * contrastFactor + 0.5;\n x = x + brightnessOffset;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n x = x ** gammaExponent;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n lut[v] = Math.round(x * 255);\n }\n\n return lut;\n}\n\nfunction gammaExponentFor(slider: number): number {\n if (slider === 0) return 1;\n if (slider > 0) return 1 - 0.5 * (slider / 100);\n return 1 + 1.0 * (-slider / 100);\n}\n\n/**\n * Apply the LUT and saturation in one pass. `src`/`dst` may be the same\n * buffer. Saturation: -100 → grayscale (Rec. 709), 0 → identity, +100 → 2×.\n */\nexport function applyFinetuneLutAndSaturation(\n src: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n lut: Uint8ClampedArray,\n state: FinetuneState,\n): void {\n const len = src.length;\n if (dst.length !== len) {\n throw new Error('applyFinetuneLutAndSaturation: src/dst length mismatch');\n }\n\n const saturation = 1 + state.saturation / 100;\n\n // Fast path for the common identity (saturation === 0).\n if (saturation === 1) {\n for (let i = 0; i < len; i += 4) {\n dst[i] = lut[src[i]];\n dst[i + 1] = lut[src[i + 1]];\n dst[i + 2] = lut[src[i + 2]];\n dst[i + 3] = src[i + 3];\n }\n return;\n }\n\n for (let i = 0; i < len; i += 4) {\n const r0 = lut[src[i]];\n const g0 = lut[src[i + 1]];\n const b0 = lut[src[i + 2]];\n // Rec. 709 luminance.\n const y = 0.2126 * r0 + 0.7152 * g0 + 0.0722 * b0;\n let r = y + (r0 - y) * saturation;\n let g = y + (g0 - y) * saturation;\n let b = y + (b0 - y) * saturation;\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n dst[i + 3] = src[i + 3];\n }\n}\n\n/**\n * Unsharp-mask local contrast: `result = dst + (clarity/100) * (dst - blurred)`.\n * `clarity === 0` is a no-op; caller is expected to skip the call.\n */\nexport function applyClarity(\n dst: Uint8ClampedArray,\n blurred: Uint8ClampedArray,\n clarity: number,\n): void {\n if (clarity === 0) return;\n const len = dst.length;\n if (blurred.length !== len) {\n throw new Error('applyClarity: dst/blurred length mismatch');\n }\n const amount = clarity / 100;\n for (let i = 0; i < len; i += 4) {\n const dr = dst[i];\n const dg = dst[i + 1];\n const db = dst[i + 2];\n let r = dr + amount * (dr - blurred[i]);\n let g = dg + amount * (dg - blurred[i + 1]);\n let b = db + amount * (db - blurred[i + 2]);\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n }\n}\n\n/** Separable 3×3 box blur (clamp at edges). Used as the unsharp-mask reference. */\nexport function boxBlur3x3(\n src: Uint8ClampedArray,\n tmp: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n width: number,\n height: number,\n): void {\n if (src.length !== tmp.length || src.length !== dst.length) {\n throw new Error('boxBlur3x3: buffer length mismatch');\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const xm = x === 0 ? 0 : x - 1;\n const xp = x === width - 1 ? width - 1 : x + 1;\n const i = (y * width + x) * 4;\n const im = (y * width + xm) * 4;\n const ip = (y * width + xp) * 4;\n tmp[i] = (src[im] + src[i] + src[ip]) / 3;\n tmp[i + 1] = (src[im + 1] + src[i + 1] + src[ip + 1]) / 3;\n tmp[i + 2] = (src[im + 2] + src[i + 2] + src[ip + 2]) / 3;\n tmp[i + 3] = src[i + 3];\n }\n }\n for (let y = 0; y < height; y++) {\n const ym = y === 0 ? 0 : y - 1;\n const yp = y === height - 1 ? height - 1 : y + 1;\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * 4;\n const im = (ym * width + x) * 4;\n const ip = (yp * width + x) * 4;\n dst[i] = (tmp[im] + tmp[i] + tmp[ip]) / 3;\n dst[i + 1] = (tmp[im + 1] + tmp[i + 1] + tmp[ip + 1]) / 3;\n dst[i + 2] = (tmp[im + 2] + tmp[i + 2] + tmp[ip + 2]) / 3;\n dst[i + 3] = tmp[i + 3];\n }\n }\n}\n\n/** Structural raster shape so tests can pass a plain object (jsdom 25 lacks `ImageData`). */\nexport interface RasterImage {\n readonly data: Uint8ClampedArray;\n readonly width: number;\n readonly height: number;\n}\n\n/** Apply the full pipeline (LUT + saturation + clarity); blur buffers allocate only when clarity ≠ 0. */\nexport function applyFinetuneToImageData(\n state: FinetuneState,\n baseline: RasterImage,\n dst: RasterImage,\n): void {\n if (\n baseline.width !== dst.width ||\n baseline.height !== dst.height ||\n baseline.data.length !== dst.data.length\n ) {\n throw new Error('applyFinetuneToImageData: baseline/dst dimensions mismatch');\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(baseline.data, dst.data, lut, state);\n\n if (state.clarity !== 0) {\n // Blur the pre-LUT baseline so the cached blur stays valid as long as\n // the baseline does; high-frequency is roughly order-independent here.\n const tmp = new Uint8ClampedArray(baseline.data.length);\n const blurred = new Uint8ClampedArray(baseline.data.length);\n boxBlur3x3(baseline.data, tmp, blurred, baseline.width, baseline.height);\n applyClarity(dst.data, blurred, state.clarity);\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { applyFinetuneToImageData } from './math.js';\nimport { type FinetuneState, isFinetuneNoOp } from './state.js';\n\n/** Apply the six finetune adjustments at full resolution. Shares the math with the live preview. */\nexport async function bakeFinetune(\n state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n if (isFinetuneNoOp(state)) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n const baseline = ctx.getImageData(0, 0, source.width, source.height);\n // When clarity is non-zero the unsharp-mask step needs `dst` and `blurred`\n // simultaneously, so we can't reuse the baseline buffer in place.\n if (state.clarity === 0) {\n applyFinetuneToImageData(state, baseline, baseline);\n ctx.putImageData(baseline, 0, 0);\n } else {\n const dst = new ImageData(\n new Uint8ClampedArray(baseline.data.length),\n baseline.width,\n baseline.height,\n );\n applyFinetuneToImageData(state, baseline, dst);\n ctx.putImageData(dst, 0, 0);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Curated `FinetuneState` shapes that one-click set the six tone numbers.\n * The filter tab is a UI view over the finetune store: clicking a preset\n * here is identical to dragging the matching sliders in finetune.\n */\nimport { DEFAULT_FINETUNE_STATE, type FinetuneState } from '../finetune/state.js';\n\nexport type FilterPresetId = 'none' | 'vivid' | 'mono' | 'soft' | 'punch' | 'mute' | 'bright';\n\nexport interface FilterPreset {\n readonly id: FilterPresetId;\n readonly label: string;\n readonly state: FinetuneState;\n}\n\n/** The seven presets in display order. `none` is the identity / off state. */\nexport const FILTER_PRESETS: readonly FilterPreset[] = [\n {\n id: 'none',\n label: 'None',\n state: DEFAULT_FINETUNE_STATE,\n },\n {\n id: 'vivid',\n label: 'Vivid',\n state: {\n brightness: 0,\n contrast: 10,\n saturation: 40,\n exposure: 0,\n clarity: 5,\n gamma: 0,\n },\n },\n {\n id: 'mono',\n label: 'Mono',\n // -100 saturation is bit-exact grayscale via Rec. 709 luminance.\n state: {\n brightness: 0,\n contrast: 15,\n saturation: -100,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n },\n },\n {\n id: 'soft',\n label: 'Soft',\n state: {\n brightness: 5,\n contrast: -10,\n saturation: 0,\n exposure: 0,\n clarity: -25,\n gamma: 0,\n },\n },\n {\n id: 'punch',\n label: 'Punch',\n state: {\n brightness: 0,\n contrast: 30,\n saturation: 5,\n exposure: 0,\n clarity: 25,\n gamma: 0,\n },\n },\n {\n id: 'mute',\n label: 'Mute',\n state: {\n brightness: 0,\n contrast: 5,\n saturation: -50,\n exposure: 0,\n clarity: -5,\n gamma: 0,\n },\n },\n {\n id: 'bright',\n label: 'Bright',\n state: {\n brightness: 5,\n contrast: 5,\n saturation: 0,\n exposure: 15,\n clarity: 0,\n gamma: 0,\n },\n },\n];\n\n/** Structural equality across the six finetune fields. */\nexport function finetuneStatesEqual(a: FinetuneState, b: FinetuneState): boolean {\n return (\n a.brightness === b.brightness &&\n a.contrast === b.contrast &&\n a.saturation === b.saturation &&\n a.exposure === b.exposure &&\n a.clarity === b.clarity &&\n a.gamma === b.gamma\n );\n}\n\n/** Preset whose state matches `state` exactly, or `undefined` if between presets. */\nexport function findActivePreset(state: FinetuneState): FilterPreset | undefined {\n for (const preset of FILTER_PRESETS) {\n if (finetuneStatesEqual(preset.state, state)) return preset;\n }\n return undefined;\n}\n","/**\n * Annotation state and pure mutators. Shapes are a flat discriminated\n * union keyed on `kind`; all coordinates are image-space pixels.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport type ShapeKind = 'text' | 'rect' | 'ellipse' | 'arrow' | 'freehand' | 'highlight';\n\n/** Tools the annotation plugin exposes. `select` is the picker. */\nexport type AnnotateTool = ShapeKind | 'select';\n\ninterface ShapeBase {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n readonly kind: ShapeKind;\n}\n\nexport interface TextShape extends ShapeBase {\n readonly kind: 'text';\n /** Top-left anchor in image-space pixels. */\n readonly x: number;\n readonly y: number;\n readonly text: string;\n /** Font size in image-space pixels. */\n readonly fontSize: number;\n /** CSS colour string. */\n readonly color: string;\n readonly textAlign: 'left' | 'center' | 'right';\n}\n\nexport interface RectShape extends ShapeBase {\n readonly kind: 'rect';\n readonly x: number;\n readonly y: number;\n /** Non-negative after gesture commit; may be negative mid-drag. */\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n /** `null` means \"no fill\". */\n readonly fillColor: string | null;\n}\n\nexport interface EllipseShape extends ShapeBase {\n readonly kind: 'ellipse';\n /** Bounding-box top-left; the ellipse fits inside the box. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n readonly fillColor: string | null;\n}\n\nexport interface ArrowShape extends ShapeBase {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n /** Arrowhead is drawn at (x2, y2). */\n readonly x2: number;\n readonly y2: number;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface FreehandShape extends ShapeBase {\n readonly kind: 'freehand';\n /** Decimated raw points in image-space; smoothing happens at render time. */\n readonly points: ReadonlyArray<Point>;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface HighlightShape extends ShapeBase {\n readonly kind: 'highlight';\n readonly points: ReadonlyArray<Point>;\n /** Default semi-transparent yellow, drawn with `multiply` blend mode. */\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport type Shape =\n | TextShape\n | RectShape\n | EllipseShape\n | ArrowShape\n | FreehandShape\n | HighlightShape;\n\nexport interface StylePalette {\n readonly color: string;\n readonly strokeWidth: number;\n /** Used for new rect/ellipse fills; `null` = unfilled. */\n readonly fillColor: string | null;\n /** Used for new text shapes. In image-space pixels. */\n readonly fontSize: number;\n}\n\nexport interface AnnotateState {\n readonly shapes: ReadonlyArray<Shape>;\n readonly selectedId: string | null;\n readonly activeTool: AnnotateTool;\n readonly currentStyle: StylePalette;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n /** Monotonic counter used to mint shape ids. Never decreases. */\n readonly nextShapeNumber: number;\n}\n\n/** Yellow @ 35% alpha; the highlight bake uses `multiply` blending. */\nexport const HIGHLIGHT_DEFAULT_COLOR = 'rgba(255, 235, 59, 0.35)';\nexport const HIGHLIGHT_DEFAULT_STROKE = 18;\nexport const FREEHAND_DEFAULT_STROKE = 6;\nexport const TEXT_DEFAULT_FONT_SIZE = 32;\nexport const DEFAULT_PALETTE_COLOR = '#ff3b30';\nexport const DEFAULT_STROKE_WIDTH = 4;\n\nexport function defaultStylePalette(): StylePalette {\n return {\n color: DEFAULT_PALETTE_COLOR,\n strokeWidth: DEFAULT_STROKE_WIDTH,\n fillColor: null,\n fontSize: TEXT_DEFAULT_FONT_SIZE,\n };\n}\n\nexport interface InitialAnnotateStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialAnnotateState(input: InitialAnnotateStateInput): AnnotateState {\n return {\n shapes: [],\n selectedId: null,\n activeTool: 'select',\n currentStyle: defaultStylePalette(),\n imageSize: input.imageSize,\n nextShapeNumber: 1,\n };\n}\n\n/** Allocate a new shape id from the monotonic counter; caller threads `nextShapeNumber` back into state. */\nexport function mintShapeId(state: AnnotateState): {\n id: string;\n nextShapeNumber: number;\n} {\n return {\n id: `s_${state.nextShapeNumber.toString(36)}`,\n nextShapeNumber: state.nextShapeNumber + 1,\n };\n}\n\nexport function setActiveTool(state: AnnotateState, tool: AnnotateTool): AnnotateState {\n if (state.activeTool === tool) return state;\n // Switching to a drawing tool deselects so the next drag starts a new shape.\n return { ...state, activeTool: tool, selectedId: tool === 'select' ? state.selectedId : null };\n}\n\nexport function setStyle(state: AnnotateState, partial: Partial<StylePalette>): AnnotateState {\n return { ...state, currentStyle: { ...state.currentStyle, ...partial } };\n}\n\nexport function selectShape(state: AnnotateState, id: string | null): AnnotateState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function addShape(state: AnnotateState, shape: Shape): AnnotateState {\n return { ...state, shapes: [...state.shapes, shape], selectedId: shape.id };\n}\n\nexport function replaceShape(state: AnnotateState, shape: Shape): AnnotateState {\n let changed = false;\n const next = state.shapes.map((existing) => {\n if (existing.id !== shape.id) return existing;\n changed = true;\n return shape;\n });\n if (!changed) return state;\n return { ...state, shapes: next };\n}\n\nexport function deleteShape(state: AnnotateState, id: string): AnnotateState {\n const next = state.shapes.filter((shape) => shape.id !== id);\n if (next.length === state.shapes.length) return state;\n return {\n ...state,\n shapes: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\nexport function findShape(state: AnnotateState, id: string | null): Shape | undefined {\n if (id === null) return undefined;\n return state.shapes.find((shape) => shape.id === id);\n}\n\n/** Normalise a rect extent so `width`/`height` are non-negative after a sign-flip drag. */\nexport function normaliseRectExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nexport function translateShape(shape: Shape, dx: number, dy: number): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'rect':\n case 'ellipse':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'arrow':\n return {\n ...shape,\n x1: shape.x1 + dx,\n y1: shape.y1 + dy,\n x2: shape.x2 + dx,\n y2: shape.y2 + dy,\n };\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => ({ x: p.x + dx, y: p.y + dy })) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Type-narrowing helper for exhaustive switches over `Shape`. */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled annotation shape kind: ${JSON.stringify(value)}`);\n}\n\n/**\n * Mirror a shape across an axis of `dims`. Rect/ellipse top-left is\n * remapped so the visible rectangle straddles the same pixels; arrow\n * endpoints and freehand points mirror independently. Text uses its\n * anchor only — the glyph rect walks slightly relative to centre.\n */\nexport function mirrorShape(\n shape: Shape,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): Shape {\n if (axis === 'horizontal') {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, x: dims.width - shape.x - shape.width };\n case 'text':\n return { ...shape, x: dims.width - shape.x };\n case 'arrow':\n return { ...shape, x1: dims.width - shape.x1, x2: dims.width - shape.x2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: dims.width - p.x, y: p.y })),\n };\n default:\n return assertNever(shape);\n }\n }\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, y: dims.height - shape.y - shape.height };\n case 'text':\n return { ...shape, y: dims.height - shape.y };\n case 'arrow':\n return { ...shape, y1: dims.height - shape.y1, y2: dims.height - shape.y2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: p.x, y: dims.height - p.y })),\n };\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Rotate a shape `turns × 90°` CW around the centre of `oldDims`. Returns\n * coordinates in the post-rotation image's coord space (dims swap on odd turns).\n */\nexport function rotateShape(\n shape: Shape,\n turns: 0 | 1 | 2 | 3,\n oldDims: { readonly width: number; readonly height: number },\n): Shape {\n if (turns === 0) return shape;\n const rotatePoint = (x: number, y: number): { x: number; y: number } => {\n if (turns === 1) return { x: oldDims.height - y, y: x };\n if (turns === 2) return { x: oldDims.width - x, y: oldDims.height - y };\n return { x: y, y: oldDims.width - x };\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n // Rotated TL + BR become two corners of the new axis-aligned box.\n const corners = [\n rotatePoint(shape.x, shape.y),\n rotatePoint(shape.x + shape.width, shape.y + shape.height),\n ];\n const newX = Math.min(corners[0].x, corners[1].x);\n const newY = Math.min(corners[0].y, corners[1].y);\n const newW = Math.abs(corners[1].x - corners[0].x);\n const newH = Math.abs(corners[1].y - corners[0].y);\n return { ...shape, x: newX, y: newY, width: newW, height: newH };\n }\n case 'text': {\n const p = rotatePoint(shape.x, shape.y);\n return { ...shape, x: p.x, y: p.y };\n }\n case 'arrow': {\n const p1 = rotatePoint(shape.x1, shape.y1);\n const p2 = rotatePoint(shape.x2, shape.y2);\n return { ...shape, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };\n }\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => rotatePoint(p.x, p.y)) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Apply a transformation to every shape in `state.shapes`. */\nexport function transformShapes(\n state: AnnotateState,\n transformer: (shape: Shape) => Shape,\n): AnnotateState {\n if (state.shapes.length === 0) return state;\n return { ...state, shapes: state.shapes.map(transformer) };\n}\n\n/**\n * Kinds placeable from the keyboard. Freehand / highlight are excluded;\n * a \"default at centre\" instance has no honest shape for those.\n */\nexport type KeyboardPlaceableKind = 'text' | 'rect' | 'ellipse' | 'arrow';\n\nexport const KEYBOARD_PLACEABLE_KINDS: ReadonlyArray<KeyboardPlaceableKind> = [\n 'text',\n 'rect',\n 'ellipse',\n 'arrow',\n];\n\nexport function isKeyboardPlaceableKind(kind: ShapeKind): kind is KeyboardPlaceableKind {\n return kind === 'text' || kind === 'rect' || kind === 'ellipse' || kind === 'arrow';\n}\n\nexport interface CreateCenteredShapeContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly style: StylePalette;\n readonly id: string;\n}\n\n/** A `Shape` whose kind is keyboard-placeable (rect / ellipse / arrow / text). */\nexport type KeyboardPlaceableShape = TextShape | RectShape | EllipseShape | ArrowShape;\n\nexport function createCenteredShape(\n kind: KeyboardPlaceableKind,\n ctx: CreateCenteredShapeContext,\n): KeyboardPlaceableShape {\n const { imageSize, style, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n\n switch (kind) {\n case 'rect':\n case 'ellipse': {\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n if (kind === 'rect') {\n return {\n id,\n kind: 'rect',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n return {\n id,\n kind: 'ellipse',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n case 'arrow': {\n const length = Math.max(100, Math.round(shortEdge * 0.3));\n const x1 = Math.round(cx - length / 2);\n const x2 = x1 + length;\n const y = Math.round(cy);\n return {\n id,\n kind: 'arrow',\n x1,\n y1: y,\n x2,\n y2: y,\n color: style.color,\n strokeWidth: style.strokeWidth,\n };\n }\n case 'text': {\n const x = Math.round(cx);\n const y = Math.round(cy - style.fontSize / 2);\n return {\n id,\n kind: 'text',\n x,\n y,\n text: '',\n fontSize: style.fontSize,\n color: style.color,\n textAlign: 'center',\n };\n }\n }\n}\n","import type { Rect } from '../../geometry/rect.js';\nimport { type Shape, assertNever } from './state.js';\n\n/**\n * Axis-aligned bounding box in image-space pixels. Text uses a font-metric\n * estimate because jsdom's `measureText` returns 0; the renderer measures\n * real text at paint time.\n */\nexport function boundingBoxOf(shape: Shape): Rect {\n switch (shape.kind) {\n case 'text': {\n const { width, height } = estimateTextSize(shape.text, shape.fontSize);\n const x = alignToOrigin(shape.x, width, shape.textAlign);\n return { x, y: shape.y, width, height };\n }\n case 'rect':\n case 'ellipse':\n return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };\n case 'arrow': {\n const x = Math.min(shape.x1, shape.x2);\n const y = Math.min(shape.y1, shape.y2);\n return {\n x,\n y,\n width: Math.abs(shape.x2 - shape.x1),\n height: Math.abs(shape.y2 - shape.y1),\n };\n }\n case 'freehand':\n case 'highlight': {\n const head = shape.points[0];\n if (!head) return { x: 0, y: 0, width: 0, height: 0 };\n let minX = head.x;\n let minY = head.y;\n let maxX = head.x;\n let maxY = head.y;\n for (const p of shape.points) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Eight-handle layout (corners + edges). Arrows reuse `tl`/`br` as\n * endpoint handles; callers detect arrow handles by shape kind.\n */\nexport type SelectionHandle = 'tl' | 'tr' | 'bl' | 'br' | 't' | 'r' | 'b' | 'l';\n\nexport const ALL_SELECTION_HANDLES: readonly SelectionHandle[] = [\n 'tl',\n 'tr',\n 'bl',\n 'br',\n 't',\n 'r',\n 'b',\n 'l',\n];\n\n/** Image-space coordinates for each handle; renderer projects to display. */\nexport function selectionHandlePositions(\n rect: Rect,\n): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\n/** Apply a handle drag to a rect. Returns the new rect; the caller normalises. */\nexport function rectFromHandleDrag(\n initial: Rect,\n handle: SelectionHandle,\n pointer: { x: number; y: number },\n): Rect {\n let x = initial.x;\n let y = initial.y;\n let right = initial.x + initial.width;\n let bottom = initial.y + initial.height;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') x = pointer.x;\n if (handle === 'tr' || handle === 'r' || handle === 'br') right = pointer.x;\n if (handle === 'tl' || handle === 't' || handle === 'tr') y = pointer.y;\n if (handle === 'bl' || handle === 'b' || handle === 'br') bottom = pointer.y;\n\n return { x, y, width: right - x, height: bottom - y };\n}\n\n/**\n * Estimate painted text size without `measureText` (jsdom returns 0).\n * Uses 0.55em mean Latin char width and 1.2em line height — close enough\n * for selection-handle placement.\n */\nexport function estimateTextSize(\n text: string,\n fontSize: number,\n): { width: number; height: number } {\n const lines = text.length === 0 ? [''] : text.split('\\n');\n let maxLineLen = 0;\n for (const line of lines) {\n if (line.length > maxLineLen) maxLineLen = line.length;\n }\n const width = Math.max(fontSize * 0.6, maxLineLen * fontSize * 0.55);\n const height = lines.length * fontSize * 1.2;\n return { width, height };\n}\n\n/** Convert a text shape's anchor to the bounding box's top-left given `textAlign`. */\nexport function alignToOrigin(\n anchorX: number,\n width: number,\n align: 'left' | 'center' | 'right',\n): number {\n switch (align) {\n case 'left':\n return anchorX;\n case 'center':\n return anchorX - width / 2;\n case 'right':\n return anchorX - width;\n }\n}\n","import type { Point } from '../../geometry/rect.js';\nimport { boundingBoxOf } from './geometry.js';\nimport { type Shape, assertNever } from './state.js';\n\n/** Picking margin added to every stroked-shape hit-test, in image-space pixels. */\nexport const PICK_TOLERANCE = 4;\n\n/** Find the topmost shape under `point` (image-space). Iterates back-to-front. */\nexport function pickShape(shapes: ReadonlyArray<Shape>, point: Point): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape && hitTest(shape, point)) return shape;\n }\n return undefined;\n}\n\nexport function hitTest(shape: Shape, point: Point): boolean {\n switch (shape.kind) {\n case 'text':\n return pointInRect(point, boundingBoxOf(shape));\n case 'rect': {\n const inside = pointInRect(point, normaliseBox(shape));\n // Filled rects pick anywhere inside; outline-only picks on the stroke (with tolerance).\n if (shape.fillColor !== null) return inside;\n const outer = expandRect(normaliseBox(shape), shape.strokeWidth / 2 + PICK_TOLERANCE);\n const inner = expandRect(normaliseBox(shape), -(shape.strokeWidth / 2 + PICK_TOLERANCE));\n return pointInRect(point, outer) && !pointInRect(point, inner);\n }\n case 'ellipse': {\n const box = normaliseBox(shape);\n const rx = box.width / 2;\n const ry = box.height / 2;\n const cx = box.x + rx;\n const cy = box.y + ry;\n if (rx <= 0 || ry <= 0) return false;\n const nx = (point.x - cx) / rx;\n const ny = (point.y - cy) / ry;\n const r2 = nx * nx + ny * ny;\n const tolerance = (shape.strokeWidth / 2 + PICK_TOLERANCE) / Math.min(rx, ry);\n if (shape.fillColor !== null) return r2 <= (1 + tolerance) ** 2;\n return r2 <= (1 + tolerance) ** 2 && r2 >= (1 - tolerance) ** 2;\n }\n case 'arrow':\n return pointNearSegment(\n point,\n { x: shape.x1, y: shape.y1 },\n { x: shape.x2, y: shape.y2 },\n shape.strokeWidth / 2 + PICK_TOLERANCE,\n );\n case 'freehand':\n case 'highlight': {\n const box = boundingBoxOf(shape);\n const expanded = expandRect(box, shape.strokeWidth / 2 + PICK_TOLERANCE);\n if (!pointInRect(point, expanded)) return false;\n const tolerance = shape.strokeWidth / 2 + PICK_TOLERANCE;\n for (let i = 1; i < shape.points.length; i++) {\n const a = shape.points[i - 1];\n const b = shape.points[i];\n if (a && b && pointNearSegment(point, a, b, tolerance)) return true;\n }\n if (shape.points.length === 1) {\n const p = shape.points[0];\n if (!p) return false;\n const dx = p.x - point.x;\n const dy = p.y - point.y;\n return dx * dx + dy * dy <= tolerance * tolerance;\n }\n return false;\n }\n default:\n return assertNever(shape);\n }\n}\n\nfunction pointInRect(\n point: Point,\n rect: { x: number; y: number; width: number; height: number },\n): boolean {\n return (\n point.x >= rect.x &&\n point.y >= rect.y &&\n point.x <= rect.x + rect.width &&\n point.y <= rect.y + rect.height\n );\n}\n\nfunction expandRect(\n rect: { x: number; y: number; width: number; height: number },\n amount: number,\n): { x: number; y: number; width: number; height: number } {\n return {\n x: rect.x - amount,\n y: rect.y - amount,\n width: rect.width + amount * 2,\n height: rect.height + amount * 2,\n };\n}\n\nfunction normaliseBox(shape: { x: number; y: number; width: number; height: number }): {\n x: number;\n y: number;\n width: number;\n height: number;\n} {\n let { x, y, width, height } = shape;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nfunction pointNearSegment(point: Point, a: Point, b: Point, tolerance: number): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) {\n const ex = point.x - a.x;\n const ey = point.y - a.y;\n return ex * ex + ey * ey <= tolerance * tolerance;\n }\n let t = ((point.x - a.x) * dx + (point.y - a.y) * dy) / len2;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const projX = a.x + t * dx;\n const projY = a.y + t * dy;\n const ex = point.x - projX;\n const ey = point.y - projY;\n return ex * ex + ey * ey <= tolerance * tolerance;\n}\n","/**\n * Freehand stroke decimation + midpoint-curve smoothing. Decimation drops\n * sub-pixel samples from the raw pointer stream; the curve smoothing\n * happens at draw time so stored points stay unchanged across edits.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport const MIN_SAMPLE_DISTANCE = 2;\n\n/** Drop interior points closer than `MIN_SAMPLE_DISTANCE` to the previous kept point. */\nexport function decimatePoints(points: ReadonlyArray<Point>): Point[] {\n if (points.length <= 1) return [...points];\n const head = points[0];\n if (!head) return [];\n const out: Point[] = [head];\n let last = head;\n const minSq = MIN_SAMPLE_DISTANCE * MIN_SAMPLE_DISTANCE;\n for (let i = 1; i < points.length - 1; i++) {\n const p = points[i];\n if (!p) continue;\n const dx = p.x - last.x;\n const dy = p.y - last.y;\n if (dx * dx + dy * dy < minSq) continue;\n out.push(p);\n last = p;\n }\n // Always keep the final sample so the stroke ends where the pen lifted.\n const tail = points[points.length - 1];\n if (tail && tail !== last) out.push(tail);\n return out;\n}\n\n/**\n * Trace a smoothed path through `points` using the midpoint-curve technique.\n * Caller owns `beginPath`, stroke style, and `stroke()` so blend modes\n * (highlight) can wrap this without re-implementing the curve math.\n */\nexport function tracePath(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n points: ReadonlyArray<Point>,\n): void {\n if (points.length === 0) return;\n const head = points[0];\n if (!head) return;\n if (points.length === 1) {\n // Single tap: zero-length segment relies on round lineCap to render a dot.\n ctx.moveTo(head.x, head.y);\n ctx.lineTo(head.x, head.y);\n return;\n }\n ctx.moveTo(head.x, head.y);\n for (let i = 1; i < points.length - 1; i++) {\n const a = points[i];\n const b = points[i + 1];\n if (!a || !b) continue;\n const midX = (a.x + b.x) / 2;\n const midY = (a.y + b.y) / 2;\n ctx.quadraticCurveTo(a.x, a.y, midX, midY);\n }\n const last = points[points.length - 1];\n if (last) ctx.lineTo(last.x, last.y);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { tracePath } from './smooth.js';\nimport { type Shape, assertNever } from './state.js';\n\nexport interface AnnotateBakeInput {\n readonly shapes: ReadonlyArray<Shape>;\n}\n\n/**\n * System font stack used at bake; matches what the preview canvas renders.\n * No web font is loaded — the bundle budget rules it out and the bake\n * pipeline has no \"wait for font\" affordance.\n */\nexport const SYSTEM_FONT_STACK =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif';\n\n/** Paint every shape onto a fresh canvas at the source's dimensions. */\nexport async function bakeAnnotate(\n state: AnnotateBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.shapes.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n for (const shape of state.shapes) {\n paintShape(ctx, shape);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint one shape; caller positions the context for image-space coordinates. Shared by preview and bake. */\nexport function paintShape(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape,\n): void {\n switch (shape.kind) {\n case 'text':\n paintText(ctx, shape);\n return;\n case 'rect':\n paintRect(ctx, shape);\n return;\n case 'ellipse':\n paintEllipse(ctx, shape);\n return;\n case 'arrow':\n paintArrow(ctx, shape);\n return;\n case 'freehand':\n paintFreehand(ctx, shape);\n return;\n case 'highlight':\n paintHighlight(ctx, shape);\n return;\n default:\n assertNever(shape);\n }\n}\n\nfunction paintText(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'text' },\n): void {\n ctx.save();\n ctx.fillStyle = shape.color;\n ctx.font = `${shape.fontSize}px ${SYSTEM_FONT_STACK}`;\n ctx.textAlign = shape.textAlign;\n ctx.textBaseline = 'top';\n // Paint each line on its own; explicit `\\n` only (no auto-wrap).\n const lines = shape.text.length === 0 ? [''] : shape.text.split('\\n');\n const lineHeight = shape.fontSize * 1.2;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n ctx.fillText(line, shape.x, shape.y + i * lineHeight);\n }\n ctx.restore();\n}\n\nfunction paintRect(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'rect' },\n): void {\n ctx.save();\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fillRect(shape.x, shape.y, shape.width, shape.height);\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = 'miter';\n ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);\n }\n ctx.restore();\n}\n\nfunction paintEllipse(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'ellipse' },\n): void {\n ctx.save();\n const rx = shape.width / 2;\n const ry = shape.height / 2;\n const cx = shape.x + rx;\n const cy = shape.y + ry;\n ctx.beginPath();\n ctx.ellipse(cx, cy, Math.max(0, rx), Math.max(0, ry), 0, 0, Math.PI * 2);\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fill();\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.stroke();\n }\n ctx.restore();\n}\n\nfunction paintArrow(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'arrow' },\n): void {\n const dx = shape.x2 - shape.x1;\n const dy = shape.y2 - shape.y1;\n const length = Math.sqrt(dx * dx + dy * dy);\n if (length < 0.5) return;\n\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n\n // Floor on head size so thin strokes still get a readable head.\n const headLength = Math.min(Math.max(shape.strokeWidth * 5, 28), length * 0.6);\n const headWidth = Math.max(shape.strokeWidth * 4, 18);\n const ux = dx / length;\n const uy = dy / length;\n // Shaft stops short of the tip so the head's base sits flush with the cap.\n const shaftEndX = shape.x2 - ux * headLength * 0.6;\n const shaftEndY = shape.y2 - uy * headLength * 0.6;\n\n ctx.beginPath();\n ctx.moveTo(shape.x1, shape.y1);\n ctx.lineTo(shaftEndX, shaftEndY);\n ctx.stroke();\n\n const baseX = shape.x2 - ux * headLength;\n const baseY = shape.y2 - uy * headLength;\n const px = -uy;\n const py = ux;\n ctx.beginPath();\n ctx.moveTo(shape.x2, shape.y2);\n ctx.lineTo(baseX + (px * headWidth) / 2, baseY + (py * headWidth) / 2);\n ctx.lineTo(baseX - (px * headWidth) / 2, baseY - (py * headWidth) / 2);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n}\n\nfunction paintFreehand(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'freehand' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction paintHighlight(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'highlight' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n // `multiply` tints pixels like a highlighter pen; engines without it fall back to alpha-blend.\n ctx.globalCompositeOperation = 'multiply';\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n","/**\n * Redact state and mutators. Mirrors the annotate plugin's vocabulary\n * (id + kind, monotonic mint, replace/delete) so the selection layer\n * can be reused.\n */\n\nimport type { Rect } from '../../geometry/rect.js';\n\n/** `pixelate`, `blur`, or `solid` (flat fill). */\nexport type RedactMode = 'pixelate' | 'blur' | 'solid';\n\nexport const REDACT_MODES: readonly RedactMode[] = ['pixelate', 'blur', 'solid'];\n\nexport interface RedactRegion {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n /** Image-space rectangle. May be temporarily negative-extent mid-drag. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly mode: RedactMode;\n /** Used only when `mode === 'solid'`. CSS hex string. */\n readonly color: string;\n}\n\nexport interface RedactState {\n readonly regions: ReadonlyArray<RedactRegion>;\n /** Monotonic id source for new regions. Never decreases. */\n readonly nextRegionNumber: number;\n /** The currently-selected region id, or `null` when none. */\n readonly selectedId: string | null;\n /** The mode new regions are created with. Persists across selections. */\n readonly currentMode: RedactMode;\n /** Default fill colour for the `solid` mode. */\n readonly currentColor: string;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport const DEFAULT_REDACT_COLOR = '#000000';\nexport const DEFAULT_REDACT_MODE: RedactMode = 'pixelate';\n\nexport interface InitialRedactStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialRedactState(input: InitialRedactStateInput): RedactState {\n return {\n regions: [],\n nextRegionNumber: 1,\n selectedId: null,\n currentMode: DEFAULT_REDACT_MODE,\n currentColor: DEFAULT_REDACT_COLOR,\n imageSize: input.imageSize,\n };\n}\n\n/** Allocate a new region id; caller threads `nextRegionNumber` back into state. */\nexport function mintRegionId(state: RedactState): {\n id: string;\n nextRegionNumber: number;\n} {\n return {\n id: `r_${state.nextRegionNumber.toString(36)}`,\n nextRegionNumber: state.nextRegionNumber + 1,\n };\n}\n\nexport function addRegion(state: RedactState, region: RedactRegion): RedactState {\n return {\n ...state,\n regions: [...state.regions, region],\n selectedId: region.id,\n };\n}\n\nexport function replaceRegion(state: RedactState, region: RedactRegion): RedactState {\n let changed = false;\n const next = state.regions.map((existing) => {\n if (existing.id !== region.id) return existing;\n changed = true;\n return region;\n });\n if (!changed) return state;\n return { ...state, regions: next };\n}\n\nexport function deleteRegion(state: RedactState, id: string): RedactState {\n const next = state.regions.filter((region) => region.id !== id);\n if (next.length === state.regions.length) return state;\n return {\n ...state,\n regions: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\n/** Mirror every region across an axis of `dims`. */\nexport function mirrorRegions(\n state: RedactState,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return state;\n const next = state.regions.map((region) => {\n if (axis === 'horizontal') {\n return { ...region, x: dims.width - region.x - region.width };\n }\n return { ...region, y: dims.height - region.y - region.height };\n });\n return { ...state, regions: next, imageSize: dims };\n}\n\n/** Translate every region by `(dx, dy)`. Out-of-bounds regions are kept (bake clips on Save). */\nexport function translateRegions(\n state: RedactState,\n dx: number,\n dy: number,\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return { ...state, imageSize: dims };\n if (dx === 0 && dy === 0 && state.imageSize === dims) return state;\n const next = state.regions.map((region) => ({\n ...region,\n x: region.x + dx,\n y: region.y + dy,\n }));\n return { ...state, regions: next, imageSize: dims };\n}\n\n/**\n * Rotate every region `turns × 90°` CW around the image centre. Caller\n * passes post-rotation dims as `newDims`; pre-rotation dims come from\n * `state.imageSize`.\n */\nexport function rotateRegions(\n state: RedactState,\n turns: 0 | 1 | 2 | 3,\n newDims: { readonly width: number; readonly height: number },\n): RedactState {\n if (turns === 0) return { ...state, imageSize: newDims };\n if (state.regions.length === 0) return { ...state, imageSize: newDims };\n const oldW = state.imageSize.width;\n const oldH = state.imageSize.height;\n const next = state.regions.map((region) => {\n const { x, y, width, height } = region;\n if (turns === 1) {\n return { ...region, x: oldH - y - height, y: x, width: height, height: width };\n }\n if (turns === 2) {\n return {\n ...region,\n x: oldW - x - width,\n y: oldH - y - height,\n };\n }\n return { ...region, x: y, y: oldW - x - width, width: height, height: width };\n });\n return { ...state, regions: next, imageSize: newDims };\n}\n\nexport function selectRegion(state: RedactState, id: string | null): RedactState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function setCurrentMode(state: RedactState, mode: RedactMode): RedactState {\n if (state.currentMode === mode) return state;\n return { ...state, currentMode: mode };\n}\n\nexport function setCurrentColor(state: RedactState, color: string): RedactState {\n if (state.currentColor === color) return state;\n return { ...state, currentColor: color };\n}\n\n/** Update the mode of a region; `color` is preserved across mode flips. */\nexport function setRegionMode(state: RedactState, id: string, mode: RedactMode): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.mode === mode) return state;\n return replaceRegion(state, { ...region, mode });\n}\n\nexport function setRegionColor(state: RedactState, id: string, color: string): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.color === color) return state;\n return replaceRegion(state, { ...region, color });\n}\n\nexport function findRegion(state: RedactState, id: string | null): RedactRegion | undefined {\n if (id === null) return undefined;\n return state.regions.find((r) => r.id === id);\n}\n\nexport function selectedRegionOf(state: RedactState): RedactRegion | null {\n if (state.selectedId === null) return null;\n return state.regions.find((r) => r.id === state.selectedId) ?? null;\n}\n\n/** Normalise a region's rect so `width` / `height` are non-negative. */\nexport function normaliseRegionExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/** Default-sized region centred on the image. Used by the keyboard \"Insert\" path. */\nexport interface CreateCenteredRegionContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly mode: RedactMode;\n readonly color: string;\n readonly id: string;\n}\n\nexport function createCenteredRegion(ctx: CreateCenteredRegionContext): RedactRegion {\n const { imageSize, mode, color, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n return {\n id,\n x,\n y,\n width: size,\n height: size,\n mode,\n color,\n };\n}\n\n/** Clamp regions against new bounds; drop regions fully outside. \"Clamp, don't reset\". */\nexport function revalidateAgainstBounds(\n state: RedactState,\n bounds: { width: number; height: number },\n): RedactState {\n if (state.imageSize.width === bounds.width && state.imageSize.height === bounds.height) {\n return state;\n }\n const kept: RedactRegion[] = [];\n for (const region of state.regions) {\n if (\n region.x + region.width <= 0 ||\n region.y + region.height <= 0 ||\n region.x >= bounds.width ||\n region.y >= bounds.height\n ) {\n continue;\n }\n kept.push(clampRegion(region, bounds));\n }\n const selectedDropped = state.selectedId !== null && !kept.some((r) => r.id === state.selectedId);\n return {\n ...state,\n regions: kept,\n imageSize: { width: bounds.width, height: bounds.height },\n selectedId: selectedDropped ? null : state.selectedId,\n };\n}\n\nfunction clampRegion(\n region: RedactRegion,\n bounds: { width: number; height: number },\n): RedactRegion {\n const x = Math.max(0, region.x);\n const y = Math.max(0, region.y);\n const right = Math.min(bounds.width, region.x + region.width);\n const bottom = Math.min(bounds.height, region.y + region.height);\n return {\n ...region,\n x,\n y,\n width: Math.max(0, right - x),\n height: Math.max(0, bottom - y),\n };\n}\n\n/** Bounding-box shape used by the selection layer; matches `Rect`. */\nexport function regionBoundingBox(region: RedactRegion): Rect {\n return { x: region.x, y: region.y, width: region.width, height: region.height };\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport type { RedactRegion } from './state.js';\n\nexport interface RedactBakeInput {\n readonly regions: ReadonlyArray<RedactRegion>;\n}\n\n/** Paint every redaction region onto a copy of `source` in creation order. */\nexport async function bakeRedact(\n state: RedactBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.regions.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n // Pixelate/blur read from the post-source canvas, so overlapping\n // earlier regions are redacted again — \"redact wins\".\n for (const region of state.regions) {\n paintRegion(ctx, bake.canvas, region, source);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/**\n * Paint a single redaction region. `canvas` is needed because pixelate\n * and blur read from it and redraw a transformed copy in place.\n */\nexport function paintRegion(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n region: RedactRegion,\n source: SourceImage,\n): void {\n // Degenerate rects would crash `getImageData` on some engines.\n const w = Math.round(region.width);\n const h = Math.round(region.height);\n if (w < 1 || h < 1) return;\n const x = Math.round(region.x);\n const y = Math.round(region.y);\n\n switch (region.mode) {\n case 'solid':\n paintSolid(ctx, region, x, y, w, h);\n return;\n case 'pixelate':\n paintPixelate(ctx, canvas, source, x, y, w, h);\n return;\n case 'blur':\n paintBlur(ctx, canvas, source, x, y, w, h);\n return;\n }\n}\n\nfunction paintSolid(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n region: RedactRegion,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n ctx.save();\n ctx.fillStyle = region.color;\n ctx.fillRect(x, y, w, h);\n ctx.restore();\n}\n\n/**\n * Pixelate by downsampling to a small grid then upsampling with\n * nearest-neighbour. Capped at 8 cells on the longer side; floored at\n * 4 so tiny regions still read as chunky rather than as anti-aliasing.\n */\nfunction paintPixelate(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const longer = Math.max(w, h);\n const cells = Math.max(4, Math.round(8 * Math.min(1, longer / 240)));\n const gridW = Math.max(1, Math.round((w / longer) * cells));\n const gridH = Math.max(1, Math.round((h / longer) * cells));\n\n ctx.save();\n const small = createSmallCanvas(gridW, gridH);\n if (!small) {\n ctx.restore();\n return;\n }\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'low';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, gridW, gridH);\n\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(small.canvas, 0, 0, gridW, gridH, x, y, w, h);\n ctx.restore();\n}\n\n/** Downscale-and-back blur. Two passes (1/8 then 1/2) approximate a Gaussian. */\nfunction paintBlur(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const downscale = 1 / 8;\n const smallW = Math.max(1, Math.round(w * downscale));\n const smallH = Math.max(1, Math.round(h * downscale));\n\n const small = createSmallCanvas(smallW, smallH);\n if (!small) return;\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'high';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, smallW, smallH);\n\n const tinyW = Math.max(1, Math.round(smallW * 0.5));\n const tinyH = Math.max(1, Math.round(smallH * 0.5));\n const tiny = createSmallCanvas(tinyW, tinyH);\n if (!tiny) {\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(small.canvas, 0, 0, smallW, smallH, x, y, w, h);\n ctx.restore();\n return;\n }\n tiny.ctx.imageSmoothingEnabled = true;\n tiny.ctx.imageSmoothingQuality = 'high';\n tiny.ctx.drawImage(small.canvas, 0, 0, smallW, smallH, 0, 0, tinyW, tinyH);\n\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tiny.canvas, 0, 0, tinyW, tinyH, x, y, w, h);\n ctx.restore();\n}\n\ninterface SmallCanvas {\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n readonly ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n}\n\n/** Small intermediate canvas; prefers OffscreenCanvas, falls back to detached `<canvas>`. */\nfunction createSmallCanvas(width: number, height: number): SmallCanvas | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n try {\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d');\n if (ctx) return { canvas: offscreen, ctx };\n } catch {\n // Some engines throw on zero-size construction.\n }\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n return { canvas, ctx };\n}\n","/**\n * Frame preset ids match Ghost's `frameOptions` identifiers directly so\n * a contract change is a state rename, not a translation table.\n */\nexport type FramePresetId =\n | 'none'\n | 'solidSharp'\n | 'solidRound'\n | 'lineSingle'\n | 'hook'\n | 'polaroid';\n\nexport const FRAME_PRESET_IDS: readonly FramePresetId[] = [\n 'none',\n 'solidSharp',\n 'solidRound',\n 'lineSingle',\n 'hook',\n 'polaroid',\n];\n\nexport interface FramePreset {\n readonly id: FramePresetId;\n /** UI label. The Ghost adapter overrides these from `frameOptions[i][1]`. */\n readonly label: string;\n /** Whether the preset's colour can be customised by the user. */\n readonly acceptsColor: boolean;\n /** Default colour when `acceptsColor`. Polaroid defaults to white. */\n readonly defaultColor: string;\n}\n\n/** Six presets in Ghost's `frameOptions` order. Labels are English defaults; adapter localises. */\nexport const FRAME_PRESETS: readonly FramePreset[] = [\n {\n id: 'none',\n label: 'None',\n acceptsColor: false,\n defaultColor: '#000000',\n },\n {\n id: 'solidSharp',\n label: 'Mat Sharp',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'solidRound',\n label: 'Mat Round',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'lineSingle',\n label: 'Line Single',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'hook',\n label: 'Corner Hooks',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'polaroid',\n label: 'Polaroid',\n acceptsColor: true,\n defaultColor: '#ffffff',\n },\n];\n\nexport interface FrameState {\n readonly presetId: FramePresetId;\n /** CSS hex colour. Used by every preset whose `acceptsColor` is true. */\n readonly color: string;\n}\n\nexport const DEFAULT_FRAME_STATE: FrameState = {\n presetId: 'none',\n color: '#000000',\n};\n\nexport function initialFrameState(): FrameState {\n return DEFAULT_FRAME_STATE;\n}\n\nexport function isFrameNoOp(state: FrameState): boolean {\n return state.presetId === 'none';\n}\n\nexport function setFramePreset(state: FrameState, presetId: FramePresetId): FrameState {\n if (state.presetId === presetId) return state;\n // Reset colour to the new preset's default iff the current colour\n // matches the previous preset's default (i.e. user hadn't customised it).\n const currentPreset = findFramePreset(state.presetId);\n const nextPreset = findFramePreset(presetId);\n if (!nextPreset) return { ...state, presetId };\n const wasOnDefault = currentPreset !== undefined && state.color === currentPreset.defaultColor;\n const nextColor = wasOnDefault ? nextPreset.defaultColor : state.color;\n return { presetId, color: nextColor };\n}\n\nexport function setFrameColor(state: FrameState, color: string): FrameState {\n if (state.color === color) return state;\n return { ...state, color };\n}\n\nexport function findFramePreset(id: FramePresetId): FramePreset | undefined {\n return FRAME_PRESETS.find((p) => p.id === id);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FramePresetId, type FrameState, isFrameNoOp } from './state.js';\n\n/**\n * Apply the active frame preset. Frame thickness scales with `source`\n * so it stays consistent across resize choices. Output dimensions match\n * the input except for Polaroid, which extends the canvas.\n */\nexport async function bakeFrame(state: FrameState, source: SourceImage): Promise<SourceImage> {\n if (isFrameNoOp(state)) return source;\n\n if (state.presetId === 'polaroid') {\n return bakePolaroid(state.color, source);\n }\n if (state.presetId === 'none') return source;\n return bakeInsideFrame(state.presetId, state.color, source);\n}\n\nfunction bakeInsideFrame(\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n source: SourceImage,\n): SourceImage {\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n paintInsideFrame(ctx, presetId, color, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint a non-extending frame; caller has already drawn the source image. */\nexport function paintInsideFrame(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n width: number,\n height: number,\n): void {\n switch (presetId) {\n case 'solidSharp':\n paintMatSharp(ctx, color, width, height);\n return;\n case 'solidRound':\n paintMatRound(ctx, color, width, height);\n return;\n case 'lineSingle':\n paintLineSingle(ctx, color, width, height);\n return;\n case 'hook':\n paintCornerHooks(ctx, color, width, height);\n return;\n }\n}\n\n/** Mat Sharp: 4%-of-shorter-edge solid border with sharp corners. */\nfunction paintMatSharp(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n ctx.save();\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, width, t);\n ctx.fillRect(0, height - t, width, t);\n ctx.fillRect(0, t, t, height - 2 * t);\n ctx.fillRect(width - t, t, t, height - 2 * t);\n ctx.restore();\n}\n\n/** Mat Round: Mat Sharp with the four outer corners knocked out via destination-out. */\nfunction paintMatRound(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n const r = t;\n paintMatSharp(ctx, color, width, height);\n\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillStyle = '#000';\n drawCornerCutout(ctx, 0, 0, r, 'tl');\n drawCornerCutout(ctx, width, 0, r, 'tr');\n drawCornerCutout(ctx, 0, height, r, 'bl');\n drawCornerCutout(ctx, width, height, r, 'br');\n ctx.restore();\n}\n\nfunction drawCornerCutout(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n cx: number,\n cy: number,\n r: number,\n corner: 'tl' | 'tr' | 'bl' | 'br',\n): void {\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n switch (corner) {\n case 'tl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy + r, r, -Math.PI / 2, Math.PI, true);\n ctx.lineTo(cx, cy);\n break;\n case 'tr':\n ctx.lineTo(cx, cy + r);\n ctx.arc(cx - r, cy + r, r, 0, -Math.PI / 2, true);\n ctx.lineTo(cx, cy);\n break;\n case 'bl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy - r, r, Math.PI / 2, Math.PI, false);\n ctx.lineTo(cx, cy);\n break;\n case 'br':\n ctx.lineTo(cx - r, cy);\n ctx.arc(cx - r, cy - r, r, Math.PI / 2, 0, true);\n ctx.lineTo(cx, cy);\n break;\n }\n ctx.closePath();\n ctx.fill();\n}\n\n/** Line Single: thin stroked rect inset 5% from the edge. */\nfunction paintLineSingle(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const inset = Math.round(Math.min(width, height) * 0.05);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n // strokeRect centres on the path; offset by half-stroke to fit inside the inset.\n const half = stroke / 2;\n ctx.strokeRect(\n inset + half,\n inset + half,\n width - 2 * inset - stroke,\n height - 2 * inset - stroke,\n );\n ctx.restore();\n}\n\n/** Corner Hooks: four L-shapes at the corners. */\nfunction paintCornerHooks(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const arm = Math.round(Math.min(width, height) * 0.08);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n const inset = Math.round(Math.min(width, height) * 0.05);\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n ctx.lineCap = 'square';\n\n const half = stroke / 2;\n const drawHook = (vx: number, vy: number, horizDir: -1 | 1, vertDir: -1 | 1): void => {\n ctx.beginPath();\n ctx.moveTo(vx + horizDir * arm, vy);\n ctx.lineTo(vx, vy);\n ctx.lineTo(vx, vy + vertDir * arm);\n ctx.stroke();\n };\n\n drawHook(inset + half, inset + half, 1, 1);\n drawHook(width - inset - half, inset + half, -1, 1);\n drawHook(inset + half, height - inset - half, 1, -1);\n drawHook(width - inset - half, height - inset - half, -1, -1);\n ctx.restore();\n}\n\n/** Polaroid: 5% top/left/right + 18% bottom border; output canvas is larger than input. */\nfunction bakePolaroid(color: string, source: SourceImage): SourceImage {\n const shorter = Math.min(source.width, source.height);\n const top = Math.round(shorter * 0.05);\n const left = top;\n const right = top;\n const bottom = Math.round(shorter * 0.18);\n\n const outW = source.width + left + right;\n const outH = source.height + top + bottom;\n\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, outW, outH);\n ctx.drawImage(source.bitmap, left, top, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: outW,\n height: outH,\n mimeType: source.mimeType,\n };\n}\n\n/** 4% of the shorter dimension, floored at 4px. */\nfunction matThickness(width: number, height: number): number {\n return Math.max(4, Math.round(Math.min(width, height) * 0.04));\n}\n\n/** Output dimensions for a preset; equals input except for Polaroid. */\nexport function frameOutputSize(\n presetId: FramePresetId,\n inputWidth: number,\n inputHeight: number,\n): { width: number; height: number } {\n if (presetId !== 'polaroid') {\n return { width: inputWidth, height: inputHeight };\n }\n const shorter = Math.min(inputWidth, inputHeight);\n const top = Math.round(shorter * 0.05);\n const bottom = Math.round(shorter * 0.18);\n return {\n width: inputWidth + 2 * top,\n height: inputHeight + top + bottom,\n };\n}\n"],"names":["EventBus","event","listener","set","payload","error","loadImage","src","blob","toBlob","bitmap","loadViaImageElement","response","url","ownsObjectUrl","element","resolve","reject","img","IDENTITY_VIEWPORT_TRANSFORM","computeViewport","stage","image","transform","innerWidth","innerHeight","fitScale","zoom","scale","width","height","baselineX","baselineY","x","y","clampedX","clampAxis","clampedY","rawPos","baseline","size","innerSize","padding","innerStart","innerEnd","minLeft","maxLeft","pointImageToDisplay","point","viewport","pointDisplayToImage","rectImageToDisplay","rect","rectDisplayToImage","MAX_ZOOM","MIN_ZOOM","ViewportController","initial","clampTransform","next","transformsEqual","deltaZoom","anchor","stageCenter","oldZoom","requested","newZoom","clamp","ratio","anchorRelX","anchorRelY","newPanX","newPanY","dx","dy","value","snapshot","min","max","panX","panY","a","b","createBakeCanvas","canUseOffscreenForBlobs","canvas","getBakeContext2D","bake","ctx","bakeCanvasToBlob","mimeType","quality","mimeSupportCache","canEncodeMime","cached","probe","rectFromPoints","rectRight","rectBottom","rectCenter","pointInRect","rectsEqual","translateClampedRect","bounds","moved","clampRectInside","roundRect","createStore","state","listeners","notify","previous","updater","HISTORY_MAX_ENTRIES","History","cloneSnapshot","current","snapshotsEqual","diffSnapshots","id","stableJson","prev","changed","_key","v","sorted","k","fitRectToBoundsWithRatio","targetRatio","boundsRatio","applyAspectRatio","currentRatio","reshaped","anchorRect","clamped","clampedRatio","RATIO_TOLERANCE","fitInsideAtAnchor","fitted","resizeRectFromHandle","handle","pointer","options","minSize","left","top","right","bottom","newLeft","newTop","newRight","newBottom","nx","ny","nw","nh","resized","anchorFor","isPresetVisible","preset","filter","filterPresets","presets","initialCropState","input","findCustomIndex","applyPresetByIndex","presetIndex","bakeCrop","source","rounded","w","h","n","lo","hi","DEFAULT_OUTPUT_STATE","ENCODABLE_MIMES","clampQuality","setOutputMime","mimeChoice","setOutputQuality","setStripMetadata","stripMetadata","SOI","APP1_MARKER","EXIF_HEADER","copyJpegExif","sourceBytes","readBlobBytes","startsWith","exifSegment","findExifApp1","outputBytes","merged","reader","result","bytes","prefix","i","marker","length","segmentEnd","hasExifHeader","offset","j","FALLBACK_MIME","ALPHA_CARRYING_SOURCE_MIMES","resolveOutputMime","deriveOutputName","sourceName","ext","extensionForMime","basename","lastPathSegment","stem","stripExtension","encodeSourceImage","outputState","name","baseBlob","mime","subtype","segments","stripQuery","q","dot","runUtilityChain","links","baked","link","initialFlipState","toggleFlip","axis","isFlipNoOp","bakeFlip","sx","sy","tx","ty","FREE_ANGLE_MIN","FREE_ANGLE_MAX","FREE_ANGLE_STEP","initialRotateState","rotateClockwise","rotateCounterClockwise","setFreeAngle","angleDeg","snapped","isRotateNoOp","effectiveAngleDeg","largestInscribedRect","angleRad","c","s","denomA","denomD","capA","EPSILON","capD","outWidth","outHeight","bakeRotate","sub90Deg","isQuarterOnly","inscribed","MAX_DIMENSION","MIN_DIMENSION","initialResizeState","isResizeNoOp","resolveOutputSize","upstream","clampInt","setWidthPx","widthPx","scaleX","scaleY","setHeightPx","heightPx","setPercent","percent","clampScaleForPercent","setLockAspect","locked","effectivePercent","bakeResize","targetW","targetH","halvings","countHalvingsNeeded","stepW","stepH","drawScaled","srcW","srcH","count","outW","outH","FINETUNE_MIN","FINETUNE_MAX","FINETUNE_STEP","DEFAULT_FINETUNE_STATE","FINETUNE_ADJUSTMENTS","initialFinetuneState","isFinetuneNoOp","setFinetune","key","clampSliderValue","resetFinetune","resetAllFinetune","buildFinetuneLut","lut","brightnessOffset","contrastFactor","exposureFactor","gammaExponent","gammaExponentFor","slider","applyFinetuneLutAndSaturation","dst","len","saturation","r0","g0","b0","r","g","applyClarity","blurred","clarity","amount","dr","dg","db","boxBlur3x3","tmp","xm","xp","im","ip","ym","yp","applyFinetuneToImageData","bakeFinetune","FILTER_PRESETS","finetuneStatesEqual","findActivePreset","HIGHLIGHT_DEFAULT_COLOR","HIGHLIGHT_DEFAULT_STROKE","FREEHAND_DEFAULT_STROKE","TEXT_DEFAULT_FONT_SIZE","DEFAULT_PALETTE_COLOR","DEFAULT_STROKE_WIDTH","defaultStylePalette","initialAnnotateState","mintShapeId","setActiveTool","tool","setStyle","partial","selectShape","addShape","shape","replaceShape","existing","deleteShape","findShape","normaliseRectExtent","extent","translateShape","p","assertNever","mirrorShape","dims","rotateShape","turns","oldDims","rotatePoint","corners","newX","newY","newW","newH","p1","p2","transformShapes","transformer","KEYBOARD_PLACEABLE_KINDS","isKeyboardPlaceableKind","kind","createCenteredShape","imageSize","style","shortEdge","cx","cy","x1","x2","boundingBoxOf","estimateTextSize","alignToOrigin","head","minX","minY","maxX","maxY","ALL_SELECTION_HANDLES","selectionHandlePositions","rectFromHandleDrag","text","fontSize","lines","maxLineLen","line","anchorX","align","PICK_TOLERANCE","pickShape","shapes","hitTest","inside","normaliseBox","outer","expandRect","inner","box","rx","ry","r2","tolerance","pointNearSegment","expanded","len2","ex","ey","t","projX","projY","MIN_SAMPLE_DISTANCE","decimatePoints","points","out","last","minSq","tail","tracePath","midX","midY","SYSTEM_FONT_STACK","bakeAnnotate","paintShape","paintText","paintRect","paintEllipse","paintArrow","paintFreehand","paintHighlight","lineHeight","headLength","headWidth","ux","uy","shaftEndX","shaftEndY","baseX","baseY","px","py","REDACT_MODES","DEFAULT_REDACT_COLOR","DEFAULT_REDACT_MODE","initialRedactState","mintRegionId","addRegion","region","replaceRegion","deleteRegion","mirrorRegions","translateRegions","rotateRegions","newDims","oldW","oldH","selectRegion","setCurrentMode","mode","setCurrentColor","color","setRegionMode","setRegionColor","findRegion","selectedRegionOf","normaliseRegionExtent","createCenteredRegion","revalidateAgainstBounds","kept","clampRegion","selectedDropped","regionBoundingBox","bakeRedact","paintRegion","paintSolid","paintPixelate","paintBlur","_source","longer","cells","gridW","gridH","small","createSmallCanvas","smallW","smallH","tinyW","tinyH","tiny","offscreen","FRAME_PRESET_IDS","FRAME_PRESETS","DEFAULT_FRAME_STATE","initialFrameState","isFrameNoOp","setFramePreset","presetId","currentPreset","findFramePreset","nextPreset","nextColor","setFrameColor","bakeFrame","bakePolaroid","bakeInsideFrame","paintInsideFrame","paintMatSharp","paintMatRound","paintLineSingle","paintCornerHooks","matThickness","drawCornerCutout","corner","inset","stroke","half","arm","drawHook","vx","vy","horizDir","vertDir","shorter","frameOutputSize","inputWidth","inputHeight"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/canvas/bake-canvas.ts","../src/canvas/load-image.ts","../src/canvas/viewport.ts","../src/canvas/viewport-controller.ts","../src/events/event-bus.ts","../src/geometry/rect.ts","../src/history/history.ts","../src/output/state.ts","../src/pipeline/exif.ts","../src/pipeline/encode.ts","../src/pipeline/run-chain.ts","../src/plugins/annotate/smooth.ts","../src/plugins/annotate/state.ts","../src/plugins/annotate/bake.ts","../src/plugins/annotate/geometry.ts","../src/plugins/annotate/hit-test.ts","../src/plugins/crop/aspect-ratio.ts","../src/plugins/crop/bake.ts","../src/plugins/crop/preset-filter.ts","../src/plugins/crop/resize.ts","../src/plugins/crop/state.ts","../src/plugins/finetune/state.ts","../src/plugins/filter/presets.ts","../src/plugins/finetune/math.ts","../src/plugins/finetune/bake.ts","../src/plugins/flip/state.ts","../src/plugins/flip/bake.ts","../src/plugins/frame/state.ts","../src/plugins/frame/bake.ts","../src/plugins/redact/bake.ts","../src/plugins/redact/state.ts","../src/plugins/resize/state.ts","../src/plugins/resize/bake.ts","../src/plugins/rotate/inscribe.ts","../src/plugins/rotate/state.ts","../src/plugins/rotate/bake.ts","../src/state/store.ts"],"sourcesContent":["/**\n * Allocate a canvas suitable for an off-screen bake operation.\n *\n * The two return shapes are a discriminated union because the `toBlob`\n * vs `convertToBlob` signatures differ — callers pass through whichever\n * they got.\n */\nexport type BakeCanvas =\n | { readonly kind: 'offscreen'; readonly canvas: OffscreenCanvas }\n | { readonly kind: 'html'; readonly canvas: HTMLCanvasElement };\n\nexport function createBakeCanvas(width: number, height: number): BakeCanvas {\n if (canUseOffscreenForBlobs()) {\n const canvas = new OffscreenCanvas(width, height);\n return { kind: 'offscreen', canvas };\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return { kind: 'html', canvas };\n}\n\n/**\n * Get a 2D rendering context from either canvas shape with a single\n * narrowed type. Branching on `bake.kind` first lets TS narrow each\n * canvas's `getContext('2d')` correctly; calling it on the un-discriminated\n * union collapses the return to the broader `RenderingContext`.\n */\nexport function getBakeContext2D(\n bake: BakeCanvas,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n}\n\nexport async function bakeCanvasToBlob(\n bake: BakeCanvas,\n mimeType: string,\n quality: number,\n): Promise<Blob> {\n if (bake.kind === 'offscreen') {\n return bake.canvas.convertToBlob({ type: mimeType, quality });\n }\n return new Promise<Blob>((resolve, reject) => {\n bake.canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob produced null'));\n },\n mimeType,\n quality,\n );\n });\n}\n\nfunction canUseOffscreenForBlobs(): boolean {\n if (typeof OffscreenCanvas === 'undefined') return false;\n // WebKit historically shipped OffscreenCanvas without convertToBlob,\n // so test the actual capability rather than the constructor.\n return typeof OffscreenCanvas.prototype.convertToBlob === 'function';\n}\n\n/** Probe whether the runtime canvas can encode `mimeType` to a non-empty blob. Cached per-mime. */\nconst mimeSupportCache = new Map<string, Promise<boolean>>();\n\nexport function canEncodeMime(mimeType: string): Promise<boolean> {\n const cached = mimeSupportCache.get(mimeType);\n if (cached) return cached;\n const probe = (async () => {\n try {\n const bake = createBakeCanvas(1, 1);\n const blob = await bakeCanvasToBlob(bake, mimeType, 0.5);\n // `toBlob` will silently fall back to PNG on unsupported types,\n // so verify the result advertises the requested mime.\n return blob.type === mimeType && blob.size > 0;\n } catch {\n return false;\n }\n })();\n mimeSupportCache.set(mimeType, probe);\n return probe;\n}\n","export interface LoadedImage {\n readonly element: ImageBitmap | HTMLImageElement;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Load a source image and decode it for canvas use.\n *\n * Modern path uses `createImageBitmap(blob, { imageOrientation: 'from-image' })`\n * so EXIF orientation is baked into the pixels — without this, phone photos\n * load sideways because canvas `drawImage` ignores the EXIF flag.\n * Fallback to `HTMLImageElement` when `createImageBitmap` isn't usable;\n * EXIF orientation is not applied on that path.\n */\nexport async function loadImage(src: string | Blob | File): Promise<LoadedImage> {\n if (typeof createImageBitmap === 'function') {\n const blob = await toBlob(src);\n if (blob) {\n try {\n const bitmap = await createImageBitmap(blob, { imageOrientation: 'from-image' });\n return { element: bitmap, width: bitmap.width, height: bitmap.height };\n } catch {\n // Older Safari rejects the options object; fall through.\n }\n }\n }\n\n return loadViaImageElement(src);\n}\n\nasync function toBlob(src: string | Blob | File): Promise<Blob | null> {\n if (src instanceof Blob) return src;\n if (typeof fetch !== 'function') return null;\n try {\n const response = await fetch(src, { credentials: 'omit' });\n if (!response.ok) return null;\n return await response.blob();\n } catch {\n return null;\n }\n}\n\nasync function loadViaImageElement(src: string | Blob | File): Promise<LoadedImage> {\n const url = typeof src === 'string' ? src : URL.createObjectURL(src);\n const ownsObjectUrl = typeof src !== 'string';\n\n try {\n const element = await new Promise<HTMLImageElement>((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load image: ${url}`));\n img.src = url;\n });\n return {\n element,\n width: element.naturalWidth,\n height: element.naturalHeight,\n };\n } finally {\n if (ownsObjectUrl) URL.revokeObjectURL(url);\n }\n}\n","import type { Point, Rect, Size } from '../geometry/rect.js';\n\nexport interface StageDimensions {\n /** Stage width in CSS pixels. */\n readonly width: number;\n /** Stage height in CSS pixels. */\n readonly height: number;\n /** Padding on each side around the image, in CSS pixels. */\n readonly padding: number;\n}\n\n/**\n * User-driven zoom and pan applied on top of the fit-to-screen letterbox.\n * `computeViewport` folds the transform into `displayRect` and `scale` so\n * plugin-level draw calls stay zoom-agnostic. Pan is in stage CSS pixels\n * at the current zoom and is clamped at viewport emission.\n */\nexport interface ViewportTransform {\n /** 1 at fit; > 1 zoomed in; < 1 zoomed out. */\n readonly zoom: number;\n /** CSS pixels of pan offset, applied after the centered-fit baseline. */\n readonly panX: number;\n readonly panY: number;\n}\n\nexport const IDENTITY_VIEWPORT_TRANSFORM: ViewportTransform = Object.freeze({\n zoom: 1,\n panX: 0,\n panY: 0,\n});\n\nexport interface Viewport {\n /** Where the image is drawn in stage CSS pixels (post-zoom, post-pan). */\n readonly displayRect: Rect;\n /** Display CSS pixels per 1 image pixel (uniform on both axes, post-zoom). */\n readonly scale: number;\n}\n\n/**\n * Compute the post-zoom, post-pan display rect for an image inside the stage.\n *\n * At identity, the image is fit-scaled inside the stage minus padding and\n * centered. With a non-identity transform the fit scale is multiplied by\n * `zoom`, then the pan offset is added and clamped so at least 1 image\n * pixel remains inside the inner stage area on each axis.\n */\nexport function computeViewport(\n stage: StageDimensions,\n image: Size,\n transform: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM,\n): Viewport {\n const innerWidth = Math.max(0, stage.width - stage.padding * 2);\n const innerHeight = Math.max(0, stage.height - stage.padding * 2);\n\n if (image.width <= 0 || image.height <= 0 || innerWidth <= 0 || innerHeight <= 0) {\n return {\n displayRect: { x: stage.padding, y: stage.padding, width: 0, height: 0 },\n scale: 0,\n };\n }\n\n const fitScale = Math.min(innerWidth / image.width, innerHeight / image.height);\n const zoom = Math.max(0, transform.zoom || 0);\n const scale = fitScale * zoom;\n\n const width = image.width * scale;\n const height = image.height * scale;\n\n // Centered baseline inside the inner stage area, then add the pan.\n const baselineX = stage.padding + (innerWidth - width) / 2;\n const baselineY = stage.padding + (innerHeight - height) / 2;\n\n const x = baselineX + transform.panX;\n const y = baselineY + transform.panY;\n\n const clampedX = clampAxis(x, baselineX, width, innerWidth, stage.padding);\n const clampedY = clampAxis(y, baselineY, height, innerHeight, stage.padding);\n\n return {\n displayRect: { x: clampedX, y: clampedY, width, height },\n scale,\n };\n}\n\nfunction clampAxis(\n rawPos: number,\n baseline: number,\n size: number,\n innerSize: number,\n padding: number,\n): number {\n // Image narrower than the inner stage: no off-center pan, baseline is centered.\n if (size <= innerSize) return baseline;\n\n // At least 1 image pixel always remains inside the inner stage.\n const innerStart = padding;\n const innerEnd = padding + innerSize;\n const minLeft = innerStart + 1 - size;\n const maxLeft = innerEnd - 1;\n if (rawPos < minLeft) return minLeft;\n if (rawPos > maxLeft) return maxLeft;\n return rawPos;\n}\n\nexport function pointImageToDisplay(point: Point, viewport: Viewport): Point {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nexport function pointDisplayToImage(point: Point, viewport: Viewport): Point {\n if (viewport.scale === 0) return { x: 0, y: 0 };\n return {\n x: (point.x - viewport.displayRect.x) / viewport.scale,\n y: (point.y - viewport.displayRect.y) / viewport.scale,\n };\n}\n\nexport function rectImageToDisplay(rect: Rect, viewport: Viewport): Rect {\n return {\n x: viewport.displayRect.x + rect.x * viewport.scale,\n y: viewport.displayRect.y + rect.y * viewport.scale,\n width: rect.width * viewport.scale,\n height: rect.height * viewport.scale,\n };\n}\n\nexport function rectDisplayToImage(rect: Rect, viewport: Viewport): Rect {\n if (viewport.scale === 0) return { x: 0, y: 0, width: 0, height: 0 };\n return {\n x: (rect.x - viewport.displayRect.x) / viewport.scale,\n y: (rect.y - viewport.displayRect.y) / viewport.scale,\n width: rect.width / viewport.scale,\n height: rect.height / viewport.scale,\n };\n}\n","import type { Point, Size } from '../geometry/rect.js';\nimport {\n computeViewport,\n IDENTITY_VIEWPORT_TRANSFORM,\n type StageDimensions,\n type Viewport,\n type ViewportTransform,\n} from './viewport.js';\n\nexport const MAX_ZOOM = 8;\nexport const MIN_ZOOM = 0.25;\n\nexport interface ViewportControllerSnapshot {\n readonly transform: ViewportTransform;\n readonly pinching: boolean;\n}\n\nexport type ViewportControllerListener = (snapshot: ViewportControllerSnapshot) => void;\n\n/** Editor-level zoom + pan state, shared with every plugin's `UtilityContext`. */\nexport class ViewportController {\n private current: ViewportTransform;\n private isPinching: boolean;\n private readonly listeners: Set<ViewportControllerListener>;\n\n constructor(initial: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM) {\n this.current = clampTransform(initial);\n this.isPinching = false;\n this.listeners = new Set();\n }\n\n getTransform(): ViewportTransform {\n return this.current;\n }\n\n getSnapshot(): ViewportControllerSnapshot {\n return { transform: this.current, pinching: this.isPinching };\n }\n\n setTransform(transform: ViewportTransform): void {\n const next = clampTransform(transform);\n if (transformsEqual(this.current, next)) return;\n this.current = next;\n this.emit();\n }\n\n /**\n * Apply a multiplicative zoom delta anchored on a stage CSS-pixel point.\n * The point under `anchor` stays under `anchor` after the zoom.\n *\n * Pan is the displaced image-center's offset from the stage center, so the\n * dolly-zoom reduces to `pan_new = ratio · pan_old + (1 − ratio) · (anchor − stageCenter)`,\n * independent of the active plugin's image intrinsic.\n */\n zoomAt(deltaZoom: number, anchor: Point, stageCenter: Point): ViewportTransform {\n if (!Number.isFinite(deltaZoom) || deltaZoom <= 0) return this.current;\n const oldZoom = this.current.zoom;\n const requested = oldZoom * deltaZoom;\n const newZoom = clamp(requested, MIN_ZOOM, MAX_ZOOM);\n if (newZoom === oldZoom) return this.current;\n\n const ratio = newZoom / oldZoom;\n const anchorRelX = anchor.x - stageCenter.x;\n const anchorRelY = anchor.y - stageCenter.y;\n const newPanX = ratio * this.current.panX + (1 - ratio) * anchorRelX;\n const newPanY = ratio * this.current.panY + (1 - ratio) * anchorRelY;\n\n this.current = { zoom: newZoom, panX: newPanX, panY: newPanY };\n this.emit();\n return this.current;\n }\n\n /**\n * Translate pan by `(dx, dy)` in stage CSS pixels at the current zoom.\n * Pan is stored raw; `computeViewport` clamps at emission time so the\n * gesture handler's math stays stable when the user pans past the clamp.\n */\n panBy(dx: number, dy: number): void {\n if (dx === 0 && dy === 0) return;\n this.current = {\n zoom: this.current.zoom,\n panX: this.current.panX + dx,\n panY: this.current.panY + dy,\n };\n this.emit();\n }\n\n resetToFit(): void {\n if (transformsEqual(this.current, IDENTITY_VIEWPORT_TRANSFORM)) return;\n this.current = IDENTITY_VIEWPORT_TRANSFORM;\n this.emit();\n }\n\n /** Reset only the pan, preserving zoom. Used on plugin switch. */\n resetPan(): void {\n if (this.current.panX === 0 && this.current.panY === 0) return;\n this.current = { zoom: this.current.zoom, panX: 0, panY: 0 };\n this.emit();\n }\n\n /** True while a multi-pointer gesture is active. Heavy plugins read this. */\n getPinching(): boolean {\n return this.isPinching;\n }\n\n setPinching(value: boolean): void {\n if (this.isPinching === value) return;\n this.isPinching = value;\n this.emit();\n }\n\n computeViewport(stage: StageDimensions, image: Size): Viewport {\n return computeViewport(stage, image, this.current);\n }\n\n /** Subscribe to transform / pinching changes. Handler runs synchronously after every change. */\n subscribe(listener: ViewportControllerListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n private emit(): void {\n const snapshot = this.getSnapshot();\n for (const listener of this.listeners) listener(snapshot);\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nfunction clampTransform(transform: ViewportTransform): ViewportTransform {\n const zoom = clamp(Number.isFinite(transform.zoom) ? transform.zoom : 1, MIN_ZOOM, MAX_ZOOM);\n const panX = Number.isFinite(transform.panX) ? transform.panX : 0;\n const panY = Number.isFinite(transform.panY) ? transform.panY : 0;\n return { zoom, panX, panY };\n}\n\nfunction transformsEqual(a: ViewportTransform, b: ViewportTransform): boolean {\n return a.zoom === b.zoom && a.panX === b.panX && a.panY === b.panY;\n}\n","export type EventListener<T> = (payload: T) => void;\nexport type Unsubscribe = () => void;\n\nexport class EventBus<TEvents extends Record<string, unknown>> {\n private readonly listeners = new Map<keyof TEvents, Set<EventListener<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, listener: EventListener<TEvents[K]>): Unsubscribe {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as EventListener<unknown>);\n return () => {\n set?.delete(listener as EventListener<unknown>);\n };\n }\n\n off<K extends keyof TEvents>(event: K, listener: EventListener<TEvents[K]>): void {\n this.listeners.get(event)?.delete(listener as EventListener<unknown>);\n }\n\n emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of [...set]) {\n try {\n (listener as EventListener<TEvents[K]>)(payload);\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","export interface Point {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface Rect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface Size {\n readonly width: number;\n readonly height: number;\n}\n\nexport function rectFromPoints(a: Point, b: Point): Rect {\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n const width = Math.abs(a.x - b.x);\n const height = Math.abs(a.y - b.y);\n return { x, y, width, height };\n}\n\nexport function rectRight(rect: Rect): number {\n return rect.x + rect.width;\n}\n\nexport function rectBottom(rect: Rect): number {\n return rect.y + rect.height;\n}\n\nexport function rectCenter(rect: Rect): Point {\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };\n}\n\nexport function pointInRect(point: Point, rect: Rect): boolean {\n return (\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height\n );\n}\n\nexport function rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n/**\n * Translate a rect by `(dx, dy)`, then clamp it inside `bounds` so that the\n * full rect remains inside (clamping translates further if needed; size is\n * preserved). If the rect is larger than the bounds in either axis, that axis\n * is left at the bounds origin.\n */\nexport function translateClampedRect(rect: Rect, dx: number, dy: number, bounds: Rect): Rect {\n const moved: Rect = { x: rect.x + dx, y: rect.y + dy, width: rect.width, height: rect.height };\n return clampRectInside(moved, bounds);\n}\n\n/**\n * Clamp a rect so it fits entirely inside `bounds`. If the rect is larger\n * than the bounds in either axis, the rect's extent in that axis is shrunk\n * to fit (preserving the upper-left anchor of `bounds`).\n */\nexport function clampRectInside(rect: Rect, bounds: Rect): Rect {\n let { x, y, width, height } = rect;\n\n if (width > bounds.width) width = bounds.width;\n if (height > bounds.height) height = bounds.height;\n\n if (x < bounds.x) x = bounds.x;\n if (y < bounds.y) y = bounds.y;\n if (x + width > bounds.x + bounds.width) x = bounds.x + bounds.width - width;\n if (y + height > bounds.y + bounds.height) y = bounds.y + bounds.height - height;\n\n return { x, y, width, height };\n}\n\nexport function roundRect(rect: Rect): Rect {\n return {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n}\n","import type { UtilityId } from '../plugins/utility.js';\n\n/**\n * Immutable snapshot of every registered plugin's state at a single moment.\n * `History` deep-clones on capture so live store writes cannot bleed in.\n */\nexport type SessionSnapshot = ReadonlyMap<UtilityId, unknown>;\n\nexport const HISTORY_MAX_ENTRIES = 50;\n\nexport interface UndoResult {\n readonly snapshot: SessionSnapshot;\n /** Utility ids whose state changed between the prior and restored snapshot. */\n readonly changed: ReadonlySet<UtilityId>;\n}\n\n/** Editor-wide undo/redo store using a snapshot stack. State-only; the editor decides when to commit. */\nexport class History {\n private shadow: SessionSnapshot;\n private undoStack: SessionSnapshot[] = [];\n private redoStack: SessionSnapshot[] = [];\n\n constructor(initial: SessionSnapshot) {\n this.shadow = cloneSnapshot(initial);\n }\n\n /**\n * Capture a commit. Previous shadow → undo stack; `current` → shadow.\n * Redo is cleared because a new commit branches the history. A commit\n * structurally equal to the shadow is a no-op so duplicates (clicking\n * the same preset twice) don't pollute the undo stack.\n */\n commit(current: SessionSnapshot): void {\n if (snapshotsEqual(current, this.shadow)) return;\n this.undoStack.push(this.shadow);\n if (this.undoStack.length > HISTORY_MAX_ENTRIES) {\n this.undoStack.shift();\n }\n this.shadow = cloneSnapshot(current);\n this.redoStack.length = 0;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n /** Pop the most recent prior snapshot; `current` goes onto the redo stack. Returns null if undo is empty. */\n undo(current: SessionSnapshot): UndoResult | null {\n const previous = this.undoStack.pop();\n if (!previous) return null;\n this.redoStack.push(cloneSnapshot(current));\n this.shadow = previous;\n return { snapshot: previous, changed: diffSnapshots(current, previous) };\n }\n\n /** Pop the most recent redo entry; `current` goes onto the undo stack. Returns null if redo is empty. */\n redo(current: SessionSnapshot): UndoResult | null {\n const next = this.redoStack.pop();\n if (!next) return null;\n this.undoStack.push(cloneSnapshot(current));\n if (this.undoStack.length > HISTORY_MAX_ENTRIES) {\n this.undoStack.shift();\n }\n this.shadow = next;\n return { snapshot: next, changed: diffSnapshots(current, next) };\n }\n\n /** Test/debug helpers; not part of the editor's runtime API. */\n size(): { undo: number; redo: number } {\n return { undo: this.undoStack.length, redo: this.redoStack.length };\n }\n}\n\nfunction snapshotsEqual(a: SessionSnapshot, b: SessionSnapshot): boolean {\n if (a.size !== b.size) return false;\n for (const [id, value] of a) {\n if (!b.has(id)) return false;\n if (stableJson(value) !== stableJson(b.get(id))) return false;\n }\n return true;\n}\n\nfunction diffSnapshots(prev: SessionSnapshot, next: SessionSnapshot): Set<UtilityId> {\n const changed = new Set<UtilityId>();\n for (const [id, value] of next) {\n if (stableJson(value) !== stableJson(prev.get(id))) changed.add(id);\n }\n for (const id of prev.keys()) {\n if (!next.has(id)) changed.add(id);\n }\n return changed;\n}\n\nfunction cloneSnapshot(snapshot: SessionSnapshot): SessionSnapshot {\n const next = new Map<UtilityId, unknown>();\n for (const [id, value] of snapshot) {\n next.set(id, structuredClone(value));\n }\n return next;\n}\n\n/** Key-sorted JSON for structural equality regardless of insertion order. */\nfunction stableJson(value: unknown): string {\n return JSON.stringify(value, (_key, v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n const sorted: Record<string, unknown> = {};\n for (const k of Object.keys(v as Record<string, unknown>).sort()) {\n sorted[k] = (v as Record<string, unknown>)[k];\n }\n return sorted;\n }\n return v;\n });\n}\n","/**\n * `'auto'` resolves to the smallest format that preserves alpha on the\n * current runtime (WebP on evergreens, PNG fallback). AVIF never auto-\n * resolves; the user must pick it explicitly.\n */\nexport type OutputMimeChoice = 'auto' | 'image/png' | 'image/jpeg' | 'image/webp' | 'image/avif';\n\nexport interface OutputState {\n readonly mimeChoice: OutputMimeChoice;\n /** 0.0 – 1.0. Ignored for PNG (lossless). */\n readonly quality: number;\n /**\n * Strip EXIF / GPS / camera metadata on save. Canvas `convertToBlob`\n * already strips EXIF; when `false`, we attempt to preserve the source\n * EXIF segment, which only works for JPEG → JPEG. Other combinations\n * strip regardless — the toggle is a hint, not a guarantee.\n */\n readonly stripMetadata: boolean;\n}\n\nexport const DEFAULT_OUTPUT_STATE: OutputState = {\n mimeChoice: 'auto',\n quality: 0.85,\n stripMetadata: true,\n};\n\n/** The four concrete mime types the user can pick from in the popover. */\nexport const ENCODABLE_MIMES: ReadonlyArray<Exclude<OutputMimeChoice, 'auto'>> = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/avif',\n];\n\n/** Clamp a quality value to [0, 1]; non-finite inputs return the default. */\nexport function clampQuality(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_OUTPUT_STATE.quality;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nexport function setOutputMime(state: OutputState, mimeChoice: OutputMimeChoice): OutputState {\n if (state.mimeChoice === mimeChoice) return state;\n return { ...state, mimeChoice };\n}\n\nexport function setOutputQuality(state: OutputState, quality: number): OutputState {\n const clamped = clampQuality(quality);\n if (state.quality === clamped) return state;\n return { ...state, quality: clamped };\n}\n\nexport function setStripMetadata(state: OutputState, stripMetadata: boolean): OutputState {\n if (state.stripMetadata === stripMetadata) return state;\n return { ...state, stripMetadata };\n}\n","/**\n * Minimal JPEG EXIF segment copier. Extracts the EXIF APP1 segment from\n * the source JPEG and splices it into the output JPEG right after the\n * SOI marker. JPEG → JPEG only; any other combination returns the output\n * untouched. Canvas-encoded JPEGs never carry EXIF, so no duplicate-APP1\n * risk.\n */\n\nconst SOI = [0xff, 0xd8];\nconst APP1_MARKER = [0xff, 0xe1];\nconst EXIF_HEADER = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; // \"Exif\\0\\0\"\n\nexport interface CopyJpegExifOptions {\n /** The source JPEG bytes (with EXIF). */\n readonly source: Blob;\n /** The freshly-encoded canvas output (no EXIF). */\n readonly output: Blob;\n}\n\n/** Splice the source's EXIF APP1 segment into the output JPEG. Returns the output unchanged on any mismatch. */\nexport async function copyJpegExif(options: CopyJpegExifOptions): Promise<Blob> {\n if (options.output.type && options.output.type !== 'image/jpeg') return options.output;\n\n const sourceBytes = await readBlobBytes(options.source);\n if (!startsWith(sourceBytes, SOI)) return options.output;\n\n const exifSegment = findExifApp1(sourceBytes);\n if (!exifSegment) return options.output;\n\n const outputBytes = await readBlobBytes(options.output);\n if (!startsWith(outputBytes, SOI)) return options.output;\n\n // [SOI][EXIF segment][rest of output after SOI]\n const merged = new Uint8Array(outputBytes.length + exifSegment.length);\n merged.set(outputBytes.subarray(0, 2), 0);\n merged.set(exifSegment, 2);\n merged.set(outputBytes.subarray(2), 2 + exifSegment.length);\n\n return new Blob([merged], { type: 'image/jpeg' });\n}\n\n/** `Blob.arrayBuffer()` path; falls back to `FileReader` for jsdom which lacks `arrayBuffer`. */\nasync function readBlobBytes(blob: Blob): Promise<Uint8Array> {\n if (typeof blob.arrayBuffer === 'function') {\n return new Uint8Array(await blob.arrayBuffer());\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result;\n if (result instanceof ArrayBuffer) {\n resolve(new Uint8Array(result));\n } else {\n reject(new Error('FileReader returned a non-ArrayBuffer result'));\n }\n };\n reader.onerror = () => reject(reader.error ?? new Error('FileReader failed'));\n reader.readAsArrayBuffer(blob);\n });\n}\n\nfunction startsWith(bytes: Uint8Array, prefix: ReadonlyArray<number>): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\n/** Walk JPEG segments and return the EXIF APP1 segment (FF E1 + length + payload), or undefined. */\nfunction findExifApp1(bytes: Uint8Array): Uint8Array | undefined {\n let i = 2; // skip SOI\n while (i + 4 <= bytes.length) {\n if (bytes[i] !== 0xff) return undefined;\n const marker = bytes[i + 1];\n if (marker === undefined) return undefined;\n // SOS (FF DA) starts compressed data; EOI (FF D9) ends the stream.\n if (marker === 0xda) return undefined;\n if (marker === 0xd9) return undefined;\n const length =\n bytes[i + 2] !== undefined && bytes[i + 3] !== undefined\n ? (bytes[i + 2] as number) * 256 + (bytes[i + 3] as number)\n : 0;\n if (length < 2) return undefined;\n const segmentEnd = i + 2 + length;\n if (segmentEnd > bytes.length) return undefined;\n\n if (\n bytes[i] === APP1_MARKER[0] &&\n bytes[i + 1] === APP1_MARKER[1] &&\n hasExifHeader(bytes, i + 4)\n ) {\n return bytes.slice(i, segmentEnd);\n }\n\n i = segmentEnd;\n }\n return undefined;\n}\n\nfunction hasExifHeader(bytes: Uint8Array, offset: number): boolean {\n for (let j = 0; j < EXIF_HEADER.length; j++) {\n if (bytes[offset + j] !== EXIF_HEADER[j]) return false;\n }\n return true;\n}\n","import { bakeCanvasToBlob, canEncodeMime, createBakeCanvas } from '../canvas/bake-canvas.js';\nimport { clampQuality, DEFAULT_OUTPUT_STATE, type OutputState } from '../output/state.js';\nimport type { SourceImage } from '../plugins/utility.js';\nimport { copyJpegExif } from './exif.js';\n\nconst FALLBACK_MIME = 'image/png';\n\nconst ALPHA_CARRYING_SOURCE_MIMES = new Set(['image/png', 'image/webp', 'image/avif']);\n\nexport interface EncodeOptions {\n /** Original source URL or filename, if any — used to derive the output name. */\n readonly sourceName?: string;\n /** Output format + quality. Defaults to the auto-resolved mime at default quality. */\n readonly output?: OutputState;\n /**\n * Original source blob, retained so EXIF can be preserved on JPEG → JPEG\n * when the user opts out of stripping. Optional; when missing, EXIF is\n * always stripped (the canvas re-encode strips unconditionally).\n */\n readonly sourceBlob?: Blob;\n}\n\n/**\n * Resolve the concrete output mime from `OutputState` against runtime support.\n * Explicit choices fall back to WebP then PNG; `'auto'` prefers WebP, then\n * JPEG for non-alpha sources, then PNG.\n */\nexport async function resolveOutputMime(state: OutputState, source: SourceImage): Promise<string> {\n if (state.mimeChoice !== 'auto') {\n if (await canEncodeMime(state.mimeChoice)) return state.mimeChoice;\n if (await canEncodeMime('image/webp')) return 'image/webp';\n return FALLBACK_MIME;\n }\n if (await canEncodeMime('image/webp')) return 'image/webp';\n const sourceHasAlpha = ALPHA_CARRYING_SOURCE_MIMES.has(source.mimeType);\n if (!sourceHasAlpha && (await canEncodeMime('image/jpeg'))) return 'image/jpeg';\n return FALLBACK_MIME;\n}\n\n/**\n * Derive the output filename. `name.ext` → `name.<ext-of-mime>`,\n * otherwise `kalotyp-image.<ext>`.\n */\nexport function deriveOutputName(sourceName: string | undefined, mimeType: string): string {\n const ext = extensionForMime(mimeType);\n if (!sourceName) return `kalotyp-image.${ext}`;\n const basename = lastPathSegment(sourceName);\n if (!basename) return `kalotyp-image.${ext}`;\n const stem = stripExtension(basename);\n if (!stem) return `kalotyp-image.${ext}`;\n return `${stem}.${ext}`;\n}\n\nexport async function encodeSourceImage(\n source: SourceImage,\n options: EncodeOptions = {},\n): Promise<File> {\n const outputState = options.output ?? DEFAULT_OUTPUT_STATE;\n const mimeType = await resolveOutputMime(outputState, source);\n const quality = clampQuality(outputState.quality);\n const name = deriveOutputName(options.sourceName, mimeType);\n const bake = createBakeCanvas(source.width, source.height);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, 0, 0);\n } else {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, 0, 0);\n }\n const baseBlob = await bakeCanvasToBlob(bake, mimeType, quality);\n // EXIF can only be re-attached on JPEG → JPEG; canvas re-encoding strips\n // unconditionally, so this is the only place metadata can survive.\n const shouldPreserveMetadata =\n options.output?.stripMetadata === false &&\n mimeType === 'image/jpeg' &&\n source.mimeType === 'image/jpeg' &&\n options.sourceBlob !== undefined;\n const blob = shouldPreserveMetadata\n ? await copyJpegExif({ source: options.sourceBlob as Blob, output: baseBlob })\n : baseBlob;\n return new File([blob], name, { type: mimeType });\n}\n\nfunction extensionForMime(mime: string): string {\n if (mime === 'image/jpeg') return 'jpg';\n if (mime === 'image/png') return 'png';\n if (mime === 'image/webp') return 'webp';\n if (mime === 'image/avif') return 'avif';\n const subtype = mime.split('/')[1];\n return subtype && subtype.length > 0 ? subtype : 'bin';\n}\n\nfunction lastPathSegment(name: string): string | undefined {\n const trimmed = stripQuery(name);\n const segments = trimmed.split(/[/\\\\]/);\n return segments[segments.length - 1];\n}\n\nfunction stripQuery(name: string): string {\n const q = name.indexOf('?');\n return q === -1 ? name : name.slice(0, q);\n}\n\nfunction stripExtension(basename: string): string {\n const dot = basename.lastIndexOf('.');\n if (dot <= 0) return basename;\n return basename.slice(0, dot);\n}\n","import type { SourceImage, UtilityId, UtilityPlugin } from '../plugins/utility.js';\n\n/** One link in the editor's transform pipeline, with the plugin state at Save time. */\nexport interface ChainLink<TState extends object = object> {\n readonly id: UtilityId;\n readonly plugin: UtilityPlugin<TState>;\n readonly state: TState;\n}\n\n/** Apply each plugin's `bake` in sequence; result of each link feeds the next. */\nexport async function runUtilityChain(\n links: readonly ChainLink[],\n source: SourceImage,\n): Promise<SourceImage> {\n let baked = source;\n for (const link of links) {\n baked = await link.plugin.bake(link.state, baked);\n }\n return baked;\n}\n","/**\n * Freehand stroke decimation + midpoint-curve smoothing. Decimation drops\n * sub-pixel samples from the raw pointer stream; the curve smoothing\n * happens at draw time so stored points stay unchanged across edits.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport const MIN_SAMPLE_DISTANCE = 2;\n\n/** Drop interior points closer than `MIN_SAMPLE_DISTANCE` to the previous kept point. */\nexport function decimatePoints(points: ReadonlyArray<Point>): Point[] {\n if (points.length <= 1) return [...points];\n const head = points[0];\n if (!head) return [];\n const out: Point[] = [head];\n let last = head;\n const minSq = MIN_SAMPLE_DISTANCE * MIN_SAMPLE_DISTANCE;\n for (let i = 1; i < points.length - 1; i++) {\n const p = points[i];\n if (!p) continue;\n const dx = p.x - last.x;\n const dy = p.y - last.y;\n if (dx * dx + dy * dy < minSq) continue;\n out.push(p);\n last = p;\n }\n // Always keep the final sample so the stroke ends where the pen lifted.\n const tail = points[points.length - 1];\n if (tail && tail !== last) out.push(tail);\n return out;\n}\n\n/**\n * Trace a smoothed path through `points` using the midpoint-curve technique.\n * Caller owns `beginPath`, stroke style, and `stroke()` so blend modes\n * (highlight) can wrap this without re-implementing the curve math.\n */\nexport function tracePath(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n points: ReadonlyArray<Point>,\n): void {\n if (points.length === 0) return;\n const head = points[0];\n if (!head) return;\n if (points.length === 1) {\n // Single tap: zero-length segment relies on round lineCap to render a dot.\n ctx.moveTo(head.x, head.y);\n ctx.lineTo(head.x, head.y);\n return;\n }\n ctx.moveTo(head.x, head.y);\n for (let i = 1; i < points.length - 1; i++) {\n const a = points[i];\n const b = points[i + 1];\n if (!a || !b) continue;\n const midX = (a.x + b.x) / 2;\n const midY = (a.y + b.y) / 2;\n ctx.quadraticCurveTo(a.x, a.y, midX, midY);\n }\n const last = points[points.length - 1];\n if (last) ctx.lineTo(last.x, last.y);\n}\n","/**\n * Annotation state and pure mutators. Shapes are a flat discriminated\n * union keyed on `kind`; all coordinates are image-space pixels.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport type ShapeKind = 'text' | 'rect' | 'ellipse' | 'arrow' | 'freehand' | 'highlight';\n\n/** Tools the annotation plugin exposes. `select` is the picker. */\nexport type AnnotateTool = ShapeKind | 'select';\n\ninterface ShapeBase {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n readonly kind: ShapeKind;\n}\n\nexport interface TextShape extends ShapeBase {\n readonly kind: 'text';\n /** Top-left anchor in image-space pixels. */\n readonly x: number;\n readonly y: number;\n readonly text: string;\n /** Font size in image-space pixels. */\n readonly fontSize: number;\n /** CSS colour string. */\n readonly color: string;\n readonly textAlign: 'left' | 'center' | 'right';\n}\n\nexport interface RectShape extends ShapeBase {\n readonly kind: 'rect';\n readonly x: number;\n readonly y: number;\n /** Non-negative after gesture commit; may be negative mid-drag. */\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n /** `null` means \"no fill\". */\n readonly fillColor: string | null;\n}\n\nexport interface EllipseShape extends ShapeBase {\n readonly kind: 'ellipse';\n /** Bounding-box top-left; the ellipse fits inside the box. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n readonly fillColor: string | null;\n}\n\nexport interface ArrowShape extends ShapeBase {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n /** Arrowhead is drawn at (x2, y2). */\n readonly x2: number;\n readonly y2: number;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface FreehandShape extends ShapeBase {\n readonly kind: 'freehand';\n /** Decimated raw points in image-space; smoothing happens at render time. */\n readonly points: ReadonlyArray<Point>;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface HighlightShape extends ShapeBase {\n readonly kind: 'highlight';\n readonly points: ReadonlyArray<Point>;\n /** Default semi-transparent yellow, drawn with `multiply` blend mode. */\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport type Shape =\n | TextShape\n | RectShape\n | EllipseShape\n | ArrowShape\n | FreehandShape\n | HighlightShape;\n\nexport interface StylePalette {\n readonly color: string;\n readonly strokeWidth: number;\n /** Used for new rect/ellipse fills; `null` = unfilled. */\n readonly fillColor: string | null;\n /** Used for new text shapes. In image-space pixels. */\n readonly fontSize: number;\n}\n\nexport interface AnnotateState {\n readonly shapes: ReadonlyArray<Shape>;\n readonly selectedId: string | null;\n readonly activeTool: AnnotateTool;\n readonly currentStyle: StylePalette;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n /** Monotonic counter used to mint shape ids. Never decreases. */\n readonly nextShapeNumber: number;\n}\n\n/** Yellow @ 35% alpha; the highlight bake uses `multiply` blending. */\nexport const HIGHLIGHT_DEFAULT_COLOR = 'rgba(255, 235, 59, 0.35)';\nexport const HIGHLIGHT_DEFAULT_STROKE = 18;\nexport const FREEHAND_DEFAULT_STROKE = 6;\nexport const TEXT_DEFAULT_FONT_SIZE = 32;\nexport const DEFAULT_PALETTE_COLOR = '#ff3b30';\nexport const DEFAULT_STROKE_WIDTH = 4;\n\nexport function defaultStylePalette(): StylePalette {\n return {\n color: DEFAULT_PALETTE_COLOR,\n strokeWidth: DEFAULT_STROKE_WIDTH,\n fillColor: null,\n fontSize: TEXT_DEFAULT_FONT_SIZE,\n };\n}\n\nexport interface InitialAnnotateStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialAnnotateState(input: InitialAnnotateStateInput): AnnotateState {\n return {\n shapes: [],\n selectedId: null,\n activeTool: 'select',\n currentStyle: defaultStylePalette(),\n imageSize: input.imageSize,\n nextShapeNumber: 1,\n };\n}\n\n/** Allocate a new shape id from the monotonic counter; caller threads `nextShapeNumber` back into state. */\nexport function mintShapeId(state: AnnotateState): {\n id: string;\n nextShapeNumber: number;\n} {\n return {\n id: `s_${state.nextShapeNumber.toString(36)}`,\n nextShapeNumber: state.nextShapeNumber + 1,\n };\n}\n\nexport function setActiveTool(state: AnnotateState, tool: AnnotateTool): AnnotateState {\n if (state.activeTool === tool) return state;\n // Switching to a drawing tool deselects so the next drag starts a new shape.\n return { ...state, activeTool: tool, selectedId: tool === 'select' ? state.selectedId : null };\n}\n\nexport function setStyle(state: AnnotateState, partial: Partial<StylePalette>): AnnotateState {\n return { ...state, currentStyle: { ...state.currentStyle, ...partial } };\n}\n\nexport function selectShape(state: AnnotateState, id: string | null): AnnotateState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function addShape(state: AnnotateState, shape: Shape): AnnotateState {\n return { ...state, shapes: [...state.shapes, shape], selectedId: shape.id };\n}\n\nexport function replaceShape(state: AnnotateState, shape: Shape): AnnotateState {\n let changed = false;\n const next = state.shapes.map((existing) => {\n if (existing.id !== shape.id) return existing;\n changed = true;\n return shape;\n });\n if (!changed) return state;\n return { ...state, shapes: next };\n}\n\nexport function deleteShape(state: AnnotateState, id: string): AnnotateState {\n const next = state.shapes.filter((shape) => shape.id !== id);\n if (next.length === state.shapes.length) return state;\n return {\n ...state,\n shapes: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\nexport function findShape(state: AnnotateState, id: string | null): Shape | undefined {\n if (id === null) return undefined;\n return state.shapes.find((shape) => shape.id === id);\n}\n\n/** Normalise a rect extent so `width`/`height` are non-negative after a sign-flip drag. */\nexport function normaliseRectExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nexport function translateShape(shape: Shape, dx: number, dy: number): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'rect':\n case 'ellipse':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'arrow':\n return {\n ...shape,\n x1: shape.x1 + dx,\n y1: shape.y1 + dy,\n x2: shape.x2 + dx,\n y2: shape.y2 + dy,\n };\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => ({ x: p.x + dx, y: p.y + dy })) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Type-narrowing helper for exhaustive switches over `Shape`. */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled annotation shape kind: ${JSON.stringify(value)}`);\n}\n\n/**\n * Mirror a shape across an axis of `dims`. Rect/ellipse top-left is\n * remapped so the visible rectangle straddles the same pixels; arrow\n * endpoints and freehand points mirror independently. Text uses its\n * anchor only — the glyph rect walks slightly relative to centre.\n */\nexport function mirrorShape(\n shape: Shape,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): Shape {\n if (axis === 'horizontal') {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, x: dims.width - shape.x - shape.width };\n case 'text':\n return { ...shape, x: dims.width - shape.x };\n case 'arrow':\n return { ...shape, x1: dims.width - shape.x1, x2: dims.width - shape.x2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: dims.width - p.x, y: p.y })),\n };\n default:\n return assertNever(shape);\n }\n }\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, y: dims.height - shape.y - shape.height };\n case 'text':\n return { ...shape, y: dims.height - shape.y };\n case 'arrow':\n return { ...shape, y1: dims.height - shape.y1, y2: dims.height - shape.y2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: p.x, y: dims.height - p.y })),\n };\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Rotate a shape `turns × 90°` CW around the centre of `oldDims`. Returns\n * coordinates in the post-rotation image's coord space (dims swap on odd turns).\n */\nexport function rotateShape(\n shape: Shape,\n turns: 0 | 1 | 2 | 3,\n oldDims: { readonly width: number; readonly height: number },\n): Shape {\n if (turns === 0) return shape;\n const rotatePoint = (x: number, y: number): { x: number; y: number } => {\n if (turns === 1) return { x: oldDims.height - y, y: x };\n if (turns === 2) return { x: oldDims.width - x, y: oldDims.height - y };\n return { x: y, y: oldDims.width - x };\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n // Rotated TL + BR become two corners of the new axis-aligned box.\n const corners = [\n rotatePoint(shape.x, shape.y),\n rotatePoint(shape.x + shape.width, shape.y + shape.height),\n ];\n const newX = Math.min(corners[0].x, corners[1].x);\n const newY = Math.min(corners[0].y, corners[1].y);\n const newW = Math.abs(corners[1].x - corners[0].x);\n const newH = Math.abs(corners[1].y - corners[0].y);\n return { ...shape, x: newX, y: newY, width: newW, height: newH };\n }\n case 'text': {\n const p = rotatePoint(shape.x, shape.y);\n return { ...shape, x: p.x, y: p.y };\n }\n case 'arrow': {\n const p1 = rotatePoint(shape.x1, shape.y1);\n const p2 = rotatePoint(shape.x2, shape.y2);\n return { ...shape, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };\n }\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => rotatePoint(p.x, p.y)) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Apply a transformation to every shape in `state.shapes`. */\nexport function transformShapes(\n state: AnnotateState,\n transformer: (shape: Shape) => Shape,\n): AnnotateState {\n if (state.shapes.length === 0) return state;\n return { ...state, shapes: state.shapes.map(transformer) };\n}\n\n/**\n * Kinds placeable from the keyboard. Freehand / highlight are excluded;\n * a \"default at centre\" instance has no honest shape for those.\n */\nexport type KeyboardPlaceableKind = 'text' | 'rect' | 'ellipse' | 'arrow';\n\nexport const KEYBOARD_PLACEABLE_KINDS: ReadonlyArray<KeyboardPlaceableKind> = [\n 'text',\n 'rect',\n 'ellipse',\n 'arrow',\n];\n\nexport function isKeyboardPlaceableKind(kind: ShapeKind): kind is KeyboardPlaceableKind {\n return kind === 'text' || kind === 'rect' || kind === 'ellipse' || kind === 'arrow';\n}\n\nexport interface CreateCenteredShapeContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly style: StylePalette;\n readonly id: string;\n}\n\n/** A `Shape` whose kind is keyboard-placeable (rect / ellipse / arrow / text). */\nexport type KeyboardPlaceableShape = TextShape | RectShape | EllipseShape | ArrowShape;\n\nexport function createCenteredShape(\n kind: KeyboardPlaceableKind,\n ctx: CreateCenteredShapeContext,\n): KeyboardPlaceableShape {\n const { imageSize, style, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n\n switch (kind) {\n case 'rect':\n case 'ellipse': {\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n if (kind === 'rect') {\n return {\n id,\n kind: 'rect',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n return {\n id,\n kind: 'ellipse',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n case 'arrow': {\n const length = Math.max(100, Math.round(shortEdge * 0.3));\n const x1 = Math.round(cx - length / 2);\n const x2 = x1 + length;\n const y = Math.round(cy);\n return {\n id,\n kind: 'arrow',\n x1,\n y1: y,\n x2,\n y2: y,\n color: style.color,\n strokeWidth: style.strokeWidth,\n };\n }\n case 'text': {\n const x = Math.round(cx);\n const y = Math.round(cy - style.fontSize / 2);\n return {\n id,\n kind: 'text',\n x,\n y,\n text: '',\n fontSize: style.fontSize,\n color: style.color,\n textAlign: 'center',\n };\n }\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { tracePath } from './smooth.js';\nimport { assertNever, type Shape } from './state.js';\n\nexport interface AnnotateBakeInput {\n readonly shapes: ReadonlyArray<Shape>;\n}\n\n/**\n * System font stack used at bake; matches what the preview canvas renders.\n * No web font is loaded — the bundle budget rules it out and the bake\n * pipeline has no \"wait for font\" affordance.\n */\nexport const SYSTEM_FONT_STACK =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif';\n\n/** Paint every shape onto a fresh canvas at the source's dimensions. */\nexport async function bakeAnnotate(\n state: AnnotateBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.shapes.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n for (const shape of state.shapes) {\n paintShape(ctx, shape);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint one shape; caller positions the context for image-space coordinates. Shared by preview and bake. */\nexport function paintShape(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape,\n): void {\n switch (shape.kind) {\n case 'text':\n paintText(ctx, shape);\n return;\n case 'rect':\n paintRect(ctx, shape);\n return;\n case 'ellipse':\n paintEllipse(ctx, shape);\n return;\n case 'arrow':\n paintArrow(ctx, shape);\n return;\n case 'freehand':\n paintFreehand(ctx, shape);\n return;\n case 'highlight':\n paintHighlight(ctx, shape);\n return;\n default:\n assertNever(shape);\n }\n}\n\nfunction paintText(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'text' },\n): void {\n ctx.save();\n ctx.fillStyle = shape.color;\n ctx.font = `${shape.fontSize}px ${SYSTEM_FONT_STACK}`;\n ctx.textAlign = shape.textAlign;\n ctx.textBaseline = 'top';\n // Paint each line on its own; explicit `\\n` only (no auto-wrap).\n const lines = shape.text.length === 0 ? [''] : shape.text.split('\\n');\n const lineHeight = shape.fontSize * 1.2;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n ctx.fillText(line, shape.x, shape.y + i * lineHeight);\n }\n ctx.restore();\n}\n\nfunction paintRect(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'rect' },\n): void {\n ctx.save();\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fillRect(shape.x, shape.y, shape.width, shape.height);\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = 'miter';\n ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);\n }\n ctx.restore();\n}\n\nfunction paintEllipse(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'ellipse' },\n): void {\n ctx.save();\n const rx = shape.width / 2;\n const ry = shape.height / 2;\n const cx = shape.x + rx;\n const cy = shape.y + ry;\n ctx.beginPath();\n ctx.ellipse(cx, cy, Math.max(0, rx), Math.max(0, ry), 0, 0, Math.PI * 2);\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fill();\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.stroke();\n }\n ctx.restore();\n}\n\nfunction paintArrow(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'arrow' },\n): void {\n const dx = shape.x2 - shape.x1;\n const dy = shape.y2 - shape.y1;\n const length = Math.sqrt(dx * dx + dy * dy);\n if (length < 0.5) return;\n\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n\n // Floor on head size so thin strokes still get a readable head.\n const headLength = Math.min(Math.max(shape.strokeWidth * 5, 28), length * 0.6);\n const headWidth = Math.max(shape.strokeWidth * 4, 18);\n const ux = dx / length;\n const uy = dy / length;\n // Shaft stops short of the tip so the head's base sits flush with the cap.\n const shaftEndX = shape.x2 - ux * headLength * 0.6;\n const shaftEndY = shape.y2 - uy * headLength * 0.6;\n\n ctx.beginPath();\n ctx.moveTo(shape.x1, shape.y1);\n ctx.lineTo(shaftEndX, shaftEndY);\n ctx.stroke();\n\n const baseX = shape.x2 - ux * headLength;\n const baseY = shape.y2 - uy * headLength;\n const px = -uy;\n const py = ux;\n ctx.beginPath();\n ctx.moveTo(shape.x2, shape.y2);\n ctx.lineTo(baseX + (px * headWidth) / 2, baseY + (py * headWidth) / 2);\n ctx.lineTo(baseX - (px * headWidth) / 2, baseY - (py * headWidth) / 2);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n}\n\nfunction paintFreehand(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'freehand' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction paintHighlight(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'highlight' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n // `multiply` tints pixels like a highlighter pen; engines without it fall back to alpha-blend.\n ctx.globalCompositeOperation = 'multiply';\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n","import type { Rect } from '../../geometry/rect.js';\nimport { assertNever, type Shape } from './state.js';\n\n/**\n * Axis-aligned bounding box in image-space pixels. Text uses a font-metric\n * estimate because jsdom's `measureText` returns 0; the renderer measures\n * real text at paint time.\n */\nexport function boundingBoxOf(shape: Shape): Rect {\n switch (shape.kind) {\n case 'text': {\n const { width, height } = estimateTextSize(shape.text, shape.fontSize);\n const x = alignToOrigin(shape.x, width, shape.textAlign);\n return { x, y: shape.y, width, height };\n }\n case 'rect':\n case 'ellipse':\n return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };\n case 'arrow': {\n const x = Math.min(shape.x1, shape.x2);\n const y = Math.min(shape.y1, shape.y2);\n return {\n x,\n y,\n width: Math.abs(shape.x2 - shape.x1),\n height: Math.abs(shape.y2 - shape.y1),\n };\n }\n case 'freehand':\n case 'highlight': {\n const head = shape.points[0];\n if (!head) return { x: 0, y: 0, width: 0, height: 0 };\n let minX = head.x;\n let minY = head.y;\n let maxX = head.x;\n let maxY = head.y;\n for (const p of shape.points) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Eight-handle layout (corners + edges). Arrows reuse `tl`/`br` as\n * endpoint handles; callers detect arrow handles by shape kind.\n */\nexport type SelectionHandle = 'tl' | 'tr' | 'bl' | 'br' | 't' | 'r' | 'b' | 'l';\n\nexport const ALL_SELECTION_HANDLES: readonly SelectionHandle[] = [\n 'tl',\n 'tr',\n 'bl',\n 'br',\n 't',\n 'r',\n 'b',\n 'l',\n];\n\n/** Image-space coordinates for each handle; renderer projects to display. */\nexport function selectionHandlePositions(\n rect: Rect,\n): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\n/** Apply a handle drag to a rect. Returns the new rect; the caller normalises. */\nexport function rectFromHandleDrag(\n initial: Rect,\n handle: SelectionHandle,\n pointer: { x: number; y: number },\n): Rect {\n let x = initial.x;\n let y = initial.y;\n let right = initial.x + initial.width;\n let bottom = initial.y + initial.height;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') x = pointer.x;\n if (handle === 'tr' || handle === 'r' || handle === 'br') right = pointer.x;\n if (handle === 'tl' || handle === 't' || handle === 'tr') y = pointer.y;\n if (handle === 'bl' || handle === 'b' || handle === 'br') bottom = pointer.y;\n\n return { x, y, width: right - x, height: bottom - y };\n}\n\n/**\n * Estimate painted text size without `measureText` (jsdom returns 0).\n * Uses 0.55em mean Latin char width and 1.2em line height — close enough\n * for selection-handle placement.\n */\nexport function estimateTextSize(\n text: string,\n fontSize: number,\n): { width: number; height: number } {\n const lines = text.length === 0 ? [''] : text.split('\\n');\n let maxLineLen = 0;\n for (const line of lines) {\n if (line.length > maxLineLen) maxLineLen = line.length;\n }\n const width = Math.max(fontSize * 0.6, maxLineLen * fontSize * 0.55);\n const height = lines.length * fontSize * 1.2;\n return { width, height };\n}\n\n/** Convert a text shape's anchor to the bounding box's top-left given `textAlign`. */\nexport function alignToOrigin(\n anchorX: number,\n width: number,\n align: 'left' | 'center' | 'right',\n): number {\n switch (align) {\n case 'left':\n return anchorX;\n case 'center':\n return anchorX - width / 2;\n case 'right':\n return anchorX - width;\n }\n}\n","import type { Point } from '../../geometry/rect.js';\nimport { boundingBoxOf } from './geometry.js';\nimport { assertNever, type Shape } from './state.js';\n\n/** Picking margin added to every stroked-shape hit-test, in image-space pixels. */\nexport const PICK_TOLERANCE = 4;\n\n/** Find the topmost shape under `point` (image-space). Iterates back-to-front. */\nexport function pickShape(shapes: ReadonlyArray<Shape>, point: Point): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape && hitTest(shape, point)) return shape;\n }\n return undefined;\n}\n\nexport function hitTest(shape: Shape, point: Point): boolean {\n switch (shape.kind) {\n case 'text':\n return pointInRect(point, boundingBoxOf(shape));\n case 'rect': {\n const inside = pointInRect(point, normaliseBox(shape));\n // Filled rects pick anywhere inside; outline-only picks on the stroke (with tolerance).\n if (shape.fillColor !== null) return inside;\n const outer = expandRect(normaliseBox(shape), shape.strokeWidth / 2 + PICK_TOLERANCE);\n const inner = expandRect(normaliseBox(shape), -(shape.strokeWidth / 2 + PICK_TOLERANCE));\n return pointInRect(point, outer) && !pointInRect(point, inner);\n }\n case 'ellipse': {\n const box = normaliseBox(shape);\n const rx = box.width / 2;\n const ry = box.height / 2;\n const cx = box.x + rx;\n const cy = box.y + ry;\n if (rx <= 0 || ry <= 0) return false;\n const nx = (point.x - cx) / rx;\n const ny = (point.y - cy) / ry;\n const r2 = nx * nx + ny * ny;\n const tolerance = (shape.strokeWidth / 2 + PICK_TOLERANCE) / Math.min(rx, ry);\n if (shape.fillColor !== null) return r2 <= (1 + tolerance) ** 2;\n return r2 <= (1 + tolerance) ** 2 && r2 >= (1 - tolerance) ** 2;\n }\n case 'arrow':\n return pointNearSegment(\n point,\n { x: shape.x1, y: shape.y1 },\n { x: shape.x2, y: shape.y2 },\n shape.strokeWidth / 2 + PICK_TOLERANCE,\n );\n case 'freehand':\n case 'highlight': {\n const box = boundingBoxOf(shape);\n const expanded = expandRect(box, shape.strokeWidth / 2 + PICK_TOLERANCE);\n if (!pointInRect(point, expanded)) return false;\n const tolerance = shape.strokeWidth / 2 + PICK_TOLERANCE;\n for (let i = 1; i < shape.points.length; i++) {\n const a = shape.points[i - 1];\n const b = shape.points[i];\n if (a && b && pointNearSegment(point, a, b, tolerance)) return true;\n }\n if (shape.points.length === 1) {\n const p = shape.points[0];\n if (!p) return false;\n const dx = p.x - point.x;\n const dy = p.y - point.y;\n return dx * dx + dy * dy <= tolerance * tolerance;\n }\n return false;\n }\n default:\n return assertNever(shape);\n }\n}\n\nfunction pointInRect(\n point: Point,\n rect: { x: number; y: number; width: number; height: number },\n): boolean {\n return (\n point.x >= rect.x &&\n point.y >= rect.y &&\n point.x <= rect.x + rect.width &&\n point.y <= rect.y + rect.height\n );\n}\n\nfunction expandRect(\n rect: { x: number; y: number; width: number; height: number },\n amount: number,\n): { x: number; y: number; width: number; height: number } {\n return {\n x: rect.x - amount,\n y: rect.y - amount,\n width: rect.width + amount * 2,\n height: rect.height + amount * 2,\n };\n}\n\nfunction normaliseBox(shape: { x: number; y: number; width: number; height: number }): {\n x: number;\n y: number;\n width: number;\n height: number;\n} {\n let { x, y, width, height } = shape;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nfunction pointNearSegment(point: Point, a: Point, b: Point, tolerance: number): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) {\n const ex = point.x - a.x;\n const ey = point.y - a.y;\n return ex * ex + ey * ey <= tolerance * tolerance;\n }\n let t = ((point.x - a.x) * dx + (point.y - a.y) * dy) / len2;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const projX = a.x + t * dx;\n const projY = a.y + t * dy;\n const ex = point.x - projX;\n const ey = point.y - projY;\n return ex * ex + ey * ey <= tolerance * tolerance;\n}\n","import { clampRectInside, type Rect } from '../../geometry/rect.js';\n\n/**\n * Compute the largest axis-aligned rectangle of `targetRatio` (= w/h) that\n * fits inside `bounds`, centered. Used to seed the crop rectangle when the\n * user picks an aspect-ratio preset before any drag.\n */\nexport function fitRectToBoundsWithRatio(bounds: Rect, targetRatio: number): Rect {\n if (targetRatio <= 0 || bounds.width <= 0 || bounds.height <= 0) {\n return { x: bounds.x, y: bounds.y, width: 0, height: 0 };\n }\n\n const boundsRatio = bounds.width / bounds.height;\n let width: number;\n let height: number;\n if (targetRatio >= boundsRatio) {\n width = bounds.width;\n height = width / targetRatio;\n } else {\n height = bounds.height;\n width = height * targetRatio;\n }\n\n return {\n x: bounds.x + (bounds.width - width) / 2,\n y: bounds.y + (bounds.height - height) / 2,\n width,\n height,\n };\n}\n\n/**\n * Reshape `rect` to `targetRatio`, anchored at `anchor`, clamped inside\n * `bounds`. If clamping breaks the ratio, falls back to the largest same-ratio\n * sub-rect that fits, anchored identically.\n */\nexport function applyAspectRatio(\n rect: Rect,\n targetRatio: number,\n anchor: AspectAnchor,\n bounds: Rect,\n): Rect {\n if (targetRatio <= 0) return rect;\n if (rect.width <= 0 || rect.height <= 0) return fitRectToBoundsWithRatio(bounds, targetRatio);\n\n const currentRatio = rect.width / rect.height;\n let width: number;\n let height: number;\n if (currentRatio > targetRatio) {\n height = rect.height;\n width = height * targetRatio;\n } else {\n width = rect.width;\n height = width / targetRatio;\n }\n\n const reshaped = anchorRect(rect, width, height, anchor);\n const clamped = clampRectInside(reshaped, bounds);\n\n const clampedRatio = clamped.height === 0 ? 0 : clamped.width / clamped.height;\n if (Math.abs(clampedRatio - targetRatio) <= RATIO_TOLERANCE) {\n return clamped;\n }\n return fitInsideAtAnchor(clamped, targetRatio, anchor);\n}\n\nconst RATIO_TOLERANCE = 1e-6;\n\nexport type AspectAnchor = 'tl' | 'tr' | 'bl' | 'br' | 'center';\n\nfunction anchorRect(rect: Rect, width: number, height: number, anchor: AspectAnchor): Rect {\n switch (anchor) {\n case 'tl':\n return { x: rect.x, y: rect.y, width, height };\n case 'tr':\n return { x: rect.x + rect.width - width, y: rect.y, width, height };\n case 'bl':\n return { x: rect.x, y: rect.y + rect.height - height, width, height };\n case 'br':\n return {\n x: rect.x + rect.width - width,\n y: rect.y + rect.height - height,\n width,\n height,\n };\n case 'center':\n return {\n x: rect.x + (rect.width - width) / 2,\n y: rect.y + (rect.height - height) / 2,\n width,\n height,\n };\n }\n}\n\nfunction fitInsideAtAnchor(bounds: Rect, targetRatio: number, anchor: AspectAnchor): Rect {\n const fitted = fitRectToBoundsWithRatio(bounds, targetRatio);\n return anchorRect(bounds, fitted.width, fitted.height, anchor);\n}\n","import { createBakeCanvas } from '../../canvas/bake-canvas.js';\nimport { type Rect, roundRect } from '../../geometry/rect.js';\nimport type { SourceImage } from '../utility.js';\n\nexport interface CropBakeInput {\n /** The cropped region, in image-space pixels. */\n readonly rect: Rect;\n}\n\n/**\n * Apply a crop and return a SourceImage at the crop's pixel size. The\n * rect is rounded and clamped against the source so an oversized rect\n * doesn't crash — we draw what fits.\n */\nexport function bakeCrop(source: SourceImage, input: CropBakeInput): SourceImage {\n const rounded = roundRect(input.rect);\n const x = clamp(rounded.x, 0, source.width);\n const y = clamp(rounded.y, 0, source.height);\n const w = clamp(rounded.width, 1, source.width - x);\n const h = clamp(rounded.height, 1, source.height - y);\n\n const bake = createBakeCanvas(w, h);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","/**\n * Filters crop presets by aspect-ratio relative to 1. `landscape` keeps\n * ratio ≥ 1, `portrait` keeps ratio < 1; `undefined` ratios (Custom) and\n * unknown tokens stay visible.\n */\nexport type CropPresetFilter = 'landscape' | 'portrait';\nexport type CropPreset = readonly [number | undefined, string];\n\nexport function isPresetVisible(preset: CropPreset, filter: CropPresetFilter | undefined): boolean {\n const [ratio] = preset;\n if (ratio === undefined) return true;\n if (filter === undefined) return true;\n if (filter === 'landscape') return ratio >= 1;\n if (filter === 'portrait') return ratio < 1;\n return true;\n}\n\nexport function filterPresets(\n presets: readonly CropPreset[],\n filter: CropPresetFilter | undefined,\n): readonly CropPreset[] {\n return presets.filter((preset) => isPresetVisible(preset, filter));\n}\n","import { clampRectInside, type Point, type Rect } from '../../geometry/rect.js';\nimport { type AspectAnchor, applyAspectRatio } from './aspect-ratio.js';\n\nexport type CornerHandle = 'tl' | 'tr' | 'bl' | 'br';\nexport type EdgeHandle = 't' | 'r' | 'b' | 'l';\nexport type HandleDirection = CornerHandle | EdgeHandle;\n\nexport interface ResizeOptions {\n /** Image-space bounds the rect must stay inside. */\n readonly bounds: Rect;\n /** Aspect ratio to enforce, or `undefined` for free crop. */\n readonly aspectRatio?: number;\n /** Minimum size on either axis, in image-space units. Defaults to 1. */\n readonly minSize?: number;\n}\n\n/**\n * Resize a rect from one of its eight handles to `pointer`. Opposite\n * corner/edge anchors; result clamped to `bounds` and reshaped to\n * `aspectRatio` (anchored at the same opposite corner) when supplied.\n */\nexport function resizeRectFromHandle(\n rect: Rect,\n handle: HandleDirection,\n pointer: Point,\n options: ResizeOptions,\n): Rect {\n const minSize = options.minSize ?? 1;\n const left = rect.x;\n const top = rect.y;\n const right = rect.x + rect.width;\n const bottom = rect.y + rect.height;\n\n // Pointer may swap sides (drag through the anchor); recompute from anchor + live edge.\n let newLeft = left;\n let newTop = top;\n let newRight = right;\n let newBottom = bottom;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n newLeft = pointer.x;\n }\n if (handle === 'tr' || handle === 'r' || handle === 'br') {\n newRight = pointer.x;\n }\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n newTop = pointer.y;\n }\n if (handle === 'bl' || handle === 'b' || handle === 'br') {\n newBottom = pointer.y;\n }\n\n if (handle === 'l' || handle === 'r') {\n newTop = top;\n newBottom = bottom;\n }\n if (handle === 't' || handle === 'b') {\n newLeft = left;\n newRight = right;\n }\n\n let nx = Math.min(newLeft, newRight);\n let ny = Math.min(newTop, newBottom);\n let nw = Math.abs(newRight - newLeft);\n let nh = Math.abs(newBottom - newTop);\n\n if (nw < minSize) {\n nw = minSize;\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n nx = right - minSize;\n } else if (handle === 'tr' || handle === 'r' || handle === 'br') {\n nx = left;\n }\n }\n if (nh < minSize) {\n nh = minSize;\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n ny = bottom - minSize;\n } else if (handle === 'bl' || handle === 'b' || handle === 'br') {\n ny = top;\n }\n }\n\n let resized: Rect = { x: nx, y: ny, width: nw, height: nh };\n resized = clampRectInside(resized, options.bounds);\n\n if (options.aspectRatio !== undefined && options.aspectRatio > 0) {\n resized = applyAspectRatio(resized, options.aspectRatio, anchorFor(handle), options.bounds);\n }\n\n return resized;\n}\n\nfunction anchorFor(handle: HandleDirection): AspectAnchor {\n switch (handle) {\n case 'tl':\n return 'br';\n case 'tr':\n return 'bl';\n case 'bl':\n return 'tr';\n case 'br':\n return 'tl';\n case 't':\n return 'bl';\n case 'b':\n return 'tl';\n case 'l':\n return 'tr';\n case 'r':\n return 'tl';\n }\n}\n","import type { Rect, Size } from '../../geometry/rect.js';\nimport { fitRectToBoundsWithRatio } from './aspect-ratio.js';\nimport type { CropPreset, CropPresetFilter } from './preset-filter.js';\n\nexport interface CropState {\n /** Crop rectangle in image-space pixels. */\n readonly rect: Rect;\n /** Active aspect-ratio constraint (image w/h), or `undefined` for free. */\n readonly aspectRatio: number | undefined;\n /** Index into `presets`, or `-1` if no preset is active. */\n readonly activePresetIndex: number;\n /** Visible presets after applying `cropSelectPresetFilter`. */\n readonly presets: readonly CropPreset[];\n /** Image dimensions, in pixels. The bounds the crop can move within. */\n readonly imageSize: Size;\n}\n\nexport interface InitialCropStateInput {\n readonly imageSize: Size;\n readonly presets: readonly CropPreset[];\n readonly filter: CropPresetFilter | undefined;\n}\n\n/** Full-frame crop, \"Custom\" preset active. */\nexport function initialCropState(input: InitialCropStateInput): CropState {\n const bounds: Rect = { x: 0, y: 0, width: input.imageSize.width, height: input.imageSize.height };\n return {\n rect: bounds,\n aspectRatio: undefined,\n activePresetIndex: findCustomIndex(input.presets),\n presets: input.presets,\n imageSize: input.imageSize,\n };\n}\n\nexport function applyPresetByIndex(state: CropState, presetIndex: number): CropState {\n const preset = state.presets[presetIndex];\n if (!preset) return state;\n const [ratio] = preset;\n if (ratio === undefined) {\n return { ...state, aspectRatio: undefined, activePresetIndex: presetIndex };\n }\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: state.imageSize.width,\n height: state.imageSize.height,\n };\n const fitted = fitRectToBoundsWithRatio(bounds, ratio);\n return { ...state, rect: fitted, aspectRatio: ratio, activePresetIndex: presetIndex };\n}\n\nfunction findCustomIndex(presets: readonly CropPreset[]): number {\n return presets.findIndex(([ratio]) => ratio === undefined);\n}\n","/**\n * Six tone adjustments stored as slider values in [-100, +100]; math\n * constants (gamma exponent, contrast multiplier, etc.) are computed\n * from these in `math.ts` at LUT-build time.\n */\nexport interface FinetuneState {\n readonly brightness: number;\n readonly contrast: number;\n readonly saturation: number;\n readonly exposure: number;\n readonly clarity: number;\n readonly gamma: number;\n}\n\nexport const FINETUNE_MIN = -100;\nexport const FINETUNE_MAX = 100;\nexport const FINETUNE_STEP = 1;\n\nexport const DEFAULT_FINETUNE_STATE: FinetuneState = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n};\n\nexport type FinetuneKey = keyof FinetuneState;\n\nexport const FINETUNE_ADJUSTMENTS: readonly {\n readonly key: FinetuneKey;\n readonly label: string;\n}[] = [\n { key: 'brightness', label: 'Brightness' },\n { key: 'contrast', label: 'Contrast' },\n { key: 'saturation', label: 'Saturation' },\n { key: 'exposure', label: 'Exposure' },\n { key: 'clarity', label: 'Clarity' },\n { key: 'gamma', label: 'Gamma' },\n];\n\nexport function initialFinetuneState(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nexport function isFinetuneNoOp(state: FinetuneState): boolean {\n return (\n state.brightness === 0 &&\n state.contrast === 0 &&\n state.saturation === 0 &&\n state.exposure === 0 &&\n state.clarity === 0 &&\n state.gamma === 0\n );\n}\n\n/** Update a single adjustment, clamped to the legal range. */\nexport function setFinetune(state: FinetuneState, key: FinetuneKey, value: number): FinetuneState {\n const next = clampSliderValue(value);\n if (state[key] === next) return state;\n return { ...state, [key]: next };\n}\n\n/** Reset a single adjustment to its default of 0. */\nexport function resetFinetune(state: FinetuneState, key: FinetuneKey): FinetuneState {\n if (state[key] === 0) return state;\n return { ...state, [key]: 0 };\n}\n\n/** Reset every adjustment to its default. */\nexport function resetAllFinetune(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nfunction clampSliderValue(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value <= FINETUNE_MIN) return FINETUNE_MIN;\n if (value >= FINETUNE_MAX) return FINETUNE_MAX;\n // Snap to slider step so programmatic setters round-trip through the input.\n return Math.round(value / FINETUNE_STEP) * FINETUNE_STEP;\n}\n","/**\n * Curated `FinetuneState` shapes that one-click set the six tone numbers.\n * The filter tab is a UI view over the finetune store: clicking a preset\n * here is identical to dragging the matching sliders in finetune.\n */\nimport { DEFAULT_FINETUNE_STATE, type FinetuneState } from '../finetune/state.js';\n\nexport type FilterPresetId = 'none' | 'vivid' | 'mono' | 'soft' | 'punch' | 'mute' | 'bright';\n\nexport interface FilterPreset {\n readonly id: FilterPresetId;\n readonly label: string;\n readonly state: FinetuneState;\n}\n\n/** The seven presets in display order. `none` is the identity / off state. */\nexport const FILTER_PRESETS: readonly FilterPreset[] = [\n {\n id: 'none',\n label: 'None',\n state: DEFAULT_FINETUNE_STATE,\n },\n {\n id: 'vivid',\n label: 'Vivid',\n state: {\n brightness: 0,\n contrast: 10,\n saturation: 40,\n exposure: 0,\n clarity: 5,\n gamma: 0,\n },\n },\n {\n id: 'mono',\n label: 'Mono',\n // -100 saturation is bit-exact grayscale via Rec. 709 luminance.\n state: {\n brightness: 0,\n contrast: 15,\n saturation: -100,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n },\n },\n {\n id: 'soft',\n label: 'Soft',\n state: {\n brightness: 5,\n contrast: -10,\n saturation: 0,\n exposure: 0,\n clarity: -25,\n gamma: 0,\n },\n },\n {\n id: 'punch',\n label: 'Punch',\n state: {\n brightness: 0,\n contrast: 30,\n saturation: 5,\n exposure: 0,\n clarity: 25,\n gamma: 0,\n },\n },\n {\n id: 'mute',\n label: 'Mute',\n state: {\n brightness: 0,\n contrast: 5,\n saturation: -50,\n exposure: 0,\n clarity: -5,\n gamma: 0,\n },\n },\n {\n id: 'bright',\n label: 'Bright',\n state: {\n brightness: 5,\n contrast: 5,\n saturation: 0,\n exposure: 15,\n clarity: 0,\n gamma: 0,\n },\n },\n];\n\n/** Structural equality across the six finetune fields. */\nexport function finetuneStatesEqual(a: FinetuneState, b: FinetuneState): boolean {\n return (\n a.brightness === b.brightness &&\n a.contrast === b.contrast &&\n a.saturation === b.saturation &&\n a.exposure === b.exposure &&\n a.clarity === b.clarity &&\n a.gamma === b.gamma\n );\n}\n\n/** Preset whose state matches `state` exactly, or `undefined` if between presets. */\nexport function findActivePreset(state: FinetuneState): FilterPreset | undefined {\n for (const preset of FILTER_PRESETS) {\n if (finetuneStatesEqual(preset.state, state)) return preset;\n }\n return undefined;\n}\n","/**\n * Pure math for the finetune adjustments. Slider values arrive in\n * [-100, +100] from `state.ts`. Shared by the bake (full-resolution)\n * and the live preview (display-resolution).\n */\n\nimport type { FinetuneState } from './state.js';\n\n/**\n * 256-entry LUT collapsing brightness + contrast + exposure + gamma into\n * one per-byte mapping. Saturation and clarity run in separate passes.\n */\nexport function buildFinetuneLut(state: FinetuneState): Uint8ClampedArray {\n const lut = new Uint8ClampedArray(256);\n\n // brightness: -100 → -0.5, +100 → +0.5 on the normalised value\n const brightnessOffset = state.brightness / 200;\n // contrast multiplier around mid-gray: -100 → 0×, +100 → 2×\n const contrastFactor = 1 + state.contrast / 100;\n // exposure multiplier: -100 → 0.5×, +100 → 1.5×\n const exposureFactor = 1 + state.exposure / 200;\n // gamma exponent: -100 → 2.0, 0 → 1.0, +100 → 0.5\n const gammaExponent = gammaExponentFor(state.gamma);\n\n for (let v = 0; v < 256; v++) {\n let x = v / 255;\n x = x * exposureFactor;\n x = (x - 0.5) * contrastFactor + 0.5;\n x = x + brightnessOffset;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n x = x ** gammaExponent;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n lut[v] = Math.round(x * 255);\n }\n\n return lut;\n}\n\nfunction gammaExponentFor(slider: number): number {\n if (slider === 0) return 1;\n if (slider > 0) return 1 - 0.5 * (slider / 100);\n return 1 + 1.0 * (-slider / 100);\n}\n\n/**\n * Apply the LUT and saturation in one pass. `src`/`dst` may be the same\n * buffer. Saturation: -100 → grayscale (Rec. 709), 0 → identity, +100 → 2×.\n */\nexport function applyFinetuneLutAndSaturation(\n src: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n lut: Uint8ClampedArray,\n state: FinetuneState,\n): void {\n const len = src.length;\n if (dst.length !== len) {\n throw new Error('applyFinetuneLutAndSaturation: src/dst length mismatch');\n }\n\n const saturation = 1 + state.saturation / 100;\n\n // Fast path for the common identity (saturation === 0).\n if (saturation === 1) {\n for (let i = 0; i < len; i += 4) {\n dst[i] = lut[src[i]];\n dst[i + 1] = lut[src[i + 1]];\n dst[i + 2] = lut[src[i + 2]];\n dst[i + 3] = src[i + 3];\n }\n return;\n }\n\n for (let i = 0; i < len; i += 4) {\n const r0 = lut[src[i]];\n const g0 = lut[src[i + 1]];\n const b0 = lut[src[i + 2]];\n // Rec. 709 luminance.\n const y = 0.2126 * r0 + 0.7152 * g0 + 0.0722 * b0;\n let r = y + (r0 - y) * saturation;\n let g = y + (g0 - y) * saturation;\n let b = y + (b0 - y) * saturation;\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n dst[i + 3] = src[i + 3];\n }\n}\n\n/**\n * Unsharp-mask local contrast: `result = dst + (clarity/100) * (dst - blurred)`.\n * `clarity === 0` is a no-op; caller is expected to skip the call.\n */\nexport function applyClarity(\n dst: Uint8ClampedArray,\n blurred: Uint8ClampedArray,\n clarity: number,\n): void {\n if (clarity === 0) return;\n const len = dst.length;\n if (blurred.length !== len) {\n throw new Error('applyClarity: dst/blurred length mismatch');\n }\n const amount = clarity / 100;\n for (let i = 0; i < len; i += 4) {\n const dr = dst[i];\n const dg = dst[i + 1];\n const db = dst[i + 2];\n let r = dr + amount * (dr - blurred[i]);\n let g = dg + amount * (dg - blurred[i + 1]);\n let b = db + amount * (db - blurred[i + 2]);\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n }\n}\n\n/** Separable 3×3 box blur (clamp at edges). Used as the unsharp-mask reference. */\nexport function boxBlur3x3(\n src: Uint8ClampedArray,\n tmp: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n width: number,\n height: number,\n): void {\n if (src.length !== tmp.length || src.length !== dst.length) {\n throw new Error('boxBlur3x3: buffer length mismatch');\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const xm = x === 0 ? 0 : x - 1;\n const xp = x === width - 1 ? width - 1 : x + 1;\n const i = (y * width + x) * 4;\n const im = (y * width + xm) * 4;\n const ip = (y * width + xp) * 4;\n tmp[i] = (src[im] + src[i] + src[ip]) / 3;\n tmp[i + 1] = (src[im + 1] + src[i + 1] + src[ip + 1]) / 3;\n tmp[i + 2] = (src[im + 2] + src[i + 2] + src[ip + 2]) / 3;\n tmp[i + 3] = src[i + 3];\n }\n }\n for (let y = 0; y < height; y++) {\n const ym = y === 0 ? 0 : y - 1;\n const yp = y === height - 1 ? height - 1 : y + 1;\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * 4;\n const im = (ym * width + x) * 4;\n const ip = (yp * width + x) * 4;\n dst[i] = (tmp[im] + tmp[i] + tmp[ip]) / 3;\n dst[i + 1] = (tmp[im + 1] + tmp[i + 1] + tmp[ip + 1]) / 3;\n dst[i + 2] = (tmp[im + 2] + tmp[i + 2] + tmp[ip + 2]) / 3;\n dst[i + 3] = tmp[i + 3];\n }\n }\n}\n\n/** Structural raster shape so tests can pass a plain object (some jsdom versions lack `ImageData`). */\nexport interface RasterImage {\n // ArrayBuffer-pinned so data is assignable to `new ImageData(...)`.\n readonly data: Uint8ClampedArray<ArrayBuffer>;\n readonly width: number;\n readonly height: number;\n}\n\n/** Apply the full pipeline (LUT + saturation + clarity); blur buffers allocate only when clarity ≠ 0. */\nexport function applyFinetuneToImageData(\n state: FinetuneState,\n baseline: RasterImage,\n dst: RasterImage,\n): void {\n if (\n baseline.width !== dst.width ||\n baseline.height !== dst.height ||\n baseline.data.length !== dst.data.length\n ) {\n throw new Error('applyFinetuneToImageData: baseline/dst dimensions mismatch');\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(baseline.data, dst.data, lut, state);\n\n if (state.clarity !== 0) {\n // Blur the pre-LUT baseline so the cached blur stays valid as long as\n // the baseline does; high-frequency is roughly order-independent here.\n const tmp = new Uint8ClampedArray(baseline.data.length);\n const blurred = new Uint8ClampedArray(baseline.data.length);\n boxBlur3x3(baseline.data, tmp, blurred, baseline.width, baseline.height);\n applyClarity(dst.data, blurred, state.clarity);\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { applyFinetuneToImageData } from './math.js';\nimport { type FinetuneState, isFinetuneNoOp } from './state.js';\n\n/** Apply the six finetune adjustments at full resolution. Shares the math with the live preview. */\nexport async function bakeFinetune(\n state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n if (isFinetuneNoOp(state)) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n const baseline = ctx.getImageData(0, 0, source.width, source.height);\n // When clarity is non-zero the unsharp-mask step needs `dst` and `blurred`\n // simultaneously, so we can't reuse the baseline buffer in place.\n if (state.clarity === 0) {\n applyFinetuneToImageData(state, baseline, baseline);\n ctx.putImageData(baseline, 0, 0);\n } else {\n const dst = new ImageData(\n new Uint8ClampedArray(baseline.data.length),\n baseline.width,\n baseline.height,\n );\n applyFinetuneToImageData(state, baseline, dst);\n ctx.putImageData(dst, 0, 0);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n","/** Two independent axis flips; their composition equals a 180° rotation. */\nexport interface FlipState {\n readonly horizontal: boolean;\n readonly vertical: boolean;\n}\n\nexport function initialFlipState(): FlipState {\n return { horizontal: false, vertical: false };\n}\n\nexport function toggleFlip(state: FlipState, axis: 'horizontal' | 'vertical'): FlipState {\n return axis === 'horizontal'\n ? { ...state, horizontal: !state.horizontal }\n : { ...state, vertical: !state.vertical };\n}\n\nexport function isFlipNoOp(state: FlipState): boolean {\n return !state.horizontal && !state.vertical;\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FlipState, isFlipNoOp } from './state.js';\n\n/** Apply horizontal / vertical flips via a sign-flipped scale on `drawImage`. */\nexport async function bakeFlip(state: FlipState, source: SourceImage): Promise<SourceImage> {\n if (isFlipNoOp(state)) return source;\n\n const { width, height } = source;\n const bake = createBakeCanvas(width, height);\n const ctx = getBakeContext2D(bake);\n\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n const tx = state.horizontal ? width : 0;\n const ty = state.vertical ? height : 0;\n\n ctx.setTransform(sx, 0, 0, sy, tx, ty);\n ctx.drawImage(source.bitmap, 0, 0);\n\n return {\n bitmap: bake.canvas,\n width,\n height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Frame preset ids match Ghost's `frameOptions` identifiers directly so\n * a contract change is a state rename, not a translation table.\n */\nexport type FramePresetId =\n | 'none'\n | 'solidSharp'\n | 'solidRound'\n | 'lineSingle'\n | 'hook'\n | 'polaroid';\n\nexport const FRAME_PRESET_IDS: readonly FramePresetId[] = [\n 'none',\n 'solidSharp',\n 'solidRound',\n 'lineSingle',\n 'hook',\n 'polaroid',\n];\n\nexport interface FramePreset {\n readonly id: FramePresetId;\n /** UI label. The Ghost adapter overrides these from `frameOptions[i][1]`. */\n readonly label: string;\n /** Whether the preset's colour can be customised by the user. */\n readonly acceptsColor: boolean;\n /** Default colour when `acceptsColor`. Polaroid defaults to white. */\n readonly defaultColor: string;\n}\n\n/** Six presets in Ghost's `frameOptions` order. Labels are English defaults; adapter localises. */\nexport const FRAME_PRESETS: readonly FramePreset[] = [\n {\n id: 'none',\n label: 'None',\n acceptsColor: false,\n defaultColor: '#000000',\n },\n {\n id: 'solidSharp',\n label: 'Mat Sharp',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'solidRound',\n label: 'Mat Round',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'lineSingle',\n label: 'Line Single',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'hook',\n label: 'Corner Hooks',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'polaroid',\n label: 'Polaroid',\n acceptsColor: true,\n defaultColor: '#ffffff',\n },\n];\n\nexport interface FrameState {\n readonly presetId: FramePresetId;\n /** CSS hex colour. Used by every preset whose `acceptsColor` is true. */\n readonly color: string;\n}\n\nexport const DEFAULT_FRAME_STATE: FrameState = {\n presetId: 'none',\n color: '#000000',\n};\n\nexport function initialFrameState(): FrameState {\n return DEFAULT_FRAME_STATE;\n}\n\nexport function isFrameNoOp(state: FrameState): boolean {\n return state.presetId === 'none';\n}\n\nexport function setFramePreset(state: FrameState, presetId: FramePresetId): FrameState {\n if (state.presetId === presetId) return state;\n // Reset colour to the new preset's default iff the current colour\n // matches the previous preset's default (i.e. user hadn't customised it).\n const currentPreset = findFramePreset(state.presetId);\n const nextPreset = findFramePreset(presetId);\n if (!nextPreset) return { ...state, presetId };\n const wasOnDefault = currentPreset !== undefined && state.color === currentPreset.defaultColor;\n const nextColor = wasOnDefault ? nextPreset.defaultColor : state.color;\n return { presetId, color: nextColor };\n}\n\nexport function setFrameColor(state: FrameState, color: string): FrameState {\n if (state.color === color) return state;\n return { ...state, color };\n}\n\nexport function findFramePreset(id: FramePresetId): FramePreset | undefined {\n return FRAME_PRESETS.find((p) => p.id === id);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FramePresetId, type FrameState, isFrameNoOp } from './state.js';\n\n/**\n * Apply the active frame preset. Frame thickness scales with `source`\n * so it stays consistent across resize choices. Output dimensions match\n * the input except for Polaroid, which extends the canvas.\n */\nexport async function bakeFrame(state: FrameState, source: SourceImage): Promise<SourceImage> {\n if (isFrameNoOp(state)) return source;\n\n if (state.presetId === 'polaroid') {\n return bakePolaroid(state.color, source);\n }\n if (state.presetId === 'none') return source;\n return bakeInsideFrame(state.presetId, state.color, source);\n}\n\nfunction bakeInsideFrame(\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n source: SourceImage,\n): SourceImage {\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n paintInsideFrame(ctx, presetId, color, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint a non-extending frame; caller has already drawn the source image. */\nexport function paintInsideFrame(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n width: number,\n height: number,\n): void {\n switch (presetId) {\n case 'solidSharp':\n paintMatSharp(ctx, color, width, height);\n return;\n case 'solidRound':\n paintMatRound(ctx, color, width, height);\n return;\n case 'lineSingle':\n paintLineSingle(ctx, color, width, height);\n return;\n case 'hook':\n paintCornerHooks(ctx, color, width, height);\n return;\n }\n}\n\n/** Mat Sharp: 4%-of-shorter-edge solid border with sharp corners. */\nfunction paintMatSharp(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n ctx.save();\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, width, t);\n ctx.fillRect(0, height - t, width, t);\n ctx.fillRect(0, t, t, height - 2 * t);\n ctx.fillRect(width - t, t, t, height - 2 * t);\n ctx.restore();\n}\n\n/** Mat Round: Mat Sharp with the four outer corners knocked out via destination-out. */\nfunction paintMatRound(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n const r = t;\n paintMatSharp(ctx, color, width, height);\n\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillStyle = '#000';\n drawCornerCutout(ctx, 0, 0, r, 'tl');\n drawCornerCutout(ctx, width, 0, r, 'tr');\n drawCornerCutout(ctx, 0, height, r, 'bl');\n drawCornerCutout(ctx, width, height, r, 'br');\n ctx.restore();\n}\n\nfunction drawCornerCutout(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n cx: number,\n cy: number,\n r: number,\n corner: 'tl' | 'tr' | 'bl' | 'br',\n): void {\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n switch (corner) {\n case 'tl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy + r, r, -Math.PI / 2, Math.PI, true);\n ctx.lineTo(cx, cy);\n break;\n case 'tr':\n ctx.lineTo(cx, cy + r);\n ctx.arc(cx - r, cy + r, r, 0, -Math.PI / 2, true);\n ctx.lineTo(cx, cy);\n break;\n case 'bl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy - r, r, Math.PI / 2, Math.PI, false);\n ctx.lineTo(cx, cy);\n break;\n case 'br':\n ctx.lineTo(cx - r, cy);\n ctx.arc(cx - r, cy - r, r, Math.PI / 2, 0, true);\n ctx.lineTo(cx, cy);\n break;\n }\n ctx.closePath();\n ctx.fill();\n}\n\n/** Line Single: thin stroked rect inset 5% from the edge. */\nfunction paintLineSingle(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const inset = Math.round(Math.min(width, height) * 0.05);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n // strokeRect centres on the path; offset by half-stroke to fit inside the inset.\n const half = stroke / 2;\n ctx.strokeRect(\n inset + half,\n inset + half,\n width - 2 * inset - stroke,\n height - 2 * inset - stroke,\n );\n ctx.restore();\n}\n\n/** Corner Hooks: four L-shapes at the corners. */\nfunction paintCornerHooks(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const arm = Math.round(Math.min(width, height) * 0.08);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n const inset = Math.round(Math.min(width, height) * 0.05);\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n ctx.lineCap = 'square';\n\n const half = stroke / 2;\n const drawHook = (vx: number, vy: number, horizDir: -1 | 1, vertDir: -1 | 1): void => {\n ctx.beginPath();\n ctx.moveTo(vx + horizDir * arm, vy);\n ctx.lineTo(vx, vy);\n ctx.lineTo(vx, vy + vertDir * arm);\n ctx.stroke();\n };\n\n drawHook(inset + half, inset + half, 1, 1);\n drawHook(width - inset - half, inset + half, -1, 1);\n drawHook(inset + half, height - inset - half, 1, -1);\n drawHook(width - inset - half, height - inset - half, -1, -1);\n ctx.restore();\n}\n\n/** Polaroid: 5% top/left/right + 18% bottom border; output canvas is larger than input. */\nfunction bakePolaroid(color: string, source: SourceImage): SourceImage {\n const shorter = Math.min(source.width, source.height);\n const top = Math.round(shorter * 0.05);\n const left = top;\n const right = top;\n const bottom = Math.round(shorter * 0.18);\n\n const outW = source.width + left + right;\n const outH = source.height + top + bottom;\n\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, outW, outH);\n ctx.drawImage(source.bitmap, left, top, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: outW,\n height: outH,\n mimeType: source.mimeType,\n };\n}\n\n/** 4% of the shorter dimension, floored at 4px. */\nfunction matThickness(width: number, height: number): number {\n return Math.max(4, Math.round(Math.min(width, height) * 0.04));\n}\n\n/** Output dimensions for a preset; equals input except for Polaroid. */\nexport function frameOutputSize(\n presetId: FramePresetId,\n inputWidth: number,\n inputHeight: number,\n): { width: number; height: number } {\n if (presetId !== 'polaroid') {\n return { width: inputWidth, height: inputHeight };\n }\n const shorter = Math.min(inputWidth, inputHeight);\n const top = Math.round(shorter * 0.05);\n const bottom = Math.round(shorter * 0.18);\n return {\n width: inputWidth + 2 * top,\n height: inputHeight + top + bottom,\n };\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport type { RedactRegion } from './state.js';\n\nexport interface RedactBakeInput {\n readonly regions: ReadonlyArray<RedactRegion>;\n}\n\n/** Paint every redaction region onto a copy of `source` in creation order. */\nexport async function bakeRedact(\n state: RedactBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.regions.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n // Pixelate/blur read from the post-source canvas, so overlapping\n // earlier regions are redacted again — \"redact wins\".\n for (const region of state.regions) {\n paintRegion(ctx, bake.canvas, region, source);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/**\n * Paint a single redaction region. `canvas` is needed because pixelate\n * and blur read from it and redraw a transformed copy in place.\n */\nexport function paintRegion(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n region: RedactRegion,\n source: SourceImage,\n): void {\n // Degenerate rects would crash `getImageData` on some engines.\n const w = Math.round(region.width);\n const h = Math.round(region.height);\n if (w < 1 || h < 1) return;\n const x = Math.round(region.x);\n const y = Math.round(region.y);\n\n switch (region.mode) {\n case 'solid':\n paintSolid(ctx, region, x, y, w, h);\n return;\n case 'pixelate':\n paintPixelate(ctx, canvas, source, x, y, w, h);\n return;\n case 'blur':\n paintBlur(ctx, canvas, source, x, y, w, h);\n return;\n }\n}\n\nfunction paintSolid(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n region: RedactRegion,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n ctx.save();\n ctx.fillStyle = region.color;\n ctx.fillRect(x, y, w, h);\n ctx.restore();\n}\n\n/**\n * Pixelate by downsampling to a small grid then upsampling with\n * nearest-neighbour. Capped at 8 cells on the longer side; floored at\n * 4 so tiny regions still read as chunky rather than as anti-aliasing.\n */\nfunction paintPixelate(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const longer = Math.max(w, h);\n const cells = Math.max(4, Math.round(8 * Math.min(1, longer / 240)));\n const gridW = Math.max(1, Math.round((w / longer) * cells));\n const gridH = Math.max(1, Math.round((h / longer) * cells));\n\n ctx.save();\n const small = createSmallCanvas(gridW, gridH);\n if (!small) {\n ctx.restore();\n return;\n }\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'low';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, gridW, gridH);\n\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(small.canvas, 0, 0, gridW, gridH, x, y, w, h);\n ctx.restore();\n}\n\n/** Downscale-and-back blur. Two passes (1/8 then 1/2) approximate a Gaussian. */\nfunction paintBlur(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const downscale = 1 / 8;\n const smallW = Math.max(1, Math.round(w * downscale));\n const smallH = Math.max(1, Math.round(h * downscale));\n\n const small = createSmallCanvas(smallW, smallH);\n if (!small) return;\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'high';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, smallW, smallH);\n\n const tinyW = Math.max(1, Math.round(smallW * 0.5));\n const tinyH = Math.max(1, Math.round(smallH * 0.5));\n const tiny = createSmallCanvas(tinyW, tinyH);\n if (!tiny) {\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(small.canvas, 0, 0, smallW, smallH, x, y, w, h);\n ctx.restore();\n return;\n }\n tiny.ctx.imageSmoothingEnabled = true;\n tiny.ctx.imageSmoothingQuality = 'high';\n tiny.ctx.drawImage(small.canvas, 0, 0, smallW, smallH, 0, 0, tinyW, tinyH);\n\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tiny.canvas, 0, 0, tinyW, tinyH, x, y, w, h);\n ctx.restore();\n}\n\ninterface SmallCanvas {\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n readonly ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n}\n\n/** Small intermediate canvas; prefers OffscreenCanvas, falls back to detached `<canvas>`. */\nfunction createSmallCanvas(width: number, height: number): SmallCanvas | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n try {\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d');\n if (ctx) return { canvas: offscreen, ctx };\n } catch {\n // Some engines throw on zero-size construction.\n }\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n return { canvas, ctx };\n}\n","/**\n * Redact state and mutators. Mirrors the annotate plugin's vocabulary\n * (id + kind, monotonic mint, replace/delete) so the selection layer\n * can be reused.\n */\n\nimport type { Rect } from '../../geometry/rect.js';\n\n/** `pixelate`, `blur`, or `solid` (flat fill). */\nexport type RedactMode = 'pixelate' | 'blur' | 'solid';\n\nexport const REDACT_MODES: readonly RedactMode[] = ['pixelate', 'blur', 'solid'];\n\nexport interface RedactRegion {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n /** Image-space rectangle. May be temporarily negative-extent mid-drag. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly mode: RedactMode;\n /** Used only when `mode === 'solid'`. CSS hex string. */\n readonly color: string;\n}\n\nexport interface RedactState {\n readonly regions: ReadonlyArray<RedactRegion>;\n /** Monotonic id source for new regions. Never decreases. */\n readonly nextRegionNumber: number;\n /** The currently-selected region id, or `null` when none. */\n readonly selectedId: string | null;\n /** The mode new regions are created with. Persists across selections. */\n readonly currentMode: RedactMode;\n /** Default fill colour for the `solid` mode. */\n readonly currentColor: string;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport const DEFAULT_REDACT_COLOR = '#000000';\nexport const DEFAULT_REDACT_MODE: RedactMode = 'pixelate';\n\nexport interface InitialRedactStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialRedactState(input: InitialRedactStateInput): RedactState {\n return {\n regions: [],\n nextRegionNumber: 1,\n selectedId: null,\n currentMode: DEFAULT_REDACT_MODE,\n currentColor: DEFAULT_REDACT_COLOR,\n imageSize: input.imageSize,\n };\n}\n\n/** Allocate a new region id; caller threads `nextRegionNumber` back into state. */\nexport function mintRegionId(state: RedactState): {\n id: string;\n nextRegionNumber: number;\n} {\n return {\n id: `r_${state.nextRegionNumber.toString(36)}`,\n nextRegionNumber: state.nextRegionNumber + 1,\n };\n}\n\nexport function addRegion(state: RedactState, region: RedactRegion): RedactState {\n return {\n ...state,\n regions: [...state.regions, region],\n selectedId: region.id,\n };\n}\n\nexport function replaceRegion(state: RedactState, region: RedactRegion): RedactState {\n let changed = false;\n const next = state.regions.map((existing) => {\n if (existing.id !== region.id) return existing;\n changed = true;\n return region;\n });\n if (!changed) return state;\n return { ...state, regions: next };\n}\n\nexport function deleteRegion(state: RedactState, id: string): RedactState {\n const next = state.regions.filter((region) => region.id !== id);\n if (next.length === state.regions.length) return state;\n return {\n ...state,\n regions: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\n/** Mirror every region across an axis of `dims`. */\nexport function mirrorRegions(\n state: RedactState,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return state;\n const next = state.regions.map((region) => {\n if (axis === 'horizontal') {\n return { ...region, x: dims.width - region.x - region.width };\n }\n return { ...region, y: dims.height - region.y - region.height };\n });\n return { ...state, regions: next, imageSize: dims };\n}\n\n/** Translate every region by `(dx, dy)`. Out-of-bounds regions are kept (bake clips on Save). */\nexport function translateRegions(\n state: RedactState,\n dx: number,\n dy: number,\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return { ...state, imageSize: dims };\n if (dx === 0 && dy === 0 && state.imageSize === dims) return state;\n const next = state.regions.map((region) => ({\n ...region,\n x: region.x + dx,\n y: region.y + dy,\n }));\n return { ...state, regions: next, imageSize: dims };\n}\n\n/**\n * Rotate every region `turns × 90°` CW around the image centre. Caller\n * passes post-rotation dims as `newDims`; pre-rotation dims come from\n * `state.imageSize`.\n */\nexport function rotateRegions(\n state: RedactState,\n turns: 0 | 1 | 2 | 3,\n newDims: { readonly width: number; readonly height: number },\n): RedactState {\n if (turns === 0) return { ...state, imageSize: newDims };\n if (state.regions.length === 0) return { ...state, imageSize: newDims };\n const oldW = state.imageSize.width;\n const oldH = state.imageSize.height;\n const next = state.regions.map((region) => {\n const { x, y, width, height } = region;\n if (turns === 1) {\n return { ...region, x: oldH - y - height, y: x, width: height, height: width };\n }\n if (turns === 2) {\n return {\n ...region,\n x: oldW - x - width,\n y: oldH - y - height,\n };\n }\n return { ...region, x: y, y: oldW - x - width, width: height, height: width };\n });\n return { ...state, regions: next, imageSize: newDims };\n}\n\nexport function selectRegion(state: RedactState, id: string | null): RedactState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function setCurrentMode(state: RedactState, mode: RedactMode): RedactState {\n if (state.currentMode === mode) return state;\n return { ...state, currentMode: mode };\n}\n\nexport function setCurrentColor(state: RedactState, color: string): RedactState {\n if (state.currentColor === color) return state;\n return { ...state, currentColor: color };\n}\n\n/** Update the mode of a region; `color` is preserved across mode flips. */\nexport function setRegionMode(state: RedactState, id: string, mode: RedactMode): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.mode === mode) return state;\n return replaceRegion(state, { ...region, mode });\n}\n\nexport function setRegionColor(state: RedactState, id: string, color: string): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.color === color) return state;\n return replaceRegion(state, { ...region, color });\n}\n\nexport function findRegion(state: RedactState, id: string | null): RedactRegion | undefined {\n if (id === null) return undefined;\n return state.regions.find((r) => r.id === id);\n}\n\nexport function selectedRegionOf(state: RedactState): RedactRegion | null {\n if (state.selectedId === null) return null;\n return state.regions.find((r) => r.id === state.selectedId) ?? null;\n}\n\n/** Normalise a region's rect so `width` / `height` are non-negative. */\nexport function normaliseRegionExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/** Default-sized region centred on the image. Used by the keyboard \"Insert\" path. */\nexport interface CreateCenteredRegionContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly mode: RedactMode;\n readonly color: string;\n readonly id: string;\n}\n\nexport function createCenteredRegion(ctx: CreateCenteredRegionContext): RedactRegion {\n const { imageSize, mode, color, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n return {\n id,\n x,\n y,\n width: size,\n height: size,\n mode,\n color,\n };\n}\n\n/** Clamp regions against new bounds; drop regions fully outside. \"Clamp, don't reset\". */\nexport function revalidateAgainstBounds(\n state: RedactState,\n bounds: { width: number; height: number },\n): RedactState {\n if (state.imageSize.width === bounds.width && state.imageSize.height === bounds.height) {\n return state;\n }\n const kept: RedactRegion[] = [];\n for (const region of state.regions) {\n if (\n region.x + region.width <= 0 ||\n region.y + region.height <= 0 ||\n region.x >= bounds.width ||\n region.y >= bounds.height\n ) {\n continue;\n }\n kept.push(clampRegion(region, bounds));\n }\n const selectedDropped = state.selectedId !== null && !kept.some((r) => r.id === state.selectedId);\n return {\n ...state,\n regions: kept,\n imageSize: { width: bounds.width, height: bounds.height },\n selectedId: selectedDropped ? null : state.selectedId,\n };\n}\n\nfunction clampRegion(\n region: RedactRegion,\n bounds: { width: number; height: number },\n): RedactRegion {\n const x = Math.max(0, region.x);\n const y = Math.max(0, region.y);\n const right = Math.min(bounds.width, region.x + region.width);\n const bottom = Math.min(bounds.height, region.y + region.height);\n return {\n ...region,\n x,\n y,\n width: Math.max(0, right - x),\n height: Math.max(0, bottom - y),\n };\n}\n\n/** Bounding-box shape used by the selection layer; matches `Rect`. */\nexport function regionBoundingBox(region: RedactRegion): Rect {\n return { x: region.x, y: region.y, width: region.width, height: region.height };\n}\n","/**\n * Per-axis scale factors. `lockAspect` makes the editor keep `scaleX === scaleY`.\n * Output pixels are computed at bake time as `round(upstream * scale)`,\n * clamped to `[MIN_DIMENSION, MAX_DIMENSION]`.\n */\nexport interface ResizeState {\n readonly scaleX: number;\n readonly scaleY: number;\n readonly lockAspect: boolean;\n}\n\nexport const MAX_DIMENSION = 8000;\nexport const MIN_DIMENSION = 1;\n\nexport function initialResizeState(): ResizeState {\n return { scaleX: 1, scaleY: 1, lockAspect: true };\n}\n\nexport function isResizeNoOp(state: ResizeState): boolean {\n return Math.abs(state.scaleX - 1) < 1e-9 && Math.abs(state.scaleY - 1) < 1e-9;\n}\n\n/** Integer output dimensions for an upstream image, clamped per axis. */\nexport function resolveOutputSize(\n state: ResizeState,\n upstream: { readonly width: number; readonly height: number },\n): { width: number; height: number } {\n const width = clampInt(Math.round(upstream.width * state.scaleX));\n const height = clampInt(Math.round(upstream.height * state.scaleY));\n return { width, height };\n}\n\n/** Set width via a pixel value; with `lockAspect` the vertical scale follows. */\nexport function setWidthPx(\n state: ResizeState,\n widthPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.width <= 0) return state;\n const target = clampInt(Math.round(widthPx));\n const scaleX = target / upstream.width;\n const scaleY = state.lockAspect ? scaleX : state.scaleY;\n return { ...state, scaleX, scaleY };\n}\n\nexport function setHeightPx(\n state: ResizeState,\n heightPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.height <= 0) return state;\n const target = clampInt(Math.round(heightPx));\n const scaleY = target / upstream.height;\n const scaleX = state.lockAspect ? scaleY : state.scaleX;\n return { ...state, scaleX, scaleY };\n}\n\n/** Uniform percentage change; always touches both axes regardless of `lockAspect`. */\nexport function setPercent(state: ResizeState, percent: number): ResizeState {\n const scale = clampScaleForPercent(percent / 100);\n return { ...state, scaleX: scale, scaleY: scale };\n}\n\nexport function setLockAspect(state: ResizeState, locked: boolean): ResizeState {\n if (state.lockAspect === locked) return state;\n if (!locked) return { ...state, lockAspect: false };\n // Average the two scales so neither axis arbitrarily wins on lock.\n const merged = (state.scaleX + state.scaleY) / 2;\n return { scaleX: merged, scaleY: merged, lockAspect: true };\n}\n\n/** Percent display value; when axes differ, returns the larger scale. */\nexport function effectivePercent(state: ResizeState): number {\n const max = Math.max(state.scaleX, state.scaleY);\n return Math.round(max * 1000) / 10;\n}\n\nfunction clampInt(n: number): number {\n if (!Number.isFinite(n)) return MIN_DIMENSION;\n return Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, Math.trunc(n)));\n}\n\nfunction clampScaleForPercent(scale: number): number {\n if (!Number.isFinite(scale) || scale <= 0) return 0.01;\n return Math.max(0.01, Math.min(scale, MAX_DIMENSION));\n}\n","import { type BakeCanvas, createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { isResizeNoOp, type ResizeState, resolveOutputSize } from './state.js';\n\n/**\n * Resize `source` to the dimensions implied by `state`. Downscales > 2×\n * run a halving pyramid first so the bilinear-ish `drawImage` scaler\n * doesn't alias.\n */\nexport async function bakeResize(state: ResizeState, source: SourceImage): Promise<SourceImage> {\n if (isResizeNoOp(state)) return source;\n const { width: targetW, height: targetH } = resolveOutputSize(state, source);\n if (targetW <= 0 || targetH <= 0) return source;\n\n const halvings = countHalvingsNeeded(source.width, source.height, targetW, targetH);\n\n let current: { bitmap: CanvasImageSource; width: number; height: number } = {\n bitmap: source.bitmap,\n width: source.width,\n height: source.height,\n };\n\n // Each halving writes into its own canvas so the source is never reused as destination.\n const intermediates: BakeCanvas[] = [];\n for (let i = 0; i < halvings; i++) {\n const stepW = Math.max(targetW, Math.floor(current.width / 2));\n const stepH = Math.max(targetH, Math.floor(current.height / 2));\n const step = drawScaled(current, stepW, stepH);\n intermediates.push(step);\n current = { bitmap: step.canvas, width: stepW, height: stepH };\n }\n\n const final = drawScaled(current, targetW, targetH);\n\n intermediates.length = 0;\n\n return {\n bitmap: final.canvas,\n width: targetW,\n height: targetH,\n mimeType: source.mimeType,\n };\n}\n\nfunction countHalvingsNeeded(srcW: number, srcH: number, targetW: number, targetH: number): number {\n let w = srcW;\n let h = srcH;\n let count = 0;\n // Defensive cap so a malformed input can't spin forever.\n while ((w / 2 > targetW || h / 2 > targetH) && count < 16) {\n w = Math.floor(w / 2);\n h = Math.floor(h / 2);\n count += 1;\n }\n return count;\n}\n\nfunction drawScaled(\n current: { bitmap: CanvasImageSource; width: number; height: number },\n outW: number,\n outH: number,\n): BakeCanvas {\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(current.bitmap, 0, 0, current.width, current.height, 0, 0, outW, outH);\n return bake;\n}\n","import type { Size } from '../../geometry/rect.js';\n\n/**\n * Largest axis-aligned rect of the source's aspect ratio that fits\n * inside the source rotated by `angleRad`. Used by free-angle rotation\n * to avoid transparent corners.\n *\n * The two binding constraints reduce to\n * `2x ≤ W² / (W|cosθ| + H|sinθ|)`\n * `2x ≤ W·H / (W|sinθ| + H|cosθ|)`\n * — the inscribed half-width is the smaller bound. Works on absolute\n * sin/cos so the result is symmetric in the sign of the angle.\n */\nexport function largestInscribedRect(source: Size, angleRad: number): Size {\n const width = source.width;\n const height = source.height;\n if (width <= 0 || height <= 0) return { width: 0, height: 0 };\n\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n\n const denomA = width * c + height * s;\n const denomD = width * s + height * c;\n const capA = denomA > EPSILON ? (width * width) / denomA : Number.POSITIVE_INFINITY;\n const capD = denomD > EPSILON ? (width * height) / denomD : Number.POSITIVE_INFINITY;\n\n const outWidth = Math.min(capA, capD);\n const outHeight = (outWidth * height) / width;\n return { width: outWidth, height: outHeight };\n}\n\nconst EPSILON = 1e-9;\n","/**\n * Independent quarter-turns (lossless 90° CW) and free angle (±45°).\n * Bake applies `quarterTurns * 90° + freeAngle` so a press of \"rotate 90°\"\n * doesn't reset a straighten correction and vice versa.\n */\nexport interface RotateState {\n readonly quarterTurns: 0 | 1 | 2 | 3;\n /** Free-angle offset in degrees. Range: [-45, 45]. */\n readonly freeAngle: number;\n}\n\nexport const FREE_ANGLE_MIN = -45;\nexport const FREE_ANGLE_MAX = 45;\nexport const FREE_ANGLE_STEP = 0.1;\n\nexport function initialRotateState(): RotateState {\n return { quarterTurns: 0, freeAngle: 0 };\n}\n\nexport function rotateClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 1) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function rotateCounterClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 3) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function setFreeAngle(state: RotateState, angleDeg: number): RotateState {\n const clamped = clamp(angleDeg, FREE_ANGLE_MIN, FREE_ANGLE_MAX);\n // Snap to 0.1° to match the slider step and avoid sub-step float noise.\n const snapped = Math.round(clamped * 10) / 10;\n return { ...state, freeAngle: snapped };\n}\n\nexport function isRotateNoOp(state: RotateState): boolean {\n return state.quarterTurns === 0 && Math.abs(state.freeAngle) < 1e-6;\n}\n\n/** Effective rotation applied during bake, in degrees clockwise. */\nexport function effectiveAngleDeg(state: RotateState): number {\n return state.quarterTurns * 90 + state.freeAngle;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { largestInscribedRect } from './inscribe.js';\nimport { effectiveAngleDeg, isRotateNoOp, type RotateState } from './state.js';\n\n/**\n * Apply rotation = quarter-turns + free-angle in one `drawImage`. Free\n * angles auto-crop to the largest same-aspect rect inside the rotated\n * bounding box so transparent corners stay out of the output.\n */\nexport async function bakeRotate(state: RotateState, source: SourceImage): Promise<SourceImage> {\n if (isRotateNoOp(state)) return source;\n\n const angleDeg = effectiveAngleDeg(state);\n const angleRad = (angleDeg * Math.PI) / 180;\n\n const sub90Deg = angleDeg - state.quarterTurns * 90; // ∈ [-45, 45]\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n let outWidth: number;\n let outHeight: number;\n\n if (isQuarterOnly) {\n if (state.quarterTurns === 1 || state.quarterTurns === 3) {\n outWidth = source.height;\n outHeight = source.width;\n } else {\n outWidth = source.width;\n outHeight = source.height;\n }\n } else {\n const inscribed = largestInscribedRect(source, angleRad);\n outWidth = Math.max(1, Math.round(inscribed.width));\n outHeight = Math.max(1, Math.round(inscribed.height));\n }\n\n const bake = createBakeCanvas(outWidth, outHeight);\n const ctx = getBakeContext2D(bake);\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.translate(outWidth / 2, outHeight / 2);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -source.width / 2, -source.height / 2);\n\n return {\n bitmap: bake.canvas,\n width: outWidth,\n height: outHeight,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Minimal observable store. Subscribers fire after every `set`/`update`;\n * the store does no equality check — subscribers diff if they care.\n */\nexport type StoreListener<T> = (state: T, previous: T) => void;\nexport type Unsubscribe = () => void;\n\nexport interface Store<T> {\n get(): T;\n set(next: Partial<T>): void;\n update(updater: (state: T) => Partial<T>): void;\n subscribe(listener: StoreListener<T>): Unsubscribe;\n}\n\nexport function createStore<T extends object>(initial: T): Store<T> {\n let state = initial;\n const listeners = new Set<StoreListener<T>>();\n\n function notify(previous: T): void {\n for (const listener of [...listeners]) {\n try {\n listener(state, previous);\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n return {\n get(): T {\n return state;\n },\n set(next: Partial<T>): void {\n const previous = state;\n state = { ...state, ...next };\n notify(previous);\n },\n update(updater: (state: T) => Partial<T>): void {\n const previous = state;\n state = { ...state, ...updater(state) };\n notify(previous);\n },\n subscribe(listener: StoreListener<T>): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"mappings":"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"}