@fieldnotes/core 0.3.1 → 0.4.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.cjs CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  PencilTool: () => PencilTool,
41
41
  RemoveElementCommand: () => RemoveElementCommand,
42
42
  SelectTool: () => SelectTool,
43
+ ShapeTool: () => ShapeTool,
43
44
  TextTool: () => TextTool,
44
45
  ToolManager: () => ToolManager,
45
46
  UpdateElementCommand: () => UpdateElementCommand,
@@ -51,6 +52,7 @@ __export(index_exports, {
51
52
  createId: () => createId,
52
53
  createImage: () => createImage,
53
54
  createNote: () => createNote,
55
+ createShape: () => createShape,
54
56
  createStroke: () => createStroke,
55
57
  createText: () => createText,
56
58
  exportState: () => exportState,
@@ -144,7 +146,7 @@ function validateState(data) {
144
146
  }
145
147
  cleanBindings(obj["elements"]);
146
148
  }
147
- var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text"]);
149
+ var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text", "shape"]);
148
150
  function validateElement(el) {
149
151
  if (!el || typeof el !== "object") {
150
152
  throw new Error("Invalid element: expected an object");
@@ -185,6 +187,9 @@ function migrateElement(obj) {
185
187
  }
186
188
  }
187
189
  }
190
+ if (obj["type"] === "shape" && typeof obj["shape"] !== "string") {
191
+ obj["shape"] = "rectangle";
192
+ }
188
193
  }
189
194
 
190
195
  // src/core/auto-save.ts
@@ -768,7 +773,7 @@ function isNearLine(point, a, b, threshold) {
768
773
  }
769
774
 
770
775
  // src/elements/arrow-binding.ts
771
- var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html"]);
776
+ var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
772
777
  function isBindable(element) {
773
778
  return BINDABLE_TYPES.has(element.type);
774
779
  }
@@ -988,6 +993,9 @@ var ElementRenderer = class {
988
993
  case "arrow":
989
994
  this.renderArrow(ctx, element);
990
995
  break;
996
+ case "shape":
997
+ this.renderShape(ctx, element);
998
+ break;
991
999
  }
992
1000
  }
993
1001
  renderStroke(ctx, stroke) {
@@ -1080,6 +1088,49 @@ var ElementRenderer = class {
1080
1088
  }
1081
1089
  return { visualFrom, visualTo };
1082
1090
  }
1091
+ renderShape(ctx, shape) {
1092
+ ctx.save();
1093
+ if (shape.fillColor !== "none") {
1094
+ ctx.fillStyle = shape.fillColor;
1095
+ this.fillShapePath(ctx, shape);
1096
+ }
1097
+ if (shape.strokeWidth > 0) {
1098
+ ctx.strokeStyle = shape.strokeColor;
1099
+ ctx.lineWidth = shape.strokeWidth;
1100
+ this.strokeShapePath(ctx, shape);
1101
+ }
1102
+ ctx.restore();
1103
+ }
1104
+ fillShapePath(ctx, shape) {
1105
+ switch (shape.shape) {
1106
+ case "rectangle":
1107
+ ctx.fillRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
1108
+ break;
1109
+ case "ellipse": {
1110
+ const cx = shape.position.x + shape.size.w / 2;
1111
+ const cy = shape.position.y + shape.size.h / 2;
1112
+ ctx.beginPath();
1113
+ ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
1114
+ ctx.fill();
1115
+ break;
1116
+ }
1117
+ }
1118
+ }
1119
+ strokeShapePath(ctx, shape) {
1120
+ switch (shape.shape) {
1121
+ case "rectangle":
1122
+ ctx.strokeRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
1123
+ break;
1124
+ case "ellipse": {
1125
+ const cx = shape.position.x + shape.size.w / 2;
1126
+ const cy = shape.position.y + shape.size.h / 2;
1127
+ ctx.beginPath();
1128
+ ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
1129
+ ctx.stroke();
1130
+ break;
1131
+ }
1132
+ }
1133
+ }
1083
1134
  };
1084
1135
 
1085
1136
  // src/elements/note-editor.ts
@@ -1487,6 +1538,20 @@ function createHtmlElement(input) {
1487
1538
  size: input.size
1488
1539
  };
1489
1540
  }
