@fieldnotes/core 0.2.2 → 0.3.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.cjs CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  PencilTool: () => PencilTool,
41
41
  RemoveElementCommand: () => RemoveElementCommand,
42
42
  SelectTool: () => SelectTool,
43
+ TextTool: () => TextTool,
43
44
  ToolManager: () => ToolManager,
44
45
  UpdateElementCommand: () => UpdateElementCommand,
45
46
  VERSION: () => VERSION,
@@ -50,6 +51,7 @@ __export(index_exports, {
50
51
  createImage: () => createImage,
51
52
  createNote: () => createNote,
52
53
  createStroke: () => createStroke,
54
+ createText: () => createText,
53
55
  exportState: () => exportState,
54
56
  getArrowBounds: () => getArrowBounds,
55
57
  getArrowControlPoint: () => getArrowControlPoint,
@@ -132,7 +134,7 @@ function validateState(data) {
132
134
  migrateElement(el);
133
135
  }
134
136
  }
135
- var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html"]);
137
+ var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text"]);
136
138
  function validateElement(el) {
137
139
  if (!el || typeof el !== "object") {
138
140
  throw new Error("Invalid element: expected an object");
@@ -815,7 +817,7 @@ function smoothToSegments(points) {
815
817
  }
816
818
 
817
819
  // src/elements/element-renderer.ts
818
- var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html"]);
820
+ var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html", "text"]);
819
821
  var ARROWHEAD_LENGTH = 12;
820
822
  var ARROWHEAD_ANGLE = Math.PI / 6;
821
823
  var ElementRenderer = class {
@@ -894,12 +896,16 @@ var NoteEditor = class {
894
896
  keyHandler = null;
895
897
  pointerHandler = null;
896
898
  pendingEditId = null;
899
+ onStopCallback = null;
897
900
  get isEditing() {
898
901
  return this.editingId !== null;
899
902
  }
900
903
  get editingElementId() {
901
904
  return this.editingId;
902
905
  }
906
+ setOnStop(callback) {
907
+ this.onStopCallback = callback;
908
+ }
903
909
  startEditing(node, elementId, store) {
904
910
  if (this.editingId === elementId) return;
905
911
  if (this.editingId) {
@@ -931,6 +937,9 @@ var NoteEditor = class {
931
937
  if (this.pointerHandler) {
932
938
  this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
933
939
  }
940
+ if (this.editingId && this.onStopCallback) {
941
+ this.onStopCallback(this.editingId);
942
+ }
934
943
  this.editingId = null;
935
944
  this.editingNode = null;
936
945
  this.blurHandler = null;
@@ -1281,6 +1290,20 @@ function createHtmlElement(input) {
1281
1290
  size: input.size
1282
1291
  };
1283
1292
  }
1293
+ function createText(input) {
1294
+ return {
1295
+ id: createId("text"),
1296
+ type: "text",
1297
+ position: input.position,
1298
+ zIndex: input.zIndex ?? 0,
1299
+ locked: input.locked ?? false,
1300
+ size: input.size ?? { w: 200, h: 28 },
1301
+ text: input.text ?? "",
1302
+ fontSize: input.fontSize ?? 16,
1303
+ color: input.color ?? "#1a1a1a",
1304
+ textAlign: input.textAlign ?? "left"
1305
+ };
1306
+ }
1284
1307
 
1285
1308
  // src/canvas/viewport.ts
1286
1309
  var Viewport = class {
@@ -1292,6 +1315,7 @@ var Viewport = class {
1292
1315
  this.toolManager = new ToolManager();
1293
1316
  this.renderer = new ElementRenderer();
1294
1317
  this.noteEditor = new NoteEditor();
1318
+ this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
1295
1319
  this.history = new HistoryStack();
1296
1320
  this.historyRecorder = new HistoryRecorder(this.store, this.history);
1297
1321
  this.wrapper = this.createWrapper();
@@ -1305,7 +1329,7 @@ var Viewport = class {
1305
1329
  store: this.store,
1306
1330
  requestRender: () => this.requestRender(),
1307
1331
  switchTool: (name) => this.toolManager.setTool(name, this.toolContext),
1308
- editElement: (id) => this.startEditingNote(id),
1332
+ editElement: (id) => this.startEditingElement(id),
1309
1333
  setCursor: (cursor) => {
1310
1334
  this.wrapper.style.cursor = cursor;
1311
1335
  }
@@ -1459,15 +1483,34 @@ var Viewport = class {
1459
1483
  ctx.restore();
1460
1484
  ctx.restore();
1461
1485
  }
1462
- startEditingNote(id) {
1486
+ startEditingElement(id) {
1463
1487
  const element = this.store.getById(id);
1464
- if (!element || element.type !== "note") return;
1488
+ if (!element || element.type !== "note" && element.type !== "text") return;
1465
1489
  this.render();
1466
1490
  const node = this.domNodes.get(id);
1467
1491
  if (node) {
1468
1492
  this.noteEditor.startEditing(node, id, this.store);
1469
1493
  }
1470
1494
  }
1495
+ onTextEditStop(elementId) {
1496
+ const element = this.store.getById(elementId);
1497
+ if (!element || element.type !== "text") return;
1498
+ if (!element.text || element.text.trim() === "") {
1499
+ this.historyRecorder.begin();
1500
+ this.store.remove(elementId);
1501
+ this.historyRecorder.commit();
1502
+ return;
1503
+ }
1504
+ const node = this.domNodes.get(elementId);
1505
+ if (node && "size" in element) {
1506
+ const measuredHeight = node.scrollHeight;
1507
+ if (measuredHeight !== element.size.h) {
1508
+ this.store.update(elementId, {
1509
+ size: { w: element.size.w, h: measuredHeight }
1510
+ });
1511
+ }
1512
+ }
1513
+ }
1471
1514
  onDblClick = (e) => {
1472
1515
  const el = document.elementFromPoint(e.clientX, e.clientY);
1473
1516
  const nodeEl = el?.closest("[data-element-id]");
@@ -1475,8 +1518,8 @@ var Viewport = class {
1475
1518
  const elementId = nodeEl.dataset["elementId"];
1476
1519
  if (elementId) {
1477
1520
  const element = this.store.getById(elementId);
1478
- if (element?.type === "note") {
1479
- this.startEditingNote(elementId);
1521
+ if (element?.type === "note" || element?.type === "text") {
1522
+ this.startEditingElement(elementId);
1480
1523
  return;
1481
1524
  }
1482
1525
  }
@@ -1600,7 +1643,7 @@ var Viewport = class {
1600
1643
  node.addEventListener("dblclick", (e) => {
1601
1644
  e.stopPropagation();
1602
1645
  const id = node.dataset["elementId"];
1603
- if (id) this.startEditingNote(id);
1646
+ if (id) this.startEditingElement(id);
1604
1647
  });
1605
1648
  }
1606
1649
  if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
@@ -1641,6 +1684,42 @@ var Viewport = class {
1641
1684
  node.appendChild(content);
1642
1685
  }
1643
1686
  }
1687
+ if (element.type === "text") {
1688
+ if (!node.dataset["initialized"]) {
1689
+ node.dataset["initialized"] = "true";
1690
+ Object.assign(node.style, {
1691
+ padding: "2px",
1692
+ fontSize: `${element.fontSize}px`,
1693
+ color: element.color,
1694
+ textAlign: element.textAlign,
1695
+ background: "none",
1696
+ border: "none",
1697
+ boxShadow: "none",
1698
+ overflow: "visible",
1699
+ cursor: "default",
1700
+ userSelect: "none",
1701
+ wordWrap: "break-word",
1702
+ whiteSpace: "pre-wrap",
1703
+ lineHeight: "1.4"
1704
+ });
1705
+ node.textContent = element.text || "";
1706
+ node.addEventListener("dblclick", (e) => {
1707
+ e.stopPropagation();
1708
+ const id = node.dataset["elementId"];
1709
+ if (id) this.startEditingElement(id);
1710
+ });
1711
+ }
1712
+ if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
1713
+ if (node.textContent !== element.text) {
1714
+ node.textContent = element.text || "";
1715
+ }
1716
+ Object.assign(node.style, {
1717
+ fontSize: `${element.fontSize}px`,
1718
+ color: element.color,
1719
+ textAlign: element.textAlign
1720
+ });
1721
+ }
1722
+ }
1644
1723
  }
1645
1724
  removeDomNode(id) {
1646
1725
  this.htmlContent.delete(id);
@@ -2394,6 +2473,47 @@ var NoteTool = class {
2394
2473
  }
2395
2474
  };
2396
2475
 
2476
+ // src/tools/text-tool.ts
2477
+ var TextTool = class {
2478
+ name = "text";
2479
+ fontSize;
2480
+ color;
2481
+ textAlign;
2482
+ constructor(options = {}) {
2483
+ this.fontSize = options.fontSize ?? 16;
2484
+ this.color = options.color ?? "#1a1a1a";
2485
+ this.textAlign = options.textAlign ?? "left";
2486
+ }
2487
+ setOptions(options) {
2488
+ if (options.fontSize !== void 0) this.fontSize = options.fontSize;
2489
+ if (options.color !== void 0) this.color = options.color;
2490
+ if (options.textAlign !== void 0) this.textAlign = options.textAlign;
2491
+ }
2492
+ onActivate(ctx) {
2493
+ ctx.setCursor?.("text");
2494
+ }
2495
+ onDeactivate(ctx) {
2496
+ ctx.setCursor?.("default");
2497
+ }
2498
+ onPointerDown(_state, _ctx) {
2499
+ }
2500
+ onPointerMove(_state, _ctx) {
2501
+ }
2502
+ onPointerUp(state, ctx) {
2503
+ const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2504
+ const textEl = createText({
2505
+ position: world,
2506
+ fontSize: this.fontSize,
2507
+ color: this.color,
2508
+ textAlign: this.textAlign
2509
+ });
2510
+ ctx.store.add(textEl);
2511
+ ctx.requestRender();
2512
+ ctx.switchTool?.("select");
2513
+ ctx.editElement?.(textEl.id);
2514
+ }
2515
+ };
2516
+
2397
2517
  // src/tools/image-tool.ts
2398
2518
  var ImageTool = class {
2399
2519
  name = "image";
@@ -2425,7 +2545,7 @@ var ImageTool = class {
2425
2545
  };
2426
2546
 
2427
2547
  // src/index.ts
2428
- var VERSION = "0.2.2";
2548
+ var VERSION = "0.3.0";
2429
2549
  // Annotate the CommonJS export names for ESM import in node:
2430
2550
  0 && (module.exports = {
2431
2551
  AddElementCommand,
@@ -2448,6 +2568,7 @@ var VERSION = "0.2.2";
2448
2568
  PencilTool,
2449
2569
  RemoveElementCommand,
2450
2570
  SelectTool,
2571
+ TextTool,
2451
2572
  ToolManager,
2452
2573
  UpdateElementCommand,
2453
2574
  VERSION,
@@ -2458,6 +2579,7 @@ var VERSION = "0.2.2";
2458
2579
  createImage,
2459
2580
  createNote,
2460
2581
  createStroke,
2582
+ createText,
2461
2583
  exportState,
2462
2584
  getArrowBounds,
2463
2585
  getArrowControlPoint,