@tbib-image-viewer/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.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +86 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -145,9 +145,14 @@ declare class AnnotationStore {
|
|
|
145
145
|
* at original native resolution.
|
|
146
146
|
*/
|
|
147
147
|
declare function renderToOffscreenCanvas(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<HTMLCanvasElement>;
|
|
148
|
+
declare function renderToOffscreenCanvasWithCleanup(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<{
|
|
149
|
+
canvas: HTMLCanvasElement;
|
|
150
|
+
cleanup?: () => void;
|
|
151
|
+
}>;
|
|
148
152
|
/**
|
|
149
153
|
* Triggers a browser download of the image (with or without annotations).
|
|
154
|
+
* Guarantees zero unhandled SecurityError on tainted canvases with automatic fallback.
|
|
150
155
|
*/
|
|
151
156
|
declare function downloadAnnotatedImage(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<void>;
|
|
152
157
|
|
|
153
|
-
export { AnnotationStore, type AnnotationStroke, DEFAULT_TRANSFORM, type DrawTool, type ExportFormat, type ExportOptions, type GalleryItem, type HistoryState, type ImageDimensions, type InteractionMode, MAX_SCALE, MIN_SCALE, type NormalizedPoint, type ScreenPoint, type SlideAnnotations, type TransformState, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, screenToNormalizedImage };
|
|
158
|
+
export { AnnotationStore, type AnnotationStroke, DEFAULT_TRANSFORM, type DrawTool, type ExportFormat, type ExportOptions, type GalleryItem, type HistoryState, type ImageDimensions, type InteractionMode, MAX_SCALE, MIN_SCALE, type NormalizedPoint, type ScreenPoint, type SlideAnnotations, type TransformState, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, renderToOffscreenCanvasWithCleanup, screenToNormalizedImage };
|
package/dist/index.d.ts
CHANGED
|
@@ -145,9 +145,14 @@ declare class AnnotationStore {
|
|
|
145
145
|
* at original native resolution.
|
|
146
146
|
*/
|
|
147
147
|
declare function renderToOffscreenCanvas(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<HTMLCanvasElement>;
|
|
148
|
+
declare function renderToOffscreenCanvasWithCleanup(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<{
|
|
149
|
+
canvas: HTMLCanvasElement;
|
|
150
|
+
cleanup?: () => void;
|
|
151
|
+
}>;
|
|
148
152
|
/**
|
|
149
153
|
* Triggers a browser download of the image (with or without annotations).
|
|
154
|
+
* Guarantees zero unhandled SecurityError on tainted canvases with automatic fallback.
|
|
150
155
|
*/
|
|
151
156
|
declare function downloadAnnotatedImage(imageSource: string | HTMLImageElement, strokes: AnnotationStroke[], options?: ExportOptions): Promise<void>;
|
|
152
157
|
|
|
153
|
-
export { AnnotationStore, type AnnotationStroke, DEFAULT_TRANSFORM, type DrawTool, type ExportFormat, type ExportOptions, type GalleryItem, type HistoryState, type ImageDimensions, type InteractionMode, MAX_SCALE, MIN_SCALE, type NormalizedPoint, type ScreenPoint, type SlideAnnotations, type TransformState, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, screenToNormalizedImage };
|
|
158
|
+
export { AnnotationStore, type AnnotationStroke, DEFAULT_TRANSFORM, type DrawTool, type ExportFormat, type ExportOptions, type GalleryItem, type HistoryState, type ImageDimensions, type InteractionMode, MAX_SCALE, MIN_SCALE, type NormalizedPoint, type ScreenPoint, type SlideAnnotations, type TransformState, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, renderToOffscreenCanvasWithCleanup, screenToNormalizedImage };
|
package/dist/index.js
CHANGED
|
@@ -276,23 +276,62 @@ var AnnotationStore = class {
|
|
|
276
276
|
};
|
|
277
277
|
|
|
278
278
|
// src/export/canvas.ts
|
|
279
|
-
async function
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
async function loadCleanImage(imageSource) {
|
|
280
|
+
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
281
|
+
try {
|
|
282
|
+
const res = await fetch(src, { mode: "cors" });
|
|
283
|
+
if (res.ok) {
|
|
284
|
+
const blob = await res.blob();
|
|
285
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
286
|
+
const img = new Image();
|
|
287
|
+
await new Promise((resolve, reject) => {
|
|
288
|
+
img.onload = () => resolve();
|
|
289
|
+
img.onerror = reject;
|
|
290
|
+
img.src = blobUrl;
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
img,
|
|
294
|
+
cleanup: () => URL.revokeObjectURL(blobUrl)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
const img = new Image();
|
|
284
301
|
img.crossOrigin = "anonymous";
|
|
302
|
+
const sep = src.includes("?") ? "&" : "?";
|
|
303
|
+
const corsSrc = `${src}${sep}_export=${Date.now()}`;
|
|
285
304
|
await new Promise((resolve, reject) => {
|
|
286
305
|
img.onload = () => resolve();
|
|
287
|
-
img.onerror =
|
|
288
|
-
img.src =
|
|
306
|
+
img.onerror = reject;
|
|
307
|
+
img.src = corsSrc;
|
|
289
308
|
});
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
return { img };
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
if (typeof imageSource !== "string" && imageSource.naturalWidth > 0) {
|
|
313
|
+
return { img: imageSource };
|
|
292
314
|
}
|
|
315
|
+
const fallbackImg = new Image();
|
|
316
|
+
await new Promise((resolve, reject) => {
|
|
317
|
+
fallbackImg.onload = () => resolve();
|
|
318
|
+
fallbackImg.onerror = reject;
|
|
319
|
+
fallbackImg.src = src;
|
|
320
|
+
});
|
|
321
|
+
return { img: fallbackImg };
|
|
322
|
+
}
|
|
323
|
+
async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
324
|
+
const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);
|
|
325
|
+
cleanup?.();
|
|
326
|
+
return canvas;
|
|
327
|
+
}
|
|
328
|
+
async function renderToOffscreenCanvasWithCleanup(imageSource, strokes, options = {}) {
|
|
329
|
+
const { includeAnnotations = true, backgroundColor } = options;
|
|
330
|
+
const { img, cleanup } = await loadCleanImage(imageSource);
|
|
293
331
|
const naturalWidth = img.naturalWidth || img.width;
|
|
294
332
|
const naturalHeight = img.naturalHeight || img.height;
|
|
295
333
|
if (naturalWidth === 0 || naturalHeight === 0) {
|
|
334
|
+
cleanup?.();
|
|
296
335
|
throw new Error("Invalid image dimensions: width or height is 0");
|
|
297
336
|
}
|
|
298
337
|
const canvas = document.createElement("canvas");
|
|
@@ -300,6 +339,7 @@ async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
|
300
339
|
canvas.height = naturalHeight;
|
|
301
340
|
const ctx = canvas.getContext("2d");
|
|
302
341
|
if (!ctx) {
|
|
342
|
+
cleanup?.();
|
|
303
343
|
throw new Error("Unable to obtain 2D canvas context for export");
|
|
304
344
|
}
|
|
305
345
|
if (backgroundColor) {
|
|
@@ -346,7 +386,7 @@ async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
|
346
386
|
ctx.restore();
|
|
347
387
|
}
|
|
348
388
|
}
|
|
349
|
-
return canvas;
|
|
389
|
+
return { canvas, cleanup };
|
|
350
390
|
}
|
|
351
391
|
async function downloadAnnotatedImage(imageSource, strokes, options = {}) {
|
|
352
392
|
const {
|
|
@@ -354,38 +394,45 @@ async function downloadAnnotatedImage(imageSource, strokes, options = {}) {
|
|
|
354
394
|
quality = 0.95,
|
|
355
395
|
fileName = `tbib-annotated-image-${Date.now()}.${format === "image/jpeg" ? "jpg" : format === "image/webp" ? "webp" : "png"}`
|
|
356
396
|
} = options;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return new Promise((resolve, reject) => {
|
|
360
|
-
canvas.toBlob(
|
|
361
|
-
(blob) => {
|
|
362
|
-
if (!blob) {
|
|
363
|
-
reject(new Error("Failed to generate image blob from canvas"));
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
const url = URL.createObjectURL(blob);
|
|
367
|
-
const link = document.createElement("a");
|
|
368
|
-
link.href = url;
|
|
369
|
-
link.download = fileName;
|
|
370
|
-
document.body.appendChild(link);
|
|
371
|
-
link.click();
|
|
372
|
-
document.body.removeChild(link);
|
|
373
|
-
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
374
|
-
resolve();
|
|
375
|
-
},
|
|
376
|
-
format,
|
|
377
|
-
quality
|
|
378
|
-
);
|
|
379
|
-
});
|
|
380
|
-
} catch (err) {
|
|
381
|
-
console.warn("Offscreen canvas render encountered an issue, falling back to direct asset download:", err);
|
|
382
|
-
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
397
|
+
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
398
|
+
const directDownload = (downloadUrl, name) => {
|
|
383
399
|
const link = document.createElement("a");
|
|
384
|
-
link.href =
|
|
385
|
-
link.download =
|
|
400
|
+
link.href = downloadUrl;
|
|
401
|
+
link.download = name;
|
|
386
402
|
document.body.appendChild(link);
|
|
387
403
|
link.click();
|
|
388
404
|
document.body.removeChild(link);
|
|
405
|
+
};
|
|
406
|
+
try {
|
|
407
|
+
const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);
|
|
408
|
+
await new Promise((resolve) => {
|
|
409
|
+
try {
|
|
410
|
+
canvas.toBlob(
|
|
411
|
+
(blob) => {
|
|
412
|
+
cleanup?.();
|
|
413
|
+
if (!blob) {
|
|
414
|
+
directDownload(src, fileName);
|
|
415
|
+
resolve();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const url = URL.createObjectURL(blob);
|
|
419
|
+
directDownload(url, fileName);
|
|
420
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
421
|
+
resolve();
|
|
422
|
+
},
|
|
423
|
+
format,
|
|
424
|
+
quality
|
|
425
|
+
);
|
|
426
|
+
} catch (taintedErr) {
|
|
427
|
+
cleanup?.();
|
|
428
|
+
console.warn("Canvas was tainted by cross-origin security rules, downloading direct asset:", taintedErr);
|
|
429
|
+
directDownload(src, fileName);
|
|
430
|
+
resolve();
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
} catch (err) {
|
|
434
|
+
console.warn("Offscreen canvas render encountered an issue, falling back to direct asset download:", err);
|
|
435
|
+
directDownload(src, fileName);
|
|
389
436
|
}
|
|
390
437
|
}
|
|
391
438
|
|
|
@@ -402,6 +449,7 @@ exports.isPointNearStroke = isPointNearStroke;
|
|
|
402
449
|
exports.normalizedImageToScreen = normalizedImageToScreen;
|
|
403
450
|
exports.pointsToSvgPath = pointsToSvgPath;
|
|
404
451
|
exports.renderToOffscreenCanvas = renderToOffscreenCanvas;
|
|
452
|
+
exports.renderToOffscreenCanvasWithCleanup = renderToOffscreenCanvasWithCleanup;
|
|
405
453
|
exports.screenToNormalizedImage = screenToNormalizedImage;
|
|
406
454
|
//# sourceMappingURL=index.js.map
|
|
407
455
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/transform.ts","../src/annotations/smooth.ts","../src/annotations/store.ts","../src/export/canvas.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ImageDimensions, NormalizedPoint, ScreenPoint, TransformState } from \"../types\";\n\nexport const DEFAULT_TRANSFORM: TransformState = {\n scale: 1,\n translateX: 0,\n translateY: 0,\n rotate: 0,\n};\n\nexport const MIN_SCALE = 0.5;\nexport const MAX_SCALE = 8.0;\n\n/**\n * Clamps scale between min and max allowed bounds.\n */\nexport function clampScale(scale: number, min = MIN_SCALE, max = MAX_SCALE): number {\n return Math.min(Math.max(scale, min), max);\n}\n\n/**\n * Converts a screen/viewport coordinate (e.g. mouse or touch event)\n * into a normalized image coordinate (0 to 1) based on the image bounding box\n * and current scale/pan/rotation transform.\n */\nexport function screenToNormalizedImage(\n screenPt: ScreenPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): NormalizedPoint | null {\n if (imageDimensions.width <= 0 || imageDimensions.height <= 0) return null;\n\n // 1. Point relative to container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n let dx = screenPt.x - centerX - transform.translateX;\n let dy = screenPt.y - centerY - transform.translateY;\n\n // 2. Undo rotation\n const rad = (-transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const unrotatedX = dx * cos - dy * sin;\n const unrotatedY = dx * sin + dy * cos;\n\n // 3. Undo scale\n const scaledX = unrotatedX / transform.scale;\n const scaledY = unrotatedY / transform.scale;\n\n // 4. Compute displayed image fitted bounds at scale = 1\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // 5. Convert to normalized 0..1 coordinates relative to fitted image box\n const normX = (scaledX + fittedW / 2) / fittedW;\n const normY = (scaledY + fittedH / 2) / fittedH;\n\n return {\n x: Math.max(0, Math.min(1, normX)),\n y: Math.max(0, Math.min(1, normY)),\n };\n}\n\n/**\n * Converts a normalized image coordinate (0 to 1) to screen/pixel coordinates\n * inside the given container.\n */\nexport function normalizedImageToScreen(\n normPt: NormalizedPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): ScreenPoint {\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // Local unscaled, unrotated position relative to center\n const localX = normPt.x * fittedW - fittedW / 2;\n const localY = normPt.y * fittedH - fittedH / 2;\n\n // Apply scale\n const scaledX = localX * transform.scale;\n const scaledY = localY * transform.scale;\n\n // Apply rotation\n const rad = (transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedX = scaledX * cos - scaledY * sin;\n const rotatedY = scaledX * sin + scaledY * cos;\n\n // Apply translation and container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n return {\n x: centerX + transform.translateX + rotatedX,\n y: centerY + transform.translateY + rotatedY,\n };\n}\n\n/**\n * Computes fitted size of an image inside container maintaining aspect ratio.\n */\nexport function getFittedImageBounds(\n containerW: number,\n containerH: number,\n imgW: number,\n imgH: number\n): { width: number; height: number } {\n if (imgW <= 0 || imgH <= 0 || containerW <= 0 || containerH <= 0) {\n return { width: 0, height: 0 };\n }\n const aspect = imgW / imgH;\n const containerAspect = containerW / containerH;\n\n if (containerAspect > aspect) {\n const height = containerH;\n return { width: height * aspect, height };\n } else {\n const width = containerW;\n return { width, height: width / aspect };\n }\n}\n","import type { NormalizedPoint } from \"../types\";\n\n/**\n * Converts a list of normalized points into a smooth SVG path definition\n * using quadratic Bézier midpoint interpolation.\n */\nexport function pointsToSvgPath(\n points: NormalizedPoint[],\n width: number,\n height: number\n): string {\n if (points.length === 0) return \"\";\n if (points.length === 1) {\n const p = points[0];\n const px = p.x * width;\n const py = p.y * height;\n return `M ${px} ${py} L ${px + 0.1} ${py + 0.1}`;\n }\n\n const p0 = points[0];\n let d = `M ${p0.x * width} ${p0.y * height}`;\n\n for (let i = 1; i < points.length - 1; i++) {\n const current = points[i];\n const next = points[i + 1];\n\n const cx = (current.x * width + next.x * width) / 2;\n const cy = (current.y * height + next.y * height) / 2;\n\n d += ` Q ${current.x * width} ${current.y * height}, ${cx} ${cy}`;\n }\n\n const last = points[points.length - 1];\n d += ` L ${last.x * width} ${last.y * height}`;\n\n return d;\n}\n\n/**\n * Calculates squared Euclidean distance between two points in normalized coordinates.\n */\nexport function distanceSq(p1: NormalizedPoint, p2: NormalizedPoint): number {\n const dx = p1.x - p2.x;\n const dy = p1.y - p2.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Checks if a test point in normalized coordinates is close to any point in the stroke\n * within an interaction threshold (used for eraser hit detection).\n */\nexport function isPointNearStroke(\n testPt: NormalizedPoint,\n points: NormalizedPoint[],\n thresholdNorm: number\n): boolean {\n const thresholdSq = thresholdNorm * thresholdNorm;\n for (let i = 0; i < points.length; i++) {\n if (distanceSq(testPt, points[i]) <= thresholdSq) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Converts hex color (#rrggbb or #rgb) to an rgba string with alpha.\n */\nexport function hexToRgba(hex: string, alpha = 1): string {\n if (hex.startsWith(\"rgba\")) return hex;\n const clean = hex.trim().replace(\"#\", \"\");\n if (clean.length === 3) {\n const r = parseInt(clean[0] + clean[0], 16);\n const g = parseInt(clean[1] + clean[1], 16);\n const b = parseInt(clean[2] + clean[2], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n if (clean.length === 6) {\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hex;\n}\n","import type { AnnotationStroke, SlideAnnotations } from \"../types\";\n\nexport interface HistoryState {\n past: AnnotationStroke[][];\n present: AnnotationStroke[];\n future: AnnotationStroke[][];\n}\n\nexport class AnnotationStore {\n private slides = new Map<string, HistoryState>();\n private listeners = new Set<(slideId: string, strokes: AnnotationStroke[]) => void>();\n\n constructor(initialData?: SlideAnnotations[]) {\n if (initialData) {\n for (const item of initialData) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n }\n }\n }\n\n private getOrCreate(slideId: string): HistoryState {\n let state = this.slides.get(slideId);\n if (!state) {\n state = {\n past: [],\n present: [],\n future: [],\n };\n this.slides.set(slideId, state);\n }\n return state;\n }\n\n public getStrokes(slideId: string): AnnotationStroke[] {\n return this.getOrCreate(slideId).present;\n }\n\n public canUndo(slideId: string): boolean {\n return this.getOrCreate(slideId).past.length > 0;\n }\n\n public canRedo(slideId: string): boolean {\n return this.getOrCreate(slideId).future.length > 0;\n }\n\n public addStroke(slideId: string, stroke: AnnotationStroke): void {\n const state = this.getOrCreate(slideId);\n state.past.push([...state.present]);\n state.present = [...state.present, stroke];\n state.future = []; // Clear redo tree on new action\n this.notify(slideId);\n }\n\n public removeStroke(slideId: string, strokeId: string): void {\n const state = this.getOrCreate(slideId);\n const filtered = state.present.filter((s) => s.id !== strokeId);\n if (filtered.length !== state.present.length) {\n state.past.push([...state.present]);\n state.present = filtered;\n state.future = [];\n this.notify(slideId);\n }\n }\n\n public removeStrokesNear(\n slideId: string,\n testPt: { x: number; y: number },\n thresholdNorm: number,\n isPointNearStrokeFn: (pt: { x: number; y: number }, pts: { x: number; y: number }[], thresh: number) => boolean\n ): boolean {\n const state = this.getOrCreate(slideId);\n const toKeep: AnnotationStroke[] = [];\n let removedAny = false;\n\n for (const stroke of state.present) {\n if (isPointNearStrokeFn(testPt, stroke.points, thresholdNorm)) {\n removedAny = true;\n } else {\n toKeep.push(stroke);\n }\n }\n\n if (removedAny) {\n state.past.push([...state.present]);\n state.present = toKeep;\n state.future = [];\n this.notify(slideId);\n return true;\n }\n return false;\n }\n\n public clear(slideId: string): void {\n const state = this.getOrCreate(slideId);\n if (state.present.length === 0) return;\n state.past.push([...state.present]);\n state.present = [];\n state.future = [];\n this.notify(slideId);\n }\n\n public undo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.past.length === 0) return false;\n\n const previous = state.past.pop()!;\n state.future.unshift([...state.present]);\n state.present = previous;\n this.notify(slideId);\n return true;\n }\n\n public redo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.future.length === 0) return false;\n\n const next = state.future.shift()!;\n state.past.push([...state.present]);\n state.present = next;\n this.notify(slideId);\n return true;\n }\n\n public subscribe(listener: (slideId: string, strokes: AnnotationStroke[]) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(slideId: string): void {\n const strokes = this.getStrokes(slideId);\n for (const listener of this.listeners) {\n listener(slideId, strokes);\n }\n }\n\n public exportJSON(): SlideAnnotations[] {\n const result: SlideAnnotations[] = [];\n for (const [slideId, state] of this.slides.entries()) {\n if (state.present.length > 0) {\n result.push({\n slideId,\n strokes: [...state.present],\n });\n }\n }\n return result;\n }\n\n public importJSON(data: SlideAnnotations[]): void {\n for (const item of data) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n this.notify(item.slideId);\n }\n }\n}\n","import type { AnnotationStroke, ExportOptions } from \"../types\";\nimport { hexToRgba } from \"../annotations/smooth\";\n\n/**\n * Loads an image from a URL or source element and renders it\n * along with its annotations onto an off-screen HTMLCanvasElement\n * at original native resolution.\n */\nexport async function renderToOffscreenCanvas(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<HTMLCanvasElement> {\n const { includeAnnotations = true, backgroundColor } = options;\n\n // 1. Resolve source HTMLImageElement\n let img: HTMLImageElement;\n if (typeof imageSource === \"string\") {\n img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = (e) => reject(new Error(`Failed to load image from source: ${imageSource}`));\n img.src = imageSource;\n });\n } else {\n img = imageSource;\n }\n\n const naturalWidth = img.naturalWidth || img.width;\n const naturalHeight = img.naturalHeight || img.height;\n\n if (naturalWidth === 0 || naturalHeight === 0) {\n throw new Error(\"Invalid image dimensions: width or height is 0\");\n }\n\n // 2. Setup high-res canvas matching native dimensions\n const canvas = document.createElement(\"canvas\");\n canvas.width = naturalWidth;\n canvas.height = naturalHeight;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Unable to obtain 2D canvas context for export\");\n }\n\n // 3. Optional background color\n if (backgroundColor) {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n }\n\n // 4. Draw base image\n ctx.drawImage(img, 0, 0, naturalWidth, naturalHeight);\n\n // 5. Burn in annotations if requested\n if (includeAnnotations && strokes.length > 0) {\n for (const stroke of strokes) {\n if (stroke.points.length === 0) continue;\n\n ctx.save();\n ctx.beginPath();\n ctx.lineCap = \"round\";\n ctx.lineJoin = \"round\";\n\n // Scale stroke width relative to the maximum dimension of the image\n // Stroke width in model is relative to fitted display (e.g. 3px on 800px)\n // We scale it proportionally so it looks identical to what user saw\n const strokeScale = Math.max(naturalWidth, naturalHeight) / 1000;\n ctx.lineWidth = Math.max(1, stroke.width * strokeScale);\n\n if (stroke.tool === \"highlighter\") {\n ctx.globalAlpha = stroke.opacity || 0.35;\n ctx.strokeStyle = hexToRgba(stroke.color, 1);\n // Multiply blend looks most natural on medical illustrations & documents\n ctx.globalCompositeOperation = \"multiply\";\n } else {\n ctx.globalAlpha = stroke.opacity || 1.0;\n ctx.strokeStyle = stroke.color;\n ctx.globalCompositeOperation = \"source-over\";\n }\n\n const p0 = stroke.points[0];\n const startX = p0.x * naturalWidth;\n const startY = p0.y * naturalHeight;\n ctx.moveTo(startX, startY);\n\n if (stroke.points.length === 1) {\n ctx.lineTo(startX + 0.5, startY + 0.5);\n } else {\n for (let i = 1; i < stroke.points.length - 1; i++) {\n const pt = stroke.points[i];\n const next = stroke.points[i + 1];\n const cx = (pt.x * naturalWidth + next.x * naturalWidth) / 2;\n const cy = (pt.y * naturalHeight + next.y * naturalHeight) / 2;\n ctx.quadraticCurveTo(pt.x * naturalWidth, pt.y * naturalHeight, cx, cy);\n }\n const last = stroke.points[stroke.points.length - 1];\n ctx.lineTo(last.x * naturalWidth, last.y * naturalHeight);\n }\n\n ctx.stroke();\n ctx.restore();\n }\n }\n\n return canvas;\n}\n\n/**\n * Triggers a browser download of the image (with or without annotations).\n */\nexport async function downloadAnnotatedImage(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<void> {\n const {\n format = \"image/png\",\n quality = 0.95,\n fileName = `tbib-annotated-image-${Date.now()}.${format === \"image/jpeg\" ? \"jpg\" : format === \"image/webp\" ? \"webp\" : \"png\"}`,\n } = options;\n\n try {\n const canvas = await renderToOffscreenCanvas(imageSource, strokes, options);\n\n return new Promise<void>((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to generate image blob from canvas\"));\n return;\n }\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n resolve();\n },\n format,\n quality\n );\n });\n } catch (err) {\n console.warn(\"Offscreen canvas render encountered an issue, falling back to direct asset download:\", err);\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n const link = document.createElement(\"a\");\n link.href = src;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/transform.ts","../src/annotations/smooth.ts","../src/annotations/store.ts","../src/export/canvas.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ImageDimensions, NormalizedPoint, ScreenPoint, TransformState } from \"../types\";\n\nexport const DEFAULT_TRANSFORM: TransformState = {\n scale: 1,\n translateX: 0,\n translateY: 0,\n rotate: 0,\n};\n\nexport const MIN_SCALE = 0.5;\nexport const MAX_SCALE = 8.0;\n\n/**\n * Clamps scale between min and max allowed bounds.\n */\nexport function clampScale(scale: number, min = MIN_SCALE, max = MAX_SCALE): number {\n return Math.min(Math.max(scale, min), max);\n}\n\n/**\n * Converts a screen/viewport coordinate (e.g. mouse or touch event)\n * into a normalized image coordinate (0 to 1) based on the image bounding box\n * and current scale/pan/rotation transform.\n */\nexport function screenToNormalizedImage(\n screenPt: ScreenPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): NormalizedPoint | null {\n if (imageDimensions.width <= 0 || imageDimensions.height <= 0) return null;\n\n // 1. Point relative to container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n let dx = screenPt.x - centerX - transform.translateX;\n let dy = screenPt.y - centerY - transform.translateY;\n\n // 2. Undo rotation\n const rad = (-transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const unrotatedX = dx * cos - dy * sin;\n const unrotatedY = dx * sin + dy * cos;\n\n // 3. Undo scale\n const scaledX = unrotatedX / transform.scale;\n const scaledY = unrotatedY / transform.scale;\n\n // 4. Compute displayed image fitted bounds at scale = 1\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // 5. Convert to normalized 0..1 coordinates relative to fitted image box\n const normX = (scaledX + fittedW / 2) / fittedW;\n const normY = (scaledY + fittedH / 2) / fittedH;\n\n return {\n x: Math.max(0, Math.min(1, normX)),\n y: Math.max(0, Math.min(1, normY)),\n };\n}\n\n/**\n * Converts a normalized image coordinate (0 to 1) to screen/pixel coordinates\n * inside the given container.\n */\nexport function normalizedImageToScreen(\n normPt: NormalizedPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): ScreenPoint {\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // Local unscaled, unrotated position relative to center\n const localX = normPt.x * fittedW - fittedW / 2;\n const localY = normPt.y * fittedH - fittedH / 2;\n\n // Apply scale\n const scaledX = localX * transform.scale;\n const scaledY = localY * transform.scale;\n\n // Apply rotation\n const rad = (transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedX = scaledX * cos - scaledY * sin;\n const rotatedY = scaledX * sin + scaledY * cos;\n\n // Apply translation and container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n return {\n x: centerX + transform.translateX + rotatedX,\n y: centerY + transform.translateY + rotatedY,\n };\n}\n\n/**\n * Computes fitted size of an image inside container maintaining aspect ratio.\n */\nexport function getFittedImageBounds(\n containerW: number,\n containerH: number,\n imgW: number,\n imgH: number\n): { width: number; height: number } {\n if (imgW <= 0 || imgH <= 0 || containerW <= 0 || containerH <= 0) {\n return { width: 0, height: 0 };\n }\n const aspect = imgW / imgH;\n const containerAspect = containerW / containerH;\n\n if (containerAspect > aspect) {\n const height = containerH;\n return { width: height * aspect, height };\n } else {\n const width = containerW;\n return { width, height: width / aspect };\n }\n}\n","import type { NormalizedPoint } from \"../types\";\n\n/**\n * Converts a list of normalized points into a smooth SVG path definition\n * using quadratic Bézier midpoint interpolation.\n */\nexport function pointsToSvgPath(\n points: NormalizedPoint[],\n width: number,\n height: number\n): string {\n if (points.length === 0) return \"\";\n if (points.length === 1) {\n const p = points[0];\n const px = p.x * width;\n const py = p.y * height;\n return `M ${px} ${py} L ${px + 0.1} ${py + 0.1}`;\n }\n\n const p0 = points[0];\n let d = `M ${p0.x * width} ${p0.y * height}`;\n\n for (let i = 1; i < points.length - 1; i++) {\n const current = points[i];\n const next = points[i + 1];\n\n const cx = (current.x * width + next.x * width) / 2;\n const cy = (current.y * height + next.y * height) / 2;\n\n d += ` Q ${current.x * width} ${current.y * height}, ${cx} ${cy}`;\n }\n\n const last = points[points.length - 1];\n d += ` L ${last.x * width} ${last.y * height}`;\n\n return d;\n}\n\n/**\n * Calculates squared Euclidean distance between two points in normalized coordinates.\n */\nexport function distanceSq(p1: NormalizedPoint, p2: NormalizedPoint): number {\n const dx = p1.x - p2.x;\n const dy = p1.y - p2.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Checks if a test point in normalized coordinates is close to any point in the stroke\n * within an interaction threshold (used for eraser hit detection).\n */\nexport function isPointNearStroke(\n testPt: NormalizedPoint,\n points: NormalizedPoint[],\n thresholdNorm: number\n): boolean {\n const thresholdSq = thresholdNorm * thresholdNorm;\n for (let i = 0; i < points.length; i++) {\n if (distanceSq(testPt, points[i]) <= thresholdSq) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Converts hex color (#rrggbb or #rgb) to an rgba string with alpha.\n */\nexport function hexToRgba(hex: string, alpha = 1): string {\n if (hex.startsWith(\"rgba\")) return hex;\n const clean = hex.trim().replace(\"#\", \"\");\n if (clean.length === 3) {\n const r = parseInt(clean[0] + clean[0], 16);\n const g = parseInt(clean[1] + clean[1], 16);\n const b = parseInt(clean[2] + clean[2], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n if (clean.length === 6) {\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hex;\n}\n","import type { AnnotationStroke, SlideAnnotations } from \"../types\";\n\nexport interface HistoryState {\n past: AnnotationStroke[][];\n present: AnnotationStroke[];\n future: AnnotationStroke[][];\n}\n\nexport class AnnotationStore {\n private slides = new Map<string, HistoryState>();\n private listeners = new Set<(slideId: string, strokes: AnnotationStroke[]) => void>();\n\n constructor(initialData?: SlideAnnotations[]) {\n if (initialData) {\n for (const item of initialData) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n }\n }\n }\n\n private getOrCreate(slideId: string): HistoryState {\n let state = this.slides.get(slideId);\n if (!state) {\n state = {\n past: [],\n present: [],\n future: [],\n };\n this.slides.set(slideId, state);\n }\n return state;\n }\n\n public getStrokes(slideId: string): AnnotationStroke[] {\n return this.getOrCreate(slideId).present;\n }\n\n public canUndo(slideId: string): boolean {\n return this.getOrCreate(slideId).past.length > 0;\n }\n\n public canRedo(slideId: string): boolean {\n return this.getOrCreate(slideId).future.length > 0;\n }\n\n public addStroke(slideId: string, stroke: AnnotationStroke): void {\n const state = this.getOrCreate(slideId);\n state.past.push([...state.present]);\n state.present = [...state.present, stroke];\n state.future = []; // Clear redo tree on new action\n this.notify(slideId);\n }\n\n public removeStroke(slideId: string, strokeId: string): void {\n const state = this.getOrCreate(slideId);\n const filtered = state.present.filter((s) => s.id !== strokeId);\n if (filtered.length !== state.present.length) {\n state.past.push([...state.present]);\n state.present = filtered;\n state.future = [];\n this.notify(slideId);\n }\n }\n\n public removeStrokesNear(\n slideId: string,\n testPt: { x: number; y: number },\n thresholdNorm: number,\n isPointNearStrokeFn: (pt: { x: number; y: number }, pts: { x: number; y: number }[], thresh: number) => boolean\n ): boolean {\n const state = this.getOrCreate(slideId);\n const toKeep: AnnotationStroke[] = [];\n let removedAny = false;\n\n for (const stroke of state.present) {\n if (isPointNearStrokeFn(testPt, stroke.points, thresholdNorm)) {\n removedAny = true;\n } else {\n toKeep.push(stroke);\n }\n }\n\n if (removedAny) {\n state.past.push([...state.present]);\n state.present = toKeep;\n state.future = [];\n this.notify(slideId);\n return true;\n }\n return false;\n }\n\n public clear(slideId: string): void {\n const state = this.getOrCreate(slideId);\n if (state.present.length === 0) return;\n state.past.push([...state.present]);\n state.present = [];\n state.future = [];\n this.notify(slideId);\n }\n\n public undo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.past.length === 0) return false;\n\n const previous = state.past.pop()!;\n state.future.unshift([...state.present]);\n state.present = previous;\n this.notify(slideId);\n return true;\n }\n\n public redo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.future.length === 0) return false;\n\n const next = state.future.shift()!;\n state.past.push([...state.present]);\n state.present = next;\n this.notify(slideId);\n return true;\n }\n\n public subscribe(listener: (slideId: string, strokes: AnnotationStroke[]) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(slideId: string): void {\n const strokes = this.getStrokes(slideId);\n for (const listener of this.listeners) {\n listener(slideId, strokes);\n }\n }\n\n public exportJSON(): SlideAnnotations[] {\n const result: SlideAnnotations[] = [];\n for (const [slideId, state] of this.slides.entries()) {\n if (state.present.length > 0) {\n result.push({\n slideId,\n strokes: [...state.present],\n });\n }\n }\n return result;\n }\n\n public importJSON(data: SlideAnnotations[]): void {\n for (const item of data) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n this.notify(item.slideId);\n }\n }\n}\n","import type { AnnotationStroke, ExportOptions } from \"../types\";\nimport { hexToRgba } from \"../annotations/smooth\";\n\n/**\n * Loads an image from a source URL or HTMLImageElement cleanly without\n * tainting the canvas, using fetch->Blob or crossOrigin cache-busting.\n */\nasync function loadCleanImage(\n imageSource: string | HTMLImageElement\n): Promise<{ img: HTMLImageElement; cleanup?: () => void }> {\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n\n // Strategy 1: Fetch as Blob URL (Guarantees same-origin blob: URL, zero canvas tainting)\n try {\n const res = await fetch(src, { mode: \"cors\" });\n if (res.ok) {\n const blob = await res.blob();\n const blobUrl = URL.createObjectURL(blob);\n const img = new Image();\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = blobUrl;\n });\n return {\n img,\n cleanup: () => URL.revokeObjectURL(blobUrl),\n };\n }\n } catch {\n // Strategy 1 failed (e.g. strict origin restrictions on fetch)\n }\n\n // Strategy 2: In-memory Image with crossOrigin = \"anonymous\" and cache buster\n try {\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n const sep = src.includes(\"?\") ? \"&\" : \"?\";\n const corsSrc = `${src}${sep}_export=${Date.now()}`;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = corsSrc;\n });\n return { img };\n } catch {\n // Strategy 2 failed\n }\n\n // Strategy 3: Fall back to existing image element or non-CORS element\n if (typeof imageSource !== \"string\" && imageSource.naturalWidth > 0) {\n return { img: imageSource };\n }\n\n const fallbackImg = new Image();\n await new Promise<void>((resolve, reject) => {\n fallbackImg.onload = () => resolve();\n fallbackImg.onerror = reject;\n fallbackImg.src = src;\n });\n return { img: fallbackImg };\n}\n\n/**\n * Loads an image from a URL or source element and renders it\n * along with its annotations onto an off-screen HTMLCanvasElement\n * at original native resolution.\n */\nexport async function renderToOffscreenCanvas(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<HTMLCanvasElement> {\n const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);\n cleanup?.();\n return canvas;\n}\n\nexport async function renderToOffscreenCanvasWithCleanup(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<{ canvas: HTMLCanvasElement; cleanup?: () => void }> {\n const { includeAnnotations = true, backgroundColor } = options;\n\n // 1. Resolve clean HTMLImageElement\n const { img, cleanup } = await loadCleanImage(imageSource);\n\n const naturalWidth = img.naturalWidth || img.width;\n const naturalHeight = img.naturalHeight || img.height;\n\n if (naturalWidth === 0 || naturalHeight === 0) {\n cleanup?.();\n throw new Error(\"Invalid image dimensions: width or height is 0\");\n }\n\n // 2. Setup high-res canvas matching native dimensions\n const canvas = document.createElement(\"canvas\");\n canvas.width = naturalWidth;\n canvas.height = naturalHeight;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n cleanup?.();\n throw new Error(\"Unable to obtain 2D canvas context for export\");\n }\n\n // 3. Optional background color\n if (backgroundColor) {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n }\n\n // 4. Draw base image\n ctx.drawImage(img, 0, 0, naturalWidth, naturalHeight);\n\n // 5. Burn in annotations if requested\n if (includeAnnotations && strokes.length > 0) {\n for (const stroke of strokes) {\n if (stroke.points.length === 0) continue;\n\n ctx.save();\n ctx.beginPath();\n ctx.lineCap = \"round\";\n ctx.lineJoin = \"round\";\n\n // Scale stroke width relative to the maximum dimension of the image\n const strokeScale = Math.max(naturalWidth, naturalHeight) / 1000;\n ctx.lineWidth = Math.max(1, stroke.width * strokeScale);\n\n if (stroke.tool === \"highlighter\") {\n ctx.globalAlpha = stroke.opacity || 0.35;\n ctx.strokeStyle = hexToRgba(stroke.color, 1);\n ctx.globalCompositeOperation = \"multiply\";\n } else {\n ctx.globalAlpha = stroke.opacity || 1.0;\n ctx.strokeStyle = stroke.color;\n ctx.globalCompositeOperation = \"source-over\";\n }\n\n const p0 = stroke.points[0];\n const startX = p0.x * naturalWidth;\n const startY = p0.y * naturalHeight;\n ctx.moveTo(startX, startY);\n\n if (stroke.points.length === 1) {\n ctx.lineTo(startX + 0.5, startY + 0.5);\n } else {\n for (let i = 1; i < stroke.points.length - 1; i++) {\n const pt = stroke.points[i];\n const next = stroke.points[i + 1];\n const cx = (pt.x * naturalWidth + next.x * naturalWidth) / 2;\n const cy = (pt.y * naturalHeight + next.y * naturalHeight) / 2;\n ctx.quadraticCurveTo(pt.x * naturalWidth, pt.y * naturalHeight, cx, cy);\n }\n const last = stroke.points[stroke.points.length - 1];\n ctx.lineTo(last.x * naturalWidth, last.y * naturalHeight);\n }\n\n ctx.stroke();\n ctx.restore();\n }\n }\n\n return { canvas, cleanup };\n}\n\n/**\n * Triggers a browser download of the image (with or without annotations).\n * Guarantees zero unhandled SecurityError on tainted canvases with automatic fallback.\n */\nexport async function downloadAnnotatedImage(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<void> {\n const {\n format = \"image/png\",\n quality = 0.95,\n fileName = `tbib-annotated-image-${Date.now()}.${format === \"image/jpeg\" ? \"jpg\" : format === \"image/webp\" ? \"webp\" : \"png\"}`,\n } = options;\n\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n\n const directDownload = (downloadUrl: string, name: string) => {\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = name;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n try {\n const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);\n\n await new Promise<void>((resolve) => {\n try {\n canvas.toBlob(\n (blob) => {\n cleanup?.();\n if (!blob) {\n directDownload(src, fileName);\n resolve();\n return;\n }\n\n const url = URL.createObjectURL(blob);\n directDownload(url, fileName);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n resolve();\n },\n format,\n quality\n );\n } catch (taintedErr) {\n cleanup?.();\n console.warn(\"Canvas was tainted by cross-origin security rules, downloading direct asset:\", taintedErr);\n directDownload(src, fileName);\n resolve();\n }\n });\n } catch (err) {\n console.warn(\"Offscreen canvas render encountered an issue, falling back to direct asset download:\", err);\n directDownload(src, fileName);\n }\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -274,23 +274,62 @@ var AnnotationStore = class {
|
|
|
274
274
|
};
|
|
275
275
|
|
|
276
276
|
// src/export/canvas.ts
|
|
277
|
-
async function
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
async function loadCleanImage(imageSource) {
|
|
278
|
+
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
279
|
+
try {
|
|
280
|
+
const res = await fetch(src, { mode: "cors" });
|
|
281
|
+
if (res.ok) {
|
|
282
|
+
const blob = await res.blob();
|
|
283
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
284
|
+
const img = new Image();
|
|
285
|
+
await new Promise((resolve, reject) => {
|
|
286
|
+
img.onload = () => resolve();
|
|
287
|
+
img.onerror = reject;
|
|
288
|
+
img.src = blobUrl;
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
img,
|
|
292
|
+
cleanup: () => URL.revokeObjectURL(blobUrl)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const img = new Image();
|
|
282
299
|
img.crossOrigin = "anonymous";
|
|
300
|
+
const sep = src.includes("?") ? "&" : "?";
|
|
301
|
+
const corsSrc = `${src}${sep}_export=${Date.now()}`;
|
|
283
302
|
await new Promise((resolve, reject) => {
|
|
284
303
|
img.onload = () => resolve();
|
|
285
|
-
img.onerror =
|
|
286
|
-
img.src =
|
|
304
|
+
img.onerror = reject;
|
|
305
|
+
img.src = corsSrc;
|
|
287
306
|
});
|
|
288
|
-
|
|
289
|
-
|
|
307
|
+
return { img };
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
if (typeof imageSource !== "string" && imageSource.naturalWidth > 0) {
|
|
311
|
+
return { img: imageSource };
|
|
290
312
|
}
|
|
313
|
+
const fallbackImg = new Image();
|
|
314
|
+
await new Promise((resolve, reject) => {
|
|
315
|
+
fallbackImg.onload = () => resolve();
|
|
316
|
+
fallbackImg.onerror = reject;
|
|
317
|
+
fallbackImg.src = src;
|
|
318
|
+
});
|
|
319
|
+
return { img: fallbackImg };
|
|
320
|
+
}
|
|
321
|
+
async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
322
|
+
const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);
|
|
323
|
+
cleanup?.();
|
|
324
|
+
return canvas;
|
|
325
|
+
}
|
|
326
|
+
async function renderToOffscreenCanvasWithCleanup(imageSource, strokes, options = {}) {
|
|
327
|
+
const { includeAnnotations = true, backgroundColor } = options;
|
|
328
|
+
const { img, cleanup } = await loadCleanImage(imageSource);
|
|
291
329
|
const naturalWidth = img.naturalWidth || img.width;
|
|
292
330
|
const naturalHeight = img.naturalHeight || img.height;
|
|
293
331
|
if (naturalWidth === 0 || naturalHeight === 0) {
|
|
332
|
+
cleanup?.();
|
|
294
333
|
throw new Error("Invalid image dimensions: width or height is 0");
|
|
295
334
|
}
|
|
296
335
|
const canvas = document.createElement("canvas");
|
|
@@ -298,6 +337,7 @@ async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
|
298
337
|
canvas.height = naturalHeight;
|
|
299
338
|
const ctx = canvas.getContext("2d");
|
|
300
339
|
if (!ctx) {
|
|
340
|
+
cleanup?.();
|
|
301
341
|
throw new Error("Unable to obtain 2D canvas context for export");
|
|
302
342
|
}
|
|
303
343
|
if (backgroundColor) {
|
|
@@ -344,7 +384,7 @@ async function renderToOffscreenCanvas(imageSource, strokes, options = {}) {
|
|
|
344
384
|
ctx.restore();
|
|
345
385
|
}
|
|
346
386
|
}
|
|
347
|
-
return canvas;
|
|
387
|
+
return { canvas, cleanup };
|
|
348
388
|
}
|
|
349
389
|
async function downloadAnnotatedImage(imageSource, strokes, options = {}) {
|
|
350
390
|
const {
|
|
@@ -352,41 +392,48 @@ async function downloadAnnotatedImage(imageSource, strokes, options = {}) {
|
|
|
352
392
|
quality = 0.95,
|
|
353
393
|
fileName = `tbib-annotated-image-${Date.now()}.${format === "image/jpeg" ? "jpg" : format === "image/webp" ? "webp" : "png"}`
|
|
354
394
|
} = options;
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return new Promise((resolve, reject) => {
|
|
358
|
-
canvas.toBlob(
|
|
359
|
-
(blob) => {
|
|
360
|
-
if (!blob) {
|
|
361
|
-
reject(new Error("Failed to generate image blob from canvas"));
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
const url = URL.createObjectURL(blob);
|
|
365
|
-
const link = document.createElement("a");
|
|
366
|
-
link.href = url;
|
|
367
|
-
link.download = fileName;
|
|
368
|
-
document.body.appendChild(link);
|
|
369
|
-
link.click();
|
|
370
|
-
document.body.removeChild(link);
|
|
371
|
-
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
372
|
-
resolve();
|
|
373
|
-
},
|
|
374
|
-
format,
|
|
375
|
-
quality
|
|
376
|
-
);
|
|
377
|
-
});
|
|
378
|
-
} catch (err) {
|
|
379
|
-
console.warn("Offscreen canvas render encountered an issue, falling back to direct asset download:", err);
|
|
380
|
-
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
395
|
+
const src = typeof imageSource === "string" ? imageSource : imageSource.src;
|
|
396
|
+
const directDownload = (downloadUrl, name) => {
|
|
381
397
|
const link = document.createElement("a");
|
|
382
|
-
link.href =
|
|
383
|
-
link.download =
|
|
398
|
+
link.href = downloadUrl;
|
|
399
|
+
link.download = name;
|
|
384
400
|
document.body.appendChild(link);
|
|
385
401
|
link.click();
|
|
386
402
|
document.body.removeChild(link);
|
|
403
|
+
};
|
|
404
|
+
try {
|
|
405
|
+
const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);
|
|
406
|
+
await new Promise((resolve) => {
|
|
407
|
+
try {
|
|
408
|
+
canvas.toBlob(
|
|
409
|
+
(blob) => {
|
|
410
|
+
cleanup?.();
|
|
411
|
+
if (!blob) {
|
|
412
|
+
directDownload(src, fileName);
|
|
413
|
+
resolve();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const url = URL.createObjectURL(blob);
|
|
417
|
+
directDownload(url, fileName);
|
|
418
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
419
|
+
resolve();
|
|
420
|
+
},
|
|
421
|
+
format,
|
|
422
|
+
quality
|
|
423
|
+
);
|
|
424
|
+
} catch (taintedErr) {
|
|
425
|
+
cleanup?.();
|
|
426
|
+
console.warn("Canvas was tainted by cross-origin security rules, downloading direct asset:", taintedErr);
|
|
427
|
+
directDownload(src, fileName);
|
|
428
|
+
resolve();
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
} catch (err) {
|
|
432
|
+
console.warn("Offscreen canvas render encountered an issue, falling back to direct asset download:", err);
|
|
433
|
+
directDownload(src, fileName);
|
|
387
434
|
}
|
|
388
435
|
}
|
|
389
436
|
|
|
390
|
-
export { AnnotationStore, DEFAULT_TRANSFORM, MAX_SCALE, MIN_SCALE, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, screenToNormalizedImage };
|
|
437
|
+
export { AnnotationStore, DEFAULT_TRANSFORM, MAX_SCALE, MIN_SCALE, clampScale, distanceSq, downloadAnnotatedImage, getFittedImageBounds, hexToRgba, isPointNearStroke, normalizedImageToScreen, pointsToSvgPath, renderToOffscreenCanvas, renderToOffscreenCanvasWithCleanup, screenToNormalizedImage };
|
|
391
438
|
//# sourceMappingURL=index.mjs.map
|
|
392
439
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/transform.ts","../src/annotations/smooth.ts","../src/annotations/store.ts","../src/export/canvas.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ImageDimensions, NormalizedPoint, ScreenPoint, TransformState } from \"../types\";\n\nexport const DEFAULT_TRANSFORM: TransformState = {\n scale: 1,\n translateX: 0,\n translateY: 0,\n rotate: 0,\n};\n\nexport const MIN_SCALE = 0.5;\nexport const MAX_SCALE = 8.0;\n\n/**\n * Clamps scale between min and max allowed bounds.\n */\nexport function clampScale(scale: number, min = MIN_SCALE, max = MAX_SCALE): number {\n return Math.min(Math.max(scale, min), max);\n}\n\n/**\n * Converts a screen/viewport coordinate (e.g. mouse or touch event)\n * into a normalized image coordinate (0 to 1) based on the image bounding box\n * and current scale/pan/rotation transform.\n */\nexport function screenToNormalizedImage(\n screenPt: ScreenPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): NormalizedPoint | null {\n if (imageDimensions.width <= 0 || imageDimensions.height <= 0) return null;\n\n // 1. Point relative to container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n let dx = screenPt.x - centerX - transform.translateX;\n let dy = screenPt.y - centerY - transform.translateY;\n\n // 2. Undo rotation\n const rad = (-transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const unrotatedX = dx * cos - dy * sin;\n const unrotatedY = dx * sin + dy * cos;\n\n // 3. Undo scale\n const scaledX = unrotatedX / transform.scale;\n const scaledY = unrotatedY / transform.scale;\n\n // 4. Compute displayed image fitted bounds at scale = 1\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // 5. Convert to normalized 0..1 coordinates relative to fitted image box\n const normX = (scaledX + fittedW / 2) / fittedW;\n const normY = (scaledY + fittedH / 2) / fittedH;\n\n return {\n x: Math.max(0, Math.min(1, normX)),\n y: Math.max(0, Math.min(1, normY)),\n };\n}\n\n/**\n * Converts a normalized image coordinate (0 to 1) to screen/pixel coordinates\n * inside the given container.\n */\nexport function normalizedImageToScreen(\n normPt: NormalizedPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): ScreenPoint {\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // Local unscaled, unrotated position relative to center\n const localX = normPt.x * fittedW - fittedW / 2;\n const localY = normPt.y * fittedH - fittedH / 2;\n\n // Apply scale\n const scaledX = localX * transform.scale;\n const scaledY = localY * transform.scale;\n\n // Apply rotation\n const rad = (transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedX = scaledX * cos - scaledY * sin;\n const rotatedY = scaledX * sin + scaledY * cos;\n\n // Apply translation and container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n return {\n x: centerX + transform.translateX + rotatedX,\n y: centerY + transform.translateY + rotatedY,\n };\n}\n\n/**\n * Computes fitted size of an image inside container maintaining aspect ratio.\n */\nexport function getFittedImageBounds(\n containerW: number,\n containerH: number,\n imgW: number,\n imgH: number\n): { width: number; height: number } {\n if (imgW <= 0 || imgH <= 0 || containerW <= 0 || containerH <= 0) {\n return { width: 0, height: 0 };\n }\n const aspect = imgW / imgH;\n const containerAspect = containerW / containerH;\n\n if (containerAspect > aspect) {\n const height = containerH;\n return { width: height * aspect, height };\n } else {\n const width = containerW;\n return { width, height: width / aspect };\n }\n}\n","import type { NormalizedPoint } from \"../types\";\n\n/**\n * Converts a list of normalized points into a smooth SVG path definition\n * using quadratic Bézier midpoint interpolation.\n */\nexport function pointsToSvgPath(\n points: NormalizedPoint[],\n width: number,\n height: number\n): string {\n if (points.length === 0) return \"\";\n if (points.length === 1) {\n const p = points[0];\n const px = p.x * width;\n const py = p.y * height;\n return `M ${px} ${py} L ${px + 0.1} ${py + 0.1}`;\n }\n\n const p0 = points[0];\n let d = `M ${p0.x * width} ${p0.y * height}`;\n\n for (let i = 1; i < points.length - 1; i++) {\n const current = points[i];\n const next = points[i + 1];\n\n const cx = (current.x * width + next.x * width) / 2;\n const cy = (current.y * height + next.y * height) / 2;\n\n d += ` Q ${current.x * width} ${current.y * height}, ${cx} ${cy}`;\n }\n\n const last = points[points.length - 1];\n d += ` L ${last.x * width} ${last.y * height}`;\n\n return d;\n}\n\n/**\n * Calculates squared Euclidean distance between two points in normalized coordinates.\n */\nexport function distanceSq(p1: NormalizedPoint, p2: NormalizedPoint): number {\n const dx = p1.x - p2.x;\n const dy = p1.y - p2.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Checks if a test point in normalized coordinates is close to any point in the stroke\n * within an interaction threshold (used for eraser hit detection).\n */\nexport function isPointNearStroke(\n testPt: NormalizedPoint,\n points: NormalizedPoint[],\n thresholdNorm: number\n): boolean {\n const thresholdSq = thresholdNorm * thresholdNorm;\n for (let i = 0; i < points.length; i++) {\n if (distanceSq(testPt, points[i]) <= thresholdSq) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Converts hex color (#rrggbb or #rgb) to an rgba string with alpha.\n */\nexport function hexToRgba(hex: string, alpha = 1): string {\n if (hex.startsWith(\"rgba\")) return hex;\n const clean = hex.trim().replace(\"#\", \"\");\n if (clean.length === 3) {\n const r = parseInt(clean[0] + clean[0], 16);\n const g = parseInt(clean[1] + clean[1], 16);\n const b = parseInt(clean[2] + clean[2], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n if (clean.length === 6) {\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hex;\n}\n","import type { AnnotationStroke, SlideAnnotations } from \"../types\";\n\nexport interface HistoryState {\n past: AnnotationStroke[][];\n present: AnnotationStroke[];\n future: AnnotationStroke[][];\n}\n\nexport class AnnotationStore {\n private slides = new Map<string, HistoryState>();\n private listeners = new Set<(slideId: string, strokes: AnnotationStroke[]) => void>();\n\n constructor(initialData?: SlideAnnotations[]) {\n if (initialData) {\n for (const item of initialData) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n }\n }\n }\n\n private getOrCreate(slideId: string): HistoryState {\n let state = this.slides.get(slideId);\n if (!state) {\n state = {\n past: [],\n present: [],\n future: [],\n };\n this.slides.set(slideId, state);\n }\n return state;\n }\n\n public getStrokes(slideId: string): AnnotationStroke[] {\n return this.getOrCreate(slideId).present;\n }\n\n public canUndo(slideId: string): boolean {\n return this.getOrCreate(slideId).past.length > 0;\n }\n\n public canRedo(slideId: string): boolean {\n return this.getOrCreate(slideId).future.length > 0;\n }\n\n public addStroke(slideId: string, stroke: AnnotationStroke): void {\n const state = this.getOrCreate(slideId);\n state.past.push([...state.present]);\n state.present = [...state.present, stroke];\n state.future = []; // Clear redo tree on new action\n this.notify(slideId);\n }\n\n public removeStroke(slideId: string, strokeId: string): void {\n const state = this.getOrCreate(slideId);\n const filtered = state.present.filter((s) => s.id !== strokeId);\n if (filtered.length !== state.present.length) {\n state.past.push([...state.present]);\n state.present = filtered;\n state.future = [];\n this.notify(slideId);\n }\n }\n\n public removeStrokesNear(\n slideId: string,\n testPt: { x: number; y: number },\n thresholdNorm: number,\n isPointNearStrokeFn: (pt: { x: number; y: number }, pts: { x: number; y: number }[], thresh: number) => boolean\n ): boolean {\n const state = this.getOrCreate(slideId);\n const toKeep: AnnotationStroke[] = [];\n let removedAny = false;\n\n for (const stroke of state.present) {\n if (isPointNearStrokeFn(testPt, stroke.points, thresholdNorm)) {\n removedAny = true;\n } else {\n toKeep.push(stroke);\n }\n }\n\n if (removedAny) {\n state.past.push([...state.present]);\n state.present = toKeep;\n state.future = [];\n this.notify(slideId);\n return true;\n }\n return false;\n }\n\n public clear(slideId: string): void {\n const state = this.getOrCreate(slideId);\n if (state.present.length === 0) return;\n state.past.push([...state.present]);\n state.present = [];\n state.future = [];\n this.notify(slideId);\n }\n\n public undo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.past.length === 0) return false;\n\n const previous = state.past.pop()!;\n state.future.unshift([...state.present]);\n state.present = previous;\n this.notify(slideId);\n return true;\n }\n\n public redo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.future.length === 0) return false;\n\n const next = state.future.shift()!;\n state.past.push([...state.present]);\n state.present = next;\n this.notify(slideId);\n return true;\n }\n\n public subscribe(listener: (slideId: string, strokes: AnnotationStroke[]) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(slideId: string): void {\n const strokes = this.getStrokes(slideId);\n for (const listener of this.listeners) {\n listener(slideId, strokes);\n }\n }\n\n public exportJSON(): SlideAnnotations[] {\n const result: SlideAnnotations[] = [];\n for (const [slideId, state] of this.slides.entries()) {\n if (state.present.length > 0) {\n result.push({\n slideId,\n strokes: [...state.present],\n });\n }\n }\n return result;\n }\n\n public importJSON(data: SlideAnnotations[]): void {\n for (const item of data) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n this.notify(item.slideId);\n }\n }\n}\n","import type { AnnotationStroke, ExportOptions } from \"../types\";\nimport { hexToRgba } from \"../annotations/smooth\";\n\n/**\n * Loads an image from a URL or source element and renders it\n * along with its annotations onto an off-screen HTMLCanvasElement\n * at original native resolution.\n */\nexport async function renderToOffscreenCanvas(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<HTMLCanvasElement> {\n const { includeAnnotations = true, backgroundColor } = options;\n\n // 1. Resolve source HTMLImageElement\n let img: HTMLImageElement;\n if (typeof imageSource === \"string\") {\n img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = (e) => reject(new Error(`Failed to load image from source: ${imageSource}`));\n img.src = imageSource;\n });\n } else {\n img = imageSource;\n }\n\n const naturalWidth = img.naturalWidth || img.width;\n const naturalHeight = img.naturalHeight || img.height;\n\n if (naturalWidth === 0 || naturalHeight === 0) {\n throw new Error(\"Invalid image dimensions: width or height is 0\");\n }\n\n // 2. Setup high-res canvas matching native dimensions\n const canvas = document.createElement(\"canvas\");\n canvas.width = naturalWidth;\n canvas.height = naturalHeight;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Unable to obtain 2D canvas context for export\");\n }\n\n // 3. Optional background color\n if (backgroundColor) {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n }\n\n // 4. Draw base image\n ctx.drawImage(img, 0, 0, naturalWidth, naturalHeight);\n\n // 5. Burn in annotations if requested\n if (includeAnnotations && strokes.length > 0) {\n for (const stroke of strokes) {\n if (stroke.points.length === 0) continue;\n\n ctx.save();\n ctx.beginPath();\n ctx.lineCap = \"round\";\n ctx.lineJoin = \"round\";\n\n // Scale stroke width relative to the maximum dimension of the image\n // Stroke width in model is relative to fitted display (e.g. 3px on 800px)\n // We scale it proportionally so it looks identical to what user saw\n const strokeScale = Math.max(naturalWidth, naturalHeight) / 1000;\n ctx.lineWidth = Math.max(1, stroke.width * strokeScale);\n\n if (stroke.tool === \"highlighter\") {\n ctx.globalAlpha = stroke.opacity || 0.35;\n ctx.strokeStyle = hexToRgba(stroke.color, 1);\n // Multiply blend looks most natural on medical illustrations & documents\n ctx.globalCompositeOperation = \"multiply\";\n } else {\n ctx.globalAlpha = stroke.opacity || 1.0;\n ctx.strokeStyle = stroke.color;\n ctx.globalCompositeOperation = \"source-over\";\n }\n\n const p0 = stroke.points[0];\n const startX = p0.x * naturalWidth;\n const startY = p0.y * naturalHeight;\n ctx.moveTo(startX, startY);\n\n if (stroke.points.length === 1) {\n ctx.lineTo(startX + 0.5, startY + 0.5);\n } else {\n for (let i = 1; i < stroke.points.length - 1; i++) {\n const pt = stroke.points[i];\n const next = stroke.points[i + 1];\n const cx = (pt.x * naturalWidth + next.x * naturalWidth) / 2;\n const cy = (pt.y * naturalHeight + next.y * naturalHeight) / 2;\n ctx.quadraticCurveTo(pt.x * naturalWidth, pt.y * naturalHeight, cx, cy);\n }\n const last = stroke.points[stroke.points.length - 1];\n ctx.lineTo(last.x * naturalWidth, last.y * naturalHeight);\n }\n\n ctx.stroke();\n ctx.restore();\n }\n }\n\n return canvas;\n}\n\n/**\n * Triggers a browser download of the image (with or without annotations).\n */\nexport async function downloadAnnotatedImage(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<void> {\n const {\n format = \"image/png\",\n quality = 0.95,\n fileName = `tbib-annotated-image-${Date.now()}.${format === \"image/jpeg\" ? \"jpg\" : format === \"image/webp\" ? \"webp\" : \"png\"}`,\n } = options;\n\n try {\n const canvas = await renderToOffscreenCanvas(imageSource, strokes, options);\n\n return new Promise<void>((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to generate image blob from canvas\"));\n return;\n }\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n resolve();\n },\n format,\n quality\n );\n });\n } catch (err) {\n console.warn(\"Offscreen canvas render encountered an issue, falling back to direct asset download:\", err);\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n const link = document.createElement(\"a\");\n link.href = src;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/transform.ts","../src/annotations/smooth.ts","../src/annotations/store.ts","../src/export/canvas.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ImageDimensions, NormalizedPoint, ScreenPoint, TransformState } from \"../types\";\n\nexport const DEFAULT_TRANSFORM: TransformState = {\n scale: 1,\n translateX: 0,\n translateY: 0,\n rotate: 0,\n};\n\nexport const MIN_SCALE = 0.5;\nexport const MAX_SCALE = 8.0;\n\n/**\n * Clamps scale between min and max allowed bounds.\n */\nexport function clampScale(scale: number, min = MIN_SCALE, max = MAX_SCALE): number {\n return Math.min(Math.max(scale, min), max);\n}\n\n/**\n * Converts a screen/viewport coordinate (e.g. mouse or touch event)\n * into a normalized image coordinate (0 to 1) based on the image bounding box\n * and current scale/pan/rotation transform.\n */\nexport function screenToNormalizedImage(\n screenPt: ScreenPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): NormalizedPoint | null {\n if (imageDimensions.width <= 0 || imageDimensions.height <= 0) return null;\n\n // 1. Point relative to container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n let dx = screenPt.x - centerX - transform.translateX;\n let dy = screenPt.y - centerY - transform.translateY;\n\n // 2. Undo rotation\n const rad = (-transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const unrotatedX = dx * cos - dy * sin;\n const unrotatedY = dx * sin + dy * cos;\n\n // 3. Undo scale\n const scaledX = unrotatedX / transform.scale;\n const scaledY = unrotatedY / transform.scale;\n\n // 4. Compute displayed image fitted bounds at scale = 1\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // 5. Convert to normalized 0..1 coordinates relative to fitted image box\n const normX = (scaledX + fittedW / 2) / fittedW;\n const normY = (scaledY + fittedH / 2) / fittedH;\n\n return {\n x: Math.max(0, Math.min(1, normX)),\n y: Math.max(0, Math.min(1, normY)),\n };\n}\n\n/**\n * Converts a normalized image coordinate (0 to 1) to screen/pixel coordinates\n * inside the given container.\n */\nexport function normalizedImageToScreen(\n normPt: NormalizedPoint,\n containerRect: DOMRect,\n imageDimensions: ImageDimensions,\n transform: TransformState\n): ScreenPoint {\n const aspect = imageDimensions.width / imageDimensions.height;\n const containerAspect = containerRect.width / containerRect.height;\n\n let fittedW: number;\n let fittedH: number;\n\n if (containerAspect > aspect) {\n fittedH = containerRect.height;\n fittedW = fittedH * aspect;\n } else {\n fittedW = containerRect.width;\n fittedH = fittedW / aspect;\n }\n\n // Local unscaled, unrotated position relative to center\n const localX = normPt.x * fittedW - fittedW / 2;\n const localY = normPt.y * fittedH - fittedH / 2;\n\n // Apply scale\n const scaledX = localX * transform.scale;\n const scaledY = localY * transform.scale;\n\n // Apply rotation\n const rad = (transform.rotate * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const rotatedX = scaledX * cos - scaledY * sin;\n const rotatedY = scaledX * sin + scaledY * cos;\n\n // Apply translation and container center\n const centerX = containerRect.left + containerRect.width / 2;\n const centerY = containerRect.top + containerRect.height / 2;\n\n return {\n x: centerX + transform.translateX + rotatedX,\n y: centerY + transform.translateY + rotatedY,\n };\n}\n\n/**\n * Computes fitted size of an image inside container maintaining aspect ratio.\n */\nexport function getFittedImageBounds(\n containerW: number,\n containerH: number,\n imgW: number,\n imgH: number\n): { width: number; height: number } {\n if (imgW <= 0 || imgH <= 0 || containerW <= 0 || containerH <= 0) {\n return { width: 0, height: 0 };\n }\n const aspect = imgW / imgH;\n const containerAspect = containerW / containerH;\n\n if (containerAspect > aspect) {\n const height = containerH;\n return { width: height * aspect, height };\n } else {\n const width = containerW;\n return { width, height: width / aspect };\n }\n}\n","import type { NormalizedPoint } from \"../types\";\n\n/**\n * Converts a list of normalized points into a smooth SVG path definition\n * using quadratic Bézier midpoint interpolation.\n */\nexport function pointsToSvgPath(\n points: NormalizedPoint[],\n width: number,\n height: number\n): string {\n if (points.length === 0) return \"\";\n if (points.length === 1) {\n const p = points[0];\n const px = p.x * width;\n const py = p.y * height;\n return `M ${px} ${py} L ${px + 0.1} ${py + 0.1}`;\n }\n\n const p0 = points[0];\n let d = `M ${p0.x * width} ${p0.y * height}`;\n\n for (let i = 1; i < points.length - 1; i++) {\n const current = points[i];\n const next = points[i + 1];\n\n const cx = (current.x * width + next.x * width) / 2;\n const cy = (current.y * height + next.y * height) / 2;\n\n d += ` Q ${current.x * width} ${current.y * height}, ${cx} ${cy}`;\n }\n\n const last = points[points.length - 1];\n d += ` L ${last.x * width} ${last.y * height}`;\n\n return d;\n}\n\n/**\n * Calculates squared Euclidean distance between two points in normalized coordinates.\n */\nexport function distanceSq(p1: NormalizedPoint, p2: NormalizedPoint): number {\n const dx = p1.x - p2.x;\n const dy = p1.y - p2.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Checks if a test point in normalized coordinates is close to any point in the stroke\n * within an interaction threshold (used for eraser hit detection).\n */\nexport function isPointNearStroke(\n testPt: NormalizedPoint,\n points: NormalizedPoint[],\n thresholdNorm: number\n): boolean {\n const thresholdSq = thresholdNorm * thresholdNorm;\n for (let i = 0; i < points.length; i++) {\n if (distanceSq(testPt, points[i]) <= thresholdSq) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Converts hex color (#rrggbb or #rgb) to an rgba string with alpha.\n */\nexport function hexToRgba(hex: string, alpha = 1): string {\n if (hex.startsWith(\"rgba\")) return hex;\n const clean = hex.trim().replace(\"#\", \"\");\n if (clean.length === 3) {\n const r = parseInt(clean[0] + clean[0], 16);\n const g = parseInt(clean[1] + clean[1], 16);\n const b = parseInt(clean[2] + clean[2], 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n if (clean.length === 6) {\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hex;\n}\n","import type { AnnotationStroke, SlideAnnotations } from \"../types\";\n\nexport interface HistoryState {\n past: AnnotationStroke[][];\n present: AnnotationStroke[];\n future: AnnotationStroke[][];\n}\n\nexport class AnnotationStore {\n private slides = new Map<string, HistoryState>();\n private listeners = new Set<(slideId: string, strokes: AnnotationStroke[]) => void>();\n\n constructor(initialData?: SlideAnnotations[]) {\n if (initialData) {\n for (const item of initialData) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n }\n }\n }\n\n private getOrCreate(slideId: string): HistoryState {\n let state = this.slides.get(slideId);\n if (!state) {\n state = {\n past: [],\n present: [],\n future: [],\n };\n this.slides.set(slideId, state);\n }\n return state;\n }\n\n public getStrokes(slideId: string): AnnotationStroke[] {\n return this.getOrCreate(slideId).present;\n }\n\n public canUndo(slideId: string): boolean {\n return this.getOrCreate(slideId).past.length > 0;\n }\n\n public canRedo(slideId: string): boolean {\n return this.getOrCreate(slideId).future.length > 0;\n }\n\n public addStroke(slideId: string, stroke: AnnotationStroke): void {\n const state = this.getOrCreate(slideId);\n state.past.push([...state.present]);\n state.present = [...state.present, stroke];\n state.future = []; // Clear redo tree on new action\n this.notify(slideId);\n }\n\n public removeStroke(slideId: string, strokeId: string): void {\n const state = this.getOrCreate(slideId);\n const filtered = state.present.filter((s) => s.id !== strokeId);\n if (filtered.length !== state.present.length) {\n state.past.push([...state.present]);\n state.present = filtered;\n state.future = [];\n this.notify(slideId);\n }\n }\n\n public removeStrokesNear(\n slideId: string,\n testPt: { x: number; y: number },\n thresholdNorm: number,\n isPointNearStrokeFn: (pt: { x: number; y: number }, pts: { x: number; y: number }[], thresh: number) => boolean\n ): boolean {\n const state = this.getOrCreate(slideId);\n const toKeep: AnnotationStroke[] = [];\n let removedAny = false;\n\n for (const stroke of state.present) {\n if (isPointNearStrokeFn(testPt, stroke.points, thresholdNorm)) {\n removedAny = true;\n } else {\n toKeep.push(stroke);\n }\n }\n\n if (removedAny) {\n state.past.push([...state.present]);\n state.present = toKeep;\n state.future = [];\n this.notify(slideId);\n return true;\n }\n return false;\n }\n\n public clear(slideId: string): void {\n const state = this.getOrCreate(slideId);\n if (state.present.length === 0) return;\n state.past.push([...state.present]);\n state.present = [];\n state.future = [];\n this.notify(slideId);\n }\n\n public undo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.past.length === 0) return false;\n\n const previous = state.past.pop()!;\n state.future.unshift([...state.present]);\n state.present = previous;\n this.notify(slideId);\n return true;\n }\n\n public redo(slideId: string): boolean {\n const state = this.getOrCreate(slideId);\n if (state.future.length === 0) return false;\n\n const next = state.future.shift()!;\n state.past.push([...state.present]);\n state.present = next;\n this.notify(slideId);\n return true;\n }\n\n public subscribe(listener: (slideId: string, strokes: AnnotationStroke[]) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(slideId: string): void {\n const strokes = this.getStrokes(slideId);\n for (const listener of this.listeners) {\n listener(slideId, strokes);\n }\n }\n\n public exportJSON(): SlideAnnotations[] {\n const result: SlideAnnotations[] = [];\n for (const [slideId, state] of this.slides.entries()) {\n if (state.present.length > 0) {\n result.push({\n slideId,\n strokes: [...state.present],\n });\n }\n }\n return result;\n }\n\n public importJSON(data: SlideAnnotations[]): void {\n for (const item of data) {\n this.slides.set(item.slideId, {\n past: [],\n present: [...item.strokes],\n future: [],\n });\n this.notify(item.slideId);\n }\n }\n}\n","import type { AnnotationStroke, ExportOptions } from \"../types\";\nimport { hexToRgba } from \"../annotations/smooth\";\n\n/**\n * Loads an image from a source URL or HTMLImageElement cleanly without\n * tainting the canvas, using fetch->Blob or crossOrigin cache-busting.\n */\nasync function loadCleanImage(\n imageSource: string | HTMLImageElement\n): Promise<{ img: HTMLImageElement; cleanup?: () => void }> {\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n\n // Strategy 1: Fetch as Blob URL (Guarantees same-origin blob: URL, zero canvas tainting)\n try {\n const res = await fetch(src, { mode: \"cors\" });\n if (res.ok) {\n const blob = await res.blob();\n const blobUrl = URL.createObjectURL(blob);\n const img = new Image();\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = blobUrl;\n });\n return {\n img,\n cleanup: () => URL.revokeObjectURL(blobUrl),\n };\n }\n } catch {\n // Strategy 1 failed (e.g. strict origin restrictions on fetch)\n }\n\n // Strategy 2: In-memory Image with crossOrigin = \"anonymous\" and cache buster\n try {\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n const sep = src.includes(\"?\") ? \"&\" : \"?\";\n const corsSrc = `${src}${sep}_export=${Date.now()}`;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = corsSrc;\n });\n return { img };\n } catch {\n // Strategy 2 failed\n }\n\n // Strategy 3: Fall back to existing image element or non-CORS element\n if (typeof imageSource !== \"string\" && imageSource.naturalWidth > 0) {\n return { img: imageSource };\n }\n\n const fallbackImg = new Image();\n await new Promise<void>((resolve, reject) => {\n fallbackImg.onload = () => resolve();\n fallbackImg.onerror = reject;\n fallbackImg.src = src;\n });\n return { img: fallbackImg };\n}\n\n/**\n * Loads an image from a URL or source element and renders it\n * along with its annotations onto an off-screen HTMLCanvasElement\n * at original native resolution.\n */\nexport async function renderToOffscreenCanvas(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<HTMLCanvasElement> {\n const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);\n cleanup?.();\n return canvas;\n}\n\nexport async function renderToOffscreenCanvasWithCleanup(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<{ canvas: HTMLCanvasElement; cleanup?: () => void }> {\n const { includeAnnotations = true, backgroundColor } = options;\n\n // 1. Resolve clean HTMLImageElement\n const { img, cleanup } = await loadCleanImage(imageSource);\n\n const naturalWidth = img.naturalWidth || img.width;\n const naturalHeight = img.naturalHeight || img.height;\n\n if (naturalWidth === 0 || naturalHeight === 0) {\n cleanup?.();\n throw new Error(\"Invalid image dimensions: width or height is 0\");\n }\n\n // 2. Setup high-res canvas matching native dimensions\n const canvas = document.createElement(\"canvas\");\n canvas.width = naturalWidth;\n canvas.height = naturalHeight;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n cleanup?.();\n throw new Error(\"Unable to obtain 2D canvas context for export\");\n }\n\n // 3. Optional background color\n if (backgroundColor) {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n }\n\n // 4. Draw base image\n ctx.drawImage(img, 0, 0, naturalWidth, naturalHeight);\n\n // 5. Burn in annotations if requested\n if (includeAnnotations && strokes.length > 0) {\n for (const stroke of strokes) {\n if (stroke.points.length === 0) continue;\n\n ctx.save();\n ctx.beginPath();\n ctx.lineCap = \"round\";\n ctx.lineJoin = \"round\";\n\n // Scale stroke width relative to the maximum dimension of the image\n const strokeScale = Math.max(naturalWidth, naturalHeight) / 1000;\n ctx.lineWidth = Math.max(1, stroke.width * strokeScale);\n\n if (stroke.tool === \"highlighter\") {\n ctx.globalAlpha = stroke.opacity || 0.35;\n ctx.strokeStyle = hexToRgba(stroke.color, 1);\n ctx.globalCompositeOperation = \"multiply\";\n } else {\n ctx.globalAlpha = stroke.opacity || 1.0;\n ctx.strokeStyle = stroke.color;\n ctx.globalCompositeOperation = \"source-over\";\n }\n\n const p0 = stroke.points[0];\n const startX = p0.x * naturalWidth;\n const startY = p0.y * naturalHeight;\n ctx.moveTo(startX, startY);\n\n if (stroke.points.length === 1) {\n ctx.lineTo(startX + 0.5, startY + 0.5);\n } else {\n for (let i = 1; i < stroke.points.length - 1; i++) {\n const pt = stroke.points[i];\n const next = stroke.points[i + 1];\n const cx = (pt.x * naturalWidth + next.x * naturalWidth) / 2;\n const cy = (pt.y * naturalHeight + next.y * naturalHeight) / 2;\n ctx.quadraticCurveTo(pt.x * naturalWidth, pt.y * naturalHeight, cx, cy);\n }\n const last = stroke.points[stroke.points.length - 1];\n ctx.lineTo(last.x * naturalWidth, last.y * naturalHeight);\n }\n\n ctx.stroke();\n ctx.restore();\n }\n }\n\n return { canvas, cleanup };\n}\n\n/**\n * Triggers a browser download of the image (with or without annotations).\n * Guarantees zero unhandled SecurityError on tainted canvases with automatic fallback.\n */\nexport async function downloadAnnotatedImage(\n imageSource: string | HTMLImageElement,\n strokes: AnnotationStroke[],\n options: ExportOptions = {}\n): Promise<void> {\n const {\n format = \"image/png\",\n quality = 0.95,\n fileName = `tbib-annotated-image-${Date.now()}.${format === \"image/jpeg\" ? \"jpg\" : format === \"image/webp\" ? \"webp\" : \"png\"}`,\n } = options;\n\n const src = typeof imageSource === \"string\" ? imageSource : imageSource.src;\n\n const directDownload = (downloadUrl: string, name: string) => {\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = name;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n try {\n const { canvas, cleanup } = await renderToOffscreenCanvasWithCleanup(imageSource, strokes, options);\n\n await new Promise<void>((resolve) => {\n try {\n canvas.toBlob(\n (blob) => {\n cleanup?.();\n if (!blob) {\n directDownload(src, fileName);\n resolve();\n return;\n }\n\n const url = URL.createObjectURL(blob);\n directDownload(url, fileName);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n resolve();\n },\n format,\n quality\n );\n } catch (taintedErr) {\n cleanup?.();\n console.warn(\"Canvas was tainted by cross-origin security rules, downloading direct asset:\", taintedErr);\n directDownload(src, fileName);\n resolve();\n }\n });\n } catch (err) {\n console.warn(\"Offscreen canvas render encountered an issue, falling back to direct asset download:\", err);\n directDownload(src, fileName);\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tbib-image-viewer/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Framework-agnostic coordinate math, annotation engine, stroke smoothing, and native-res canvas export for TBiB Image Viewer",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|