@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/README.md +8 -0
- package/dist/index.cjs +138 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +138 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -441,6 +441,14 @@ new Viewport(container, {
|
|
|
441
441
|
new PencilTool({ color: '#ff0000', width: 3, smoothing: 1.5 });
|
|
442
442
|
new EraserTool({ radius: 30 });
|
|
443
443
|
new ArrowTool({ color: '#333', width: 2 });
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Arrow Labels
|
|
447
|
+
|
|
448
|
+
Arrows support an optional `label` string, rendered as a pill at the curve midpoint. Pass it at creation or double-click an arrow on the canvas to add or edit the label inline.
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
createArrow({ from: { x: 0, y: 0 }, to: { x: 200, y: 0 }, label: 'depends on' });
|
|
444
452
|
new NoteTool({ backgroundColor: '#fff9c4', size: { w: 200, h: 150 } });
|
|
445
453
|
new ImageTool({ size: { w: 400, h: 300 } });
|
|
446
454
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -2739,6 +2739,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
|
|
|
2739
2739
|
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "html", "text"]);
|
|
2740
2740
|
var ARROWHEAD_LENGTH = 12;
|
|
2741
2741
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
2742
|
+
var ARROW_LABEL_FONT_SIZE = 14;
|
|
2742
2743
|
var ElementRenderer = class {
|
|
2743
2744
|
store = null;
|
|
2744
2745
|
imageCache = /* @__PURE__ */ new Map();
|
|
@@ -2749,6 +2750,7 @@ var ElementRenderer = class {
|
|
|
2749
2750
|
hexTileCache = null;
|
|
2750
2751
|
hexTileCacheKey = "";
|
|
2751
2752
|
gridBoundsOverride = null;
|
|
2753
|
+
labelEditingId = null;
|
|
2752
2754
|
setStore(store) {
|
|
2753
2755
|
this.store = store;
|
|
2754
2756
|
}
|
|
@@ -2767,6 +2769,9 @@ var ElementRenderer = class {
|
|
|
2767
2769
|
setGridBoundsOverride(bounds) {
|
|
2768
2770
|
this.gridBoundsOverride = bounds;
|
|
2769
2771
|
}
|
|
2772
|
+
setLabelEditingId(id) {
|
|
2773
|
+
this.labelEditingId = id;
|
|
2774
|
+
}
|
|
2770
2775
|
isDomElement(element) {
|
|
2771
2776
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
2772
2777
|
}
|
|
@@ -2843,6 +2848,28 @@ var ElementRenderer = class {
|
|
|
2843
2848
|
ctx.stroke();
|
|
2844
2849
|
this.renderArrowhead(ctx, arrow, visualTo, geometry.tangentEnd);
|
|
2845
2850
|
ctx.restore();
|
|
2851
|
+
this.renderArrowLabel(ctx, arrow);
|
|
2852
|
+
}
|
|
2853
|
+
renderArrowLabel(ctx, arrow) {
|
|
2854
|
+
if (!arrow.label || arrow.label.length === 0) return;
|
|
2855
|
+
if (arrow.id === this.labelEditingId) return;
|
|
2856
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
2857
|
+
ctx.save();
|
|
2858
|
+
ctx.font = `${ARROW_LABEL_FONT_SIZE}px system-ui, sans-serif`;
|
|
2859
|
+
const metrics = ctx.measureText(arrow.label);
|
|
2860
|
+
const padX = 6;
|
|
2861
|
+
const padY = 4;
|
|
2862
|
+
const w = metrics.width + padX * 2;
|
|
2863
|
+
const h = ARROW_LABEL_FONT_SIZE + padY * 2;
|
|
2864
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
|
|
2865
|
+
ctx.beginPath();
|
|
2866
|
+
ctx.roundRect(mid.x - w / 2, mid.y - h / 2, w, h, 4);
|
|
2867
|
+
ctx.fill();
|
|
2868
|
+
ctx.fillStyle = "#1a1a1a";
|
|
2869
|
+
ctx.textAlign = "center";
|
|
2870
|
+
ctx.textBaseline = "middle";
|
|
2871
|
+
ctx.fillText(arrow.label, mid.x, mid.y);
|
|
2872
|
+
ctx.restore();
|
|
2846
2873
|
}
|
|
2847
2874
|
renderArrowhead(ctx, arrow, tip, angle) {
|
|
2848
2875
|
ctx.beginPath();
|
|
@@ -3253,6 +3280,7 @@ function createArrow(input) {
|
|
|
3253
3280
|
};
|
|
3254
3281
|
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
3255
3282
|
if (input.toBinding) result.toBinding = input.toBinding;
|
|
3283
|
+
if (input.label !== void 0) result.label = input.label;
|
|
3256
3284
|
return result;
|
|
3257
3285
|
}
|
|
3258
3286
|
function createImage(input) {
|
|
@@ -3716,6 +3744,84 @@ var NoteEditor = class {
|
|
|
3716
3744
|
}
|
|
3717
3745
|
};
|
|
3718
3746
|
|
|
3747
|
+
// src/elements/arrow-label-editor.ts
|
|
3748
|
+
var ArrowLabelEditor = class {
|
|
3749
|
+
input = null;
|
|
3750
|
+
done = false;
|
|
3751
|
+
get isEditing() {
|
|
3752
|
+
return this.input !== null;
|
|
3753
|
+
}
|
|
3754
|
+
startEditing(start) {
|
|
3755
|
+
if (this.input) this.cleanup();
|
|
3756
|
+
const { arrow, layer, store, recorder, onDone } = start;
|
|
3757
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
3758
|
+
const input = document.createElement("input");
|
|
3759
|
+
input.type = "text";
|
|
3760
|
+
input.value = arrow.label ?? "";
|
|
3761
|
+
Object.assign(input.style, {
|
|
3762
|
+
position: "absolute",
|
|
3763
|
+
left: `${mid.x}px`,
|
|
3764
|
+
top: `${mid.y}px`,
|
|
3765
|
+
transform: "translate(-50%, -50%)",
|
|
3766
|
+
// domLayer is pointer-events:none; the input must opt back in to receive taps/clicks.
|
|
3767
|
+
pointerEvents: "auto",
|
|
3768
|
+
font: "14px system-ui, sans-serif",
|
|
3769
|
+
padding: "2px 6px",
|
|
3770
|
+
border: "1px solid #2196F3",
|
|
3771
|
+
borderRadius: "4px",
|
|
3772
|
+
background: "#ffffff",
|
|
3773
|
+
color: "#1a1a1a",
|
|
3774
|
+
outline: "none",
|
|
3775
|
+
minWidth: "40px"
|
|
3776
|
+
});
|
|
3777
|
+
this.done = false;
|
|
3778
|
+
const commit = () => {
|
|
3779
|
+
if (this.done) return;
|
|
3780
|
+
this.done = true;
|
|
3781
|
+
const next = input.value.trim() || void 0;
|
|
3782
|
+
if (next !== arrow.label) {
|
|
3783
|
+
recorder.begin();
|
|
3784
|
+
store.update(arrow.id, { label: next });
|
|
3785
|
+
recorder.commit();
|
|
3786
|
+
}
|
|
3787
|
+
this.cleanup();
|
|
3788
|
+
onDone();
|
|
3789
|
+
};
|
|
3790
|
+
const cancel = () => {
|
|
3791
|
+
if (this.done) return;
|
|
3792
|
+
this.done = true;
|
|
3793
|
+
this.cleanup();
|
|
3794
|
+
onDone();
|
|
3795
|
+
};
|
|
3796
|
+
input.addEventListener("keydown", (e) => {
|
|
3797
|
+
if (e.key === "Enter") {
|
|
3798
|
+
e.preventDefault();
|
|
3799
|
+
commit();
|
|
3800
|
+
} else if (e.key === "Escape") {
|
|
3801
|
+
e.preventDefault();
|
|
3802
|
+
cancel();
|
|
3803
|
+
}
|
|
3804
|
+
e.stopPropagation();
|
|
3805
|
+
});
|
|
3806
|
+
input.addEventListener("blur", commit);
|
|
3807
|
+
layer.appendChild(input);
|
|
3808
|
+
this.input = input;
|
|
3809
|
+
input.focus();
|
|
3810
|
+
input.select();
|
|
3811
|
+
}
|
|
3812
|
+
/** Abort any in-progress edit without committing (e.g. on viewport teardown). */
|
|
3813
|
+
cancel() {
|
|
3814
|
+
this.done = true;
|
|
3815
|
+
this.cleanup();
|
|
3816
|
+
}
|
|
3817
|
+
cleanup() {
|
|
3818
|
+
if (this.input) {
|
|
3819
|
+
this.input.remove();
|
|
3820
|
+
this.input = null;
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
};
|
|
3824
|
+
|
|
3719
3825
|
// src/tools/tool-manager.ts
|
|
3720
3826
|
var ToolManager = class {
|
|
3721
3827
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -5325,6 +5431,7 @@ function getElementStyle(element) {
|
|
|
5325
5431
|
|
|
5326
5432
|
// src/canvas/viewport.ts
|
|
5327
5433
|
var EMPTY_IDS = [];
|
|
5434
|
+
var ARROW_HIT_THRESHOLD = 10;
|
|
5328
5435
|
function noop() {
|
|
5329
5436
|
}
|
|
5330
5437
|
function sharedValue(values) {
|
|
@@ -5366,6 +5473,7 @@ var Viewport = class {
|
|
|
5366
5473
|
placeholder: options.placeholder
|
|
5367
5474
|
});
|
|
5368
5475
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
5476
|
+
this.arrowLabelEditor = new ArrowLabelEditor();
|
|
5369
5477
|
this.noteEditor.setHistoryHooks(
|
|
5370
5478
|
() => this.historyRecorder.begin(),
|
|
5371
5479
|
() => this.historyRecorder.commit()
|
|
@@ -5492,6 +5600,7 @@ var Viewport = class {
|
|
|
5492
5600
|
background;
|
|
5493
5601
|
renderer;
|
|
5494
5602
|
noteEditor;
|
|
5603
|
+
arrowLabelEditor;
|
|
5495
5604
|
historyRecorder;
|
|
5496
5605
|
toolContext;
|
|
5497
5606
|
marginViewport;
|
|
@@ -5746,6 +5855,7 @@ var Viewport = class {
|
|
|
5746
5855
|
this.renderLoop.stop();
|
|
5747
5856
|
this.interactMode.destroy();
|
|
5748
5857
|
this.noteEditor.destroy(this.store);
|
|
5858
|
+
this.arrowLabelEditor.cancel();
|
|
5749
5859
|
this.historyRecorder.destroy();
|
|
5750
5860
|
this.wrapper.removeEventListener("pointerdown", this.onTapDown);
|
|
5751
5861
|
this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
|
|
@@ -5831,8 +5941,35 @@ var Viewport = class {
|
|
|
5831
5941
|
const hit = this.hitTestWorld(world);
|
|
5832
5942
|
if (hit?.type === "html") {
|
|
5833
5943
|
this.interactMode.startInteracting(hit.id);
|
|
5944
|
+
return;
|
|
5945
|
+
}
|
|
5946
|
+
const arrow = this.findArrowAt(world);
|
|
5947
|
+
if (arrow) {
|
|
5948
|
+
this.startArrowLabelEdit(arrow);
|
|
5834
5949
|
}
|
|
5835
5950
|
};
|
|
5951
|
+
findArrowAt(world) {
|
|
5952
|
+
const candidates = this.store.queryPoint(world).reverse();
|
|
5953
|
+
for (const el of candidates) {
|
|
5954
|
+
if (el.type === "arrow" && isNearBezier(world, el.from, el.to, el.bend, ARROW_HIT_THRESHOLD)) {
|
|
5955
|
+
return el;
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
return void 0;
|
|
5959
|
+
}
|
|
5960
|
+
startArrowLabelEdit(arrow) {
|
|
5961
|
+
this.arrowLabelEditor.startEditing({
|
|
5962
|
+
arrow,
|
|
5963
|
+
layer: this.domLayer,
|
|
5964
|
+
store: this.store,
|
|
5965
|
+
recorder: this.historyRecorder,
|
|
5966
|
+
onDone: () => {
|
|
5967
|
+
this.renderer.setLabelEditingId(null);
|
|
5968
|
+
this.requestRender();
|
|
5969
|
+
}
|
|
5970
|
+
});
|
|
5971
|
+
this.renderer.setLabelEditingId(arrow.id);
|
|
5972
|
+
}
|
|
5836
5973
|
hitTestWorld(world) {
|
|
5837
5974
|
const candidates = this.store.queryPoint(world).reverse();
|
|
5838
5975
|
for (const el of candidates) {
|
|
@@ -7746,7 +7883,7 @@ var TemplateTool = class {
|
|
|
7746
7883
|
};
|
|
7747
7884
|
|
|
7748
7885
|
// src/index.ts
|
|
7749
|
-
var VERSION = "0.
|
|
7886
|
+
var VERSION = "0.28.0";
|
|
7750
7887
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7751
7888
|
0 && (module.exports = {
|
|
7752
7889
|
ArrowTool,
|