@fieldnotes/core 0.29.0 → 0.30.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
@@ -2276,6 +2276,19 @@ function getArrowRenderGeometry(arrow) {
2276
2276
  return geometry;
2277
2277
  }
2278
2278
 
2279
+ // src/elements/shape-geometry.ts
2280
+ function lineEndpoints(shape) {
2281
+ const { x, y } = shape.position;
2282
+ const { w, h } = shape.size;
2283
+ return shape.flip ? [
2284
+ { x, y: y + h },
2285
+ { x: x + w, y }
2286
+ ] : [
2287
+ { x, y },
2288
+ { x: x + w, y: y + h }
2289
+ ];
2290
+ }
2291
+
2279
2292
  // src/elements/arrow-binding.ts
2280
2293
  var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
2281
2294
  function isBindable(element) {
@@ -2800,6 +2813,7 @@ var ElementRenderer = class {
2800
2813
  renderStroke(ctx, stroke) {
2801
2814
  if (stroke.points.length < 2) return;
2802
2815
  ctx.save();
2816
+ if (stroke.blendMode) ctx.globalCompositeOperation = stroke.blendMode;
2803
2817
  ctx.translate(stroke.position.x, stroke.position.y);
2804
2818
  ctx.strokeStyle = stroke.color;
2805
2819
  ctx.lineCap = "round";
@@ -2922,7 +2936,7 @@ var ElementRenderer = class {
2922
2936
  }
2923
2937
  renderShape(ctx, shape) {
2924
2938
  ctx.save();
2925
- if (shape.fillColor !== "none") {
2939
+ if (shape.fillColor !== "none" && shape.shape !== "line") {
2926
2940
  ctx.fillStyle = shape.fillColor;
2927
2941
  this.fillShapePath(ctx, shape);
2928
2942
  }
@@ -2961,6 +2975,15 @@ var ElementRenderer = class {
2961
2975
  ctx.stroke();
2962
2976
  break;
2963
2977
  }
2978
+ case "line": {
2979
+ const [a, b] = lineEndpoints(shape);
2980
+ ctx.lineCap = "round";
2981
+ ctx.beginPath();
2982
+ ctx.moveTo(a.x, a.y);
2983
+ ctx.lineTo(b.x, b.y);
2984
+ ctx.stroke();
2985
+ break;
2986
+ }
2964
2987
  }
2965
2988
  }
2966
2989
  renderGrid(ctx, grid) {
@@ -3234,7 +3257,7 @@ var ElementRenderer = class {
3234
3257
  // src/elements/element-factory.ts
3235
3258
  var DEFAULT_NOTE_FONT_SIZE = 18;
3236
3259
  function createStroke(input) {
3237
- return {
3260
+ const result = {
3238
3261
  id: createId("stroke"),
3239
3262
  type: "stroke",
3240
3263
  position: input.position ?? { x: 0, y: 0 },
@@ -3246,6 +3269,8 @@ function createStroke(input) {
3246
3269
  width: input.width ?? 2,
3247
3270
  opacity: input.opacity ?? 1
3248
3271
  };
3272
+ if (input.blendMode) result.blendMode = input.blendMode;
3273
+ return result;
3249
3274
  }
3250
3275
  function createNote(input) {
3251
3276
  return {
@@ -3310,7 +3335,7 @@ function createHtmlElement(input) {
3310
3335
  return el;
3311
3336
  }
3312
3337
  function createShape(input) {
3313
- return {
3338
+ const result = {
3314
3339
  id: createId("shape"),
3315
3340
  type: "shape",
3316
3341
  position: input.position,
@@ -3323,6 +3348,8 @@ function createShape(input) {
3323
3348
  strokeWidth: input.strokeWidth ?? 2,
3324
3349
  fillColor: input.fillColor ?? "none"
3325
3350
  };
3351
+ if (input.flip) result.flip = input.flip;
3352
+ return result;
3326
3353
  }
3327
3354
  function createGrid(input) {
3328
3355
  return {
@@ -6153,7 +6180,7 @@ var DEFAULT_MIN_POINT_DISTANCE = 3;
6153
6180
  var DEFAULT_PROGRESSIVE_THRESHOLD = 200;
6154
6181
  var PROGRESSIVE_HOT_ZONE = 30;
6155
6182
  var PencilTool = class {
6156
- name = "pencil";
6183
+ name;
6157
6184
  drawing = false;
6158
6185
  points = [];
6159
6186
  color;
@@ -6162,14 +6189,19 @@ var PencilTool = class {
6162
6189
  minPointDistance;
6163
6190
  progressiveThreshold;
6164
6191
  nextSimplifyAt;
6192
+ opacity;
6193
+ blendMode;
6165
6194
  optionListeners = /* @__PURE__ */ new Set();
6166
6195
  constructor(options = {}) {
6196
+ this.name = options.name ?? "pencil";
6167
6197
  this.color = options.color ?? "#000000";
6168
6198
  this.width = options.width ?? 2;
6169
6199
  this.smoothing = options.smoothing ?? DEFAULT_SMOOTHING;
6170
6200
  this.minPointDistance = options.minPointDistance ?? DEFAULT_MIN_POINT_DISTANCE;
6171
6201
  this.progressiveThreshold = options.progressiveSimplifyThreshold ?? DEFAULT_PROGRESSIVE_THRESHOLD;
6172
6202
  this.nextSimplifyAt = this.progressiveThreshold;
6203
+ this.opacity = options.opacity ?? 1;
6204
+ this.blendMode = options.blendMode;
6173
6205
  }
6174
6206
  onActivate(ctx) {
6175
6207
  ctx.setCursor?.("crosshair");
@@ -6183,7 +6215,9 @@ var PencilTool = class {
6183
6215
  width: this.width,
6184
6216
  smoothing: this.smoothing,
6185
6217
  minPointDistance: this.minPointDistance,
6186
- progressiveSimplifyThreshold: this.progressiveThreshold
6218
+ progressiveSimplifyThreshold: this.progressiveThreshold,
6219
+ opacity: this.opacity,
6220
+ blendMode: this.blendMode
6187
6221
  };
6188
6222
  }
6189
6223
  onOptionsChange(listener) {
@@ -6197,6 +6231,8 @@ var PencilTool = class {
6197
6231
  if (options.minPointDistance !== void 0) this.minPointDistance = options.minPointDistance;
6198
6232
  if (options.progressiveSimplifyThreshold !== void 0)
6199
6233
  this.progressiveThreshold = options.progressiveSimplifyThreshold;
6234
+ if (options.opacity !== void 0) this.opacity = options.opacity;
6235
+ if (options.blendMode !== void 0) this.blendMode = options.blendMode;
6200
6236
  this.notifyOptionsChange();
6201
6237
  }
6202
6238
  onPointerDown(state, ctx) {
@@ -6238,7 +6274,9 @@ var PencilTool = class {
6238
6274
  points: simplified,
6239
6275
  color: this.color,
6240
6276
  width: this.width,
6241
- layerId: ctx.activeLayerId ?? ""
6277
+ layerId: ctx.activeLayerId ?? "",
6278
+ opacity: this.opacity,
6279
+ blendMode: this.blendMode
6242
6280
  });
6243
6281
  ctx.store.add(stroke);
6244
6282
  computeStrokeSegments(stroke);
@@ -6254,7 +6292,8 @@ var PencilTool = class {
6254
6292
  ctx.strokeStyle = this.color;
6255
6293
  ctx.lineCap = "round";
6256
6294
  ctx.lineJoin = "round";
6257
- ctx.globalAlpha = 0.8;
6295
+ ctx.globalAlpha = this.blendMode ? this.opacity : 0.8;
6296
+ if (this.blendMode) ctx.globalCompositeOperation = this.blendMode;
6258
6297
  const segments = smoothToSegments(this.points);
6259
6298
  for (const seg of segments) {
6260
6299
  const w = (pressureToWidth(seg.start.pressure, this.width) + pressureToWidth(seg.end.pressure, this.width)) / 2;
@@ -7144,6 +7183,11 @@ var SelectTool = class {
7144
7183
  }
7145
7184
  isInsideBounds(point, el) {
7146
7185
  if (el.type === "grid") return false;
7186
+ if (el.type === "shape" && el.shape === "line") {
7187
+ const [a, b] = lineEndpoints(el);
7188
+ const threshold = Math.max(el.strokeWidth / 2, 6);
7189
+ return distSqToSegment(point, a, b) <= threshold * threshold;
7190
+ }
7147
7191
  if ("size" in el) {
7148
7192
  const s = el.size;
7149
7193
  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;
@@ -7457,6 +7501,15 @@ var ImageTool = class {
7457
7501
  };
7458
7502
 
7459
7503
  // src/tools/shape-tool.ts
7504
+ function snapTo45(start, end) {
7505
+ const dx = end.x - start.x;
7506
+ const dy = end.y - start.y;
7507
+ const len = Math.hypot(dx, dy);
7508
+ if (len === 0) return { ...end };
7509
+ const step = Math.PI / 4;
7510
+ const angle = Math.round(Math.atan2(dy, dx) / step) * step;
7511
+ return { x: start.x + Math.cos(angle) * len, y: start.y + Math.sin(angle) * len };
7512
+ }
7460
7513
  var ShapeTool = class {
7461
7514
  name = "shape";
7462
7515
  drawing = false;
@@ -7514,13 +7567,17 @@ var ShapeTool = class {
7514
7567
  onPointerMove(state, ctx) {
7515
7568
  if (!this.drawing) return;
7516
7569
  this.end = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
7570
+ if (this.shape === "line" && this.shiftHeld) {
7571
+ this.end = snapTo45(this.start, this.end);
7572
+ }
7517
7573
  ctx.requestRender();
7518
7574
  }
7519
7575
  onPointerUp(_state, ctx) {
7520
7576
  if (!this.drawing) return;
7521
7577
  this.drawing = false;
7522
7578
  const { position, size } = this.computeRect();
7523
- if (size.w === 0 || size.h === 0) return;
7579
+ const isLine = this.shape === "line";
7580
+ if (isLine ? size.w === 0 && size.h === 0 : size.w === 0 || size.h === 0) return;
7524
7581
  const shape = createShape({
7525
7582
  position,
7526
7583
  size,
@@ -7528,6 +7585,7 @@ var ShapeTool = class {
7528
7585
  strokeColor: this.strokeColor,
7529
7586
  strokeWidth: this.strokeWidth,
7530
7587
  fillColor: this.fillColor,
7588
+ ...isLine ? { flip: this.end.x > this.start.x !== this.end.y > this.start.y } : {},
7531
7589
  layerId: ctx.activeLayerId ?? ""
7532
7590
  });
7533
7591
  ctx.store.add(shape);
@@ -7561,6 +7619,13 @@ var ShapeTool = class {
7561
7619
  ctx.stroke();
7562
7620
  break;
7563
7621
  }
7622
+ case "line":
7623
+ ctx.lineCap = "round";
7624
+ ctx.beginPath();
7625
+ ctx.moveTo(this.start.x, this.start.y);
7626
+ ctx.lineTo(this.end.x, this.end.y);
7627
+ ctx.stroke();
7628
+ break;
7564
7629
  }
7565
7630
  ctx.restore();
7566
7631
  }
@@ -7569,7 +7634,7 @@ var ShapeTool = class {
7569
7634
  let y = Math.min(this.start.y, this.end.y);
7570
7635
  let w = Math.abs(this.end.x - this.start.x);
7571
7636
  let h = Math.abs(this.end.y - this.start.y);
7572
- if (this.shiftHeld) {
7637
+ if (this.shiftHeld && this.shape !== "line") {
7573
7638
  const side = Math.max(w, h);
7574
7639
  w = side;
7575
7640
  h = side;
@@ -7985,7 +8050,7 @@ var TemplateTool = class {
7985
8050
  };
7986
8051
 
7987
8052
  // src/index.ts
7988
- var VERSION = "0.29.0";
8053
+ var VERSION = "0.30.0";
7989
8054
  // Annotate the CommonJS export names for ESM import in node:
7990
8055
  0 && (module.exports = {
7991
8056
  ArrowTool,