@fieldnotes/core 0.50.1 → 0.50.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
@@ -2320,6 +2320,9 @@ var MAX_DEPTH = 8;
2320
2320
  function intersects(a, b) {
2321
2321
  return a.x <= b.x + b.w && a.x + a.w >= b.x && a.y <= b.y + b.h && a.y + a.h >= b.y;
2322
2322
  }
2323
+ function contains(outer, inner) {
2324
+ return inner.x >= outer.x && inner.x + inner.w <= outer.x + outer.w && inner.y >= outer.y && inner.y + inner.h <= outer.y + outer.h;
2325
+ }
2323
2326
  var QuadNode = class _QuadNode {
2324
2327
  constructor(bounds, depth) {
2325
2328
  this.bounds = bounds;
@@ -2372,6 +2375,14 @@ var QuadNode = class _QuadNode {
2372
2375
  }
2373
2376
  }
2374
2377
  }
2378
+ collect(result) {
2379
+ result.push(...this.items);
2380
+ if (this.children) {
2381
+ for (const child of this.children) {
2382
+ child.collect(result);
2383
+ }
2384
+ }
2385
+ }
2375
2386
  getChildIndex(itemBounds) {
2376
2387
  const midX = this.bounds.x + this.bounds.w / 2;
2377
2388
  const midY = this.bounds.y + this.bounds.h / 2;
@@ -2435,6 +2446,7 @@ var Quadtree = class {
2435
2446
  return this._size;
2436
2447
  }
2437
2448
  insert(id, bounds) {
2449
+ this.expandToFit(bounds);
2438
2450
  this.root.insert({ id, bounds });
2439
2451
  this._size++;
2440
2452
  }
@@ -2459,6 +2471,24 @@ var Quadtree = class {
2459
2471
  this.root = new QuadNode(this.worldBounds, 0);
2460
2472
  this._size = 0;
2461
2473
  }
2474
+ expandToFit(bounds) {
2475
+ const current = this.root.bounds;
2476
+ if (contains(current, bounds)) return;
2477
+ const currentRight = current.x + current.w;
2478
+ const currentBottom = current.y + current.h;
2479
+ const requiredWidth = Math.max(currentRight, bounds.x + bounds.w) - Math.min(current.x, bounds.x);
2480
+ const requiredHeight = Math.max(currentBottom, bounds.y + bounds.h) - Math.min(current.y, bounds.y);
2481
+ const width = Math.max(current.w * 2, requiredWidth);
2482
+ const height = Math.max(current.h * 2, requiredHeight);
2483
+ const x = bounds.x < current.x ? currentRight - width : current.x;
2484
+ const y = bounds.y < current.y ? currentBottom - height : current.y;
2485
+ const entries = [];
2486
+ this.root.collect(entries);
2487
+ this.root = new QuadNode({ x, y, w: width, h: height }, 0);
2488
+ for (const entry of entries) {
2489
+ this.root.insert(entry);
2490
+ }
2491
+ }
2462
2492
  };
2463
2493
 
2464
2494
  // src/elements/stroke-smoothing.ts
