@tsdraw/core 0.8.4 → 0.9.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/dist/index.d.cts CHANGED
@@ -169,7 +169,13 @@ declare function setViewport(viewport: Viewport, updater: {
169
169
  zoom?: number;
170
170
  }): Viewport;
171
171
  declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
172
- declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
172
+ interface ZoomRange {
173
+ min: number;
174
+ max: number;
175
+ }
176
+ declare const DEFAULT_ZOOM_RANGE: ZoomRange;
177
+ declare function clampZoom(zoom: number, range?: ZoomRange): number;
178
+ declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number, zoomRange?: ZoomRange): Viewport;
173
179
 
174
180
  interface IEditor {
175
181
  getZoomLevel(): number;
@@ -264,7 +270,10 @@ declare function moveCameraPan(session: CameraPanSession, currentScreenX: number
264
270
  interface CameraSlideAnimation {
265
271
  stop: () => void;
266
272
  }
267
- declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void): CameraSlideAnimation | null;
273
+ interface CameraSlideOptions {
274
+ friction?: number;
275
+ }
276
+ declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void, slideOptions?: CameraSlideOptions): CameraSlideAnimation | null;
268
277
 
269
278
  type TsdrawRenderTheme = 'light' | 'dark';
270
279
  declare function resolveThemeColor(colorStyle: string, theme: TsdrawRenderTheme): string;
@@ -273,7 +282,8 @@ interface ICanvasRenderer {
273
282
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
274
283
  }
