@fieldnotes/core 0.50.0 → 0.50.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
@@ -367,7 +367,7 @@ function migrateElement(obj) {
367
367
  if (obj["type"] === "note" && typeof obj["textColor"] !== "string") {
368
368
  obj["textColor"] = "#000000";
369
369
  }
370
- if (obj["type"] === "note" && typeof obj["text"] === "string") {
370
+ if ((obj["type"] === "note" || obj["type"] === "text") && typeof obj["text"] === "string") {
371
371
  obj["text"] = sanitizeNoteHtml(obj["text"]);
372
372
  }
373
373
  }
@@ -2580,6 +2580,11 @@ function transferStrokeRenderData(prev, next) {
2580
2580
  }
2581
2581
 
2582
2582
  // src/elements/element-store.ts
2583
+ function sanitizeRichText(element) {
2584
+ if (element.type !== "note" && element.type !== "text") return element;
2585
+ const text = sanitizeNoteHtml(element.text);
2586
+ return text === element.text ? element : { ...element, text };
2587
+ }
2583
2588
  var ElementStore = class {
2584
2589
  elements = /* @__PURE__ */ new Map();
2585
2590
  bus = new EventBus();
@@ -2624,6 +2629,7 @@ var ElementStore = class {
2624
2629
  return angle === 0 ? bounds : rotatedAABB(bounds, angle);
2625
2630
  }
2626
2631
  add(element, meta) {
2632
+ element = sanitizeRichText(element);
2627
2633
  this.sortedCache = null;
2628
2634
  this._versions.set(element.id, 0);
2629
2635
  this.elements.set(element.id, element);
@@ -2650,7 +2656,7 @@ var ElementStore = class {
2650
2656
  const arrow = updated;
2651
2657
  arrow.cachedControlPoint = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
2652
2658
  }
2653
- if (updated.type === "note" && "text" in partial) {
2659
+ if ((updated.type === "note" || updated.type === "text") && "text" in partial) {
2654
2660
  updated.text = sanitizeNoteHtml(updated.text);
2655
2661
  }
2656
2662
  this.elements.set(id, updated);
@@ -2680,11 +2686,12 @@ var ElementStore = class {
2680
2686
  return this.getAll().map((el) => ({ ...el }));
2681
2687
  }
2682
2688
  loadSnapshot(elements, meta) {
2689
+ const sanitizedElements = elements.map(sanitizeRichText);
2683
2690
  this.sortedCache = null;
2684
2691
  this._versions.clear();
2685
2692
  this.elements.clear();
2686
2693
  this.spatialIndex.clear();
2687
- for (const el of elements) {
2694
+ for (const el of sanitizedElements) {
2688
2695
  this.elements.set(el.id, el);
2689
2696
  this._versions.set(el.id, 0);
2690
2697
  const bounds = this.indexBounds(el);
@@ -2697,7 +2704,7 @@ var ElementStore = class {
2697
2704
  }
2698
2705
  }
2699
2706
  this.bus.emit("clear", null, meta);
2700
- for (const el of elements) {
2707
+ for (const el of sanitizedElements) {
2701
2708
  this.bus.emit("add", el, meta);
2702
2709
  }
2703
2710
  }
@@ -3673,7 +3680,7 @@ function renderSquareGrid(ctx, bounds, cellSize, strokeColor, strokeWidth, opaci
3673
3680
  ctx.save();
3674
3681
  ctx.strokeStyle = strokeColor;
3675
3682
  ctx.lineWidth = strokeWidth;
3676
- ctx.globalAlpha = opacity;
3683
+ ctx.globalAlpha *= opacity;
3677
3684
  ctx.beginPath();
3678
3685
  for (const x of verticals) {
3679
3686
  ctx.moveTo(x, bounds.minY);
@@ -3704,7 +3711,7 @@ function renderHexGrid(ctx, bounds, cellSize, orientation, strokeColor, strokeWi
3704
3711
  ctx.save();
3705
3712
  ctx.strokeStyle = strokeColor;
3706
3713
  ctx.lineWidth = strokeWidth;
3707
- ctx.globalAlpha = opacity;
3714
+ ctx.globalAlpha *= opacity;
3708
3715
  ctx.beginPath();
3709
3716
  if (orientation === "pointy") {
3710
3717
  const hexW = Math.sqrt(3) * cellSize;
@@ -4112,7 +4119,7 @@ function createText(input) {
4112
4119
  locked: input.locked ?? false,
4113
4120
  layerId: input.layerId ?? "",
4114
4121
  size: input.size ?? { w: 200, h: 28 },
4115
- text: input.text ?? "",
4122
+ text: sanitizeNoteHtml(input.text ?? ""),
4116
4123
  fontSize: input.fontSize ?? 16,
4117
4124
  color: input.color ?? "#1a1a1a",
4118
4125
  textAlign: input.textAlign ?? "left"
@@ -5464,41 +5471,70 @@ async function exportImage(store, options = {}, layerManager) {
5464
5471
  const renderer = new ElementRenderer();
5465
5472
  renderer.setStore(store);
5466
5473
  const grids = [];
5467
- for (const el of visibleElements) {
5468
- if (el.type === "grid") {
5469
- grids.push(el);
5470
- continue;
5471
- }
5474
+ const renderElement = (target, el) => {
5472
5475
  if (el.type === "note") {
5473
5476
  const b = getElementBounds(el);
5474
- withRotation(ctx, el, b ? center(b) : el.position, () => renderNoteOnCanvas(ctx, el));
5475
- continue;
5477
+ withRotation(target, el, b ? center(b) : el.position, () => renderNoteOnCanvas(target, el));
5478
+ return;
5476
5479
  }
5477
5480
  if (el.type === "text") {
5478
5481
  const b = getElementBounds(el);
5479
- withRotation(ctx, el, b ? center(b) : el.position, () => renderTextOnCanvas(ctx, el));
5480
- continue;
5482
+ withRotation(target, el, b ? center(b) : el.position, () => renderTextOnCanvas(target, el));
5483
+ return;
5481
5484
  }
5482
5485
  if (el.type === "html") {
5483
- continue;
5486
+ return;
5484
5487
  }
5485
5488
  if (el.type === "image") {
5486
5489
  const img = imageCache.get(el.id);
5487
5490
  if (img) {
5488
5491
  const b = getElementBounds(el);
5489
5492
  withRotation(
5490
- ctx,
5493
+ target,
5491
5494
  el,
5492
5495
  b ? center(b) : el.position,
5493
- () => ctx.drawImage(img, el.position.x, el.position.y, el.size.w, el.size.h)
5496
+ () => target.drawImage(img, el.position.x, el.position.y, el.size.w, el.size.h)
5494
5497
  );
5495
5498
  }
5499
+ return;
5500
+ }
5501
+ renderer.renderCanvasElement(target, el);
5502
+ };
5503
+ const layerGroups = /* @__PURE__ */ new Map();
5504
+ for (const el of visibleElements) {
5505
+ if (el.type === "grid") {
5506
+ grids.push(el);
5507
+ continue;
5508
+ }
5509
+ const group = layerGroups.get(el.layerId) ?? [];
5510
+ group.push(el);
5511
+ layerGroups.set(el.layerId, group);
5512
+ }
5513
+ for (const [layerId, elements] of layerGroups) {
5514
+ const opacity = layerManager?.getLayer?.(layerId)?.opacity ?? 1;
5515
+ if (opacity === 1) {
5516
+ for (const el of elements) renderElement(ctx, el);
5496
5517
  continue;
5497
5518
  }
5498
- renderer.renderCanvasElement(ctx, el);
5519
+ const layerCanvas = document.createElement("canvas");
5520
+ layerCanvas.width = canvas.width;
5521
+ layerCanvas.height = canvas.height;
5522
+ const layerCtx = layerCanvas.getContext("2d");
5523
+ if (!layerCtx) continue;
5524
+ layerCtx.scale(scale, scale);
5525
+ layerCtx.translate(-bounds.x, -bounds.y);
5526
+ for (const el of elements) renderElement(layerCtx, el);
5527
+ ctx.save();
5528
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
5529
+ ctx.globalAlpha = opacity;
5530
+ ctx.drawImage(layerCanvas, 0, 0);
5531
+ ctx.restore();
5499
5532
  }
5500
5533
  for (const grid of grids) {
5534
+ ctx.save();
5535
+ ctx.globalAlpha = layerManager?.getLayer?.(grid.layerId)?.opacity ?? 1;
5501
5536
  renderGridForBounds(ctx, grid, bounds);
5537
+ ctx.restore();
5502
5538
  }
5503
5539
  return new Promise((resolve) => {
5504
5540
  canvas.toBlob((blob) => resolve(blob), "image/png");
@@ -5818,11 +5854,19 @@ async function exportSvg(store, options = {}, layerManager) {
5818
5854
  if (options.background) {
5819
5855
  body += `<rect x="${n(bounds.x)}" y="${n(bounds.y)}" width="${n(bounds.w)}" height="${n(bounds.h)}" fill="${esc(options.background)}" />`;
5820
5856
  }
5857
+ const layerBodies = /* @__PURE__ */ new Map();
5821
5858
  for (const el of visibleElements) {
5822
- body += emitElement(el, imageDataUris, rasterScale, firstGrid, store);
5859
+ const emitted = emitElement(el, imageDataUris, rasterScale, firstGrid, store);
5860
+ layerBodies.set(el.layerId, (layerBodies.get(el.layerId) ?? "") + emitted);
5861
+ }
5862
+ for (const [layerId, emitted] of layerBodies) {
5863
+ const opacity = layerManager?.getLayer?.(layerId)?.opacity ?? 1;
5864
+ body += opacity === 1 || emitted === "" ? emitted : `<g opacity="${n(opacity)}">${emitted}</g>`;
5823
5865
  }
5824
5866
  for (const grid of grids) {
5825
- body += emitGrid(grid, bounds);
5867
+ const emitted = emitGrid(grid, bounds);
5868
+ const opacity = layerManager?.getLayer?.(grid.layerId)?.opacity ?? 1;
5869
+ body += opacity === 1 ? emitted : `<g opacity="${n(opacity)}">${emitted}</g>`;
5826
5870
  }
5827
5871
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${n(bounds.w)}" height="${n(bounds.h)}" viewBox="${n(bounds.x)} ${n(bounds.y)} ${n(bounds.w)} ${n(bounds.h)}">${body}</svg>`;
5828
5872
  }
@@ -6141,6 +6185,7 @@ var DomNodeManager = class {
6141
6185
  getVersion;
6142
6186
  lastSyncedVersion = /* @__PURE__ */ new Map();
6143
6187
  lastSyncedZIndex = /* @__PURE__ */ new Map();
6188
+ lastSyncedOpacity = /* @__PURE__ */ new Map();
6144
6189
  constructor(deps) {
6145
6190
  this.domLayer = deps.domLayer;
6146
6191
  this.onEditRequest = deps.onEditRequest;
@@ -6160,6 +6205,7 @@ var DomNodeManager = class {
6160
6205
  this.htmlContent.delete(elementId);
6161
6206
  this.lastSyncedVersion.delete(elementId);
6162
6207
  this.lastSyncedZIndex.delete(elementId);
6208
+ this.lastSyncedOpacity.delete(elementId);
6163
6209
  const node = this.domNodes.get(elementId);
6164
6210
  if (!node) return;
6165
6211
  while (node.firstChild) {
@@ -6167,7 +6213,7 @@ var DomNodeManager = class {
6167
6213
  }
6168
6214
  delete node.dataset["initialized"];
6169
6215
  }
6170
- syncDomNode(element, zIndex = 0) {
6216
+ syncDomNode(element, zIndex = 0, opacity = 1) {
6171
6217
  let node = this.domNodes.get(element.id);
6172
6218
  if (!node) {
6173
6219
  node = document.createElement("div");
@@ -6182,13 +6228,15 @@ var DomNodeManager = class {
6182
6228
  const currentVersion = this.getVersion(element.id);
6183
6229
  const lastVersion = this.lastSyncedVersion.get(element.id);
6184
6230
  const lastZ = this.lastSyncedZIndex.get(element.id);
6185
- if (lastVersion === currentVersion && lastZ === zIndex) {
6231
+ const lastOpacity = this.lastSyncedOpacity.get(element.id);
6232
+ if (lastVersion === currentVersion && lastZ === zIndex && lastOpacity === opacity) {
6186
6233
  return;
6187
6234
  }
6188
6235
  }
6189
6236
  if (this.getVersion) {
6190
6237
  this.lastSyncedVersion.set(element.id, this.getVersion(element.id));
6191
6238
  this.lastSyncedZIndex.set(element.id, zIndex);
6239
+ this.lastSyncedOpacity.set(element.id, opacity);
6192
6240
  }
6193
6241
  const size = "size" in element ? element.size : null;
6194
6242
  Object.assign(node.style, {
@@ -6198,6 +6246,7 @@ var DomNodeManager = class {
6198
6246
  width: size ? `${size.w}px` : "auto",
6199
6247
  height: size ? `${size.h}px` : "auto",
6200
6248
  zIndex: String(zIndex),
6249
+ opacity: String(opacity),
6201
6250
  transform: element.rotation ? `rotate(${element.rotation}rad)` : "",
6202
6251
  transformOrigin: "50% 50%"
6203
6252
  });
@@ -6211,6 +6260,7 @@ var DomNodeManager = class {
6211
6260
  this.htmlContent.delete(id);
6212
6261
  this.lastSyncedVersion.delete(id);
6213
6262
  this.lastSyncedZIndex.delete(id);
6263
+ this.lastSyncedOpacity.delete(id);
6214
6264
  const node = this.domNodes.get(id);
6215
6265
  if (node) {
6216
6266
  node.remove();
@@ -6223,6 +6273,7 @@ var DomNodeManager = class {
6223
6273
  this.htmlContent.clear();
6224
6274
  this.lastSyncedVersion.clear();
6225
6275
  this.lastSyncedZIndex.clear();
6276
+ this.lastSyncedOpacity.clear();
6226
6277
  }
6227
6278
  reattachHtmlContent(store) {
6228
6279
  for (const el of store.getElementsByType("html")) {
@@ -6461,11 +6512,12 @@ var RenderLoop = class {
6461
6512
  }
6462
6513
  markAllLayersDirty() {
6463
6514
  this.layerCache.markAllDirty();
6515
+ this.gridCacheDirty = true;
6464
6516
  }
6465
6517
  getStats() {
6466
6518
  return this.stats.getSnapshot();
6467
6519
  }
6468
- compositeLayerCache(ctx, layerId) {
6520
+ compositeLayerCache(ctx, layerId, opacity) {
6469
6521
  const cached = this.layerCache.getCanvas(layerId);
6470
6522
  const offset = this.marginViewport.compositeOffset(
6471
6523
  this.camera.position.x,
@@ -6473,6 +6525,7 @@ var RenderLoop = class {
6473
6525
  );
6474
6526
  ctx.save();
6475
6527
  ctx.setTransform(1, 0, 0, 1, 0, 0);
6528
+ ctx.globalAlpha = opacity;
6476
6529
  ctx.drawImage(cached, offset.x, offset.y);
6477
6530
  ctx.restore();
6478
6531
  }
@@ -6552,11 +6605,12 @@ var RenderLoop = class {
6552
6605
  continue;
6553
6606
  }
6554
6607
  if (this.renderer.isDomElement(element)) {
6608
+ const layerOpacity = this.layerManager.getLayer?.(element.layerId)?.opacity ?? 1;
6555
6609
  const elBounds = getElementBounds(element);
6556
6610
  if (elBounds && !boundsIntersect(elBounds, cullingRect)) {
6557
6611
  this.domNodeManager.hideDomNode(element.id);
6558
6612
  } else {
6559
- this.domNodeManager.syncDomNode(element, domZIndex++);
6613
+ this.domNodeManager.syncDomNode(element, domZIndex++, layerOpacity);
6560
6614
  }
6561
6615
  continue;
6562
6616
  }
@@ -6573,15 +6627,16 @@ var RenderLoop = class {
6573
6627
  }
6574
6628
  for (const [layerId, elements] of this.layerGroups) {
6575
6629
  const isActiveDrawingLayer = layerId === this.activeDrawingLayerId;
6630
+ const layerOpacity = this.layerManager.getLayer?.(layerId)?.opacity ?? 1;
6576
6631
  if (!this.layerCache.isDirty(layerId)) {
6577
6632
  const compT0 = performance.now();
6578
- this.compositeLayerCache(ctx, layerId);
6633
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6579
6634
  compositeMs += performance.now() - compT0;
6580
6635
  continue;
6581
6636
  }
6582
6637
  if (isActiveDrawingLayer) {
6583
6638
  const compT0 = performance.now();
6584
- this.compositeLayerCache(ctx, layerId);
6639
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6585
6640
  compositeMs += performance.now() - compT0;
6586
6641
  continue;
6587
6642
  }
@@ -6601,7 +6656,7 @@ var RenderLoop = class {
6601
6656
  this.layerCache.markClean(layerId);
6602
6657
  layersMs += performance.now() - layerT0;
6603
6658
  const compT0 = performance.now();
6604
- this.compositeLayerCache(ctx, layerId);
6659
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6605
6660
  compositeMs += performance.now() - compT0;
6606
6661
  }
6607
6662
  }
@@ -6625,7 +6680,10 @@ var RenderLoop = class {
6625
6680
  this.marginViewport.applyRenderTransform(gc);
6626
6681
  try {
6627
6682
  for (const grid of gridElements) {
6683
+ gc.save();
6684
+ gc.globalAlpha = this.layerManager.getLayer?.(grid.layerId)?.opacity ?? 1;
6628
6685
  this.renderer.renderCanvasElement(gc, grid);
6686
+ gc.restore();
6629
6687
  }
6630
6688
  } finally {
6631
6689
  gc.restore();
@@ -6646,7 +6704,10 @@ var RenderLoop = class {
6646
6704
  ctx.restore();
6647
6705
  } else {
6648
6706
  for (const grid of gridElements) {
6707
+ ctx.save();
6708
+ ctx.globalAlpha = this.layerManager.getLayer?.(grid.layerId)?.opacity ?? 1;
6649
6709
  this.renderer.renderCanvasElement(ctx, grid);
6710
+ ctx.restore();
6650
6711
  }
6651
6712
  }
6652
6713
  gridMs = performance.now() - gridT0;
@@ -7551,6 +7612,7 @@ var Viewport = class {
7551
7612
  ];
7552
7613
  this.layerManager.on("change", () => {
7553
7614
  this.toolContext.activeLayerId = this.layerManager.activeLayerId;
7615
+ this.renderLoop.markAllLayersDirty();
7554
7616
  this.minimap?.scheduleDraw();
7555
7617
  this.requestRender();
7556
7618
  });
@@ -10614,7 +10676,7 @@ var LaserTool = class {
10614
10676
  };
10615
10677
 
10616
10678
  // src/index.ts
10617
- var VERSION = "0.50.0";
10679
+ var VERSION = "0.50.2";
10618
10680
  // Annotate the CommonJS export names for ESM import in node:
10619
10681
  0 && (module.exports = {
10620
10682
  ArrowTool,