@fieldnotes/core 0.50.8 → 0.52.0
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/README.md +704 -675
- package/dist/index.cjs +267 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -4
- package/dist/index.d.ts +38 -4
- package/dist/index.js +267 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -447,15 +447,49 @@ declare class HistoryStack {
|
|
|
447
447
|
private notifyChange;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
|
|
450
|
+
type HtmlExportRenderer = (element: HtmlElement) => CanvasImageSource | null | Promise<CanvasImageSource | null>;
|
|
451
|
+
type HtmlExportErrorReason = 'unsupported' | 'timeout' | 'render' | 'encode';
|
|
452
|
+
interface HtmlExportError {
|
|
453
|
+
elementId: string;
|
|
454
|
+
htmlType?: string;
|
|
455
|
+
reason: HtmlExportErrorReason;
|
|
456
|
+
cause?: unknown;
|
|
457
|
+
}
|
|
458
|
+
interface HtmlExportOptions {
|
|
459
|
+
/** Converts an application-owned HTML embed into a ready canvas image source. */
|
|
460
|
+
renderHtml?: HtmlExportRenderer;
|
|
461
|
+
/** Maximum wait for each HTML renderer. Defaults to 10 seconds. */
|
|
462
|
+
htmlTimeoutMs?: number;
|
|
463
|
+
/** Called when an HTML embed cannot be rendered or encoded. The export continues. */
|
|
464
|
+
onHtmlError?: (error: HtmlExportError) => void;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
interface ExportImageOptions extends ExportResourceOptions, HtmlExportOptions {
|
|
451
468
|
scale?: number;
|
|
452
469
|
padding?: number;
|
|
453
470
|
background?: string;
|
|
454
471
|
filter?: (element: CanvasElement) => boolean;
|
|
455
472
|
}
|
|
473
|
+
type ExportAssetErrorReason = 'load' | 'timeout' | 'encode';
|
|
474
|
+
interface ExportAssetError {
|
|
475
|
+
elementId: string;
|
|
476
|
+
src: string;
|
|
477
|
+
reason: ExportAssetErrorReason;
|
|
478
|
+
cause?: unknown;
|
|
479
|
+
}
|
|
480
|
+
interface ExportResourceOptions {
|
|
481
|
+
/** Maximum wait for each image asset. Defaults to 10 seconds. */
|
|
482
|
+
imageTimeoutMs?: number;
|
|
483
|
+
/** Maximum width or height of any allocated export canvas. Defaults to 16,384. */
|
|
484
|
+
maxDimension?: number;
|
|
485
|
+
/** Maximum pixel count of any allocated export canvas. Defaults to 67,108,864. */
|
|
486
|
+
maxPixels?: number;
|
|
487
|
+
/** Called when an image cannot be loaded or embedded. The export continues. */
|
|
488
|
+
onAssetError?: (error: ExportAssetError) => void;
|
|
489
|
+
}
|
|
456
490
|
declare function exportImage(store: ElementStore, options?: ExportImageOptions, layerManager?: LayerManager): Promise<Blob | null>;
|
|
457
491
|
|
|
458
|
-
interface ExportSvgOptions {
|
|
492
|
+
interface ExportSvgOptions extends ExportResourceOptions, HtmlExportOptions {
|
|
459
493
|
padding?: number;
|
|
460
494
|
background?: string;
|
|
461
495
|
filter?: (el: CanvasElement) => boolean;
|
|
@@ -1123,6 +1157,6 @@ declare class LaserTool implements Tool {
|
|
|
1123
1157
|
private notifyOptionsChange;
|
|
1124
1158
|
}
|
|
1125
1159
|
|
|
1126
|
-
declare const VERSION = "0.
|
|
1160
|
+
declare const VERSION = "0.52.0";
|
|
1127
1161
|
|
|
1128
|
-
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
1162
|
+
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportAssetError, type ExportAssetErrorReason, type ExportImageOptions, type ExportResourceOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type HtmlExportError, type HtmlExportErrorReason, type HtmlExportOptions, type HtmlExportRenderer, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.d.ts
CHANGED
|
@@ -447,15 +447,49 @@ declare class HistoryStack {
|
|
|
447
447
|
private notifyChange;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
|
|
450
|
+
type HtmlExportRenderer = (element: HtmlElement) => CanvasImageSource | null | Promise<CanvasImageSource | null>;
|
|
451
|
+
type HtmlExportErrorReason = 'unsupported' | 'timeout' | 'render' | 'encode';
|
|
452
|
+
interface HtmlExportError {
|
|
453
|
+
elementId: string;
|
|
454
|
+
htmlType?: string;
|
|
455
|
+
reason: HtmlExportErrorReason;
|
|
456
|
+
cause?: unknown;
|
|
457
|
+
}
|
|
458
|
+
interface HtmlExportOptions {
|
|
459
|
+
/** Converts an application-owned HTML embed into a ready canvas image source. */
|
|
460
|
+
renderHtml?: HtmlExportRenderer;
|
|
461
|
+
/** Maximum wait for each HTML renderer. Defaults to 10 seconds. */
|
|
462
|
+
htmlTimeoutMs?: number;
|
|
463
|
+
/** Called when an HTML embed cannot be rendered or encoded. The export continues. */
|
|
464
|
+
onHtmlError?: (error: HtmlExportError) => void;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
interface ExportImageOptions extends ExportResourceOptions, HtmlExportOptions {
|
|
451
468
|
scale?: number;
|
|
452
469
|
padding?: number;
|
|
453
470
|
background?: string;
|
|
454
471
|
filter?: (element: CanvasElement) => boolean;
|
|
455
472
|
}
|
|
473
|
+
type ExportAssetErrorReason = 'load' | 'timeout' | 'encode';
|
|
474
|
+
interface ExportAssetError {
|
|
475
|
+
elementId: string;
|
|
476
|
+
src: string;
|
|
477
|
+
reason: ExportAssetErrorReason;
|
|
478
|
+
cause?: unknown;
|
|
479
|
+
}
|
|
480
|
+
interface ExportResourceOptions {
|
|
481
|
+
/** Maximum wait for each image asset. Defaults to 10 seconds. */
|
|
482
|
+
imageTimeoutMs?: number;
|
|
483
|
+
/** Maximum width or height of any allocated export canvas. Defaults to 16,384. */
|
|
484
|
+
maxDimension?: number;
|
|
485
|
+
/** Maximum pixel count of any allocated export canvas. Defaults to 67,108,864. */
|
|
486
|
+
maxPixels?: number;
|
|
487
|
+
/** Called when an image cannot be loaded or embedded. The export continues. */
|
|
488
|
+
onAssetError?: (error: ExportAssetError) => void;
|
|
489
|
+
}
|
|
456
490
|
declare function exportImage(store: ElementStore, options?: ExportImageOptions, layerManager?: LayerManager): Promise<Blob | null>;
|
|
457
491
|
|
|
458
|
-
interface ExportSvgOptions {
|
|
492
|
+
interface ExportSvgOptions extends ExportResourceOptions, HtmlExportOptions {
|
|
459
493
|
padding?: number;
|
|
460
494
|
background?: string;
|
|
461
495
|
filter?: (el: CanvasElement) => boolean;
|
|
@@ -1123,6 +1157,6 @@ declare class LaserTool implements Tool {
|
|
|
1123
1157
|
private notifyOptionsChange;
|
|
1124
1158
|
}
|
|
1125
1159
|
|
|
1126
|
-
declare const VERSION = "0.
|
|
1160
|
+
declare const VERSION = "0.52.0";
|
|
1127
1161
|
|
|
1128
|
-
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
1162
|
+
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportAssetError, type ExportAssetErrorReason, type ExportImageOptions, type ExportResourceOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type HtmlExportError, type HtmlExportErrorReason, type HtmlExportOptions, type HtmlExportRenderer, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.js
CHANGED
|
@@ -5355,7 +5355,71 @@ function renderTextOnCanvas(ctx, text) {
|
|
|
5355
5355
|
ctx.restore();
|
|
5356
5356
|
}
|
|
5357
5357
|
|
|
5358
|
+
// src/canvas/html-export.ts
|
|
5359
|
+
var DEFAULT_HTML_TIMEOUT_MS = 1e4;
|
|
5360
|
+
var TIMEOUT = /* @__PURE__ */ Symbol("html-export-timeout");
|
|
5361
|
+
function validateHtmlExportOptions(options) {
|
|
5362
|
+
const timeoutMs = options.htmlTimeoutMs ?? DEFAULT_HTML_TIMEOUT_MS;
|
|
5363
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
5364
|
+
throw new RangeError("htmlTimeoutMs must be a finite number greater than 0");
|
|
5365
|
+
}
|
|
5366
|
+
return timeoutMs;
|
|
5367
|
+
}
|
|
5368
|
+
async function renderHtmlElements(elements, options) {
|
|
5369
|
+
const timeoutMs = validateHtmlExportOptions(options);
|
|
5370
|
+
const sources = /* @__PURE__ */ new Map();
|
|
5371
|
+
await Promise.all(
|
|
5372
|
+
elements.map(async (element) => {
|
|
5373
|
+
if (!options.renderHtml) {
|
|
5374
|
+
options.onHtmlError?.({
|
|
5375
|
+
elementId: element.id,
|
|
5376
|
+
htmlType: element.htmlType,
|
|
5377
|
+
reason: "unsupported"
|
|
5378
|
+
});
|
|
5379
|
+
return;
|
|
5380
|
+
}
|
|
5381
|
+
let timer;
|
|
5382
|
+
try {
|
|
5383
|
+
const timeout = new Promise((resolve) => {
|
|
5384
|
+
timer = setTimeout(() => resolve(TIMEOUT), timeoutMs);
|
|
5385
|
+
});
|
|
5386
|
+
const result = await Promise.race([Promise.resolve(options.renderHtml(element)), timeout]);
|
|
5387
|
+
if (result === TIMEOUT) {
|
|
5388
|
+
options.onHtmlError?.({
|
|
5389
|
+
elementId: element.id,
|
|
5390
|
+
htmlType: element.htmlType,
|
|
5391
|
+
reason: "timeout"
|
|
5392
|
+
});
|
|
5393
|
+
return;
|
|
5394
|
+
}
|
|
5395
|
+
if (!result) {
|
|
5396
|
+
options.onHtmlError?.({
|
|
5397
|
+
elementId: element.id,
|
|
5398
|
+
htmlType: element.htmlType,
|
|
5399
|
+
reason: "render"
|
|
5400
|
+
});
|
|
5401
|
+
return;
|
|
5402
|
+
}
|
|
5403
|
+
sources.set(element.id, result);
|
|
5404
|
+
} catch (cause) {
|
|
5405
|
+
options.onHtmlError?.({
|
|
5406
|
+
elementId: element.id,
|
|
5407
|
+
htmlType: element.htmlType,
|
|
5408
|
+
reason: "render",
|
|
5409
|
+
cause
|
|
5410
|
+
});
|
|
5411
|
+
} finally {
|
|
5412
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5413
|
+
}
|
|
5414
|
+
})
|
|
5415
|
+
);
|
|
5416
|
+
return sources;
|
|
5417
|
+
}
|
|
5418
|
+
|
|
5358
5419
|
// src/canvas/export-image.ts
|
|
5420
|
+
var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
|
|
5421
|
+
var DEFAULT_MAX_DIMENSION = 16384;
|
|
5422
|
+
var DEFAULT_MAX_PIXELS = 67108864;
|
|
5359
5423
|
var center = (b) => ({ x: b.x + b.w / 2, y: b.y + b.h / 2 });
|
|
5360
5424
|
function getStrokeBounds(el) {
|
|
5361
5425
|
if (el.type !== "stroke") return null;
|
|
@@ -5465,12 +5529,53 @@ function renderGridForBounds(ctx, grid, bounds) {
|
|
|
5465
5529
|
);
|
|
5466
5530
|
}
|
|
5467
5531
|
}
|
|
5468
|
-
function
|
|
5532
|
+
function positiveOption(value, fallback, name) {
|
|
5533
|
+
const resolved = value ?? fallback;
|
|
5534
|
+
if (!Number.isFinite(resolved) || resolved <= 0) {
|
|
5535
|
+
throw new RangeError(`${name} must be a finite number greater than 0`);
|
|
5536
|
+
}
|
|
5537
|
+
return resolved;
|
|
5538
|
+
}
|
|
5539
|
+
function nonNegativeOption(value, fallback, name) {
|
|
5540
|
+
const resolved = value ?? fallback;
|
|
5541
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
5542
|
+
throw new RangeError(`${name} must be a finite number greater than or equal to 0`);
|
|
5543
|
+
}
|
|
5544
|
+
return resolved;
|
|
5545
|
+
}
|
|
5546
|
+
function assertExportSize(width, height, options) {
|
|
5547
|
+
const maxDimension = positiveOption(options.maxDimension, DEFAULT_MAX_DIMENSION, "maxDimension");
|
|
5548
|
+
const maxPixels = positiveOption(options.maxPixels, DEFAULT_MAX_PIXELS, "maxPixels");
|
|
5549
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
5550
|
+
throw new RangeError("Export dimensions must be finite numbers greater than 0");
|
|
5551
|
+
}
|
|
5552
|
+
if (width > maxDimension || height > maxDimension) {
|
|
5553
|
+
throw new RangeError(
|
|
5554
|
+
`Export dimensions ${width}x${height} exceed the maximum dimension of ${maxDimension}`
|
|
5555
|
+
);
|
|
5556
|
+
}
|
|
5557
|
+
if (width * height > maxPixels) {
|
|
5558
|
+
throw new RangeError(
|
|
5559
|
+
`Export size ${width}x${height} exceeds the maximum of ${maxPixels} pixels`
|
|
5560
|
+
);
|
|
5561
|
+
}
|
|
5562
|
+
}
|
|
5563
|
+
function validateExportResourceOptions(options) {
|
|
5564
|
+
positiveOption(options.imageTimeoutMs, DEFAULT_IMAGE_TIMEOUT_MS, "imageTimeoutMs");
|
|
5565
|
+
positiveOption(options.maxDimension, DEFAULT_MAX_DIMENSION, "maxDimension");
|
|
5566
|
+
positiveOption(options.maxPixels, DEFAULT_MAX_PIXELS, "maxPixels");
|
|
5567
|
+
}
|
|
5568
|
+
function loadImages(elements, options = {}) {
|
|
5469
5569
|
const imageElements = elements.filter(
|
|
5470
5570
|
(el) => el.type === "image" && "src" in el
|
|
5471
5571
|
);
|
|
5472
5572
|
const cache3 = /* @__PURE__ */ new Map();
|
|
5473
5573
|
if (imageElements.length === 0) return Promise.resolve(cache3);
|
|
5574
|
+
const timeoutMs = positiveOption(
|
|
5575
|
+
options.imageTimeoutMs,
|
|
5576
|
+
DEFAULT_IMAGE_TIMEOUT_MS,
|
|
5577
|
+
"imageTimeoutMs"
|
|
5578
|
+
);
|
|
5474
5579
|
return new Promise((resolve) => {
|
|
5475
5580
|
let remaining = imageElements.length;
|
|
5476
5581
|
const done = () => {
|
|
@@ -5480,19 +5585,42 @@ function loadImages(elements) {
|
|
|
5480
5585
|
for (const el of imageElements) {
|
|
5481
5586
|
const img = new Image();
|
|
5482
5587
|
img.crossOrigin = "anonymous";
|
|
5588
|
+
let settled = false;
|
|
5589
|
+
const timer = setTimeout(() => {
|
|
5590
|
+
if (settled) return;
|
|
5591
|
+
settled = true;
|
|
5592
|
+
img.onload = null;
|
|
5593
|
+
img.onerror = null;
|
|
5594
|
+
options.onAssetError?.({ elementId: el.id, src: el.src, reason: "timeout" });
|
|
5595
|
+
done();
|
|
5596
|
+
}, timeoutMs);
|
|
5597
|
+
const settle = () => {
|
|
5598
|
+
if (settled) return false;
|
|
5599
|
+
settled = true;
|
|
5600
|
+
clearTimeout(timer);
|
|
5601
|
+
img.onload = null;
|
|
5602
|
+
img.onerror = null;
|
|
5603
|
+
return true;
|
|
5604
|
+
};
|
|
5483
5605
|
img.onload = () => {
|
|
5606
|
+
if (!settle()) return;
|
|
5484
5607
|
cache3.set(el.id, img);
|
|
5485
5608
|
done();
|
|
5486
5609
|
};
|
|
5487
|
-
img.onerror =
|
|
5488
|
-
|
|
5489
|
-
|
|
5610
|
+
img.onerror = (cause) => {
|
|
5611
|
+
if (!settle()) return;
|
|
5612
|
+
options.onAssetError?.({ elementId: el.id, src: el.src, reason: "load", cause });
|
|
5613
|
+
done();
|
|
5614
|
+
};
|
|
5615
|
+
img.src = el.src;
|
|
5490
5616
|
}
|
|
5491
5617
|
});
|
|
5492
5618
|
}
|
|
5493
5619
|
async function exportImage(store, options = {}, layerManager) {
|
|
5494
|
-
const scale = options.scale
|
|
5495
|
-
const padding = options.padding
|
|
5620
|
+
const scale = positiveOption(options.scale, 2, "scale");
|
|
5621
|
+
const padding = nonNegativeOption(options.padding, 0, "padding");
|
|
5622
|
+
validateExportResourceOptions(options);
|
|
5623
|
+
validateHtmlExportOptions(options);
|
|
5496
5624
|
const background = options.background ?? "#ffffff";
|
|
5497
5625
|
const filter = options.filter;
|
|
5498
5626
|
const allElements = store.getAll();
|
|
@@ -5502,10 +5630,15 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5502
5630
|
}
|
|
5503
5631
|
const bounds = computeBounds(visibleElements, padding);
|
|
5504
5632
|
if (!bounds) return null;
|
|
5505
|
-
const
|
|
5633
|
+
const width = Math.ceil(bounds.w * scale);
|
|
5634
|
+
const height = Math.ceil(bounds.h * scale);
|
|
5635
|
+
assertExportSize(width, height, options);
|
|
5636
|
+
const imageCache = await loadImages(visibleElements, options);
|
|
5637
|
+
const htmlElements = visibleElements.filter((el) => el.type === "html");
|
|
5638
|
+
const htmlSources = await renderHtmlElements(htmlElements, options);
|
|
5506
5639
|
const canvas = document.createElement("canvas");
|
|
5507
|
-
canvas.width =
|
|
5508
|
-
canvas.height =
|
|
5640
|
+
canvas.width = width;
|
|
5641
|
+
canvas.height = height;
|
|
5509
5642
|
const ctx = canvas.getContext("2d");
|
|
5510
5643
|
if (!ctx) return null;
|
|
5511
5644
|
ctx.scale(scale, scale);
|
|
@@ -5527,6 +5660,24 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5527
5660
|
return;
|
|
5528
5661
|
}
|
|
5529
5662
|
if (el.type === "html") {
|
|
5663
|
+
const source = htmlSources.get(el.id);
|
|
5664
|
+
if (!source) return;
|
|
5665
|
+
const b = getElementBounds(el);
|
|
5666
|
+
try {
|
|
5667
|
+
withRotation(
|
|
5668
|
+
target,
|
|
5669
|
+
el,
|
|
5670
|
+
b ? center(b) : el.position,
|
|
5671
|
+
() => target.drawImage(source, el.position.x, el.position.y, el.size.w, el.size.h)
|
|
5672
|
+
);
|
|
5673
|
+
} catch (cause) {
|
|
5674
|
+
options.onHtmlError?.({
|
|
5675
|
+
elementId: el.id,
|
|
5676
|
+
htmlType: el.htmlType,
|
|
5677
|
+
reason: "render",
|
|
5678
|
+
cause
|
|
5679
|
+
});
|
|
5680
|
+
}
|
|
5530
5681
|
return;
|
|
5531
5682
|
}
|
|
5532
5683
|
if (el.type === "image") {
|
|
@@ -5682,20 +5833,23 @@ function emitArrow(arrow, store) {
|
|
|
5682
5833
|
return out;
|
|
5683
5834
|
}
|
|
5684
5835
|
function emitImage(image, dataUri) {
|
|
5685
|
-
const href = dataUri ?? image.src;
|
|
5836
|
+
const href = dataUri ?? ("src" in image ? image.src : "");
|
|
5686
5837
|
if (!href) return "";
|
|
5687
5838
|
const { x, y } = image.position;
|
|
5688
5839
|
const { w, h } = image.size;
|
|
5689
5840
|
return `<image href="${esc(href)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
|
|
5690
5841
|
}
|
|
5691
|
-
function emitText(text, rasterScale) {
|
|
5842
|
+
function emitText(text, rasterScale, resourceOptions) {
|
|
5692
5843
|
if (!text.text) return "";
|
|
5693
5844
|
const { x, y } = text.position;
|
|
5694
5845
|
const { w, h } = text.size;
|
|
5695
5846
|
if (typeof document === "undefined") return "";
|
|
5847
|
+
const width = Math.max(1, Math.ceil(w * rasterScale));
|
|
5848
|
+
const height = Math.max(1, Math.ceil(h * rasterScale));
|
|
5849
|
+
assertExportSize(width, height, resourceOptions);
|
|
5696
5850
|
const canvas = document.createElement("canvas");
|
|
5697
|
-
canvas.width =
|
|
5698
|
-
canvas.height =
|
|
5851
|
+
canvas.width = width;
|
|
5852
|
+
canvas.height = height;
|
|
5699
5853
|
const ctx = canvas.getContext("2d");
|
|
5700
5854
|
if (!ctx) return "";
|
|
5701
5855
|
ctx.scale(rasterScale, rasterScale);
|
|
@@ -5710,13 +5864,16 @@ function emitText(text, rasterScale) {
|
|
|
5710
5864
|
if (!dataUri || !dataUri.startsWith("data:")) return "";
|
|
5711
5865
|
return `<image href="${esc(dataUri)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
|
|
5712
5866
|
}
|
|
5713
|
-
function emitNote(note, rasterScale) {
|
|
5867
|
+
function emitNote(note, rasterScale, resourceOptions) {
|
|
5714
5868
|
const { x, y } = note.position;
|
|
5715
5869
|
const { w, h } = note.size;
|
|
5716
5870
|
if (typeof document === "undefined") return emitNotePlaceholder(note);
|
|
5871
|
+
const width = Math.max(1, Math.ceil(w * rasterScale));
|
|
5872
|
+
const height = Math.max(1, Math.ceil(h * rasterScale));
|
|
5873
|
+
assertExportSize(width, height, resourceOptions);
|
|
5717
5874
|
const canvas = document.createElement("canvas");
|
|
5718
|
-
canvas.width =
|
|
5719
|
-
canvas.height =
|
|
5875
|
+
canvas.width = width;
|
|
5876
|
+
canvas.height = height;
|
|
5720
5877
|
const ctx = canvas.getContext("2d");
|
|
5721
5878
|
if (!ctx) return emitNotePlaceholder(note);
|
|
5722
5879
|
ctx.scale(rasterScale, rasterScale);
|
|
@@ -5877,8 +6034,10 @@ function emitHexTemplate(t, grid) {
|
|
|
5877
6034
|
return `<path d="${d}" fill="${esc(t.fillColor)}" stroke="${esc(t.strokeColor)}" stroke-width="${n(t.strokeWidth)}" opacity="${n(t.opacity)}" />`;
|
|
5878
6035
|
}
|
|
5879
6036
|
async function exportSvg(store, options = {}, layerManager) {
|
|
5880
|
-
const padding = options.padding
|
|
5881
|
-
const rasterScale = options.rasterScale
|
|
6037
|
+
const padding = nonNegativeOption(options.padding, 0, "padding");
|
|
6038
|
+
const rasterScale = positiveOption(options.rasterScale, 2, "rasterScale");
|
|
6039
|
+
validateExportResourceOptions(options);
|
|
6040
|
+
validateHtmlExportOptions(options);
|
|
5882
6041
|
const filter = options.filter;
|
|
5883
6042
|
const allElements = store.getAll();
|
|
5884
6043
|
let visibleElements = layerManager ? allElements.filter((el) => layerManager.isLayerVisible(el.layerId)) : allElements;
|
|
@@ -5887,11 +6046,15 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
5887
6046
|
if (!bounds) {
|
|
5888
6047
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"></svg>`;
|
|
5889
6048
|
}
|
|
6049
|
+
assertExportSize(Math.ceil(bounds.w), Math.ceil(bounds.h), options);
|
|
5890
6050
|
const remoteImages = visibleElements.filter(
|
|
5891
6051
|
(el) => el.type === "image" && !el.src.startsWith("data:")
|
|
5892
6052
|
);
|
|
5893
|
-
const imageCache = await loadImages(remoteImages);
|
|
5894
|
-
const imageDataUris = encodeImages(visibleElements, imageCache, rasterScale);
|
|
6053
|
+
const imageCache = await loadImages(remoteImages, options);
|
|
6054
|
+
const imageDataUris = encodeImages(visibleElements, imageCache, rasterScale, options);
|
|
6055
|
+
const htmlElements = visibleElements.filter((el) => el.type === "html");
|
|
6056
|
+
const htmlSources = await renderHtmlElements(htmlElements, options);
|
|
6057
|
+
const htmlDataUris = encodeHtmlElements(htmlElements, htmlSources, rasterScale, options);
|
|
5895
6058
|
const grids = visibleElements.filter((el) => el.type === "grid");
|
|
5896
6059
|
const firstGrid = grids[0];
|
|
5897
6060
|
let body = "";
|
|
@@ -5900,7 +6063,15 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
5900
6063
|
}
|
|
5901
6064
|
const layerBodies = /* @__PURE__ */ new Map();
|
|
5902
6065
|
for (const el of visibleElements) {
|
|
5903
|
-
const emitted = emitElement(
|
|
6066
|
+
const emitted = emitElement(
|
|
6067
|
+
el,
|
|
6068
|
+
imageDataUris,
|
|
6069
|
+
htmlDataUris,
|
|
6070
|
+
rasterScale,
|
|
6071
|
+
firstGrid,
|
|
6072
|
+
store,
|
|
6073
|
+
options
|
|
6074
|
+
);
|
|
5904
6075
|
layerBodies.set(el.layerId, (layerBodies.get(el.layerId) ?? "") + emitted);
|
|
5905
6076
|
}
|
|
5906
6077
|
for (const [layerId, emitted] of layerBodies) {
|
|
@@ -5914,7 +6085,7 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
5914
6085
|
}
|
|
5915
6086
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${n(bounds.w)}" height="${n(bounds.h)}" viewBox="${n(bounds.x)} ${n(bounds.y)} ${n(bounds.w)} ${n(bounds.h)}">${body}</svg>`;
|
|
5916
6087
|
}
|
|
5917
|
-
function emitElement(el, imageDataUris, rasterScale, firstGrid, store) {
|
|
6088
|
+
function emitElement(el, imageDataUris, htmlDataUris, rasterScale, firstGrid, store, resourceOptions) {
|
|
5918
6089
|
switch (el.type) {
|
|
5919
6090
|
case "stroke":
|
|
5920
6091
|
return withRotationSvg(el, emitStroke(el));
|
|
@@ -5925,20 +6096,71 @@ function emitElement(el, imageDataUris, rasterScale, firstGrid, store) {
|
|
|
5925
6096
|
case "image":
|
|
5926
6097
|
return withRotationSvg(el, emitImage(el, imageDataUris.get(el.id)));
|
|
5927
6098
|
case "text":
|
|
5928
|
-
return withRotationSvg(el, emitText(el, rasterScale));
|
|
6099
|
+
return withRotationSvg(el, emitText(el, rasterScale, resourceOptions));
|
|
5929
6100
|
case "note":
|
|
5930
|
-
return withRotationSvg(el, emitNote(el, rasterScale));
|
|
6101
|
+
return withRotationSvg(el, emitNote(el, rasterScale, resourceOptions));
|
|
5931
6102
|
case "template":
|
|
5932
6103
|
return emitTemplate(el, firstGrid);
|
|
5933
6104
|
case "grid":
|
|
5934
6105
|
return "";
|
|
5935
6106
|
case "html":
|
|
5936
|
-
return
|
|
6107
|
+
return withRotationSvg(el, emitImage(el, htmlDataUris.get(el.id)));
|
|
5937
6108
|
default:
|
|
5938
6109
|
return "";
|
|
5939
6110
|
}
|
|
5940
6111
|
}
|
|
5941
|
-
function
|
|
6112
|
+
function encodeHtmlElements(elements, sources, rasterScale, options) {
|
|
6113
|
+
const encoded = /* @__PURE__ */ new Map();
|
|
6114
|
+
for (const element of elements) {
|
|
6115
|
+
const source = sources.get(element.id);
|
|
6116
|
+
if (!source) continue;
|
|
6117
|
+
const width = Math.max(1, Math.ceil(element.size.w * rasterScale));
|
|
6118
|
+
const height = Math.max(1, Math.ceil(element.size.h * rasterScale));
|
|
6119
|
+
assertExportSize(width, height, options);
|
|
6120
|
+
if (typeof document === "undefined") {
|
|
6121
|
+
options.onHtmlError?.({
|
|
6122
|
+
elementId: element.id,
|
|
6123
|
+
htmlType: element.htmlType,
|
|
6124
|
+
reason: "encode"
|
|
6125
|
+
});
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
6128
|
+
const canvas = document.createElement("canvas");
|
|
6129
|
+
canvas.width = width;
|
|
6130
|
+
canvas.height = height;
|
|
6131
|
+
const ctx = canvas.getContext("2d");
|
|
6132
|
+
if (!ctx) {
|
|
6133
|
+
options.onHtmlError?.({
|
|
6134
|
+
elementId: element.id,
|
|
6135
|
+
htmlType: element.htmlType,
|
|
6136
|
+
reason: "encode"
|
|
6137
|
+
});
|
|
6138
|
+
continue;
|
|
6139
|
+
}
|
|
6140
|
+
try {
|
|
6141
|
+
ctx.drawImage(source, 0, 0, width, height);
|
|
6142
|
+
const dataUri = canvas.toDataURL();
|
|
6143
|
+
if (dataUri.startsWith("data:")) {
|
|
6144
|
+
encoded.set(element.id, dataUri);
|
|
6145
|
+
} else {
|
|
6146
|
+
options.onHtmlError?.({
|
|
6147
|
+
elementId: element.id,
|
|
6148
|
+
htmlType: element.htmlType,
|
|
6149
|
+
reason: "encode"
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
6152
|
+
} catch (cause) {
|
|
6153
|
+
options.onHtmlError?.({
|
|
6154
|
+
elementId: element.id,
|
|
6155
|
+
htmlType: element.htmlType,
|
|
6156
|
+
reason: "encode",
|
|
6157
|
+
cause
|
|
6158
|
+
});
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
return encoded;
|
|
6162
|
+
}
|
|
6163
|
+
function encodeImages(elements, imageCache, rasterScale, resourceOptions) {
|
|
5942
6164
|
const out = /* @__PURE__ */ new Map();
|
|
5943
6165
|
for (const el of elements) {
|
|
5944
6166
|
if (el.type !== "image") continue;
|
|
@@ -5948,16 +6170,24 @@ function encodeImages(elements, imageCache, rasterScale) {
|
|
|
5948
6170
|
}
|
|
5949
6171
|
const img = imageCache.get(el.id);
|
|
5950
6172
|
if (!img || typeof document === "undefined") continue;
|
|
6173
|
+
const width = Math.max(1, Math.ceil(el.size.w * rasterScale));
|
|
6174
|
+
const height = Math.max(1, Math.ceil(el.size.h * rasterScale));
|
|
6175
|
+
assertExportSize(width, height, resourceOptions);
|
|
5951
6176
|
const canvas = document.createElement("canvas");
|
|
5952
|
-
canvas.width =
|
|
5953
|
-
canvas.height =
|
|
6177
|
+
canvas.width = width;
|
|
6178
|
+
canvas.height = height;
|
|
5954
6179
|
const ctx = canvas.getContext("2d");
|
|
5955
|
-
if (!ctx)
|
|
6180
|
+
if (!ctx) {
|
|
6181
|
+
resourceOptions.onAssetError?.({ elementId: el.id, src: el.src, reason: "encode" });
|
|
6182
|
+
continue;
|
|
6183
|
+
}
|
|
5956
6184
|
try {
|
|
5957
6185
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
5958
6186
|
const uri = canvas.toDataURL();
|
|
5959
6187
|
if (uri.startsWith("data:")) out.set(el.id, uri);
|
|
5960
|
-
|
|
6188
|
+
else resourceOptions.onAssetError?.({ elementId: el.id, src: el.src, reason: "encode" });
|
|
6189
|
+
} catch (cause) {
|
|
6190
|
+
resourceOptions.onAssetError?.({ elementId: el.id, src: el.src, reason: "encode", cause });
|
|
5961
6191
|
}
|
|
5962
6192
|
}
|
|
5963
6193
|
return out;
|
|
@@ -6506,7 +6736,7 @@ var RenderLoop = class {
|
|
|
6506
6736
|
layerGroups = /* @__PURE__ */ new Map();
|
|
6507
6737
|
gridCacheCanvas = null;
|
|
6508
6738
|
gridCacheCtx = null;
|
|
6509
|
-
|
|
6739
|
+
lastGridRefs = [];
|
|
6510
6740
|
constructor(deps) {
|
|
6511
6741
|
this.canvasEl = deps.canvasEl;
|
|
6512
6742
|
this.camera = deps.camera;
|
|
@@ -6706,8 +6936,8 @@ var RenderLoop = class {
|
|
|
6706
6936
|
}
|
|
6707
6937
|
if (gridElements.length > 0) {
|
|
6708
6938
|
const gridT0 = performance.now();
|
|
6709
|
-
const
|
|
6710
|
-
const gridDirty = this.gridCacheDirty ||
|
|
6939
|
+
const gridsChanged = gridElements.length !== this.lastGridRefs.length || gridElements.some((grid, index) => grid !== this.lastGridRefs[index]);
|
|
6940
|
+
const gridDirty = this.gridCacheDirty || gridsChanged;
|
|
6711
6941
|
if (gridDirty) {
|
|
6712
6942
|
this.ensureGridCache();
|
|
6713
6943
|
if (this.gridCacheCtx && this.gridCacheCanvas) {
|
|
@@ -6735,7 +6965,7 @@ var RenderLoop = class {
|
|
|
6735
6965
|
}
|
|
6736
6966
|
}
|
|
6737
6967
|
this.gridCacheDirty = false;
|
|
6738
|
-
this.
|
|
6968
|
+
this.lastGridRefs = [...gridElements];
|
|
6739
6969
|
}
|
|
6740
6970
|
if (this.gridCacheCanvas) {
|
|
6741
6971
|
const offset = this.marginViewport.compositeOffset(
|
|
@@ -6755,6 +6985,8 @@ var RenderLoop = class {
|
|
|
6755
6985
|
}
|
|
6756
6986
|
}
|
|
6757
6987
|
gridMs = performance.now() - gridT0;
|
|
6988
|
+
} else {
|
|
6989
|
+
this.lastGridRefs = [];
|
|
6758
6990
|
}
|
|
6759
6991
|
const overlayT0 = performance.now();
|
|
6760
6992
|
const activeTool = this.toolManager.activeTool;
|
|
@@ -10728,7 +10960,7 @@ var LaserTool = class {
|
|
|
10728
10960
|
};
|
|
10729
10961
|
|
|
10730
10962
|
// src/index.ts
|
|
10731
|
-
var VERSION = "0.
|
|
10963
|
+
var VERSION = "0.52.0";
|
|
10732
10964
|
export {
|
|
10733
10965
|
ArrowTool,
|
|
10734
10966
|
AutoSave,
|