@fieldnotes/core 0.6.1 → 0.7.1

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
@@ -67,6 +67,7 @@ interface ImageElement extends BaseElement {
67
67
  interface HtmlElement extends BaseElement {
68
68
  type: 'html';
69
69
  size: Size;
70
+ domId?: string;
70
71
  }
71
72
  interface TextElement extends BaseElement {
72
73
  type: 'text';
@@ -85,7 +86,17 @@ interface ShapeElement extends BaseElement {
85
86
  strokeWidth: number;
86
87
  fillColor: string;
87
88
  }
88
- type CanvasElement = StrokeElement | NoteElement | ArrowElement | ImageElement | HtmlElement | TextElement | ShapeElement;
89
+ type HexOrientation = 'pointy' | 'flat';
90
+ interface GridElement extends BaseElement {
91
+ type: 'grid';
92
+ gridType: 'square' | 'hex';
93
+ hexOrientation: HexOrientation;
94
+ cellSize: number;
95
+ strokeColor: string;
96
+ strokeWidth: number;
97
+ opacity: number;
98
+ }
99
+ type CanvasElement = StrokeElement | NoteElement | ArrowElement | ImageElement | HtmlElement | TextElement | ShapeElement | GridElement;
89
100
  type ElementType = CanvasElement['type'];
90
101
 
91
102
  interface Layer {
@@ -437,6 +448,16 @@ declare class Viewport {
437
448
  w: number;
438
449
  h: number;
439
450
  }): string;
451
+ addGrid(input: {
452
+ gridType?: 'square' | 'hex';
453
+ hexOrientation?: 'pointy' | 'flat';
454
+ cellSize?: number;
455
+ strokeColor?: string;
456
+ strokeWidth?: number;
457
+ opacity?: number;
458
+ }): string;
459
+ updateGrid(updates: Partial<Pick<GridElement, 'gridType' | 'hexOrientation' | 'cellSize' | 'strokeColor' | 'strokeWidth' | 'opacity'>>): void;
460
+ removeGrid(): void;
440
461
  destroy(): void;
441
462
  private startRenderLoop;
442
463
  private render;
@@ -457,6 +478,7 @@ declare class Viewport {
457
478
  private hideDomNode;
458
479
  private removeDomNode;
459
480
  private clearDomNodes;
481
+ private reattachHtmlContent;
460
482
  private createWrapper;
461
483
  private createCanvas;
462
484
  private createDomLayer;
@@ -469,8 +491,12 @@ declare class ElementRenderer {
469
491
  private store;
470
492
  private imageCache;
471
493
  private onImageLoad;
494
+ private camera;
495
+ private canvasSize;
472
496
  setStore(store: ElementStore): void;
473
497
  setOnImageLoad(callback: () => void): void;
498
+ setCamera(camera: Camera): void;
499
+ setCanvasSize(w: number, h: number): void;
474
500
  isDomElement(element: CanvasElement): boolean;
475
501
  renderCanvasElement(ctx: CanvasRenderingContext2D, element: CanvasElement): void;
476
502
  private renderStroke;
@@ -480,6 +506,7 @@ declare class ElementRenderer {
480
506
  private renderShape;
481
507
  private fillShapePath;
482
508
  private strokeShapePath;
509
+ private renderGrid;
483
510
  private renderImage;
484
511
  private getImage;
485
512
  }
@@ -539,6 +566,7 @@ interface ImageInput extends BaseDefaults {
539
566
  interface HtmlInput extends BaseDefaults {
540
567
  position: Point;
541
568
  size: Size;
569
+ domId?: string;
542
570
  }
543
571
  interface TextInput extends BaseDefaults {
544
572
  position: Point;
@@ -562,6 +590,15 @@ interface ShapeInput extends BaseDefaults {
562
590
  fillColor?: string;
563
591
  }
564
592
  declare function createShape(input: ShapeInput): ShapeElement;
593
+ interface GridInput extends BaseDefaults {
594
+ gridType?: 'square' | 'hex';
595
+ hexOrientation?: HexOrientation;
596
+ cellSize?: number;
597
+ strokeColor?: string;
598
+ strokeWidth?: number;
599
+ opacity?: number;
600
+ }
601
+ declare function createGrid(input: GridInput): GridElement;
565
602
  declare function createText(input: TextInput): TextElement;
566
603
 
567
604
  interface Rect {
@@ -824,6 +861,6 @@ declare class UpdateLayerCommand implements Command {
824
861
  undo(_store: ElementStore): void;
825
862
  }
826
863
 
827
- declare const VERSION = "0.6.1";
864
+ declare const VERSION = "0.7.0";
828
865
 
829
- export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, RemoveLayerCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
866
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type GridElement, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, RemoveLayerCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
package/dist/index.d.ts CHANGED
@@ -67,6 +67,7 @@ interface ImageElement extends BaseElement {
67
67
  interface HtmlElement extends BaseElement {
68
68
  type: 'html';
69
69
  size: Size;
70
+ domId?: string;
70
71
  }
71
72
  interface TextElement extends BaseElement {
72
73
  type: 'text';
@@ -85,7 +86,17 @@ interface ShapeElement extends BaseElement {
85
86
  strokeWidth: number;
86
87
  fillColor: string;
87
88
  }
88
- type CanvasElement = StrokeElement | NoteElement | ArrowElement | ImageElement | HtmlElement | TextElement | ShapeElement;
89
+ type HexOrientation = 'pointy' | 'flat';
90
+ interface GridElement extends BaseElement {
91
+ type: 'grid';
92
+ gridType: 'square' | 'hex';
93
+ hexOrientation: HexOrientation;
94
+ cellSize: number;
95
+ strokeColor: string;
96
+ strokeWidth: number;
97
+ opacity: number;
98
+ }
99
+ type CanvasElement = StrokeElement | NoteElement | ArrowElement | ImageElement | HtmlElement | TextElement | ShapeElement | GridElement;
89
100
  type ElementType = CanvasElement['type'];
90
101
 
91
102
  interface Layer {
@@ -437,6 +448,16 @@ declare class Viewport {
437
448
  w: number;
438
449
  h: number;
439
450
  }): string;
451
+ addGrid(input: {
452
+ gridType?: 'square' | 'hex';
453
+ hexOrientation?: 'pointy' | 'flat';
454
+ cellSize?: number;
455
+ strokeColor?: string;
456
+ strokeWidth?: number;
457
+ opacity?: number;
458
+ }): string;
459
+ updateGrid(updates: Partial<Pick<GridElement, 'gridType' | 'hexOrientation' | 'cellSize' | 'strokeColor' | 'strokeWidth' | 'opacity'>>): void;
460
+ removeGrid(): void;
440
461
  destroy(): void;
441
462
  private startRenderLoop;
442
463
  private render;
@@ -457,6 +478,7 @@ declare class Viewport {
457
478
  private hideDomNode;
458
479
  private removeDomNode;
459
480
  private clearDomNodes;
481
+ private reattachHtmlContent;
460
482
  private createWrapper;
461
483
  private createCanvas;
462
484
  private createDomLayer;
@@ -469,8 +491,12 @@ declare class ElementRenderer {
469
491
  private store;
470
492
  private imageCache;
471
493
  private onImageLoad;
494
+ private camera;
495
+ private canvasSize;
472
496
  setStore(store: ElementStore): void;
473
497
  setOnImageLoad(callback: () => void): void;
498
+ setCamera(camera: Camera): void;
499
+ setCanvasSize(w: number, h: number): void;
474
500
  isDomElement(element: CanvasElement): boolean;
475
501
  renderCanvasElement(ctx: CanvasRenderingContext2D, element: CanvasElement): void;
476
502
  private renderStroke;
@@ -480,6 +506,7 @@ declare class ElementRenderer {
480
506
  private renderShape;
481
507
  private fillShapePath;
482
508
  private strokeShapePath;
509
+ private renderGrid;
483
510
  private renderImage;
484
511
  private getImage;
485
512
  }
@@ -539,6 +566,7 @@ interface ImageInput extends BaseDefaults {
539
566
  interface HtmlInput extends BaseDefaults {
540
567
  position: Point;
541
568
  size: Size;
569
+ domId?: string;
542
570
  }
543
571
  interface TextInput extends BaseDefaults {
544
572
  position: Point;
@@ -562,6 +590,15 @@ interface ShapeInput extends BaseDefaults {
562
590
  fillColor?: string;
563
591
  }
564
592
  declare function createShape(input: ShapeInput): ShapeElement;
593
+ interface GridInput extends BaseDefaults {
594
+ gridType?: 'square' | 'hex';
595
+ hexOrientation?: HexOrientation;
596
+ cellSize?: number;
597
+ strokeColor?: string;
598
+ strokeWidth?: number;
599
+ opacity?: number;
600
+ }
601
+ declare function createGrid(input: GridInput): GridElement;
565
602
  declare function createText(input: TextInput): TextElement;
566
603
 
567
604
  interface Rect {
@@ -824,6 +861,6 @@ declare class UpdateLayerCommand implements Command {
824
861
  undo(_store: ElementStore): void;
825
862
  }
826
863
 
827
- declare const VERSION = "0.6.1";
864
+ declare const VERSION = "0.7.0";
828
865
 
829
- export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, RemoveLayerCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
866
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type GridElement, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, RemoveLayerCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ function validateState(data) {
84
84
  ];
85
85
  }
86
86
  }
87
- var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text", "shape"]);
87
+ var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text", "shape", "grid"]);
88
88
  function validateElement(el) {
89
89
  if (!el || typeof el !== "object") {
90
90
  throw new Error("Invalid element: expected an object");
@@ -945,6 +945,118 @@ function smoothToSegments(points) {
945
945
  return segments;
946
946
  }
947
947
 
948
+ // src/elements/grid-renderer.ts
949
+ function getSquareGridLines(bounds, cellSize) {
950
+ if (cellSize <= 0) return { verticals: [], horizontals: [] };
951
+ const verticals = [];
952
+ const startX = Math.floor(bounds.minX / cellSize) * cellSize;
953
+ const endX = Math.ceil(bounds.maxX / cellSize) * cellSize;
954
+ for (let x = startX; x <= endX; x += cellSize) {
955
+ verticals.push(x);
956
+ }
957
+ const horizontals = [];
958
+ const startY = Math.floor(bounds.minY / cellSize) * cellSize;
959
+ const endY = Math.ceil(bounds.maxY / cellSize) * cellSize;
960
+ for (let y = startY; y <= endY; y += cellSize) {
961
+ horizontals.push(y);
962
+ }
963
+ return { verticals, horizontals };
964
+ }
965
+ function getHexVertices(cx, cy, circumradius, orientation) {
966
+ const vertices = [];
967
+ const angleOffset = orientation === "pointy" ? -Math.PI / 2 : 0;
968
+ for (let i = 0; i < 6; i++) {
969
+ const angle = Math.PI / 3 * i + angleOffset;
970
+ vertices.push({
971
+ x: cx + circumradius * Math.cos(angle),
972
+ y: cy + circumradius * Math.sin(angle)
973
+ });
974
+ }
975
+ return vertices;
976
+ }
977
+ function getHexCenters(bounds, circumradius, orientation) {
978
+ if (circumradius <= 0) return [];
979
+ const centers = [];
980
+ if (orientation === "pointy") {
981
+ const hexW = Math.sqrt(3) * circumradius;
982
+ const hexH = 2 * circumradius;
983
+ const rowH = hexH * 0.75;
984
+ const startRow = Math.floor((bounds.minY - circumradius) / rowH);
985
+ const endRow = Math.ceil((bounds.maxY + circumradius) / rowH);
986
+ const startCol = Math.floor((bounds.minX - hexW) / hexW);
987
+ const endCol = Math.ceil((bounds.maxX + hexW) / hexW);
988
+ for (let row = startRow; row <= endRow; row++) {
989
+ const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
990
+ for (let col = startCol; col <= endCol; col++) {
991
+ centers.push({
992
+ x: col * hexW + offsetX,
993
+ y: row * rowH
994
+ });
995
+ }
996
+ }
997
+ } else {
998
+ const hexW = 2 * circumradius;
999
+ const hexH = Math.sqrt(3) * circumradius;
1000
+ const colW = hexW * 0.75;
1001
+ const startCol = Math.floor((bounds.minX - circumradius) / colW);
1002
+ const endCol = Math.ceil((bounds.maxX + circumradius) / colW);
1003
+ const startRow = Math.floor((bounds.minY - hexH) / hexH);
1004
+ const endRow = Math.ceil((bounds.maxY + hexH) / hexH);
1005
+ for (let col = startCol; col <= endCol; col++) {
1006
+ const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
1007
+ for (let row = startRow; row <= endRow; row++) {
1008
+ centers.push({
1009
+ x: col * colW,
1010
+ y: row * hexH + offsetY
1011
+ });
1012
+ }
1013
+ }
1014
+ }
1015
+ return centers;
1016
+ }
1017
+ function renderSquareGrid(ctx, bounds, cellSize, strokeColor, strokeWidth, opacity) {
1018
+ if (cellSize <= 0) return;
1019
+ const { verticals, horizontals } = getSquareGridLines(bounds, cellSize);
1020
+ ctx.save();
1021
+ ctx.strokeStyle = strokeColor;
1022
+ ctx.lineWidth = strokeWidth;
1023
+ ctx.globalAlpha = opacity;
1024
+ ctx.beginPath();
1025
+ for (const x of verticals) {
1026
+ ctx.moveTo(x, bounds.minY);
1027
+ ctx.lineTo(x, bounds.maxY);
1028
+ }
1029
+ for (const y of horizontals) {
1030
+ ctx.moveTo(bounds.minX, y);
1031
+ ctx.lineTo(bounds.maxX, y);
1032
+ }
1033
+ ctx.stroke();
1034
+ ctx.restore();
1035
+ }
1036
+ function renderHexGrid(ctx, bounds, cellSize, orientation, strokeColor, strokeWidth, opacity) {
1037
+ if (cellSize <= 0) return;
1038
+ const centers = getHexCenters(bounds, cellSize, orientation);
1039
+ ctx.save();
1040
+ ctx.strokeStyle = strokeColor;
1041
+ ctx.lineWidth = strokeWidth;
1042
+ ctx.globalAlpha = opacity;
1043
+ ctx.beginPath();
1044
+ for (const center of centers) {
1045
+ const verts = getHexVertices(center.x, center.y, cellSize, orientation);
1046
+ const first = verts[0];
1047
+ if (!first) continue;
1048
+ ctx.moveTo(first.x, first.y);
1049
+ for (let i = 1; i < verts.length; i++) {
1050
+ const v = verts[i];
1051
+ if (!v) continue;
1052
+ ctx.lineTo(v.x, v.y);
1053
+ }
1054
+ ctx.closePath();
1055
+ }
1056
+ ctx.stroke();
1057
+ ctx.restore();
1058
+ }
1059
+
948
1060
  // src/elements/element-renderer.ts
949
1061
  var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "html", "text"]);
950
1062
  var ARROWHEAD_LENGTH = 12;
@@ -953,12 +1065,20 @@ var ElementRenderer = class {
953
1065
  store = null;
954
1066
  imageCache = /* @__PURE__ */ new Map();
955
1067
  onImageLoad = null;
1068
+ camera = null;
1069
+ canvasSize = null;
956
1070
  setStore(store) {
957
1071
  this.store = store;
958
1072
  }
959
1073
  setOnImageLoad(callback) {
960
1074
  this.onImageLoad = callback;
961
1075
  }
1076
+ setCamera(camera) {
1077
+ this.camera = camera;
1078
+ }
1079
+ setCanvasSize(w, h) {
1080
+ this.canvasSize = { w, h };
1081
+ }
962
1082
  isDomElement(element) {
963
1083
  return DOM_ELEMENT_TYPES.has(element.type);
964
1084
  }
@@ -976,6 +1096,9 @@ var ElementRenderer = class {
976
1096
  case "image":
977
1097
  this.renderImage(ctx, element);
978
1098
  break;
1099
+ case "grid":
1100
+ this.renderGrid(ctx, element);
1101
+ break;
979
1102
  }
980
1103
  }
981
1104
  renderStroke(ctx, stroke) {
@@ -1111,6 +1234,42 @@ var ElementRenderer = class {
1111
1234
  }
1112
1235
  }
1113
1236
  }
1237
+ renderGrid(ctx, grid) {
1238
+ if (!this.canvasSize) return;
1239
+ const cam = this.camera;
1240
+ if (!cam) return;
1241
+ const topLeft = cam.screenToWorld({ x: 0, y: 0 });
1242
+ const bottomRight = cam.screenToWorld({
1243
+ x: this.canvasSize.w,
1244
+ y: this.canvasSize.h
1245
+ });
1246
+ const bounds = {
1247
+ minX: topLeft.x,
1248
+ minY: topLeft.y,
1249
+ maxX: bottomRight.x,
1250
+ maxY: bottomRight.y
1251
+ };
1252
+ if (grid.gridType === "hex") {
1253
+ renderHexGrid(
1254
+ ctx,
1255
+ bounds,
1256
+ grid.cellSize,
1257
+ grid.hexOrientation,
1258
+ grid.strokeColor,
1259
+ grid.strokeWidth,
1260
+ grid.opacity
1261
+ );
1262
+ } else {
1263
+ renderSquareGrid(
1264
+ ctx,
1265
+ bounds,
1266
+ grid.cellSize,
1267
+ grid.strokeColor,
1268
+ grid.strokeWidth,
1269
+ grid.opacity
1270
+ );
1271
+ }
1272
+ }
1114
1273
  renderImage(ctx, image) {
1115
1274
  const img = this.getImage(image.src);
1116
1275
  if (!img) return;
@@ -1528,7 +1687,7 @@ function createImage(input) {
1528
1687
  };
1529
1688
  }
1530
1689
  function createHtmlElement(input) {
1531
- return {
1690
+ const el = {
1532
1691
  id: createId("html"),
1533
1692
  type: "html",
1534
1693
  position: input.position,
@@ -1537,6 +1696,8 @@ function createHtmlElement(input) {
1537
1696
  layerId: input.layerId ?? "",
1538
1697
  size: input.size
1539
1698
  };
1699
+ if (input.domId) el.domId = input.domId;
1700
+ return el;
1540
1701
  }
1541
1702
  function createShape(input) {
1542
1703
  return {
@@ -1553,6 +1714,22 @@ function createShape(input) {
1553
1714
  fillColor: input.fillColor ?? "none"
1554
1715
  };
1555
1716
  }
1717
+ function createGrid(input) {
1718
+ return {
1719
+ id: createId("grid"),
1720
+ type: "grid",
1721
+ position: input.position ?? { x: 0, y: 0 },
1722
+ zIndex: input.zIndex ?? 0,
1723
+ locked: input.locked ?? false,
1724
+ layerId: input.layerId ?? "",
1725
+ gridType: input.gridType ?? "square",
1726
+ hexOrientation: input.hexOrientation ?? "pointy",
1727
+ cellSize: input.cellSize ?? 40,
1728
+ strokeColor: input.strokeColor ?? "#000000",
1729
+ strokeWidth: input.strokeWidth ?? 1,
1730
+ opacity: input.opacity ?? 1
1731
+ };
1732
+ }
1556
1733
  function createText(input) {
1557
1734
  return {
1558
1735
  id: createId("text"),
@@ -1730,6 +1907,7 @@ var Viewport = class {
1730
1907
  this.toolManager = new ToolManager();
1731
1908
  this.renderer = new ElementRenderer();
1732
1909
  this.renderer.setStore(this.store);
1910
+ this.renderer.setCamera(this.camera);
1733
1911
  this.renderer.setOnImageLoad(() => this.requestRender());
1734
1912
  this.noteEditor = new NoteEditor();
1735
1913
  this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
@@ -1837,6 +2015,7 @@ var Viewport = class {
1837
2015
  if (state.layers && state.layers.length > 0) {
1838
2016
  this.layerManager.loadSnapshot(state.layers);
1839
2017
  }
2018
+ this.reattachHtmlContent();
1840
2019
  this.history.clear();
1841
2020
  this.historyRecorder.resume();
1842
2021
  this.camera.moveTo(state.camera.position.x, state.camera.position.y);
@@ -1868,7 +2047,8 @@ var Viewport = class {
1868
2047
  return image.id;
1869
2048
  }
1870
2049
  addHtmlElement(dom, position, size = { w: 200, h: 150 }) {
1871
- const el = createHtmlElement({ position, size, layerId: this.layerManager.activeLayerId });
2050
+ const domId = dom.id || void 0;
2051
+ const el = createHtmlElement({ position, size, domId, layerId: this.layerManager.activeLayerId });
1872
2052
  this.htmlContent.set(el.id, dom);
1873
2053
  this.historyRecorder.begin();
1874
2054
  this.store.add(el);
@@ -1876,6 +2056,34 @@ var Viewport = class {
1876
2056
  this.requestRender();
1877
2057
  return el.id;
1878
2058
  }
2059
+ addGrid(input) {
2060
+ const existing = this.store.getElementsByType("grid")[0];
2061
+ this.historyRecorder.begin();
2062
+ if (existing) {
2063
+ this.store.remove(existing.id);
2064
+ }
2065
+ const grid = createGrid({ ...input, layerId: this.layerManager.activeLayerId });
2066
+ this.store.add(grid);
2067
+ this.historyRecorder.commit();
2068
+ this.requestRender();
2069
+ return grid.id;
2070
+ }
2071
+ updateGrid(updates) {
2072
+ const grid = this.store.getElementsByType("grid")[0];
2073
+ if (!grid) return;
2074
+ this.historyRecorder.begin();
2075
+ this.store.update(grid.id, updates);
2076
+ this.historyRecorder.commit();
2077
+ this.requestRender();
2078
+ }
2079
+ removeGrid() {
2080
+ const grid = this.store.getElementsByType("grid")[0];
2081
+ if (!grid) return;
2082
+ this.historyRecorder.begin();
2083
+ this.store.remove(grid.id);
2084
+ this.historyRecorder.commit();
2085
+ this.requestRender();
2086
+ }
1879
2087
  destroy() {
1880
2088
  cancelAnimationFrame(this.animFrameId);
1881
2089
  this.stopInteracting();
@@ -1907,6 +2115,7 @@ var Viewport = class {
1907
2115
  const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
1908
2116
  ctx.save();
1909
2117
  ctx.scale(dpr, dpr);
2118
+ this.renderer.setCanvasSize(this.canvasEl.clientWidth, this.canvasEl.clientHeight);
1910
2119
  this.background.render(ctx, this.camera);
1911
2120
  ctx.save();
1912
2121
  ctx.translate(this.camera.position.x, this.camera.position.y);
@@ -2208,6 +2417,16 @@ var Viewport = class {
2208
2417
  this.htmlContent.clear();
2209
2418
  this.requestRender();
2210
2419
  }
2420
+ reattachHtmlContent() {
2421
+ for (const el of this.store.getElementsByType("html")) {
2422
+ if (el.domId) {
2423
+ const dom = document.getElementById(el.domId);
2424
+ if (dom) {
2425
+ this.htmlContent.set(el.id, dom);
2426
+ }
2427
+ }
2428
+ }
2429
+ }
2211
2430
  createWrapper() {
2212
2431
  const el = document.createElement("div");
2213
2432
  Object.assign(el.style, {
@@ -2466,10 +2685,11 @@ function applyArrowHandleDrag(handle, elementId, world, ctx) {
2466
2685
  const el = ctx.store.getById(elementId);
2467
2686
  if (!el || el.type !== "arrow") return;
2468
2687
  const threshold = BIND_THRESHOLD / ctx.camera.zoom;
2688
+ const layerFilter = (candidate) => candidate.layerId === el.layerId;
2469
2689
  switch (handle) {
2470
2690
  case "start": {
2471
2691
  const excludeId = el.toBinding?.elementId;
2472
- const target = findBindTarget(world, ctx.store, threshold, excludeId);
2692
+ const target = findBindTarget(world, ctx.store, threshold, excludeId, layerFilter);
2473
2693
  if (target) {
2474
2694
  const center = getElementCenter(target);
2475
2695
  ctx.store.update(elementId, {
@@ -2488,7 +2708,7 @@ function applyArrowHandleDrag(handle, elementId, world, ctx) {
2488
2708
  }
2489
2709
  case "end": {
2490
2710
  const excludeId = el.fromBinding?.elementId;
2491
- const target = findBindTarget(world, ctx.store, threshold, excludeId);
2711
+ const target = findBindTarget(world, ctx.store, threshold, excludeId, layerFilter);
2492
2712
  if (target) {
2493
2713
  const center = getElementCenter(target);
2494
2714
  ctx.store.update(elementId, {
@@ -2517,7 +2737,8 @@ function getArrowHandleDragTarget(handle, elementId, world, ctx) {
2517
2737
  if (!el || el.type !== "arrow") return null;
2518
2738
  const threshold = BIND_THRESHOLD / ctx.camera.zoom;
2519
2739
  const excludeId = handle === "start" ? el.toBinding?.elementId : el.fromBinding?.elementId;
2520
- const target = findBindTarget(world, ctx.store, threshold, excludeId);
2740
+ const layerFilter = (candidate) => candidate.layerId === el.layerId;
2741
+ const target = findBindTarget(world, ctx.store, threshold, excludeId, layerFilter);
2521
2742
  if (!target) return null;
2522
2743
  return getElementBounds(target);
2523
2744
  }
@@ -2893,6 +3114,7 @@ var SelectTool = class {
2893
3114
  for (const el of ctx.store.getAll()) {
2894
3115
  if (ctx.isLayerVisible && !ctx.isLayerVisible(el.layerId)) continue;
2895
3116
  if (ctx.isLayerLocked && ctx.isLayerLocked(el.layerId)) continue;
3117
+ if (el.type === "grid") continue;
2896
3118
  const bounds = this.getElementBounds(el);
2897
3119
  if (bounds && this.rectsOverlap(marquee, bounds)) {
2898
3120
  ids.push(el.id);
@@ -2929,11 +3151,13 @@ var SelectTool = class {
2929
3151
  for (const el of elements) {
2930
3152
  if (ctx.isLayerVisible && !ctx.isLayerVisible(el.layerId)) continue;
2931
3153
  if (ctx.isLayerLocked && ctx.isLayerLocked(el.layerId)) continue;
3154
+ if (el.type === "grid") continue;
2932
3155
  if (this.isInsideBounds(world, el)) return el;
2933
3156
  }
2934
3157
  return null;
2935
3158
  }
2936
3159
  isInsideBounds(point, el) {
3160
+ if (el.type === "grid") return false;
2937
3161
  if ("size" in el) {
2938
3162
  const s = el.size;
2939
3163
  return point.x >= el.position.x && point.x <= el.position.x + s.w && point.y >= el.position.y && point.y <= el.position.y + s.h;
@@ -2974,8 +3198,10 @@ var ArrowTool = class {
2974
3198
  if (options.width !== void 0) this.width = options.width;
2975
3199
  }
2976
3200
  layerFilter(ctx) {
2977
- if (!ctx.isLayerVisible && !ctx.isLayerLocked) return void 0;
3201
+ const activeLayerId = ctx.activeLayerId;
3202
+ if (!activeLayerId && !ctx.isLayerVisible && !ctx.isLayerLocked) return void 0;
2978
3203
  return (el) => {
3204
+ if (activeLayerId && el.layerId !== activeLayerId) return false;
2979
3205
  if (ctx.isLayerVisible && !ctx.isLayerVisible(el.layerId)) return false;
2980
3206
  if (ctx.isLayerLocked && ctx.isLayerLocked(el.layerId)) return false;
2981
3207
  return true;
@@ -3359,7 +3585,7 @@ var UpdateLayerCommand = class {
3359
3585
  };
3360
3586
 
3361
3587
  // src/index.ts
3362
- var VERSION = "0.6.1";
3588
+ var VERSION = "0.7.0";
3363
3589
  export {
3364
3590
  AddElementCommand,
3365
3591
  ArrowTool,
@@ -3393,6 +3619,7 @@ export {
3393
3619
  Viewport,
3394
3620
  clearStaleBindings,
3395
3621
  createArrow,
3622
+ createGrid,
3396
3623
  createHtmlElement,
3397
3624
  createId,
3398
3625
  createImage,