@fieldnotes/core 0.3.1 → 0.4.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.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,12 @@ function migrateElement(obj) {
185
187
  }
186
188
  }
187
189
  }
190
+ if (obj["type"] === "shape" && typeof obj["shape"] !== "string") {
191
+ obj["shape"] = "rectangle";
192
+ }
193
+ if (obj["type"] === "note" && typeof obj["textColor"] !== "string") {
194
+ obj["textColor"] = "#000000";
195
+ }
188
196
  }
189
197
 
190
198
  // src/core/auto-save.ts
@@ -768,7 +776,7 @@ function isNearLine(point, a, b, threshold) {
768
776
  }
769
777
 
770
778
  // src/elements/arrow-binding.ts
771
- var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html"]);
779
+ var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
772
780
  function isBindable(element) {
773
781
  return BINDABLE_TYPES.has(element.type);
774
782
  }
@@ -988,6 +996,9 @@ var ElementRenderer = class {
988
996
  case "arrow":
989
997
  this.renderArrow(ctx, element);
990
998
  break;
999
+ case "shape":
1000
+ this.renderShape(ctx, element);
1001
+ break;
991
1002
  }
992
1003
  }
993
1004
  renderStroke(ctx, stroke) {
@@ -1080,6 +1091,49 @@ var ElementRenderer = class {
1080
1091
  }
1081
1092
  return { visualFrom, visualTo };
1082
1093
  }
1094
+ renderShape(ctx, shape) {
1095
+ ctx.save();
1096
+ if (shape.fillColor !== "none") {
1097
+ ctx.fillStyle = shape.fillColor;
1098
+ this.fillShapePath(ctx, shape);
1099
+ }
1100
+ if (shape.strokeWidth > 0) {
1101
+ ctx.strokeStyle = shape.strokeColor;
1102
+ ctx.lineWidth = shape.strokeWidth;
1103
+ this.strokeShapePath(ctx, shape);
1104
+ }
1105
+ ctx.restore();
1106
+ }
1107
+ fillShapePath(ctx, shape) {
1108
+ switch (shape.shape) {
1109
+ case "rectangle":
1110
+ ctx.fillRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
1111
+ break;
1112
+ case "ellipse": {
1113
+ const cx = shape.position.x + shape.size.w / 2;
1114
+ const cy = shape.position.y + shape.size.h / 2;
1115
+ ctx.beginPath();
1116
+ ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
1117
+ ctx.fill();
1118
+ break;
1119
+ }
1120
+ }
1121
+ }
1122
+ strokeShapePath(ctx, shape) {
1123
+ switch (shape.shape) {
1124
+ case "rectangle":
1125
+ ctx.strokeRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
1126
+ break;
1127
+ case "ellipse": {
1128
+ const cx = shape.position.x + shape.size.w / 2;
1129
+ const cy = shape.position.y + shape.size.h / 2;
1130
+ ctx.beginPath();
1131
+ ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
1132
+ ctx.stroke();
1133
+ break;
1134
+ }
1135
+ }
1136
+ }
1083
1137
  };
1084
1138
 
1085
1139
  // src/elements/note-editor.ts
@@ -1446,7 +1500,8 @@ function createNote(input) {
1446
1500
  locked: input.locked ?? false,
1447
1501
  size: input.size ?? { w: 200, h: 100 },
1448
1502
  text: input.text ?? "",
1449
- backgroundColor: input.backgroundColor ?? "#ffeb3b"
1503
+ backgroundColor: input.backgroundColor ?? "#ffeb3b",
1504
+ textColor: input.textColor ?? "#000000"
1450
1505
  };
1451
1506
  }
1452
1507
  function createArrow(input) {
@@ -1487,6 +1542,20 @@ function createHtmlElement(input) {
1487
1542
  size: input.size
1488
1543
  };
1489
1544
  }
