@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.cjs CHANGED
@@ -4031,6 +4031,7 @@ var NoteEditor = class {
4031
4031
  inputHandler = null;
4032
4032
  pendingEditId = null;
4033
4033
  onStopCallback = null;
4034
+ onInputCallback = null;
4034
4035
  beginHistory = null;
4035
4036
  commitHistory = null;
4036
4037
  toolbar;
@@ -4048,6 +4049,9 @@ var NoteEditor = class {
4048
4049
  setOnStop(callback) {
4049
4050
  this.onStopCallback = callback;
4050
4051
  }
4052
+ setOnInput(callback) {
4053
+ this.onInputCallback = callback;
4054
+ }
4051
4055
  setHistoryHooks(begin, commit) {
4052
4056
  this.beginHistory = begin;
4053
4057
  this.commitHistory = commit;
@@ -4090,7 +4094,6 @@ var NoteEditor = class {
4090
4094
  cursor: "default"
4091
4095
  });
4092
4096
  this.toolbar?.hide();
4093
- this.beginHistory?.();
4094
4097
  if (textChanged) {
4095
4098
  store.update(this.editingId, { text });
4096
4099
  }
@@ -4119,6 +4122,7 @@ var NoteEditor = class {
4119
4122
  activateEditing(node, elementId, store) {
4120
4123
  this.editingId = elementId;
4121
4124
  this.editingNode = node;
4125
+ this.beginHistory?.();
4122
4126
  node.contentEditable = "true";
4123
4127
  Object.assign(node.style, {
4124
4128
  userSelect: "text",
@@ -4139,6 +4143,7 @@ var NoteEditor = class {
4139
4143
  node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
4140
4144
  this.inputHandler = () => {
4141
4145
  node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
4146
+ this.onInputCallback?.(elementId);
4142
4147
  };
4143
4148
  node.addEventListener("input", this.inputHandler);
4144
4149
  this.toolbar?.show(node);
@@ -4716,29 +4721,42 @@ function buildFontString(run) {
4716
4721
  const weight = run.bold ? "bold" : "normal";
4717
4722
  return `${style} ${weight} ${run.fontSize}px system-ui, sans-serif`;
4718
4723
  }
4719
- function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
4724
+ function renderStyledRuns(ctx, runs, startX, startY, maxWidth, opts = {}) {
4725
+ const align = opts.align ?? "left";
4726
+ const lhMul = opts.lineHeight ?? 1.3;
4720
4727
  ctx.textBaseline = "top";
4728
+ const lines = [];
4721
4729
  let cursorX = startX;
4722
- let cursorY = startY;
4730
+ let lineY = startY;
4723
4731
  let lineHeight = 0;
4732
+ let fragments = [];
4733
+ const finalizeLine = () => {
4734
+ const last = fragments[fragments.length - 1];
4735
+ const width = last ? last.x + last.width : 0;
4736
+ lines.push({ y: lineY, width, fragments });
4737
+ };
4738
+ const breakLine = (runLineHeight) => {
4739
+ finalizeLine();
4740
+ lineY += lineHeight;
4741
+ lineHeight = runLineHeight;
4742
+ fragments = [];
4743
+ cursorX = startX;
4744
+ };
4724
4745
  for (const run of runs) {
4725
- ctx.font = buildFontString(run);
4726
- const runLineHeight = run.fontSize * 1.3;
4746
+ const font = buildFontString(run);
4747
+ ctx.font = font;
4748
+ const runLineHeight = run.fontSize * lhMul;
4727
4749
  lineHeight = Math.max(lineHeight, runLineHeight);
4728
4750
  const words = run.text.split(/(\n| )/);
4729
4751
  for (const word of words) {
4730
4752
  if (word === "\n") {
4731
- cursorX = startX;
4732
- cursorY += lineHeight;
4733
- lineHeight = runLineHeight;
4753
+ breakLine(runLineHeight);
4734
4754
  continue;
4735
4755
  }
4736
4756
  if (word === " ") {
4737
4757
  const spaceWidth = ctx.measureText(" ").width;
4738
4758
  if (cursorX + spaceWidth > startX + maxWidth && cursorX > startX) {
4739
- cursorX = startX;
4740
- cursorY += lineHeight;
4741
- lineHeight = runLineHeight;
4759
+ breakLine(runLineHeight);
4742
4760
  } else {
4743
4761
  cursorX += spaceWidth;
4744
4762
  }
@@ -4747,20 +4765,33 @@ function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
4747
4765
  if (!word) continue;
4748
4766
  const metrics = ctx.measureText(word);
4749
4767
  if (cursorX + metrics.width > startX + maxWidth && cursorX > startX) {
4750
- cursorX = startX;
4751
- cursorY += lineHeight;
4752
- lineHeight = runLineHeight;
4753
- }
4754
- ctx.fillText(word, cursorX, cursorY);
4755
- if (run.underline) {
4756
- const underY = cursorY + run.fontSize + 1;
4757
- ctx.fillRect(cursorX, underY, metrics.width, 1);
4768
+ breakLine(runLineHeight);
4769
+ }
4770
+ fragments.push({
4771
+ text: word,
4772
+ font,
4773
+ x: cursorX - startX,
4774
+ width: metrics.width,
4775
+ fontSize: run.fontSize,
4776
+ underline: run.underline,
4777
+ strikethrough: run.strikethrough
4778
+ });
4779
+ cursorX += metrics.width;
4780
+ }
4781
+ }
4782
+ finalizeLine();
4783
+ for (const line of lines) {
4784
+ const offset = align === "center" ? (maxWidth - line.width) / 2 : align === "right" ? maxWidth - line.width : 0;
4785
+ for (const fragment of line.fragments) {
4786
+ ctx.font = fragment.font;
4787
+ const fx = startX + offset + fragment.x;
4788
+ ctx.fillText(fragment.text, fx, line.y);
4789
+ if (fragment.underline) {
4790
+ ctx.fillRect(fx, line.y + fragment.fontSize + 1, fragment.width, 1);
4758
4791
  }
4759
- if (run.strikethrough) {
4760
- const strikeY = cursorY + run.fontSize * 0.55;
4761
- ctx.fillRect(cursorX, strikeY, metrics.width, 1);
4792
+ if (fragment.strikethrough) {
4793
+ ctx.fillRect(fx, line.y + fragment.fontSize * 0.55, fragment.width, 1);
4762
4794
  }
4763
- cursorX += metrics.width;
4764
4795
  }
4765
4796
  }
4766
4797
  }
@@ -4771,7 +4802,10 @@ function renderTextOnCanvas(ctx, text) {
4771
4802
  ctx.save();
4772
4803
  ctx.fillStyle = text.color;
4773
4804
  const runs = parseStyledRuns(text.text ?? "", text.fontSize);
4774
- renderStyledRuns(ctx, runs, text.position.x + pad, text.position.y + pad, text.size.w - pad * 2);
4805
+ renderStyledRuns(ctx, runs, text.position.x + pad, text.position.y + pad, text.size.w - pad * 2, {
4806
+ align: text.textAlign,
4807
+ lineHeight: 1.4
4808
+ });
4775
4809
  ctx.restore();
4776
4810
  }
4777
4811
 
@@ -6602,6 +6636,16 @@ var ViewportInteractions = class {
6602
6636
  this.deps.noteEditor.startEditing(node, id, this.deps.store);
6603
6637
  }
6604
6638
  }
6639
+ liveFitHeight(elementId) {
6640
+ const element = this.deps.store.getById(elementId);
6641
+ if (!element || element.type !== "note" && element.type !== "text") return;
6642
+ const node = this.deps.domNodeManager.getNode(elementId);
6643
+ if (!node) return;
6644
+ const measured = node.scrollHeight;
6645
+ if (measured > 0 && measured !== element.size.h) {
6646
+ this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6647
+ }
6648
+ }
6605
6649
  fitNoteHeight(elementId) {
6606
6650
  const element = this.deps.store.getById(elementId);
6607
6651
  if (!element || element.type !== "note") return;
@@ -6621,7 +6665,7 @@ var ViewportInteractions = class {
6621
6665
  this.deps.store.remove(elementId);
6622
6666
  return;
6623
6667
  }
6624
- this.fitNoteHeight(elementId);
6668
+ this.liveFitHeight(elementId);
6625
6669
  return;
6626
6670
  }
6627
6671
  if (element.type !== "text") return;
@@ -6629,13 +6673,7 @@ var ViewportInteractions = class {
6629
6673
  this.deps.store.remove(elementId);
6630
6674
  return;
6631
6675
  }
6632
- const node = this.deps.domNodeManager.getNode(elementId);
6633
- if (node && "size" in element) {
6634
- const measured = node.scrollHeight;
6635
- if (measured !== element.size.h) {
6636
- this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6637
- }
6638
- }
6676
+ this.liveFitHeight(elementId);
6639
6677
  }
6640
6678
  onTapDown = (e) => {
6641
6679
  this.tapDownX = e.clientX;
@@ -6771,6 +6809,7 @@ var Viewport = class {
6771
6809
  placeholder: options.placeholder
6772
6810
  });
6773
6811
  this.noteEditor.setOnStop((id) => this.interactions.onTextEditStop(id));
6812
+ this.noteEditor.setOnInput((id) => this.interactions.liveFitHeight(id));
6774
6813
  this.arrowLabelEditor = new ArrowLabelEditor();
6775
6814
  this.noteEditor.setHistoryHooks(
6776
6815
  () => this.historyRecorder.begin(),
@@ -9737,7 +9776,7 @@ var LaserTool = class {
9737
9776
  };
9738
9777
 
9739
9778
  // src/index.ts
9740
- var VERSION = "0.40.1";
9779
+ var VERSION = "0.40.3";
9741
9780
  // Annotate the CommonJS export names for ESM import in node:
9742
9781
  0 && (module.exports = {
9743
9782
  ArrowTool,