@fieldnotes/core 0.27.0 → 0.28.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
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
50
50
  toBinding?: Binding;
51
51
  /** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
52
52
  cachedControlPoint?: Point;
53
+ /** Optional text rendered at the curve midpoint. */
54
+ label?: string;
53
55
  }
54
56
  interface ImageElement extends BaseElement {
55
57
  type: 'image';
@@ -446,6 +448,7 @@ declare class Viewport {
446
448
  private readonly background;
447
449
  private readonly renderer;
448
450
  private readonly noteEditor;
451
+ private readonly arrowLabelEditor;
449
452
  private readonly historyRecorder;
450
453
  readonly toolContext: ToolContext;
451
454
  private readonly marginViewport;
@@ -517,6 +520,8 @@ declare class Viewport {
517
520
  private onTextEditStop;
518
521
  private onTapDown;
519
522
  private onDoubleTap;
523
+ private findArrowAt;
524
+ private startArrowLabelEdit;
520
525
  private hitTestWorld;
521
526
  stopInteracting(): void;
522
527
  private onDragOver;
@@ -574,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
574
579
  width?: number;
575
580
  fromBinding?: Binding;
576
581
  toBinding?: Binding;
582
+ label?: string;
577
583
  }
578
584
  interface ImageInput extends BaseDefaults {
579
585
  position: Point;
@@ -951,6 +957,6 @@ declare class TemplateTool implements Tool {
951
957
  private notifyOptionsChange;
952
958
  }
953
959
 
954
- declare const VERSION = "0.27.0";
960
+ declare const VERSION = "0.28.0";
955
961
 
956
962
  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
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
50
50
  toBinding?: Binding;
51
51
  /** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
52
52
  cachedControlPoint?: Point;
53
+ /** Optional text rendered at the curve midpoint. */
54
+ label?: string;
53
55
  }
54
56
  interface ImageElement extends BaseElement {
55
57
  type: 'image';
@@ -446,6 +448,7 @@ declare class Viewport {
446
448
  private readonly background;
447
449
  private readonly renderer;
448
450
  private readonly noteEditor;
451
+ private readonly arrowLabelEditor;
449
452
  private readonly historyRecorder;
450
453
  readonly toolContext: ToolContext;
451
454
  private readonly marginViewport;
@@ -517,6 +520,8 @@ declare class Viewport {
517
520
  private onTextEditStop;
518
521
  private onTapDown;
519
522
  private onDoubleTap;
523
+ private findArrowAt;
524
+ private startArrowLabelEdit;
520
525
  private hitTestWorld;
521
526
  stopInteracting(): void;
522
527
  private onDragOver;
@@ -574,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
574
579
  width?: number;
575
580
  fromBinding?: Binding;
576
581
  toBinding?: Binding;
582
+ label?: string;
577
583
  }
578
584
  interface ImageInput extends BaseDefaults {
579
585
  position: Point;
@@ -951,6 +957,6 @@ declare class TemplateTool implements Tool {
951
957
  private notifyOptionsChange;
952
958
  }
953
959
 
954
- declare const VERSION = "0.27.0";
960
+ declare const VERSION = "0.28.0";
955
961
 
956
962
  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
@@ -2658,6 +2658,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
2658
2658
  var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "html", "text"]);
2659
2659
  var ARROWHEAD_LENGTH = 12;
2660
2660
  var ARROWHEAD_ANGLE = Math.PI / 6;
2661
+ var ARROW_LABEL_FONT_SIZE = 14;
2661
2662
  var ElementRenderer = class {
2662
2663
  store = null;
2663
2664
  imageCache = /* @__PURE__ */ new Map();
@@ -2668,6 +2669,7 @@ var ElementRenderer = class {
2668
2669
  hexTileCache = null;
2669
2670
  hexTileCacheKey = "";
2670
2671
  gridBoundsOverride = null;
2672
+ labelEditingId = null;
2671
2673
  setStore(store) {
2672
2674
  this.store = store;
2673
2675
  }
@@ -2686,6 +2688,9 @@ var ElementRenderer = class {
2686
2688
  setGridBoundsOverride(bounds) {
2687
2689
  this.gridBoundsOverride = bounds;
2688
2690
  }
2691
+ setLabelEditingId(id) {
2692
+ this.labelEditingId = id;
2693
+ }
2689
2694
  isDomElement(element) {
2690
2695
  return DOM_ELEMENT_TYPES.has(element.type);
2691
2696
  }
@@ -2762,6 +2767,28 @@ var ElementRenderer = class {
2762
2767
  ctx.stroke();
2763
2768
  this.renderArrowhead(ctx, arrow, visualTo, geometry.tangentEnd);
2764
2769
  ctx.restore();
2770
+ this.renderArrowLabel(ctx, arrow);
2771
+ }
2772
+ renderArrowLabel(ctx, arrow) {
2773
+ if (!arrow.label || arrow.label.length === 0) return;
2774
+ if (arrow.id === this.labelEditingId) return;
2775
+ const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
2776
+ ctx.save();
2777
+ ctx.font = `${ARROW_LABEL_FONT_SIZE}px system-ui, sans-serif`;
2778
+ const metrics = ctx.measureText(arrow.label);
2779
+ const padX = 6;
2780
+ const padY = 4;
2781
+ const w = metrics.width + padX * 2;
2782
+ const h = ARROW_LABEL_FONT_SIZE + padY * 2;
2783
+ ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
2784
+ ctx.beginPath();
2785
+ ctx.roundRect(mid.x - w / 2, mid.y - h / 2, w, h, 4);
2786
+ ctx.fill();
2787
+ ctx.fillStyle = "#1a1a1a";
2788
+ ctx.textAlign = "center";
2789
+ ctx.textBaseline = "middle";
2790
+ ctx.fillText(arrow.label, mid.x, mid.y);
2791
+ ctx.restore();
2765
2792
  }
2766
2793
  renderArrowhead(ctx, arrow, tip, angle) {
2767
2794
  ctx.beginPath();
@@ -3172,6 +3199,7 @@ function createArrow(input) {
3172
3199
  };
3173
3200
  if (input.fromBinding) result.fromBinding = input.fromBinding;
3174
3201
  if (input.toBinding) result.toBinding = input.toBinding;
3202
+ if (input.label !== void 0) result.label = input.label;
3175
3203
  return result;
3176
3204
  }
3177
3205
  function createImage(input) {
@@ -3635,6 +3663,84 @@ var NoteEditor = class {
3635
3663
  }
3636
3664
  };
3637
3665
 
3666
+ // src/elements/arrow-label-editor.ts
3667
+ var ArrowLabelEditor = class {
3668
+ input = null;
3669
+ done = false;
3670
+ get isEditing() {
3671
+ return this.input !== null;
3672
+ }
3673
+ startEditing(start) {
3674
+ if (this.input) this.cleanup();
3675
+ const { arrow, layer, store, recorder, onDone } = start;
3676
+ const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
3677
+ const input = document.createElement("input");
3678
+ input.type = "text";
3679
+ input.value = arrow.label ?? "";
3680
+ Object.assign(input.style, {
3681
+ position: "absolute",
3682
+ left: `${mid.x}px`,
3683
+ top: `${mid.y}px`,
3684
+ transform: "translate(-50%, -50%)",
3685
+ // domLayer is pointer-events:none; the input must opt back in to receive taps/clicks.
3686
+ pointerEvents: "auto",
3687
+ font: "14px system-ui, sans-serif",
3688
+ padding: "2px 6px",
3689
+ border: "1px solid #2196F3",
3690
+ borderRadius: "4px",
3691
+ background: "#ffffff",
3692
+ color: "#1a1a1a",
3693
+ outline: "none",
3694
+ minWidth: "40px"
3695
+ });
3696
+ this.done = false;
3697
+ const commit = () => {
3698
+ if (this.done) return;
3699
+ this.done = true;
3700
+ const next = input.value.trim() || void 0;
3701
+ if (next !== arrow.label) {
3702
+ recorder.begin();
3703
+ store.update(arrow.id, { label: next });
3704
+ recorder.commit();
3705
+ }
3706
+ this.cleanup();
3707
+ onDone();
3708
+ };
3709
+ const cancel = () => {
3710
+ if (this.done) return;
3711
+ this.done = true;
3712
+ this.cleanup();
3713
+ onDone();
3714
+ };
3715
+ input.addEventListener("keydown", (e) => {
3716
+ if (e.key === "Enter") {
3717
+ e.preventDefault();
3718
+ commit();
3719
+ } else if (e.key === "Escape") {
3720
+ e.preventDefault();
3721
+ cancel();
3722
+ }
3723
+ e.stopPropagation();
3724
+ });
3725
+ input.addEventListener("blur", commit);
3726
+ layer.appendChild(input);
3727
+ this.input = input;
3728
+ input.focus();
3729
+ input.select();
3730
+ }
3731
+ /** Abort any in-progress edit without committing (e.g. on viewport teardown). */
3732
+ cancel() {
3733
+ this.done = true;
3734
+ this.cleanup();
3735
+ }
3736
+ cleanup() {
3737
+ if (this.input) {
3738
+ this.input.remove();
3739
+ this.input = null;
3740
+ }
3741
+ }
3742
+ };
3743
+
3638
3744
  // src/tools/tool-manager.ts
3639
3745
  var ToolManager = class {
3640
3746
  tools = /* @__PURE__ */ new Map();
@@ -5244,6 +5350,7 @@ function getElementStyle(element) {
5244
5350
 
5245
5351
  // src/canvas/viewport.ts
5246
5352
  var EMPTY_IDS = [];
5353
+ var ARROW_HIT_THRESHOLD = 10;
5247
5354
  function noop() {
5248
5355
  }
5249
5356
  function sharedValue(values) {
@@ -5285,6 +5392,7 @@ var Viewport = class {
5285
5392
  placeholder: options.placeholder
5286
5393
  });
5287
5394
  this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
5395
+ this.arrowLabelEditor = new ArrowLabelEditor();
5288
5396
  this.noteEditor.setHistoryHooks(
5289
5397
  () => this.historyRecorder.begin(),
5290
5398
  () => this.historyRecorder.commit()
@@ -5411,6 +5519,7 @@ var Viewport = class {
5411
5519
  background;
5412
5520
  renderer;
5413
5521
  noteEditor;
5522
+ arrowLabelEditor;
5414
5523
  historyRecorder;
5415
5524
  toolContext;
5416
5525
  marginViewport;
@@ -5665,6 +5774,7 @@ var Viewport = class {
5665
5774
  this.renderLoop.stop();
5666
5775
  this.interactMode.destroy();
5667
5776
  this.noteEditor.destroy(this.store);
5777
+ this.arrowLabelEditor.cancel();
5668
5778
  this.historyRecorder.destroy();
5669
5779
  this.wrapper.removeEventListener("pointerdown", this.onTapDown);
5670
5780
  this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
@@ -5750,8 +5860,35 @@ var Viewport = class {
5750
5860
  const hit = this.hitTestWorld(world);
5751
5861
  if (hit?.type === "html") {
5752
5862
  this.interactMode.startInteracting(hit.id);
5863
+ return;
5864
+ }
5865
+ const arrow = this.findArrowAt(world);
5866
+ if (arrow) {
5867
+ this.startArrowLabelEdit(arrow);
5753
5868
  }
5754
5869
  };
5870
+ findArrowAt(world) {
5871
+ const candidates = this.store.queryPoint(world).reverse();
5872
+ for (const el of candidates) {
5873
+ if (el.type === "arrow" && isNearBezier(world, el.from, el.to, el.bend, ARROW_HIT_THRESHOLD)) {
5874
+ return el;
5875
+ }
5876
+ }
5877
+ return void 0;
5878
+ }
5879
+ startArrowLabelEdit(arrow) {
5880
+ this.arrowLabelEditor.startEditing({
5881
+ arrow,
5882
+ layer: this.domLayer,
5883
+ store: this.store,
5884
+ recorder: this.historyRecorder,
5885
+ onDone: () => {
5886
+ this.renderer.setLabelEditingId(null);
5887
+ this.requestRender();
5888
+ }
5889
+ });
5890
+ this.renderer.setLabelEditingId(arrow.id);
5891
+ }
5755
5892
  hitTestWorld(world) {
5756
5893
  const candidates = this.store.queryPoint(world).reverse();
5757
5894
  for (const el of candidates) {
@@ -7665,7 +7802,7 @@ var TemplateTool = class {
7665
7802
  };
7666
7803
 
7667
7804
  // src/index.ts
7668
- var VERSION = "0.27.0";
7805
+ var VERSION = "0.28.0";
7669
7806
  export {
7670
7807
  ArrowTool,
7671
7808
  AutoSave,