1545
+ function createShape(input) {
1546
+ return {
1547
+ id: createId("shape"),
1548
+ type: "shape",
1549
+ position: input.position,
1550
+ zIndex: input.zIndex ?? 0,
1551
+ locked: input.locked ?? false,
1552
+ shape: input.shape ?? "rectangle",
1553
+ size: input.size,
1554
+ strokeColor: input.strokeColor ?? "#000000",
1555
+ strokeWidth: input.strokeWidth ?? 2,
1556
+ fillColor: input.fillColor ?? "none"
1557
+ };
1558
+ }
1490
1559
  function createText(input) {
1491
1560
  return {
1492
1561
  id: createId("text"),
@@ -1831,6 +1900,7 @@ var Viewport = class {
1831
1900
  node.dataset["initialized"] = "true";
1832
1901
  Object.assign(node.style, {
1833
1902
  backgroundColor: element.backgroundColor,
1903
+ color: element.textColor,
1834
1904
  padding: "8px",
1835
1905
  borderRadius: "4px",
1836
1906
  boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
@@ -1852,6 +1922,7 @@ var Viewport = class {
1852
1922
  node.textContent = element.text || "";
1853
1923
  }
1854
1924
  node.style.backgroundColor = element.backgroundColor;
1925
+ node.style.color = element.textColor;
1855
1926
  }
1856
1927
  }
1857
1928
  if (element.type === "image") {
@@ -2830,13 +2901,16 @@ var ArrowTool = class {
2830
2901
  var NoteTool = class {
2831
2902
  name = "note";
2832
2903
  backgroundColor;
2904
+ textColor;
2833
2905
  size;
2834
2906
  constructor(options = {}) {
2835
2907
  this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
2908
+ this.textColor = options.textColor ?? "#000000";
2836
2909
  this.size = options.size ?? { w: 200, h: 100 };
2837
2910
  }
2838
2911
  setOptions(options) {
2839
2912
  if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
2913
+ if (options.textColor !== void 0) this.textColor = options.textColor;
2840
2914
  if (options.size !== void 0) this.size = options.size;
2841
2915
  }
2842
2916
  onPointerDown(_state, _ctx) {
@@ -2848,7 +2922,8 @@ var NoteTool = class {
2848
2922
  const note = createNote({
2849
2923
  position: world,
2850
2924
  size: { ...this.size },
2851
- backgroundColor: this.backgroundColor
2925
+ backgroundColor: this.backgroundColor,
2926
+ textColor: this.textColor
2852
2927
  });
2853
2928
  ctx.store.add(note);
2854
2929
  ctx.requestRender();
@@ -2928,8 +3003,123 @@ var ImageTool = class {
2928
3003
  }
2929
3004
  };
2930
3005
 
3006
+ // src/tools/shape-tool.ts
3007
+ var ShapeTool = class {
3008
+ name = "shape";
3009
+ drawing = false;
3010
+ start = { x: 0, y: 0 };
3011
+ end = { x: 0, y: 0 };
3012
+ shiftHeld = false;
3013
+ shape;
3014
+ strokeColor;
3015
+ strokeWidth;
3016
+ fillColor;
3017
+ constructor(options = {}) {
3018
+ this.shape = options.shape ?? "rectangle";
3019
+ this.strokeColor = options.strokeColor ?? "#000000";
3020
+ this.strokeWidth = options.strokeWidth ?? 2;
3021
+ this.fillColor = options.fillColor ?? "none";
3022
+ }
3023
+ setOptions(options) {
3024
+ if (options.shape !== void 0) this.shape = options.shape;
3025
+ if (options.strokeColor !== void 0) this.strokeColor = options.strokeColor;
3026
+ if (options.strokeWidth !== void 0) this.strokeWidth = options.strokeWidth;
3027
+ if (options.fillColor !== void 0) this.fillColor = options.fillColor;
3028
+ }
3029
+ onActivate(_ctx) {
3030
+ if (typeof window !== "undefined") {
3031
+ window.addEventListener("keydown", this.onKeyDown);
3032
+ window.addEventListener("keyup", this.onKeyUp);
3033
+ }
3034
+ }
3035
+ onDeactivate(_ctx) {
3036
+ this.shiftHeld = false;
3037
+ if (typeof window !== "undefined") {
3038
+ window.removeEventListener("keydown", this.onKeyDown);
3039
+ window.removeEventListener("keyup", this.onKeyUp);
3040
+ }
3041
+ }
3042
+ onPointerDown(state, ctx) {
3043
+ this.drawing = true;
3044
+ this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3045
+ this.end = { ...this.start };
3046
+ }
3047
+ onPointerMove(state, ctx) {
3048
+ if (!this.drawing) return;
3049
+ this.end = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3050
+ ctx.requestRender();
3051
+ }
3052
+ onPointerUp(_state, ctx) {
3053
+ if (!this.drawing) return;
3054
+ this.drawing = false;
3055
+ const { position, size } = this.computeRect();
3056
+ if (size.w === 0 || size.h === 0) return;
3057
+ const shape = createShape({
3058
+ position,
3059
+ size,
3060
+ shape: this.shape,
3061
+ strokeColor: this.strokeColor,
3062
+ strokeWidth: this.strokeWidth,
3063
+ fillColor: this.fillColor
3064
+ });
3065
+ ctx.store.add(shape);
3066
+ ctx.requestRender();
3067
+ ctx.switchTool?.("select");
3068
+ }
3069
+ renderOverlay(ctx) {
3070
+ if (!this.drawing) return;
3071
+ const { position, size } = this.computeRect();
3072
+ if (size.w === 0 && size.h === 0) return;
3073
+ ctx.save();
3074
+ ctx.globalAlpha = 0.5;
3075
+ ctx.strokeStyle = this.strokeColor;
3076
+ ctx.lineWidth = this.strokeWidth;
3077
+ if (this.fillColor !== "none") {
3078
+ ctx.fillStyle = this.fillColor;
3079
+ }
3080
+ switch (this.shape) {
3081
+ case "rectangle":
3082
+ if (this.fillColor !== "none") {
3083
+ ctx.fillRect(position.x, position.y, size.w, size.h);
3084
+ }
3085
+ ctx.strokeRect(position.x, position.y, size.w, size.h);
3086
+ break;
3087
+ case "ellipse": {
3088
+ const cx = position.x + size.w / 2;
3089
+ const cy = position.y + size.h / 2;
3090
+ ctx.beginPath();
3091
+ ctx.ellipse(cx, cy, size.w / 2, size.h / 2, 0, 0, Math.PI * 2);
3092
+ if (this.fillColor !== "none") ctx.fill();
3093
+ ctx.stroke();
3094
+ break;
3095
+ }
3096
+ }
3097
+ ctx.restore();
3098
+ }
3099
+ computeRect() {
3100
+ let x = Math.min(this.start.x, this.end.x);
3101
+ let y = Math.min(this.start.y, this.end.y);
3102
+ let w = Math.abs(this.end.x - this.start.x);
3103
+ let h = Math.abs(this.end.y - this.start.y);
3104
+ if (this.shiftHeld) {
3105
+ const side = Math.max(w, h);
3106
+ w = side;
3107
+ h = side;
3108
+ x = this.end.x >= this.start.x ? this.start.x : this.start.x - side;
3109
+ y = this.end.y >= this.start.y ? this.start.y : this.start.y - side;
3110
+ }
3111
+ return { position: { x, y }, size: { w, h } };
3112
+ }
3113
+ onKeyDown = (e) => {
3114
+ if (e.key === "Shift") this.shiftHeld = true;
3115
+ };
3116
+ onKeyUp = (e) => {
3117
+ if (e.key === "Shift") this.shiftHeld = false;
3118
+ };
3119
+ };
3120
+
2931
3121
  // src/index.ts
2932
- var VERSION = "0.3.1";
3122
+ var VERSION = "0.4.1";
2933
3123
  // Annotate the CommonJS export names for ESM import in node:
2934
3124
  0 && (module.exports = {
2935
3125
  AddElementCommand,
@@ -2952,6 +3142,7 @@ var VERSION = "0.3.1";
2952
3142
  PencilTool,
2953
3143
  RemoveElementCommand,
2954
3144
  SelectTool,
3145
+ ShapeTool,
2955
3146
  TextTool,
2956
3147
  ToolManager,
2957
3148
  UpdateElementCommand,
@@ -2963,6 +3154,7 @@ var VERSION = "0.3.1";
2963
3154
  createId,
2964
3155
  createImage,
2965
3156
  createNote,
3157
+ createShape,
2966
3158
  createStroke,
2967
3159
  createText,
2968
3160
  exportState,