@fieldnotes/core 0.46.1 → 0.47.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
@@ -1057,6 +1057,7 @@ declare class TemplateTool implements Tool {
1057
1057
  private gridType;
1058
1058
  private hexOrientation;
1059
1059
  private snapEnabled;
1060
+ private feetScaleUnit;
1060
1061
  private templateShape;
1061
1062
  private fillColor;
1062
1063
  private strokeColor;
@@ -1078,6 +1079,7 @@ declare class TemplateTool implements Tool {
1078
1079
  private renderHexOverlay;
1079
1080
  private computeRadius;
1080
1081
  private computeAngle;
1082
+ private drawFeetLabel;
1081
1083
  private snapToGrid;
1082
1084
  private notifyOptionsChange;
1083
1085
  }
@@ -1113,6 +1115,6 @@ declare class LaserTool implements Tool {
1113
1115
  private notifyOptionsChange;
1114
1116
  }
1115
1117
 
1116
- declare const VERSION = "0.46.1";
1118
+ declare const VERSION = "0.47.0";
1117
1119
 
1118
1120
  export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, 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, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, 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 StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, exportSvg, 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
@@ -1057,6 +1057,7 @@ declare class TemplateTool implements Tool {
1057
1057
  private gridType;
1058
1058
  private hexOrientation;
1059
1059
  private snapEnabled;
1060
+ private feetScaleUnit;
1060
1061
  private templateShape;
1061
1062
  private fillColor;
1062
1063
  private strokeColor;
@@ -1078,6 +1079,7 @@ declare class TemplateTool implements Tool {
1078
1079
  private renderHexOverlay;
1079
1080
  private computeRadius;
1080
1081
  private computeAngle;
1082
+ private drawFeetLabel;
1081
1083
  private snapToGrid;
1082
1084
  private notifyOptionsChange;
1083
1085
  }
@@ -1113,6 +1115,6 @@ declare class LaserTool implements Tool {
1113
1115
  private notifyOptionsChange;
1114
1116
  }
1115
1117
 
1116
- declare const VERSION = "0.46.1";
1118
+ declare const VERSION = "0.47.0";
1117
1119
 
1118
1120
  export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, 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, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, 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 StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, exportSvg, 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
@@ -3238,6 +3238,48 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
3238
3238
  ctx.closePath();
3239
3239
  }
3240
3240
 
3241
+ // src/elements/renderers/template-measure.ts
3242
+ function renderTemplateFeetLabel(ctx, p) {
3243
+ if (p.feet <= 0) return;
3244
+ const directional = p.templateShape === "cone" || p.templateShape === "line";
3245
+ const dir = directional ? p.angle : 0;
3246
+ const length = p.templateShape === "square" ? p.radius / 2 : p.radius;
3247
+ const cos = Math.cos(dir);
3248
+ const sin = Math.sin(dir);
3249
+ const start = p.position;
3250
+ const end = { x: start.x + length * cos, y: start.y + length * sin };
3251
+ const midX = start.x + length / 2 * cos;
3252
+ const midY = start.y + length / 2 * sin;
3253
+ ctx.save();
3254
+ ctx.globalAlpha = 1;
3255
+ ctx.beginPath();
3256
+ ctx.setLineDash([4, 4]);
3257
+ ctx.strokeStyle = p.color;
3258
+ ctx.lineWidth = 1.5;
3259
+ ctx.moveTo(start.x, start.y);
3260
+ ctx.lineTo(end.x, end.y);
3261
+ ctx.stroke();
3262
+ ctx.setLineDash([]);
3263
+ const label = `${Math.round(p.feet)} ft`;
3264
+ const fontSize = Math.max(10, Math.min(14, p.radius * 0.15));
3265
+ ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
3266
+ ctx.textAlign = "center";
3267
+ ctx.textBaseline = "bottom";
3268
+ const textY = midY - 4;
3269
+ const metrics = ctx.measureText(label);
3270
+ const padX = 4;
3271
+ const padY = 2;
3272
+ const textW = metrics.width + padX * 2;
3273
+ const textH = fontSize + padY * 2;
3274
+ ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
3275
+ ctx.beginPath();
3276
+ ctx.roundRect(midX - textW / 2, textY - textH, textW, textH, 3);
3277
+ ctx.fill();
3278
+ ctx.fillStyle = p.color;
3279
+ ctx.fillText(label, midX, textY - padY);
3280
+ ctx.restore();
3281
+ }
3282
+
3241
3283
  // src/elements/renderers/template-renderer.ts
3242
3284
  function renderTemplate(ctx, template, store) {
3243
3285
  const grid = store?.getElementsByType("grid")[0];
@@ -3261,9 +3303,6 @@ function renderGeometricTemplate(ctx, template) {
3261
3303
  ctx.arc(cx, cy, r, 0, Math.PI * 2);
3262
3304
  ctx.fill();
3263
3305
  ctx.stroke();
3264
- if (template.radiusFeet != null && template.radiusFeet > 0) {
3265
- renderRadiusMarker(ctx, cx, cy, r, template.radiusFeet);
3266
- }
3267
3306
  break;
3268
3307
  case "square":
3269
3308
  ctx.fillRect(cx - r / 2, cy - r / 2, r, r);
@@ -3296,6 +3335,16 @@ function renderGeometricTemplate(ctx, template) {
3296
3335
  break;
3297
3336
  }
3298
3337
  }
3338
+ if (template.radiusFeet != null && template.radiusFeet > 0) {
3339
+ renderTemplateFeetLabel(ctx, {
3340
+ position: template.position,
3341
+ radius: template.radius,
3342
+ angle: template.angle,
3343
+ templateShape: template.templateShape,
3344
+ feet: template.radiusFeet,
3345
+ color: template.strokeColor
3346
+ });
3347
+ }
3299
3348
  ctx.restore();
3300
3349
  }
3301
3350
  function renderHexTemplate(ctx, template, cellSize, orientation) {
@@ -3342,44 +3391,18 @@ function renderHexTemplate(ctx, template, cellSize, orientation) {
3342
3391
  ctx.lineWidth = template.strokeWidth;
3343
3392
  ctx.stroke();
3344
3393
  }
3345
- if (template.templateShape === "circle" && template.radiusFeet != null && template.radiusFeet > 0) {
3346
- const r = template.radius;
3347
- renderRadiusMarker(ctx, center2.x, center2.y, r, template.radiusFeet);
3394
+ if (template.radiusFeet != null && template.radiusFeet > 0) {
3395
+ renderTemplateFeetLabel(ctx, {
3396
+ position: template.position,
3397
+ radius: template.radius,
3398
+ angle: template.angle,
3399
+ templateShape: template.templateShape,
3400
+ feet: template.radiusFeet,
3401
+ color: template.strokeColor
3402
+ });
3348
3403
  }
3349
3404
  ctx.restore();
3350
3405
  }
3351
- function renderRadiusMarker(ctx, cx, cy, r, feet) {
3352
- const markerColor = ctx.strokeStyle;
3353
- ctx.save();
3354
- ctx.globalAlpha = 1;
3355
- ctx.beginPath();
3356
- ctx.setLineDash([4, 4]);
3357
- ctx.strokeStyle = markerColor;
3358
- ctx.lineWidth = 1.5;
3359
- ctx.moveTo(cx, cy);
3360
- ctx.lineTo(cx + r, cy);
3361
- ctx.stroke();
3362
- ctx.setLineDash([]);
3363
- const label = `${Math.round(feet)} ft`;
3364
- const fontSize = Math.max(10, Math.min(14, r * 0.15));
3365
- ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
3366
- ctx.textAlign = "center";
3367
- ctx.textBaseline = "bottom";
3368
- const textX = cx + r / 2;
3369
- const textY = cy - 4;
3370
- const metrics = ctx.measureText(label);
3371
- const padX = 4;
3372
- const padY = 2;
3373
- const textW = metrics.width + padX * 2;
3374
- const textH = fontSize + padY * 2;
3375
- ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
3376
- ctx.beginPath();
3377
- ctx.roundRect(textX - textW / 2, textY - textH, textW, textH, 3);
3378
- ctx.fill();
3379
- ctx.fillStyle = markerColor;
3380
- ctx.fillText(label, textX, textY - padY);
3381
- ctx.restore();
3382
- }
3383
3406
 
3384
3407
  // src/elements/grid-renderer.ts
3385
3408
  function getSquareGridLines(bounds, cellSize) {
@@ -8225,6 +8248,20 @@ function getOverlayLayout(el, zoom) {
8225
8248
  const rotateHandle = rotatePoint(topMid, center2, angle);
8226
8249
  return { center: center2, corners, rotateHandle, angle };
8227
8250
  }
8251
+ function templateAimKnob(el, zoom) {
8252
+ if (el.type !== "template") return null;
8253
+ if (el.templateShape !== "cone" && el.templateShape !== "line") return null;
8254
+ const gap = ROTATE_HANDLE_OFFSET / zoom;
8255
+ const dist = el.radius + gap;
8256
+ const origin = el.position;
8257
+ return {
8258
+ origin,
8259
+ knob: {
8260
+ x: origin.x + dist * Math.cos(el.angle),
8261
+ y: origin.y + dist * Math.sin(el.angle)
8262
+ }
8263
+ };
8264
+ }
8228
8265
  function getHandlePositions(bounds) {
8229
8266
  return [
8230
8267
  ["nw", { x: bounds.x, y: bounds.y }],
@@ -8373,6 +8410,22 @@ function renderSelectionBoxes(ctx, p) {
8373
8410
  handleWorldSize
8374
8411
  );
8375
8412
  ctx.setLineDash([4 / zoom, 4 / zoom]);
8413
+ if (p.selectedIds.length === 1 && (el.templateShape === "cone" || el.templateShape === "line")) {
8414
+ const aim = templateAimKnob(el, zoom);
8415
+ if (aim) {
8416
+ ctx.beginPath();
8417
+ ctx.moveTo(aim.origin.x, aim.origin.y);
8418
+ ctx.lineTo(aim.knob.x, aim.knob.y);
8419
+ ctx.stroke();
8420
+ ctx.setLineDash([]);
8421
+ ctx.fillStyle = "#ffffff";
8422
+ ctx.beginPath();
8423
+ ctx.arc(aim.knob.x, aim.knob.y, handleWorldSize / 2, 0, Math.PI * 2);
8424
+ ctx.fill();
8425
+ ctx.stroke();
8426
+ ctx.setLineDash([4 / zoom, 4 / zoom]);
8427
+ }
8428
+ }
8376
8429
  }
8377
8430
  if (p.selectedIds.length === 1 && ROTATABLE_TYPES.has(el.type)) {
8378
8431
  const stemStart = topMidpoint(layout);
@@ -8528,6 +8581,19 @@ function hitTestTemplateResizeHandle(world, ctx, selectedIds) {
8528
8581
  }
8529
8582
  return null;
8530
8583
  }
8584
+ function hitTestTemplateAimHandle(world, ctx, selectedIds) {
8585
+ if (selectedIds.length !== 1) return null;
8586
+ const id = selectedIds[0];
8587
+ if (!id) return null;
8588
+ const el = ctx.store.getById(id);
8589
+ if (!el || el.locked) return null;
8590
+ const knob = templateAimKnob(el, ctx.camera.zoom);
8591
+ if (!knob) return null;
8592
+ const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / ctx.camera.zoom;
8593
+ const dx = world.x - knob.knob.x;
8594
+ const dy = world.y - knob.knob.y;
8595
+ return dx * dx + dy * dy <= r * r ? { elementId: id } : null;
8596
+ }
8531
8597
  function findElementsInRect(marquee, ctx) {
8532
8598
  const candidates = ctx.store.queryRect(marquee);
8533
8599
  const ids = [];
@@ -8777,6 +8843,12 @@ var SelectTool = class {
8777
8843
  ctx.requestRender();
8778
8844
  return;
8779
8845
  }
8846
+ const aimHit = hitTestTemplateAimHandle(world, ctx, this._selectedIds);
8847
+ if (aimHit) {
8848
+ this.mode = { type: "aiming-template", elementId: aimHit.elementId };
8849
+ ctx.requestRender();
8850
+ return;
8851
+ }
8780
8852
  const rotateHit = hitTestRotateHandle(world, ctx, this._selectedIds);
8781
8853
  if (rotateHit) {
8782
8854
  const el = ctx.store.getById(rotateHit.elementId);
@@ -8858,6 +8930,19 @@ var SelectTool = class {
8858
8930
  this.handleTemplateResize(world, ctx);
8859
8931
  return;
8860
8932
  }
8933
+ if (this.mode.type === "aiming-template") {
8934
+ const el = ctx.store.getById(this.mode.elementId);
8935
+ if (el && el.type === "template" && !el.locked) {
8936
+ let a = Math.atan2(world.y - el.position.y, world.x - el.position.x);
8937
+ if (state.shiftKey) {
8938
+ const snap = ctx.gridType === "hex" ? Math.PI / 3 : ROTATE_SNAP;
8939
+ a = Math.round(a / snap) * snap;
8940
+ }
8941
+ ctx.store.update(this.mode.elementId, { angle: normalizeAngle(a) });
8942
+ ctx.requestRender();
8943
+ }
8944
+ return;
8945
+ }
8861
8946
  if (this.mode.type === "rotating") {
8862
8947
  const { elementId, center: center2, startPointerAngle, startRotation } = this.mode;
8863
8948
  const a = Math.atan2(world.y - center2.y, world.x - center2.x);
@@ -9064,6 +9149,10 @@ var SelectTool = class {
9064
9149
  ctx.setCursor?.("nwse-resize");
9065
9150
  return null;
9066
9151
  }
9152
+ if (hitTestTemplateAimHandle(world, ctx, this._selectedIds)) {
9153
+ ctx.setCursor?.("grab");
9154
+ return null;
9155
+ }
9067
9156
  if (hitTestRotateHandle(world, ctx, this._selectedIds)) {
9068
9157
  ctx.setCursor?.("grab");
9069
9158
  return null;
@@ -9733,6 +9822,7 @@ var TemplateTool = class {
9733
9822
  gridType;
9734
9823
  hexOrientation;
9735
9824
  snapEnabled = false;
9825
+ feetScaleUnit = 0;
9736
9826
  templateShape;
9737
9827
  fillColor;
9738
9828
  strokeColor;
@@ -9781,6 +9871,7 @@ var TemplateTool = class {
9781
9871
  this.gridType = ctx.gridType;
9782
9872
  this.hexOrientation = ctx.hexOrientation;
9783
9873
  this.snapEnabled = !!ctx.gridType || (ctx.snapToGrid ?? false);
9874
+ this.feetScaleUnit = ctx.gridSize && ctx.gridSize > 0 ? ctx.gridType === "hex" ? Math.sqrt(3) * ctx.gridSize : ctx.gridSize : 0;
9784
9875
  const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
9785
9876
  this.origin = this.snapToGrid(world, ctx);
9786
9877
  this.current = { ...this.origin };
@@ -9880,6 +9971,7 @@ var TemplateTool = class {
9880
9971
  break;
9881
9972
  }
9882
9973
  }
9974
+ this.drawFeetLabel(ctx, radius);
9883
9975
  ctx.restore();
9884
9976
  }
9885
9977
  renderHexOverlay(ctx, radius) {
@@ -9930,30 +10022,7 @@ var TemplateTool = class {
9930
10022
  ctx.lineWidth = this.strokeWidth;
9931
10023
  ctx.stroke();
9932
10024
  }
9933
- if (this.templateShape === "circle") {
9934
- const feet = radiusCells * this.feetPerCell;
9935
- if (feet > 0) {
9936
- ctx.globalAlpha = 1;
9937
- const label = `${Math.round(feet)} ft`;
9938
- const fontSize = Math.max(10, Math.min(14, radius * 0.15));
9939
- ctx.font = `bold ${fontSize}px system-ui, sans-serif`;
9940
- ctx.textAlign = "center";
9941
- ctx.textBaseline = "bottom";
9942
- const textX = center2.x;
9943
- const textY = center2.y - 4;
9944
- const metrics = ctx.measureText(label);
9945
- const padX = 4;
9946
- const padY = 2;
9947
- const textW = metrics.width + padX * 2;
9948
- const textH = fontSize + padY * 2;
9949
- ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
9950
- ctx.beginPath();
9951
- ctx.roundRect(textX - textW / 2, textY - textH, textW, textH, 3);
9952
- ctx.fill();
9953
- ctx.fillStyle = this.strokeColor;
9954
- ctx.fillText(label, textX, textY - padY);
9955
- }
9956
- }
10025
+ this.drawFeetLabel(ctx, radius);
9957
10026
  ctx.restore();
9958
10027
  }
9959
10028
  computeRadius() {
@@ -9971,6 +10040,17 @@ var TemplateTool = class {
9971
10040
  const dy = this.current.y - this.origin.y;
9972
10041
  return Math.atan2(dy, dx);
9973
10042
  }
10043
+ drawFeetLabel(ctx, radius) {
10044
+ const feet = this.feetScaleUnit > 0 ? radius / this.feetScaleUnit * this.feetPerCell : 0;
10045
+ renderTemplateFeetLabel(ctx, {
10046
+ position: this.origin,
10047
+ radius,
10048
+ angle: this.computeAngle(),
10049
+ templateShape: this.templateShape,
10050
+ feet,
10051
+ color: this.strokeColor
10052
+ });
10053
+ }
9974
10054
  snapToGrid(point, ctx) {
9975
10055
  if (!ctx.gridSize) return point;
9976
10056
  if (ctx.gridType === "hex" && ctx.hexOrientation) {
@@ -10100,7 +10180,7 @@ var LaserTool = class {
10100
10180
  };
10101
10181
 
10102
10182
  // src/index.ts
10103
- var VERSION = "0.46.1";
10183
+ var VERSION = "0.47.0";
10104
10184
  export {
10105
10185
  ArrowTool,
10106
10186
  AutoSave,