@@ -3680,7 +3710,7 @@ function renderSquareGrid(ctx, bounds, cellSize, strokeColor, strokeWidth, opaci
3680
3710
  ctx.save();
3681
3711
  ctx.strokeStyle = strokeColor;
3682
3712
  ctx.lineWidth = strokeWidth;
3683
- ctx.globalAlpha = opacity;
3713
+ ctx.globalAlpha *= opacity;
3684
3714
  ctx.beginPath();
3685
3715
  for (const x of verticals) {
3686
3716
  ctx.moveTo(x, bounds.minY);
@@ -3711,7 +3741,7 @@ function renderHexGrid(ctx, bounds, cellSize, orientation, strokeColor, strokeWi
3711
3741
  ctx.save();
3712
3742
  ctx.strokeStyle = strokeColor;
3713
3743
  ctx.lineWidth = strokeWidth;
3714
- ctx.globalAlpha = opacity;
3744
+ ctx.globalAlpha *= opacity;
3715
3745
  ctx.beginPath();
3716
3746
  if (orientation === "pointy") {
3717
3747
  const hexW = Math.sqrt(3) * cellSize;
@@ -5471,41 +5501,70 @@ async function exportImage(store, options = {}, layerManager) {
5471
5501
  const renderer = new ElementRenderer();
5472
5502
  renderer.setStore(store);
5473
5503
  const grids = [];
5474
- for (const el of visibleElements) {
5475
- if (el.type === "grid") {
5476
- grids.push(el);
5477
- continue;
5478
- }
5504
+ const renderElement = (target, el) => {
5479
5505
  if (el.type === "note") {
5480
5506
  const b = getElementBounds(el);
5481
- withRotation(ctx, el, b ? center(b) : el.position, () => renderNoteOnCanvas(ctx, el));
5482
- continue;
5507
+ withRotation(target, el, b ? center(b) : el.position, () => renderNoteOnCanvas(target, el));
5508
+ return;
5483
5509
  }
5484
5510
  if (el.type === "text") {
5485
5511
  const b = getElementBounds(el);
5486
- withRotation(ctx, el, b ? center(b) : el.position, () => renderTextOnCanvas(ctx, el));
5487
- continue;
5512
+ withRotation(target, el, b ? center(b) : el.position, () => renderTextOnCanvas(target, el));
5513
+ return;
5488
5514
  }
5489
5515
  if (el.type === "html") {
5490
- continue;
5516
+ return;
5491
5517
  }
5492
5518
  if (el.type === "image") {
5493
5519
  const img = imageCache.get(el.id);
5494
5520
  if (img) {
5495
5521
  const b = getElementBounds(el);
5496
5522
  withRotation(
5497
- ctx,
5523
+ target,
5498
5524
  el,
5499
5525
  b ? center(b) : el.position,
5500
- () => ctx.drawImage(img, el.position.x, el.position.y, el.size.w, el.size.h)
5526
+ () => target.drawImage(img, el.position.x, el.position.y, el.size.w, el.size.h)
5501
5527
  );
5502
5528
  }
5529
+ return;
5530
+ }
5531
+ renderer.renderCanvasElement(target, el);
5532
+ };
5533
+ const layerGroups = /* @__PURE__ */ new Map();
5534
+ for (const el of visibleElements) {
5535
+ if (el.type === "grid") {
5536
+ grids.push(el);
5537
+ continue;
5538
+ }
5539
+ const group = layerGroups.get(el.layerId) ?? [];
5540
+ group.push(el);
5541
+ layerGroups.set(el.layerId, group);
5542
+ }
5543
+ for (const [layerId, elements] of layerGroups) {
5544
+ const opacity = layerManager?.getLayer?.(layerId)?.opacity ?? 1;
5545
+ if (opacity === 1) {
5546
+ for (const el of elements) renderElement(ctx, el);
5503
5547
  continue;
5504
5548
  }
5505
- renderer.renderCanvasElement(ctx, el);
5549
+ const layerCanvas = document.createElement("canvas");
5550
+ layerCanvas.width = canvas.width;
5551
+ layerCanvas.height = canvas.height;
5552
+ const layerCtx = layerCanvas.getContext("2d");
5553
+ if (!layerCtx) continue;
5554
+ layerCtx.scale(scale, scale);
5555
+ layerCtx.translate(-bounds.x, -bounds.y);
5556
+ for (const el of elements) renderElement(layerCtx, el);
5557
+ ctx.save();
5558
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
5559
+ ctx.globalAlpha = opacity;
5560
+ ctx.drawImage(layerCanvas, 0, 0);
5561
+ ctx.restore();
5506
5562
  }
5507
5563
  for (const grid of grids) {
5564
+ ctx.save();
5565
+ ctx.globalAlpha = layerManager?.getLayer?.(grid.layerId)?.opacity ?? 1;
5508
5566
  renderGridForBounds(ctx, grid, bounds);
5567
+ ctx.restore();
5509
5568
  }
5510
5569
  return new Promise((resolve) => {
5511
5570
  canvas.toBlob((blob) => resolve(blob), "image/png");
@@ -5825,11 +5884,19 @@ async function exportSvg(store, options = {}, layerManager) {
5825
5884
  if (options.background) {
5826
5885
  body += `<rect x="${n(bounds.x)}" y="${n(bounds.y)}" width="${n(bounds.w)}" height="${n(bounds.h)}" fill="${esc(options.background)}" />`;
5827
5886
  }
5887
+ const layerBodies = /* @__PURE__ */ new Map();
5828
5888
  for (const el of visibleElements) {
5829
- body += emitElement(el, imageDataUris, rasterScale, firstGrid, store);
5889
+ const emitted = emitElement(el, imageDataUris, rasterScale, firstGrid, store);
5890
+ layerBodies.set(el.layerId, (layerBodies.get(el.layerId) ?? "") + emitted);
5891
+ }
5892
+ for (const [layerId, emitted] of layerBodies) {
5893
+ const opacity = layerManager?.getLayer?.(layerId)?.opacity ?? 1;
5894
+ body += opacity === 1 || emitted === "" ? emitted : `<g opacity="${n(opacity)}">${emitted}</g>`;
5830
5895
  }
5831
5896
  for (const grid of grids) {
5832
- body += emitGrid(grid, bounds);
5897
+ const emitted = emitGrid(grid, bounds);
5898
+ const opacity = layerManager?.getLayer?.(grid.layerId)?.opacity ?? 1;
5899
+ body += opacity === 1 ? emitted : `<g opacity="${n(opacity)}">${emitted}</g>`;
5833
5900
  }
5834
5901
  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>`;
5835
5902
  }
@@ -6148,6 +6215,7 @@ var DomNodeManager = class {
6148
6215
  getVersion;
6149
6216
  lastSyncedVersion = /* @__PURE__ */ new Map();
6150
6217
  lastSyncedZIndex = /* @__PURE__ */ new Map();
6218
+ lastSyncedOpacity = /* @__PURE__ */ new Map();
6151
6219
  constructor(deps) {
6152
6220
  this.domLayer = deps.domLayer;
6153
6221
  this.onEditRequest = deps.onEditRequest;
@@ -6167,6 +6235,7 @@ var DomNodeManager = class {
6167
6235
  this.htmlContent.delete(elementId);
6168
6236
  this.lastSyncedVersion.delete(elementId);
6169
6237
  this.lastSyncedZIndex.delete(elementId);
6238
+ this.lastSyncedOpacity.delete(elementId);
6170
6239
  const node = this.domNodes.get(elementId);
6171
6240
  if (!node) return;
6172
6241
  while (node.firstChild) {
@@ -6174,7 +6243,7 @@ var DomNodeManager = class {
6174
6243
  }
6175
6244
  delete node.dataset["initialized"];
6176
6245
  }
6177
- syncDomNode(element, zIndex = 0) {
6246
+ syncDomNode(element, zIndex = 0, opacity = 1) {
6178
6247
  let node = this.domNodes.get(element.id);
6179
6248
  if (!node) {
6180
6249
  node = document.createElement("div");
@@ -6189,13 +6258,15 @@ var DomNodeManager = class {
6189
6258
  const currentVersion = this.getVersion(element.id);
6190
6259
  const lastVersion = this.lastSyncedVersion.get(element.id);
6191
6260
  const lastZ = this.lastSyncedZIndex.get(element.id);
6192
- if (lastVersion === currentVersion && lastZ === zIndex) {
6261
+ const lastOpacity = this.lastSyncedOpacity.get(element.id);
6262
+ if (lastVersion === currentVersion && lastZ === zIndex && lastOpacity === opacity) {
6193
6263
  return;
6194
6264
  }
6195
6265
  }
6196
6266
  if (this.getVersion) {
6197
6267
  this.lastSyncedVersion.set(element.id, this.getVersion(element.id));
6198
6268
  this.lastSyncedZIndex.set(element.id, zIndex);
6269
+ this.lastSyncedOpacity.set(element.id, opacity);
6199
6270
  }
6200
6271
  const size = "size" in element ? element.size : null;
6201
6272
  Object.assign(node.style, {
@@ -6205,6 +6276,7 @@ var DomNodeManager = class {
6205
6276
  width: size ? `${size.w}px` : "auto",
6206
6277
  height: size ? `${size.h}px` : "auto",
6207
6278
  zIndex: String(zIndex),
6279
+ opacity: String(opacity),
6208
6280
  transform: element.rotation ? `rotate(${element.rotation}rad)` : "",
6209
6281
  transformOrigin: "50% 50%"
6210
6282
  });
@@ -6218,6 +6290,7 @@ var DomNodeManager = class {
6218
6290
  this.htmlContent.delete(id);
6219
6291
  this.lastSyncedVersion.delete(id);
6220
6292
  this.lastSyncedZIndex.delete(id);
6293
+ this.lastSyncedOpacity.delete(id);
6221
6294
  const node = this.domNodes.get(id);
6222
6295
  if (node) {
6223
6296
  node.remove();
@@ -6230,6 +6303,7 @@ var DomNodeManager = class {
6230
6303
  this.htmlContent.clear();
6231
6304
  this.lastSyncedVersion.clear();
6232
6305
  this.lastSyncedZIndex.clear();
6306
+ this.lastSyncedOpacity.clear();
6233
6307
  }
6234
6308
  reattachHtmlContent(store) {
6235
6309
  for (const el of store.getElementsByType("html")) {
@@ -6468,11 +6542,12 @@ var RenderLoop = class {
6468
6542
  }
6469
6543
  markAllLayersDirty() {
6470
6544
  this.layerCache.markAllDirty();
6545
+ this.gridCacheDirty = true;
6471
6546
  }
6472
6547
  getStats() {
6473
6548
  return this.stats.getSnapshot();
6474
6549
  }
6475
- compositeLayerCache(ctx, layerId) {
6550
+ compositeLayerCache(ctx, layerId, opacity) {
6476
6551
  const cached = this.layerCache.getCanvas(layerId);
6477
6552
  const offset = this.marginViewport.compositeOffset(
6478
6553
  this.camera.position.x,
@@ -6480,6 +6555,7 @@ var RenderLoop = class {
6480
6555
  );
6481
6556
  ctx.save();
6482
6557
  ctx.setTransform(1, 0, 0, 1, 0, 0);
6558
+ ctx.globalAlpha = opacity;
6483
6559
  ctx.drawImage(cached, offset.x, offset.y);
6484
6560
  ctx.restore();
6485
6561
  }
@@ -6559,11 +6635,12 @@ var RenderLoop = class {
6559
6635
  continue;
6560
6636
  }
6561
6637
  if (this.renderer.isDomElement(element)) {
6638
+ const layerOpacity = this.layerManager.getLayer?.(element.layerId)?.opacity ?? 1;
6562
6639
  const elBounds = getElementBounds(element);
6563
6640
  if (elBounds && !boundsIntersect(elBounds, cullingRect)) {
6564
6641
  this.domNodeManager.hideDomNode(element.id);
6565
6642
  } else {
6566
- this.domNodeManager.syncDomNode(element, domZIndex++);
6643
+ this.domNodeManager.syncDomNode(element, domZIndex++, layerOpacity);
6567
6644
  }
6568
6645
  continue;
6569
6646
  }
@@ -6580,15 +6657,16 @@ var RenderLoop = class {
6580
6657
  }
6581
6658
  for (const [layerId, elements] of this.layerGroups) {
6582
6659
  const isActiveDrawingLayer = layerId === this.activeDrawingLayerId;
6660
+ const layerOpacity = this.layerManager.getLayer?.(layerId)?.opacity ?? 1;
6583
6661
  if (!this.layerCache.isDirty(layerId)) {
6584
6662
  const compT0 = performance.now();
6585
- this.compositeLayerCache(ctx, layerId);
6663
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6586
6664
  compositeMs += performance.now() - compT0;
6587
6665
  continue;
6588
6666
  }
6589
6667
  if (isActiveDrawingLayer) {
6590
6668
  const compT0 = performance.now();
6591
- this.compositeLayerCache(ctx, layerId);
6669
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6592
6670
  compositeMs += performance.now() - compT0;
6593
6671
  continue;
6594
6672
  }
@@ -6608,7 +6686,7 @@ var RenderLoop = class {
6608
6686
  this.layerCache.markClean(layerId);
6609
6687
  layersMs += performance.now() - layerT0;
6610
6688
  const compT0 = performance.now();
6611
- this.compositeLayerCache(ctx, layerId);
6689
+ this.compositeLayerCache(ctx, layerId, layerOpacity);
6612
6690
  compositeMs += performance.now() - compT0;
6613
6691
  }
6614
6692
  }
@@ -6632,7 +6710,10 @@ var RenderLoop = class {
6632
6710
  this.marginViewport.applyRenderTransform(gc);
6633
6711
  try {
6634
6712
  for (const grid of gridElements) {
6713
+ gc.save();
6714
+ gc.globalAlpha = this.layerManager.getLayer?.(grid.layerId)?.opacity ?? 1;
6635
6715
  this.renderer.renderCanvasElement(gc, grid);
6716
+ gc.restore();
6636
6717
  }
6637
6718
  } finally {
6638
6719
  gc.restore();
@@ -6653,7 +6734,10 @@ var RenderLoop = class {
6653
6734
  ctx.restore();
6654
6735
  } else {
6655
6736
  for (const grid of gridElements) {
6737
+ ctx.save();
6738
+ ctx.globalAlpha = this.layerManager.getLayer?.(grid.layerId)?.opacity ?? 1;
6656
6739
  this.renderer.renderCanvasElement(ctx, grid);
6740
+ ctx.restore();
6657
6741
  }
6658
6742
  }
6659
6743
  gridMs = performance.now() - gridT0;
@@ -7558,6 +7642,7 @@ var Viewport = class {
7558
7642
  ];
7559
7643
  this.layerManager.on("change", () => {
7560
7644
  this.toolContext.activeLayerId = this.layerManager.activeLayerId;
7645
+ this.renderLoop.markAllLayersDirty();
7561
7646
  this.minimap?.scheduleDraw();
7562
7647
  this.requestRender();
7563
7648
  });
@@ -10621,7 +10706,7 @@ var LaserTool = class {
10621
10706
  };
10622
10707
 
10623
10708
  // src/index.ts
10624
- var VERSION = "0.50.1";
10709
+ var VERSION = "0.50.3";
10625
10710
  // Annotate the CommonJS export names for ESM import in node:
10626
10711
  0 && (module.exports = {
10627
10712
  ArrowTool,