@fieldnotes/core 0.40.1 → 0.40.3

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
@@ -1048,6 +1048,6 @@ declare class LaserTool implements Tool {
1048
1048
  private notifyOptionsChange;
1049
1049
  }
1050
1050
 
1051
- declare const VERSION = "0.40.1";
1051
+ declare const VERSION = "0.40.3";
1052
1052
 
1053
1053
  export { type ActiveFormats, type AlignEdge, 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, type DistributeAxis, 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, LaserTool, type LaserToolOptions, 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, 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
@@ -1048,6 +1048,6 @@ declare class LaserTool implements Tool {
1048
1048
  private notifyOptionsChange;
1049
1049
  }
1050
1050
 
1051
- declare const VERSION = "0.40.1";
1051
+ declare const VERSION = "0.40.3";
1052
1052
 
1053
1053
  export { type ActiveFormats, type AlignEdge, 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, type DistributeAxis, 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, LaserTool, type LaserToolOptions, 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, 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
@@ -3948,6 +3948,7 @@ var NoteEditor = class {
3948
3948
  inputHandler = null;
3949
3949
  pendingEditId = null;
3950
3950
  onStopCallback = null;
3951
+ onInputCallback = null;
3951
3952
  beginHistory = null;
3952
3953
  commitHistory = null;
3953
3954
  toolbar;
@@ -3965,6 +3966,9 @@ var NoteEditor = class {
3965
3966
  setOnStop(callback) {
3966
3967
  this.onStopCallback = callback;
3967
3968
  }
3969
+ setOnInput(callback) {
3970
+ this.onInputCallback = callback;
3971
+ }
3968
3972
  setHistoryHooks(begin, commit) {
3969
3973
  this.beginHistory = begin;
3970
3974
  this.commitHistory = commit;
@@ -4007,7 +4011,6 @@ var NoteEditor = class {
4007
4011
  cursor: "default"
4008
4012
  });
4009
4013
  this.toolbar?.hide();
4010
- this.beginHistory?.();
4011
4014
  if (textChanged) {
4012
4015
  store.update(this.editingId, { text });
4013
4016
  }
@@ -4036,6 +4039,7 @@ var NoteEditor = class {
4036
4039
  activateEditing(node, elementId, store) {
4037
4040
  this.editingId = elementId;
4038
4041
  this.editingNode = node;
4042
+ this.beginHistory?.();
4039
4043
  node.contentEditable = "true";
4040
4044
  Object.assign(node.style, {
4041
4045
  userSelect: "text",
@@ -4056,6 +4060,7 @@ var NoteEditor = class {
4056
4060
  node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
4057
4061
  this.inputHandler = () => {
4058
4062
  node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
4063
+ this.onInputCallback?.(elementId);
4059
4064
  };
4060
4065
  node.addEventListener("input", this.inputHandler);
4061
4066
  this.toolbar?.show(node);
@@ -4633,29 +4638,42 @@ function buildFontString(run) {
4633
4638
  const weight = run.bold ? "bold" : "normal";
4634
4639
  return `${style} ${weight} ${run.fontSize}px system-ui, sans-serif`;
4635
4640
  }
4636
- function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
4641
+ function renderStyledRuns(ctx, runs, startX, startY, maxWidth, opts = {}) {
4642
+ const align = opts.align ?? "left";
4643
+ const lhMul = opts.lineHeight ?? 1.3;
4637
4644
  ctx.textBaseline = "top";
4645
+ const lines = [];
4638
4646
  let cursorX = startX;
4639
- let cursorY = startY;
4647
+ let lineY = startY;
4640
4648
  let lineHeight = 0;
4649
+ let fragments = [];
4650
+ const finalizeLine = () => {
4651
+ const last = fragments[fragments.length - 1];
4652
+ const width = last ? last.x + last.width : 0;
4653
+ lines.push({ y: lineY, width, fragments });
4654
+ };
4655
+ const breakLine = (runLineHeight) => {
4656
+ finalizeLine();
4657
+ lineY += lineHeight;
4658
+ lineHeight = runLineHeight;
4659
+ fragments = [];
4660
+ cursorX = startX;
4661
+ };
4641
4662
  for (const run of runs) {
4642
- ctx.font = buildFontString(run);
4643
- const runLineHeight = run.fontSize * 1.3;
4663
+ const font = buildFontString(run);
4664
+ ctx.font = font;
4665
+ const runLineHeight = run.fontSize * lhMul;
4644
4666
  lineHeight = Math.max(lineHeight, runLineHeight);
4645
4667
  const words = run.text.split(/(\n| )/);
4646
4668
  for (const word of words) {
4647
4669
  if (word === "\n") {
4648
- cursorX = startX;
4649
- cursorY += lineHeight;
4650
- lineHeight = runLineHeight;
4670
+ breakLine(runLineHeight);
4651
4671
  continue;
4652
4672
  }
4653
4673
  if (word === " ") {
4654
4674
  const spaceWidth = ctx.measureText(" ").width;
4655
4675
  if (cursorX + spaceWidth > startX + maxWidth && cursorX > startX) {
4656
- cursorX = startX;
4657
- cursorY += lineHeight;
4658
- lineHeight = runLineHeight;
4676
+ breakLine(runLineHeight);
4659
4677
  } else {
4660
4678
  cursorX += spaceWidth;
4661
4679
  }
@@ -4664,20 +4682,33 @@ function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
4664
4682
  if (!word) continue;
4665
4683
  const metrics = ctx.measureText(word);
4666
4684
  if (cursorX + metrics.width > startX + maxWidth && cursorX > startX) {
4667
- cursorX = startX;
4668
- cursorY += lineHeight;
4669
- lineHeight = runLineHeight;
4670
- }
4671
- ctx.fillText(word, cursorX, cursorY);
4672
- if (run.underline) {
4673
- const underY = cursorY + run.fontSize + 1;
4674
- ctx.fillRect(cursorX, underY, metrics.width, 1);
4685
+ breakLine(runLineHeight);
4686
+ }
4687
+ fragments.push({
4688
+ text: word,
4689
+ font,
4690
+ x: cursorX - startX,
4691
+ width: metrics.width,
4692
+ fontSize: run.fontSize,
4693
+ underline: run.underline,
4694
+ strikethrough: run.strikethrough
4695
+ });
4696
+ cursorX += metrics.width;
4697
+ }
4698
+ }
4699
+ finalizeLine();
4700
+ for (const line of lines) {
4701
+ const offset = align === "center" ? (maxWidth - line.width) / 2 : align === "right" ? maxWidth - line.width : 0;
4702
+ for (const fragment of line.fragments) {
4703
+ ctx.font = fragment.font;
4704
+ const fx = startX + offset + fragment.x;
4705
+ ctx.fillText(fragment.text, fx, line.y);
4706
+ if (fragment.underline) {
4707
+ ctx.fillRect(fx, line.y + fragment.fontSize + 1, fragment.width, 1);
4675
4708
  }
4676
- if (run.strikethrough) {
4677
- const strikeY = cursorY + run.fontSize * 0.55;
4678
- ctx.fillRect(cursorX, strikeY, metrics.width, 1);
4709
+ if (fragment.strikethrough) {
4710
+ ctx.fillRect(fx, line.y + fragment.fontSize * 0.55, fragment.width, 1);
4679
4711
  }
4680
- cursorX += metrics.width;
4681
4712
  }
4682
4713
  }
4683
4714
  }
@@ -4688,7 +4719,10 @@ function renderTextOnCanvas(ctx, text) {
4688
4719
  ctx.save();
4689
4720
  ctx.fillStyle = text.color;
4690
4721
  const runs = parseStyledRuns(text.text ?? "", text.fontSize);
4691
- renderStyledRuns(ctx, runs, text.position.x + pad, text.position.y + pad, text.size.w - pad * 2);
4722
+ renderStyledRuns(ctx, runs, text.position.x + pad, text.position.y + pad, text.size.w - pad * 2, {
4723
+ align: text.textAlign,
4724
+ lineHeight: 1.4
4725
+ });
4692
4726
  ctx.restore();
4693
4727
  }
4694
4728
 
@@ -6519,6 +6553,16 @@ var ViewportInteractions = class {
6519
6553
  this.deps.noteEditor.startEditing(node, id, this.deps.store);
6520
6554
  }
6521
6555
  }
6556
+ liveFitHeight(elementId) {
6557
+ const element = this.deps.store.getById(elementId);
6558
+ if (!element || element.type !== "note" && element.type !== "text") return;
6559
+ const node = this.deps.domNodeManager.getNode(elementId);
6560
+ if (!node) return;
6561
+ const measured = node.scrollHeight;
6562
+ if (measured > 0 && measured !== element.size.h) {
6563
+ this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6564
+ }
6565
+ }
6522
6566
  fitNoteHeight(elementId) {
6523
6567
  const element = this.deps.store.getById(elementId);
6524
6568
  if (!element || element.type !== "note") return;
@@ -6538,7 +6582,7 @@ var ViewportInteractions = class {
6538
6582
  this.deps.store.remove(elementId);
6539
6583
  return;
6540
6584
  }
6541
- this.fitNoteHeight(elementId);
6585
+ this.liveFitHeight(elementId);
6542
6586
  return;
6543
6587
  }
6544
6588
  if (element.type !== "text") return;
@@ -6546,13 +6590,7 @@ var ViewportInteractions = class {
6546
6590
  this.deps.store.remove(elementId);
6547
6591
  return;
6548
6592
  }
6549
- const node = this.deps.domNodeManager.getNode(elementId);
6550
- if (node && "size" in element) {
6551
- const measured = node.scrollHeight;
6552
- if (measured !== element.size.h) {
6553
- this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6554
- }
6555
- }
6593
+ this.liveFitHeight(elementId);
6556
6594
  }
6557
6595
  onTapDown = (e) => {
6558
6596
  this.tapDownX = e.clientX;
@@ -6688,6 +6726,7 @@ var Viewport = class {
6688
6726
  placeholder: options.placeholder
6689
6727
  });
6690
6728
  this.noteEditor.setOnStop((id) => this.interactions.onTextEditStop(id));
6729
+ this.noteEditor.setOnInput((id) => this.interactions.liveFitHeight(id));
6691
6730
  this.arrowLabelEditor = new ArrowLabelEditor();
6692
6731
  this.noteEditor.setHistoryHooks(
6693
6732
  () => this.historyRecorder.begin(),
@@ -9654,7 +9693,7 @@ var LaserTool = class {
9654
9693
  };
9655
9694
 
9656
9695
  // src/index.ts
9657
- var VERSION = "0.40.1";
9696
+ var VERSION = "0.40.3";
9658
9697
  export {
9659
9698
  ArrowTool,
9660
9699
  AutoSave,