1541
+ function createShape(input) {
1542
+ return {
1543
+ id: createId("shape"),
1544
+ type: "shape",
1545
+ position: input.position,
1546
+ zIndex: input.zIndex ?? 0,
1547
+ locked: input.locked ?? false,
1548
+ shape: input.shape ?? "rectangle",
1549
+ size: input.size,
1550
+ strokeColor: input.strokeColor ?? "#000000",
1551
+ strokeWidth: input.strokeWidth ?? 2,
1552
+ fillColor: input.fillColor ?? "none"
1553
+ };
1554
+ }
1490
1555
  function createText(input) {
1491
1556
  return {
1492
1557
  id: createId("text"),
@@ -2928,8 +2993,123 @@ var ImageTool = class {
2928
2993
  }
2929
2994
  };
2930
2995
 
2996
+ // src/tools/shape-tool.ts
2997
+ var ShapeTool = class {
2998
+ name = "shape";
2999
+ drawing = false;
3000
+ start = { x: 0, y: 0 };
3001
+ end = { x: 0, y: 0 };
3002
+ shiftHeld = false;
3003
+ shape;
3004
+ strokeColor;
3005
+ strokeWidth;
3006
+ fillColor;
3007
+ constructor(options = {}) {
3008
+ this.shape = options.shape ?? "rectangle";
3009
+ this.strokeColor = options.strokeColor ?? "#000000";
3010
+ this.strokeWidth = options.strokeWidth ?? 2;
3011
+ this.fillColor = options.fillColor ?? "none";
3012
+ }
3013
+ setOptions(options) {
3014
+ if (options.shape !== void 0) this.shape = options.shape;
3015
+ if (options.strokeColor !== void 0) this.strokeColor = options.strokeColor;
3016
+ if (options.strokeWidth !== void 0) this.strokeWidth = options.strokeWidth;
3017
+ if (options.fillColor !== void 0) this.fillColor = options.fillColor;
3018
+ }
3019
+ onActivate(_ctx) {
3020
+ if (typeof window !== "undefined") {
3021
+ window.addEventListener("keydown", this.onKeyDown);
3022
+ window.addEventListener("keyup", this.onKeyUp);
3023
+ }
3024
+ }
3025
+ onDeactivate(_ctx) {
3026
+ this.shiftHeld = false;
3027
+ if (typeof window !== "undefined") {
3028
+ window.removeEventListener("keydown", this.onKeyDown);
3029
+ window.removeEventListener("keyup", this.onKeyUp);
3030
+ }
3031
+ }
3032
+ onPointerDown(state, ctx) {
3033
+ this.drawing = true;
3034
+ this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3035
+ this.end = { ...this.start };
3036
+ }
3037
+ onPointerMove(state, ctx) {
3038
+ if (!this.drawing) return;
3039
+ this.end = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3040
+ ctx.requestRender();
3041
+ }
3042
+ onPointerUp(_state, ctx) {
3043
+ if (!this.drawing) return;
3044
+ this.drawing = false;
3045
+ const { position, size } = this.computeRect();
3046
+ if (size.w === 0 || size.h === 0) return;
3047
+ const shape = createShape({
3048
+ position,
3049
+ size,
3050
+ shape: this.shape,
3051
+ strokeColor: this.strokeColor,
3052
+ strokeWidth: this.strokeWidth,
3053
+ fillColor: this.fillColor
3054
+ });
3055
+ ctx.store.add(shape);
3056
+ ctx.requestRender();
3057
+ ctx.switchTool?.("select");
3058
+ }
3059
+ renderOverlay(ctx) {
3060
+ if (!this.drawing) return;
3061
+ const { position, size } = this.computeRect();
3062
+ if (size.w === 0 && size.h === 0) return;
3063
+ ctx.save();
3064
+ ctx.globalAlpha = 0.5;
3065
+ ctx.strokeStyle = this.strokeColor;
3066
+ ctx.lineWidth = this.strokeWidth;
3067
+ if (this.fillColor !== "none") {
3068
+ ctx.fillStyle = this.fillColor;
3069
+ }
3070
+ switch (this.shape) {
3071
+ case "rectangle":
3072
+ if (this.fillColor !== "none") {
3073
+ ctx.fillRect(position.x, position.y, size.w, size.h);
3074
+ }
3075
+ ctx.strokeRect(position.x, position.y, size.w, size.h);
3076
+ break;
3077
+ case "ellipse": {
3078
+ const cx = position.x + size.w / 2;
3079
+ const cy = position.y + size.h / 2;
3080
+ ctx.beginPath();
3081
+ ctx.ellipse(cx, cy, size.w / 2, size.h / 2, 0, 0, Math.PI * 2);
3082
+ if (this.fillColor !== "none") ctx.fill();
3083
+ ctx.stroke();
3084
+ break;
3085
+ }
3086
+ }
3087
+ ctx.restore();
3088
+ }
3089
+ computeRect() {
3090
+ let x = Math.min(this.start.x, this.end.x);
3091
+ let y = Math.min(this.start.y, this.end.y);
3092
+ let w = Math.abs(this.end.x - this.start.x);
3093
+ let h = Math.abs(this.end.y - this.start.y);
3094
+ if (this.shiftHeld) {
3095
+ const side = Math.max(w, h);
3096
+ w = side;
3097
+ h = side;
3098
+ x = this.end.x >= this.start.x ? this.start.x : this.start.x - side;
3099
+ y = this.end.y >= this.start.y ? this.start.y : this.start.y - side;
3100
+ }
3101
+ return { position: { x, y }, size: { w, h } };
3102
+ }
3103
+ onKeyDown = (e) => {
3104
+ if (e.key === "Shift") this.shiftHeld = true;
3105
+ };
3106
+ onKeyUp = (e) => {
3107
+ if (e.key === "Shift") this.shiftHeld = false;
3108
+ };
3109
+ };
3110
+
2931
3111
  // src/index.ts
2932
- var VERSION = "0.3.1";
3112
+ var VERSION = "0.4.0";
2933
3113
  // Annotate the CommonJS export names for ESM import in node:
2934
3114
  0 && (module.exports = {
2935
3115
  AddElementCommand,
@@ -2952,6 +3132,7 @@ var VERSION = "0.3.1";
2952
3132
  PencilTool,
2953
3133
  RemoveElementCommand,
2954
3134
  SelectTool,
3135
+ ShapeTool,
2955
3136
  TextTool,
2956
3137
  ToolManager,
2957
3138
  UpdateElementCommand,
@@ -2963,6 +3144,7 @@ var VERSION = "0.3.1";
2963
3144
  createId,
2964
3145
  createImage,
2965
3146
  createNote,
3147
+ createShape,
2966
3148
  createStroke,
2967
3149
  createText,
2968
3150
  exportState,