@fieldnotes/core 0.30.0 → 0.31.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 CHANGED
@@ -453,7 +453,7 @@ viewport.toolManager.register(
453
453
  viewport.setTool('highlighter');
454
454
  ```
455
455
 
456
- `ShapeTool` supports a `'line'` shape kind that draws a straight segment between two points. Hold **Shift** while drawing to snap to 45° increments. Lines are hit-tested by proximity to the segment, and `ShapeElement.flip` records which diagonal of the bounding box the line runs along.
456
+ `ShapeTool` supports a `'line'` shape kind that draws a straight segment between two points. Hold **Shift** while drawing to snap to 45° increments. Lines are hit-tested by proximity to the segment, and `ShapeElement.flip` records which diagonal of the bounding box the line runs along. When a line is selected, it shows two endpoint drag-handles instead of bounding-box resize handles — drag one endpoint to reshape the line while the other stays anchored.
457
457
 
458
458
  ### Arrow Labels
459
459
 
package/dist/index.cjs CHANGED
@@ -2277,6 +2277,13 @@ function getArrowRenderGeometry(arrow) {
2277
2277
  }
2278
2278
 
2279
2279
  // src/elements/shape-geometry.ts
2280
+ function lineFromEndpoints(a, b) {
2281
+ return {
2282
+ position: { x: Math.min(a.x, b.x), y: Math.min(a.y, b.y) },
2283
+ size: { w: Math.abs(b.x - a.x), h: Math.abs(b.y - a.y) },
2284
+ flip: b.x > a.x !== b.y > a.y
2285
+ };
2286
+ }
2280
2287
  function lineEndpoints(shape) {
2281
2288
  const { x, y } = shape.position;
2282
2289
  const { w, h } = shape.size;
@@ -6682,6 +6689,12 @@ var SelectTool = class {
6682
6689
  ctx.requestRender();
6683
6690
  return;
6684
6691
  }
6692
+ const lineHit = this.hitTestLineHandles(world, ctx);
6693
+ if (lineHit) {
6694
+ this.mode = { type: "line-handle", elementId: lineHit.elementId, fixed: lineHit.fixed };
6695
+ ctx.requestRender();
6696
+ return;
6697
+ }
6685
6698
  const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
6686
6699
  if (templateResizeHit) {
6687
6700
  this.mode = { type: "resizing-template", elementId: templateResizeHit };
@@ -6737,6 +6750,15 @@ var SelectTool = class {
6737
6750
  applyArrowHandleDrag(this.mode.handle, this.mode.elementId, world, ctx);
6738
6751
  return;
6739
6752
  }
6753
+ if (this.mode.type === "line-handle") {
6754
+ ctx.setCursor?.("grabbing");
6755
+ const el = ctx.store.getById(this.mode.elementId);
6756
+ if (el && el.type === "shape") {
6757
+ ctx.store.update(el.id, lineFromEndpoints(this.mode.fixed, world));
6758
+ }
6759
+ ctx.requestRender();
6760
+ return;
6761
+ }
6740
6762
  if (this.mode.type === "resizing-template") {
6741
6763
  ctx.setCursor?.("nwse-resize");
6742
6764
  this.handleTemplateResize(world, ctx);
@@ -6905,6 +6927,10 @@ var SelectTool = class {
6905
6927
  ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
6906
6928
  return null;
6907
6929
  }
6930
+ if (this.hitTestLineHandles(world, ctx)) {
6931
+ ctx.setCursor?.("grab");
6932
+ return null;
6933
+ }
6908
6934
  const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
6909
6935
  if (templateResizeHit) {
6910
6936
  ctx.setCursor?.("nwse-resize");
@@ -6992,6 +7018,7 @@ var SelectTool = class {
6992
7018
  for (const id of this._selectedIds) {
6993
7019
  const el = ctx.store.getById(id);
6994
7020
  if (!el || !("size" in el)) continue;
7021
+ if (el.type === "shape" && el.shape === "line") continue;
6995
7022
  const bounds = getElementBounds(el);
6996
7023
  if (!bounds) continue;
6997
7024
  const corners = this.getHandlePositions(bounds);
@@ -7003,6 +7030,20 @@ var SelectTool = class {
7003
7030
  }
7004
7031
  return null;
7005
7032
  }
7033
+ hitTestLineHandles(world, ctx) {
7034
+ if (this._selectedIds.length === 0) return null;
7035
+ const zoom = ctx.camera.zoom;
7036
+ const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / zoom;
7037
+ const r2 = r * r;
7038
+ for (const id of this._selectedIds) {
7039
+ const el = ctx.store.getById(id);
7040
+ if (!el || el.type !== "shape" || el.shape !== "line") continue;
7041
+ const [a, b] = lineEndpoints(el);
7042
+ if ((world.x - a.x) ** 2 + (world.y - a.y) ** 2 <= r2) return { elementId: id, fixed: b };
7043
+ if ((world.x - b.x) ** 2 + (world.y - b.y) ** 2 <= r2) return { elementId: id, fixed: a };
7044
+ }
7045
+ return null;
7046
+ }
7006
7047
  getHandlePositions(bounds) {
7007
7048
  return [
7008
7049
  ["nw", { x: bounds.x, y: bounds.y }],
@@ -7040,6 +7081,19 @@ var SelectTool = class {
7040
7081
  this.renderBindingHighlights(canvasCtx, el, zoom);
7041
7082
  continue;
7042
7083
  }
7084
+ if (el.type === "shape" && el.shape === "line") {
7085
+ canvasCtx.setLineDash([]);
7086
+ canvasCtx.fillStyle = "#ffffff";
7087
+ const r = handleWorldSize / 2;
7088
+ for (const p of lineEndpoints(el)) {
7089
+ canvasCtx.beginPath();
7090
+ canvasCtx.arc(p.x, p.y, r, 0, Math.PI * 2);
7091
+ canvasCtx.fill();
7092
+ canvasCtx.stroke();
7093
+ }
7094
+ canvasCtx.setLineDash([4 / zoom, 4 / zoom]);
7095
+ continue;
7096
+ }
7043
7097
  const bounds = getElementBounds(el);
7044
7098
  if (!bounds) continue;
7045
7099
  const pad = SELECTION_PAD / zoom;
@@ -8050,7 +8104,7 @@ var TemplateTool = class {
8050
8104
  };
8051
8105
 
8052
8106
  // src/index.ts
8053
- var VERSION = "0.30.0";
8107
+ var VERSION = "0.31.0";
8054
8108
  // Annotate the CommonJS export names for ESM import in node:
8055
8109
  0 && (module.exports = {
8056
8110
  ArrowTool,