@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.d.cts CHANGED
@@ -27,6 +27,8 @@ interface StrokeElement extends BaseElement {
27
27
  color: string;
28
28
  width: number;
29
29
  opacity: number;
30
+ /** Optional canvas blend mode (e.g. highlighter uses 'multiply'). */
31
+ blendMode?: 'multiply';
30
32
  }
31
33
  interface NoteElement extends BaseElement {
32
34
  type: 'note';
@@ -72,7 +74,7 @@ interface TextElement extends BaseElement {
72
74
  color: string;
73
75
  textAlign: 'left' | 'center' | 'right';
74
76
  }
75
- type ShapeKind = 'rectangle' | 'ellipse';
77
+ type ShapeKind = 'rectangle' | 'ellipse' | 'line';
76
78
  interface ShapeElement extends BaseElement {
77
79
  type: 'shape';
78
80
  shape: ShapeKind;
@@ -80,6 +82,8 @@ interface ShapeElement extends BaseElement {
80
82
  strokeColor: string;
81
83
  strokeWidth: number;
82
84
  fillColor: string;
85
+ /** Line-only: which bbox diagonal the segment runs along. Absent/false = main diagonal. */
86
+ flip?: boolean;
83
87
  }
84
88
  type HexOrientation = 'pointy' | 'flat';
85
89
  interface GridElement extends BaseElement {
@@ -562,6 +566,7 @@ interface StrokeInput extends BaseDefaults {
562
566
  color?: string;
563
567
  width?: number;
564
568
  opacity?: number;
569
+ blendMode?: 'multiply';
565
570
  }
566
571
  interface NoteInput extends BaseDefaults {
567
572
  position: Point;
@@ -612,6 +617,7 @@ interface ShapeInput extends BaseDefaults {
612
617
  strokeColor?: string;
613
618
  strokeWidth?: number;
614
619
  fillColor?: string;
620
+ flip?: boolean;
615
621
  }
616
622
  declare function createShape(input: ShapeInput): ShapeElement;
617
623
  interface GridInput extends BaseDefaults {
@@ -669,14 +675,17 @@ declare class HandTool implements Tool {
669
675
  }
670
676
 
671
677
  interface PencilToolOptions {
678
+ name?: string;
672
679
  color?: string;
673
680
  width?: number;
674
681
  smoothing?: number;
675
682
  minPointDistance?: number;
676
683
  progressiveSimplifyThreshold?: number;
684
+ opacity?: number;
685
+ blendMode?: 'multiply';
677
686
  }
678
687
  declare class PencilTool implements Tool {
679
- readonly name = "pencil";
688
+ readonly name: string;
680
689
  private drawing;
681
690
  private points;
682
691
  private color;
@@ -685,6 +694,8 @@ declare class PencilTool implements Tool {
685
694
  private minPointDistance;
686
695
  private progressiveThreshold;
687
696
  private nextSimplifyAt;
697
+ private opacity;
698
+ private blendMode;
688
699
  private optionListeners;
689
700
  constructor(options?: PencilToolOptions);
690
701
  onActivate(ctx: ToolContext): void;
@@ -960,6 +971,6 @@ declare class TemplateTool implements Tool {
960
971
  private notifyOptionsChange;
961
972
  }
962
973
 
963
- declare const VERSION = "0.29.0";
974
+ declare const VERSION = "0.30.0";
964
975
 
965
976
  export { type ActiveFormats, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.d.ts CHANGED
@@ -27,6 +27,8 @@ interface StrokeElement extends BaseElement {
27
27
  color: string;
28
28
  width: number;
29
29
  opacity: number;
30
+ /** Optional canvas blend mode (e.g. highlighter uses 'multiply'). */
31
+ blendMode?: 'multiply';
30
32
  }
31
33
  interface NoteElement extends BaseElement {
32
34
  type: 'note';
@@ -72,7 +74,7 @@ interface TextElement extends BaseElement {
72
74
  color: string;
73
75
  textAlign: 'left' | 'center' | 'right';
74
76
  }
75
- type ShapeKind = 'rectangle' | 'ellipse';
77
+ type ShapeKind = 'rectangle' | 'ellipse' | 'line';
76
78
  interface ShapeElement extends BaseElement {
77
79
  type: 'shape';
78
80
  shape: ShapeKind;
@@ -80,6 +82,8 @@ interface ShapeElement extends BaseElement {
80
82
  strokeColor: string;
81
83
  strokeWidth: number;
82
84
  fillColor: string;
85
+ /** Line-only: which bbox diagonal the segment runs along. Absent/false = main diagonal. */
86
+ flip?: boolean;
83
87
  }
84
88
  type HexOrientation = 'pointy' | 'flat';
85
89
  interface GridElement extends BaseElement {
@@ -562,6 +566,7 @@ interface StrokeInput extends BaseDefaults {
562
566
  color?: string;
563
567
  width?: number;
564
568
  opacity?: number;
569
+ blendMode?: 'multiply';
565
570
  }
566
571
  interface NoteInput extends BaseDefaults {
567
572
  position: Point;
@@ -612,6 +617,7 @@ interface ShapeInput extends BaseDefaults {
612
617
  strokeColor?: string;
613
618
  strokeWidth?: number;
614
619
  fillColor?: string;
620
+ flip?: boolean;
615
621
  }
616
622
  declare function createShape(input: ShapeInput): ShapeElement;
617
623
  interface GridInput extends BaseDefaults {
@@ -669,14 +675,17 @@ declare class HandTool implements Tool {
669
675
  }
670
676
 
671
677
  interface PencilToolOptions {
678
+ name?: string;
672
679
  color?: string;
673
680
  width?: number;
674
681
  smoothing?: number;
675
682
  minPointDistance?: number;
676
683
  progressiveSimplifyThreshold?: number;
684
+ opacity?: number;
685
+ blendMode?: 'multiply';
677
686
  }
678
687
  declare class PencilTool implements Tool {
679
- readonly name = "pencil";
688
+ readonly name: string;
680
689
  private drawing;
681
690
  private points;
682
691
  private color;
@@ -685,6 +694,8 @@ declare class PencilTool implements Tool {
685
694
  private minPointDistance;
686
695
  private progressiveThreshold;
687
696
  private nextSimplifyAt;
697
+ private opacity;
698
+ private blendMode;
688
699
  private optionListeners;
689
700
  constructor(options?: PencilToolOptions);
690
701
  onActivate(ctx: ToolContext): void;
@@ -960,6 +971,6 @@ declare class TemplateTool implements Tool {
960
971
  private notifyOptionsChange;
961
972
  }
962
973
 
963
- declare const VERSION = "0.29.0";
974
+ declare const VERSION = "0.30.0";
964
975
 
965
976
  export { type ActiveFormats, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.js CHANGED
@@ -2195,6 +2195,19 @@ function getArrowRenderGeometry(arrow) {
2195
2195
  return geometry;
2196
2196
  }
2197
2197
 
2198
+ // src/elements/shape-geometry.ts
2199
+ function lineEndpoints(shape) {
2200
+ const { x, y } = shape.position;
2201
+ const { w, h } = shape.size;
2202
+ return shape.flip ? [
2203
+ { x, y: y + h },
2204
+ { x: x + w, y }
2205
+ ] : [
2206
+ { x, y },
2207
+ { x: x + w, y: y + h }
2208
+ ];
2209
+ }
2210
+
2198
2211
  // src/elements/arrow-binding.ts
2199
2212
  var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
2200
2213
  function isBindable(element) {
@@ -2719,6 +2732,7 @@ var ElementRenderer = class {
2719
2732
  renderStroke(ctx, stroke) {
2720
2733
  if (stroke.points.length < 2) return;
2721
2734
  ctx.save();
2735
+ if (stroke.blendMode) ctx.globalCompositeOperation = stroke.blendMode;
2722
2736
  ctx.translate(stroke.position.x, stroke.position.y);
2723
2737
  ctx.strokeStyle = stroke.color;
2724
2738
  ctx.lineCap = "round";
@@ -2841,7 +2855,7 @@ var ElementRenderer = class {
2841
2855
  }
2842
2856
  renderShape(ctx, shape) {
2843
2857
  ctx.save();
2844
- if (shape.fillColor !== "none") {
2858
+ if (shape.fillColor !== "none" && shape.shape !== "line") {
2845
2859
  ctx.fillStyle = shape.fillColor;
2846
2860
  this.fillShapePath(ctx, shape);
2847
2861
  }
@@ -2880,6 +2894,15 @@ var ElementRenderer = class {
2880
2894
  ctx.stroke();
2881
2895
  break;
2882
2896
  }
2897
+ case "line": {
2898
+ const [a, b] = lineEndpoints(shape);
2899
+ ctx.lineCap = "round";
2900
+ ctx.beginPath();
2901
+ ctx.moveTo(a.x, a.y);
2902
+ ctx.lineTo(b.x, b.y);
2903
+ ctx.stroke();
2904
+ break;
2905
+ }
2883
2906
  }
2884
2907
  }
2885
2908
  renderGrid(ctx, grid) {
@@ -3153,7 +3176,7 @@ var ElementRenderer = class {
3153
3176
  // src/elements/element-factory.ts
3154
3177
  var DEFAULT_NOTE_FONT_SIZE = 18;
3155
3178
  function createStroke(input) {
3156
- return {
3179
+ const result = {
3157
3180
  id: createId("stroke"),
3158
3181
  type: "stroke",
3159
3182
  position: input.position ?? { x: 0, y: 0 },
@@ -3165,6 +3188,8 @@ function createStroke(input) {
3165
3188
  width: input.width ?? 2,
3166
3189
  opacity: input.opacity ?? 1
3167
3190
  };
3191
+ if (input.blendMode) result.blendMode = input.blendMode;
3192
+ return result;
3168
3193
  }
3169
3194
  function createNote(input) {
3170
3195
  return {
@@ -3229,7 +3254,7 @@ function createHtmlElement(input) {
3229
3254
  return el;
3230
3255
  }
3231
3256
  function createShape(input) {
3232
- return {
3257
+ const result = {
3233
3258
  id: createId("shape"),
3234
3259
  type: "shape",
3235
3260
  position: input.position,
@@ -3242,6 +3267,8 @@ function createShape(input) {
3242
3267
  strokeWidth: input.strokeWidth ?? 2,
3243
3268
  fillColor: input.fillColor ?? "none"
3244
3269
  };
3270
+ if (input.flip) result.flip = input.flip;
3271
+ return result;
3245
3272
  }
3246
3273
  function createGrid(input) {
3247
3274
  return {
@@ -6072,7 +6099,7 @@ var DEFAULT_MIN_POINT_DISTANCE = 3;
6072
6099
  var DEFAULT_PROGRESSIVE_THRESHOLD = 200;
6073
6100
  var PROGRESSIVE_HOT_ZONE = 30;
6074
6101
  var PencilTool = class {
6075
- name = "pencil";
6102
+ name;
6076
6103
  drawing = false;
6077
6104
  points = [];
6078
6105
  color;
@@ -6081,14 +6108,19 @@ var PencilTool = class {
6081
6108
  minPointDistance;
6082
6109
  progressiveThreshold;
6083
6110
  nextSimplifyAt;
6111
+ opacity;
6112
+ blendMode;
6084
6113
  optionListeners = /* @__PURE__ */ new Set();
6085
6114
  constructor(options = {}) {
6115
+ this.name = options.name ?? "pencil";
6086
6116
  this.color = options.color ?? "#000000";
6087
6117
  this.width = options.width ?? 2;
6088
6118
  this.smoothing = options.smoothing ?? DEFAULT_SMOOTHING;
6089
6119
  this.minPointDistance = options.minPointDistance ?? DEFAULT_MIN_POINT_DISTANCE;
6090
6120
  this.progressiveThreshold = options.progressiveSimplifyThreshold ?? DEFAULT_PROGRESSIVE_THRESHOLD;
6091
6121
  this.nextSimplifyAt = this.progressiveThreshold;
6122
+ this.opacity = options.opacity ?? 1;
6123
+ this.blendMode = options.blendMode;
6092
6124
  }
6093
6125
  onActivate(ctx) {
6094
6126
  ctx.setCursor?.("crosshair");
@@ -6102,7 +6134,9 @@ var PencilTool = class {
6102
6134
  width: this.width,
6103
6135
  smoothing: this.smoothing,
6104
6136
  minPointDistance: this.minPointDistance,
6105
- progressiveSimplifyThreshold: this.progressiveThreshold
6137
+ progressiveSimplifyThreshold: this.progressiveThreshold,
6138
+ opacity: this.opacity,
6139
+ blendMode: this.blendMode
6106
6140
  };
6107
6141
  }
6108
6142
  onOptionsChange(listener) {
@@ -6116,6 +6150,8 @@ var PencilTool = class {
6116
6150
  if (options.minPointDistance !== void 0) this.minPointDistance = options.minPointDistance;
6117
6151
  if (options.progressiveSimplifyThreshold !== void 0)
6118
6152
  this.progressiveThreshold = options.progressiveSimplifyThreshold;
6153
+ if (options.opacity !== void 0) this.opacity = options.opacity;
6154
+ if (options.blendMode !== void 0) this.blendMode = options.blendMode;
6119
6155
  this.notifyOptionsChange();
6120
6156
  }
6121
6157
  onPointerDown(state, ctx) {
@@ -6157,7 +6193,9 @@ var PencilTool = class {
6157
6193
  points: simplified,
6158
6194
  color: this.color,
6159
6195
  width: this.width,
6160
- layerId: ctx.activeLayerId ?? ""
6196
+ layerId: ctx.activeLayerId ?? "",
6197
+ opacity: this.opacity,
6198
+ blendMode: this.blendMode
6161
6199
  });
6162
6200
  ctx.store.add(stroke);
6163
6201
  computeStrokeSegments(stroke);
@@ -6173,7 +6211,8 @@ var PencilTool = class {
6173
6211
  ctx.strokeStyle = this.color;
6174
6212
  ctx.lineCap = "round";
6175
6213
  ctx.lineJoin = "round";
6176
- ctx.globalAlpha = 0.8;
6214
+ ctx.globalAlpha = this.blendMode ? this.opacity : 0.8;
6215
+ if (this.blendMode) ctx.globalCompositeOperation = this.blendMode;
6177
6216
  const segments = smoothToSegments(this.points);
6178
6217
  for (const seg of segments) {
6179
6218
  const w = (pressureToWidth(seg.start.pressure, this.width) + pressureToWidth(seg.end.pressure, this.width)) / 2;
@@ -7063,6 +7102,11 @@ var SelectTool = class {
7063
7102
  }
7064
7103
  isInsideBounds(point, el) {
7065
7104
  if (el.type === "grid") return false;
7105
+ if (el.type === "shape" && el.shape === "line") {
7106
+ const [a, b] = lineEndpoints(el);
7107
+ const threshold = Math.max(el.strokeWidth / 2, 6);
7108
+ return distSqToSegment(point, a, b) <= threshold * threshold;
7109
+ }
7066
7110
  if ("size" in el) {
7067
7111
  const s = el.size;
7068
7112
  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;
@@ -7376,6 +7420,15 @@ var ImageTool = class {
7376
7420
  };
7377
7421
 
7378
7422
  // src/tools/shape-tool.ts
7423
+ function snapTo45(start, end) {
7424
+ const dx = end.x - start.x;
7425
+ const dy = end.y - start.y;
7426
+ const len = Math.hypot(dx, dy);
7427
+ if (len === 0) return { ...end };
7428
+ const step = Math.PI / 4;
7429
+ const angle = Math.round(Math.atan2(dy, dx) / step) * step;
7430
+ return { x: start.x + Math.cos(angle) * len, y: start.y + Math.sin(angle) * len };
7431
+ }
7379
7432
  var ShapeTool = class {
7380
7433
  name = "shape";
7381
7434
  drawing = false;
@@ -7433,13 +7486,17 @@ var ShapeTool = class {
7433
7486
  onPointerMove(state, ctx) {
7434
7487
  if (!this.drawing) return;
7435
7488
  this.end = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
7489
+ if (this.shape === "line" && this.shiftHeld) {
7490
+ this.end = snapTo45(this.start, this.end);
7491
+ }
7436
7492
  ctx.requestRender();
7437
7493
  }
7438
7494
  onPointerUp(_state, ctx) {
7439
7495
  if (!this.drawing) return;
7440
7496
  this.drawing = false;
7441
7497
  const { position, size } = this.computeRect();
7442
- if (size.w === 0 || size.h === 0) return;
7498
+ const isLine = this.shape === "line";
7499
+ if (isLine ? size.w === 0 && size.h === 0 : size.w === 0 || size.h === 0) return;
7443
7500
  const shape = createShape({
7444
7501
  position,
7445
7502
  size,
@@ -7447,6 +7504,7 @@ var ShapeTool = class {
7447
7504
  strokeColor: this.strokeColor,
7448
7505
  strokeWidth: this.strokeWidth,
7449
7506
  fillColor: this.fillColor,
7507
+ ...isLine ? { flip: this.end.x > this.start.x !== this.end.y > this.start.y } : {},
7450
7508
  layerId: ctx.activeLayerId ?? ""
7451
7509
  });
7452
7510
  ctx.store.add(shape);
@@ -7480,6 +7538,13 @@ var ShapeTool = class {
7480
7538
  ctx.stroke();
7481
7539
  break;
7482
7540
  }
7541
+ case "line":
7542
+ ctx.lineCap = "round";
7543
+ ctx.beginPath();
7544
+ ctx.moveTo(this.start.x, this.start.y);
7545
+ ctx.lineTo(this.end.x, this.end.y);
7546
+ ctx.stroke();
7547
+ break;
7483
7548
  }
7484
7549
  ctx.restore();
7485
7550
  }
@@ -7488,7 +7553,7 @@ var ShapeTool = class {
7488
7553
  let y = Math.min(this.start.y, this.end.y);
7489
7554
  let w = Math.abs(this.end.x - this.start.x);
7490
7555
  let h = Math.abs(this.end.y - this.start.y);
7491
- if (this.shiftHeld) {
7556
+ if (this.shiftHeld && this.shape !== "line") {
7492
7557
  const side = Math.max(w, h);
7493
7558
  w = side;
7494
7559
  h = side;
@@ -7904,7 +7969,7 @@ var TemplateTool = class {
7904
7969
  };
7905
7970
 
7906
7971
  // src/index.ts
7907
- var VERSION = "0.29.0";
7972
+ var VERSION = "0.30.0";
7908
7973
  export {
7909
7974
  ArrowTool,
7910
7975
  AutoSave,