@fieldnotes/core 0.40.0 → 0.40.2

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);
@@ -4765,6 +4770,16 @@ function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
4765
4770
  }
4766
4771
  }
4767
4772
 
4773
+ // src/canvas/text-canvas-renderer.ts
4774
+ function renderTextOnCanvas(ctx, text) {
4775
+ const pad = 2;
4776
+ ctx.save();
4777
+ ctx.fillStyle = text.color;
4778
+ const runs = parseStyledRuns(text.text ?? "", text.fontSize);
4779
+ renderStyledRuns(ctx, runs, text.position.x + pad, text.position.y + pad, text.size.w - pad * 2);
4780
+ ctx.restore();
4781
+ }
4782
+
4768
4783
  // src/canvas/export-image.ts
4769
4784
  var center = (b) => ({ x: b.x + b.w / 2, y: b.y + b.h / 2 });
4770
4785
  function getStrokeBounds(el) {
@@ -4847,30 +4862,6 @@ function computeBounds(elements, padding) {
4847
4862
  h: maxY - minY + padding * 2
4848
4863
  };
4849
4864
  }
4850
- function renderTextOnCanvas(ctx, text) {
4851
- if (!text.text) return;
4852
- ctx.save();
4853
- ctx.fillStyle = text.color;
4854
- ctx.font = `${text.fontSize}px system-ui, sans-serif`;
4855
- ctx.textBaseline = "top";
4856
- ctx.textAlign = text.textAlign;
4857
- const pad = 2;
4858
- let textX = text.position.x + pad;
4859
- if (text.textAlign === "center") {
4860
- textX = text.position.x + text.size.w / 2;
4861
- } else if (text.textAlign === "right") {
4862
- textX = text.position.x + text.size.w - pad;
4863
- }
4864
- const lineHeight = text.fontSize * 1.4;
4865
- const lines = text.text.split("\n");
4866
- for (let i = 0; i < lines.length; i++) {
4867
- const line = lines[i];
4868
- if (line !== void 0) {
4869
- ctx.fillText(line, textX, text.position.y + pad + i * lineHeight);
4870
- }
4871
- }
4872
- ctx.restore();
4873
- }
4874
4865
  function renderGridForBounds(ctx, grid, bounds) {
4875
4866
  const visibleBounds = {
4876
4867
  minX: bounds.x,
@@ -5092,28 +5083,27 @@ function emitImage(image, dataUri) {
5092
5083
  const { w, h } = image.size;
5093
5084
  return `<image href="${esc(href)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
5094
5085
  }
5095
- function emitText(text) {
5086
+ function emitText(text, rasterScale) {
5096
5087
  if (!text.text) return "";
5097
- const pad = 2;
5098
- let anchor = "start";
5099
- let textX = text.position.x + pad;
5100
- if (text.textAlign === "center") {
5101
- anchor = "middle";
5102
- textX = text.position.x + text.size.w / 2;
5103
- } else if (text.textAlign === "right") {
5104
- anchor = "end";
5105
- textX = text.position.x + text.size.w - pad;
5106
- }
5107
- const lineHeight = text.fontSize * 1.4;
5108
- const lines = text.text.split("\n");
5109
- let out = "";
5110
- for (let i = 0; i < lines.length; i++) {
5111
- const line = lines[i];
5112
- if (line === void 0) continue;
5113
- const y = text.position.y + pad + i * lineHeight;
5114
- out += `<text x="${n(textX)}" y="${n(y)}" font-family="system-ui, sans-serif" font-size="${n(text.fontSize)}" fill="${esc(text.color)}" text-anchor="${anchor}" dominant-baseline="text-before-edge">${esc(line)}</text>`;
5088
+ const { x, y } = text.position;
5089
+ const { w, h } = text.size;
5090
+ if (typeof document === "undefined") return "";
5091
+ const canvas = document.createElement("canvas");
5092
+ canvas.width = Math.max(1, Math.ceil(w * rasterScale));
5093
+ canvas.height = Math.max(1, Math.ceil(h * rasterScale));
5094
+ const ctx = canvas.getContext("2d");
5095
+ if (!ctx) return "";
5096
+ ctx.scale(rasterScale, rasterScale);
5097
+ ctx.translate(-x, -y);
5098
+ renderTextOnCanvas(ctx, text);
5099
+ let dataUri;
5100
+ try {
5101
+ dataUri = canvas.toDataURL();
5102
+ } catch {
5103
+ return "";
5115
5104
  }
5116
- return out;
5105
+ if (!dataUri || !dataUri.startsWith("data:")) return "";
5106
+ return `<image href="${esc(dataUri)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
5117
5107
  }
5118
5108
  function emitNote(note, rasterScale) {
5119
5109
  const { x, y } = note.position;
@@ -5296,7 +5286,7 @@ function emitElement(el, imageDataUris, rasterScale, firstGrid, store) {
5296
5286
  case "image":
5297
5287
  return withRotationSvg(el, emitImage(el, imageDataUris.get(el.id)));
5298
5288
  case "text":
5299
- return withRotationSvg(el, emitText(el));
5289
+ return withRotationSvg(el, emitText(el, rasterScale));
5300
5290
  case "note":
5301
5291
  return withRotationSvg(el, emitNote(el, rasterScale));
5302
5292
  case "template":
@@ -5759,10 +5749,9 @@ var DomNodeManager = class {
5759
5749
  cursor: "default",
5760
5750
  userSelect: "none",
5761
5751
  wordWrap: "break-word",
5762
- whiteSpace: "pre-wrap",
5763
5752
  lineHeight: "1.4"
5764
5753
  });
5765
- node.textContent = element.text || "";
5754
+ node.innerHTML = element.text || "";
5766
5755
  const detector = new DoubleTapDetector();
5767
5756
  node.addEventListener("pointerup", (e) => {
5768
5757
  if (detector.feed(e)) {
@@ -5773,8 +5762,9 @@ var DomNodeManager = class {
5773
5762
  });
5774
5763
  }
5775
5764
  if (!this.isEditingElement(element.id)) {
5776
- if (node.textContent !== element.text) {
5777
- node.textContent = element.text || "";
5765
+ const text = element.text || "";
5766
+ if (node.innerHTML !== text) {
5767
+ node.innerHTML = text;
5778
5768
  }
5779
5769
  Object.assign(node.style, {
5780
5770
  fontSize: `${element.fontSize}px`,
@@ -6617,6 +6607,16 @@ var ViewportInteractions = class {
6617
6607
  this.deps.noteEditor.startEditing(node, id, this.deps.store);
6618
6608
  }
6619
6609
  }
6610
+ liveFitHeight(elementId) {
6611
+ const element = this.deps.store.getById(elementId);
6612
+ if (!element || element.type !== "note" && element.type !== "text") return;
6613
+ const node = this.deps.domNodeManager.getNode(elementId);
6614
+ if (!node) return;
6615
+ const measured = node.scrollHeight;
6616
+ if (measured > 0 && measured !== element.size.h) {
6617
+ this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6618
+ }
6619
+ }
6620
6620
  fitNoteHeight(elementId) {
6621
6621
  const element = this.deps.store.getById(elementId);
6622
6622
  if (!element || element.type !== "note") return;
@@ -6636,7 +6636,7 @@ var ViewportInteractions = class {
6636
6636
  this.deps.store.remove(elementId);
6637
6637
  return;
6638
6638
  }
6639
- this.fitNoteHeight(elementId);
6639
+ this.liveFitHeight(elementId);
6640
6640
  return;
6641
6641
  }
6642
6642
  if (element.type !== "text") return;
@@ -6644,13 +6644,7 @@ var ViewportInteractions = class {
6644
6644
  this.deps.store.remove(elementId);
6645
6645
  return;
6646
6646
  }
6647
- const node = this.deps.domNodeManager.getNode(elementId);
6648
- if (node && "size" in element) {
6649
- const measured = node.scrollHeight;
6650
- if (measured !== element.size.h) {
6651
- this.deps.store.update(elementId, { size: { w: element.size.w, h: measured } });
6652
- }
6653
- }
6647
+ this.liveFitHeight(elementId);
6654
6648
  }
6655
6649
  onTapDown = (e) => {
6656
6650
  this.tapDownX = e.clientX;
@@ -6786,6 +6780,7 @@ var Viewport = class {
6786
6780
  placeholder: options.placeholder
6787
6781
  });
6788
6782
  this.noteEditor.setOnStop((id) => this.interactions.onTextEditStop(id));
6783
+ this.noteEditor.setOnInput((id) => this.interactions.liveFitHeight(id));
6789
6784
  this.arrowLabelEditor = new ArrowLabelEditor();
6790
6785
  this.noteEditor.setHistoryHooks(
6791
6786
  () => this.historyRecorder.begin(),
@@ -9752,7 +9747,7 @@ var LaserTool = class {
9752
9747
  };
9753
9748
 
9754
9749
  // src/index.ts
9755
- var VERSION = "0.40.0";
9750
+ var VERSION = "0.40.2";
9756
9751
  // Annotate the CommonJS export names for ESM import in node:
9757
9752
  0 && (module.exports = {
9758
9753
  ArrowTool,