275
284
  declare class CanvasRenderer implements ICanvasRenderer {
276
- private theme;
285
+ private _theme;
286
+ get theme(): TsdrawRenderTheme;
277
287
  setTheme(theme: TsdrawRenderTheme): void;
278
288
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
279
289
  private paintStroke;
@@ -281,6 +291,22 @@ declare class CanvasRenderer implements ICanvasRenderer {
281
291
  private paintClosedShapeFill;
282
292
  }
283
293
 
294
+ type TsdrawBackgroundType = 'blank' | 'lines' | 'grid' | 'dots';
295
+ interface TsdrawBackgroundPreset {
296
+ type: TsdrawBackgroundType;
297
+ color?: string;
298
+ colorDark?: string;
299
+ spacing?: number;
300
+ size?: number;
301
+ opacity?: number;
302
+ }
303
+ interface TsdrawBackgroundCustom {
304
+ type: 'custom';
305
+ render: (ctx: CanvasRenderingContext2D, viewport: Viewport, canvasWidth: number, canvasHeight: number) => void;
306
+ }
307
+ type TsdrawBackgroundOptions = TsdrawBackgroundPreset | TsdrawBackgroundCustom;
308
+ declare function renderCanvasBackground(ctx: CanvasRenderingContext2D, viewport: Viewport, canvasWidth: number, canvasHeight: number, options: TsdrawBackgroundOptions | undefined, theme: TsdrawRenderTheme): void;
309
+
284
310
  type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand' | 'square' | 'circle';
285
311
  type ToolId = DefaultToolId | (string & {});
286
312
  interface ToolDefinition {
@@ -313,6 +339,7 @@ interface EditorOptions {
313
339
  dragDistanceSquared?: number;
314
340
  toolDefinitions?: ToolDefinition[];
315
341
  initialToolId?: ToolId;
342
+ zoomRange?: ZoomRange;
316
343
  }
317
344
  type EditorListener = () => void;
318
345
  declare class Editor {
@@ -323,6 +350,7 @@ declare class Editor {
323
350
  viewport: Viewport;
324
351
  readonly options: {
325
352
  dragDistanceSquared: number;
353
+ zoomRange?: ZoomRange;
326
354
  };
327
355
  private drawStyle;
328
356
  private readonly toolStateContext;
@@ -656,4 +684,4 @@ declare function decodePathToPoints(segments: {
656
684
  y: number;
657
685
  }[];
658
686
 
659
- export { type Bounds, type CameraPanSession, type CameraSlideAnimation, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
687
+ export { type Bounds, type CameraPanSession, type CameraSlideAnimation, type CameraSlideOptions, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DEFAULT_ZOOM_RANGE, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawBackgroundCustom, type TsdrawBackgroundOptions, type TsdrawBackgroundPreset, type TsdrawBackgroundType, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, type ZoomRange, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, clampZoom, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, renderCanvasBackground, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
package/dist/index.d.ts CHANGED
@@ -169,7 +169,13 @@ declare function setViewport(viewport: Viewport, updater: {
169
169
  zoom?: number;
170
170
  }): Viewport;
171
171
  declare function panViewport(viewport: Viewport, dx: number, dy: number): Viewport;
172
- declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number): Viewport;
172
+ interface ZoomRange {
173
+ min: number;
174
+ max: number;
175
+ }
176
+ declare const DEFAULT_ZOOM_RANGE: ZoomRange;
177
+ declare function clampZoom(zoom: number, range?: ZoomRange): number;
178
+ declare function zoomViewport(viewport: Viewport, factor: number, centerX?: number, centerY?: number, zoomRange?: ZoomRange): Viewport;
173
179
 
174
180
  interface IEditor {
175
181
  getZoomLevel(): number;
@@ -264,7 +270,10 @@ declare function moveCameraPan(session: CameraPanSession, currentScreenX: number
264
270
  interface CameraSlideAnimation {
265
271
  stop: () => void;
266
272
  }
267
- declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void): CameraSlideAnimation | null;
273
+ interface CameraSlideOptions {
274
+ friction?: number;
275
+ }
276
+ declare function startCameraSlide(session: CameraPanSession, applyPan: (dx: number, dy: number) => void, onFrame: () => void, slideOptions?: CameraSlideOptions): CameraSlideAnimation | null;
268
277
 
269
278
  type TsdrawRenderTheme = 'light' | 'dark';
270
279
  declare function resolveThemeColor(colorStyle: string, theme: TsdrawRenderTheme): string;
@@ -273,7 +282,8 @@ interface ICanvasRenderer {
273
282
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
274
283
  }
275
284
  declare class CanvasRenderer implements ICanvasRenderer {
276
- private theme;
285
+ private _theme;
286
+ get theme(): TsdrawRenderTheme;
277
287
  setTheme(theme: TsdrawRenderTheme): void;
278
288
  render(ctx: CanvasRenderingContext2D, viewport: Viewport, shapes: Shape[]): void;
279
289
  private paintStroke;
@@ -281,6 +291,22 @@ declare class CanvasRenderer implements ICanvasRenderer {
281
291
  private paintClosedShapeFill;
282
292
  }
283
293
 
294
+ type TsdrawBackgroundType = 'blank' | 'lines' | 'grid' | 'dots';
295
+ interface TsdrawBackgroundPreset {
296
+ type: TsdrawBackgroundType;
297
+ color?: string;
298
+ colorDark?: string;
299
+ spacing?: number;
300
+ size?: number;
301
+ opacity?: number;
302
+ }
303
+ interface TsdrawBackgroundCustom {
304
+ type: 'custom';
305
+ render: (ctx: CanvasRenderingContext2D, viewport: Viewport, canvasWidth: number, canvasHeight: number) => void;
306
+ }
307
+ type TsdrawBackgroundOptions = TsdrawBackgroundPreset | TsdrawBackgroundCustom;
308
+ declare function renderCanvasBackground(ctx: CanvasRenderingContext2D, viewport: Viewport, canvasWidth: number, canvasHeight: number, options: TsdrawBackgroundOptions | undefined, theme: TsdrawRenderTheme): void;
309
+
284
310
  type DefaultToolId = 'pen' | 'eraser' | 'select' | 'hand' | 'square' | 'circle';
285
311
  type ToolId = DefaultToolId | (string & {});
286
312
  interface ToolDefinition {
@@ -313,6 +339,7 @@ interface EditorOptions {
313
339
  dragDistanceSquared?: number;
314
340
  toolDefinitions?: ToolDefinition[];
315
341
  initialToolId?: ToolId;
342
+ zoomRange?: ZoomRange;
316
343
  }
317
344
  type EditorListener = () => void;
318
345
  declare class Editor {
@@ -323,6 +350,7 @@ declare class Editor {
323
350
  viewport: Viewport;
324
351
  readonly options: {
325
352
  dragDistanceSquared: number;
353
+ zoomRange?: ZoomRange;
326
354
  };
327
355
  private drawStyle;
328
356
  private readonly toolStateContext;
@@ -656,4 +684,4 @@ declare function decodePathToPoints(segments: {
656
684
  y: number;
657
685
  }[];
658
686
 
659
- export { type Bounds, type CameraPanSession, type CameraSlideAnimation, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
687
+ export { type Bounds, type CameraPanSession, type CameraSlideAnimation, type CameraSlideOptions, CanvasRenderer, CircleDrawingState, CircleIdleState, type ColorStyle, DEFAULT_COLORS, DEFAULT_ZOOM_RANGE, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, type FillStyle, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, SquareDrawingState, SquareIdleState, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawBackgroundCustom, type TsdrawBackgroundOptions, type TsdrawBackgroundPreset, type TsdrawBackgroundType, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, type ZoomRange, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, clampZoom, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, renderCanvasBackground, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
package/dist/index.js CHANGED
@@ -247,8 +247,13 @@ function setViewport(viewport, updater) {
247
247
  function panViewport(viewport, dx, dy) {
248
248
  return { ...viewport, x: viewport.x + dx, y: viewport.y + dy };
249
249
  }
250
- function zoomViewport(viewport, factor, centerX, centerY) {
251
- const zoom = Math.max(0.1, Math.min(4, viewport.zoom * factor));
250
+ var DEFAULT_ZOOM_RANGE = { min: 0.1, max: 4 };
251
+ function clampZoom(zoom, range) {
252
+ const { min, max } = range ?? DEFAULT_ZOOM_RANGE;
253
+ return Math.max(min, Math.min(max, zoom));
254
+ }
255
+ function zoomViewport(viewport, factor, centerX, centerY, zoomRange) {
256
+ const zoom = clampZoom(viewport.zoom * factor, zoomRange);
252
257
  if (centerX == null || centerY == null) {
253
258
  return { ...viewport, zoom };
254
259
  }
@@ -303,7 +308,8 @@ var SLIDE_FRICTION = 0.92;
303
308
  var SLIDE_MIN_SPEED = 0.01;
304
309
  var SLIDE_MAX_SPEED = 2;
305
310
  var SLIDE_MIN_VELOCITY_TO_START = 0.1;
306
- function startCameraSlide(session, applyPan, onFrame) {
311
+ function startCameraSlide(session, applyPan, onFrame, slideOptions) {
312
+ const friction = slideOptions?.friction ?? SLIDE_FRICTION;
307
313
  const timeSinceLastMove = performance.now() - session.lastMoveTime;
308
314
  const FRAME_DURATION = 16;
309
315
  const decayFactor = Math.pow(1 - VELOCITY_LERP_FACTOR, timeSinceLastMove / FRAME_DURATION);
@@ -323,7 +329,7 @@ function startCameraSlide(session, applyPan, onFrame) {
323
329
  lastTime = now;
324
330
  applyPan(dirX * currentSpeed * elapsed, dirY * currentSpeed * elapsed);
325
331
  onFrame();
326
- currentSpeed *= SLIDE_FRICTION;
332
+ currentSpeed *= friction;
327
333
  if (currentSpeed < SLIDE_MIN_SPEED) {
328
334
  rafId = 0;
329
335
  return;
@@ -364,9 +370,12 @@ function resolveThemeColor(colorStyle, theme) {
364
370
  return DARK_COLORS[colorStyle] ?? lightThemeColor;
365
371
  }
366
372
  var CanvasRenderer = class {
367
- theme = "light";
373
+ _theme = "light";
374
+ get theme() {
375
+ return this._theme;
376
+ }
368
377
  setTheme(theme) {
369
- this.theme = theme;
378
+ this._theme = theme;
370
379
  }
371
380
  render(ctx, viewport, shapes) {
372
381
  ctx.save();
@@ -384,7 +393,7 @@ var CanvasRenderer = class {
384
393
  const width = (STROKE_WIDTHS[shape.props.size] ?? 3.5) * shape.props.scale;
385
394
  const samples = flattenSegments(shape);
386
395
  if (samples.length === 0) return;
387
- const color = resolveThemeColor(shape.props.color, this.theme);
396
+ const color = resolveThemeColor(shape.props.color, this._theme);
388
397
  const fillStyle = shape.props.fill ?? "none";
389
398
  if (shape.props.isClosed && fillStyle !== "none") {
390
399
  this.paintClosedShapeFill(ctx, samples, color, fillStyle);
@@ -450,7 +459,7 @@ var CanvasRenderer = class {
450
459
  ctx.fillStyle = color;
451
460
  ctx.globalAlpha = 0.55;
452
461
  } else if (fillStyle === "none") {
453
- ctx.fillStyle = this.theme === "dark" ? "#0f0f0f" : "#fafafa";
462
+ ctx.fillStyle = this._theme === "dark" ? "#0f0f0f" : "#fafafa";
454
463
  ctx.globalAlpha = 1;
455
464
  } else {
456
465
  ctx.fillStyle = color;
@@ -546,6 +555,102 @@ function getLineDash(dash, width) {
546
555
  }
547
556
  }
548
557
 
558
+ // src/canvas/backgroundRenderer.ts
559
+ var DEFAULT_SPACING = 20;
560
+ var DEFAULT_LINE_WIDTH = 0.5;
561
+ var DEFAULT_DOT_RADIUS = 1;
562
+ var DEFAULT_OPACITY = 0.25;
563
+ function resolvePresetPatternColor(colorLight, colorDark, theme) {
564
+ if (theme === "dark") return colorDark ?? colorLight ?? "#888888";
565
+ return colorLight ?? "#c0c0c0";
566
+ }
567
+ function visiblePageRect(viewport, canvasWidth, canvasHeight) {
568
+ return {
569
+ minX: (0 - viewport.x) / viewport.zoom,
570
+ minY: (0 - viewport.y) / viewport.zoom,
571
+ maxX: (canvasWidth - viewport.x) / viewport.zoom,
572
+ maxY: (canvasHeight - viewport.y) / viewport.zoom
573
+ };
574
+ }
575
+ function drawHorizontalLines(ctx, visible, spacing, lineWidth, color, opacity) {
576
+ const startY = Math.floor(visible.minY / spacing) * spacing;
577
+ ctx.save();
578
+ ctx.strokeStyle = color;
579
+ ctx.lineWidth = lineWidth / ctx.getTransform().a;
580
+ ctx.globalAlpha = opacity;
581
+ ctx.beginPath();
582
+ for (let y = startY; y <= visible.maxY; y += spacing) {
583
+ ctx.moveTo(visible.minX, y);
584
+ ctx.lineTo(visible.maxX, y);
585
+ }
586
+ ctx.stroke();
587
+ ctx.restore();
588
+ }
589
+ function drawGridLines(ctx, visible, spacing, lineWidth, color, opacity) {
590
+ const startX = Math.floor(visible.minX / spacing) * spacing;
591
+ const startY = Math.floor(visible.minY / spacing) * spacing;
592
+ const compensatedWidth = lineWidth / ctx.getTransform().a;
593
+ ctx.save();
594
+ ctx.strokeStyle = color;
595
+ ctx.lineWidth = compensatedWidth;
596
+ ctx.globalAlpha = opacity;
597
+ ctx.beginPath();
598
+ for (let x = startX; x <= visible.maxX; x += spacing) {
599
+ ctx.moveTo(x, visible.minY);
600
+ ctx.lineTo(x, visible.maxY);
601
+ }
602
+ for (let y = startY; y <= visible.maxY; y += spacing) {
603
+ ctx.moveTo(visible.minX, y);
604
+ ctx.lineTo(visible.maxX, y);
605
+ }
606
+ ctx.stroke();
607
+ ctx.restore();
608
+ }
609
+ function drawDotPattern(ctx, visible, spacing, dotRadius, color, opacity) {
610
+ const startX = Math.floor(visible.minX / spacing) * spacing;
611
+ const startY = Math.floor(visible.minY / spacing) * spacing;
612
+ const compensatedRadius = dotRadius / ctx.getTransform().a;
613
+ ctx.save();
614
+ ctx.fillStyle = color;
615
+ ctx.globalAlpha = opacity;
616
+ ctx.beginPath();
617
+ for (let x = startX; x <= visible.maxX; x += spacing) {
618
+ for (let y = startY; y <= visible.maxY; y += spacing) {
619
+ ctx.moveTo(x + compensatedRadius, y);
620
+ ctx.arc(x, y, compensatedRadius, 0, Math.PI * 2);
621
+ }
622
+ }
623
+ ctx.fill();
624
+ ctx.restore();
625
+ }
626
+ function renderCanvasBackground(ctx, viewport, canvasWidth, canvasHeight, options, theme) {
627
+ if (!options || options.type === "blank") return;
628
+ if (options.type === "custom") {
629
+ options.render(ctx, viewport, canvasWidth, canvasHeight);
630
+ return;
631
+ }
632
+ const spacing = options.spacing ?? DEFAULT_SPACING;
633
+ if (spacing <= 0) return;
634
+ const color = resolvePresetPatternColor(options.color, options.colorDark, theme);
635
+ const opacity = options.opacity ?? DEFAULT_OPACITY;
636
+ const visible = visiblePageRect(viewport, canvasWidth, canvasHeight);
637
+ ctx.save();
638
+ ctx.translate(viewport.x, viewport.y);
639
+ ctx.scale(viewport.zoom, viewport.zoom);
640
+ switch (options.type) {
641
+ case "lines":
642
+ drawHorizontalLines(ctx, visible, spacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity);
643
+ break;
644
+ case "grid":
645
+ drawGridLines(ctx, visible, spacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity);
646
+ break;
647
+ case "dots":
648
+ drawDotPattern(ctx, visible, spacing, options.size ?? DEFAULT_DOT_RADIUS, color, opacity);
649
+ break;
650
+ }
651
+ ctx.restore();
652
+ }
653
+
549
654
  // src/input/inputManager.ts
550
655
  var InputManager = class {
551
656
  _current = { x: 0, y: 0 };
@@ -1709,7 +1814,7 @@ var Editor = class {
1709
1814
  historyBatchChanged = false;
1710
1815
  // Creates a new editor instance with the given options (with defaults if not provided)
1711
1816
  constructor(opts = {}) {
1712
- this.options = { dragDistanceSquared: opts.dragDistanceSquared ?? DRAG_DISTANCE_SQUARED };
1817
+ this.options = { dragDistanceSquared: opts.dragDistanceSquared ?? DRAG_DISTANCE_SQUARED, zoomRange: opts.zoomRange };
1713
1818
  this.lastDocumentSnapshot = this.getDocumentSnapshot();
1714
1819
  this.store.listen(() => {
1715
1820
  this.captureDocumentHistory();
@@ -1823,7 +1928,7 @@ var Editor = class {
1823
1928
  this.viewport = {
1824
1929
  x: partial.x ?? this.viewport.x,
1825
1930
  y: partial.y ?? this.viewport.y,
1826
- zoom: Math.max(0.1, Math.min(4, rawZoom))
1931
+ zoom: clampZoom(rawZoom, this.options.zoomRange)
1827
1932
  };
1828
1933
  this.emitChange();
1829
1934
  }
@@ -1834,7 +1939,7 @@ var Editor = class {
1834
1939
  });
1835
1940
  }
1836
1941
  zoomAt(factor, screenX, screenY) {
1837
- this.viewport = zoomViewport(this.viewport, factor, screenX, screenY);
1942
+ this.viewport = zoomViewport(this.viewport, factor, screenX, screenY, this.options.zoomRange);
1838
1943
  this.emitChange();
1839
1944
  }
1840
1945
  deleteShapes(ids) {
@@ -2254,6 +2359,6 @@ function applyResize(editor, handle, startBounds, startShapes, pointer, lockAspe
2254
2359
  }
2255
2360
  }
2256
2361
 
2257
- export { CanvasRenderer, CircleDrawingState, CircleIdleState, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, SquareDrawingState, SquareIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
2362
+ export { CanvasRenderer, CircleDrawingState, CircleIdleState, DEFAULT_COLORS, DEFAULT_ZOOM_RANGE, DRAG_DISTANCE_SQUARED, DocumentStore, ERASER_MARGIN, Editor, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, InputManager, MAX_POINTS_PER_SHAPE, PenDrawingState, PenIdleState, STROKE_WIDTHS, SelectIdleState, SquareDrawingState, SquareIdleState, StateNode, ToolManager, applyMove, applyResize, applyRotation, beginCameraPan, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, clampZoom, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds2 as getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, moveCameraPan, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, renderCanvasBackground, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, startCameraSlide, zoomViewport };
2258
2363
  //# sourceMappingURL=index.js.map
2259
2364
  //# sourceMappingURL=index.js.map