@fieldnotes/core 0.51.0 → 0.52.1
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/README.md +26 -4
- package/dist/index.cjs +321 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +321 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,7 +80,8 @@ card.querySelector('button').addEventListener('click', () => {
|
|
|
80
80
|
const elementId = viewport.addHtmlElement(card, { x: 100, y: 200 }, { w: 250, h: 150 });
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
HTML elements pan, zoom,
|
|
83
|
+
HTML elements pan, zoom, resize, and interleave by layer/`zIndex` with canvas-rendered content. They
|
|
84
|
+
use a **two-mode interaction model**:
|
|
84
85
|
|
|
85
86
|
- **Default** — the element can be selected, dragged, and resized like any other element
|
|
86
87
|
- **Double-click** — enters interact mode, making buttons, inputs, and links work
|
|
@@ -164,9 +165,30 @@ const blob = await viewport.exportImage({
|
|
|
164
165
|
});
|
|
165
166
|
```
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
Remote images are requested with anonymous CORS using their original URLs; failures are omitted from
|
|
169
|
+
the result and reported through `onAssetError` when supplied.
|
|
170
|
+
|
|
171
|
+
Application-owned HTML embeds require an explicit rasterization hook. Return a ready canvas-compatible
|
|
172
|
+
image source; Field Notes applies the element's size, rotation, paint order, and layer opacity in both
|
|
173
|
+
PNG and SVG exports:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const options = {
|
|
177
|
+
htmlTimeoutMs: 10_000,
|
|
178
|
+
renderHtml: async (element) => {
|
|
179
|
+
const node = document.querySelector(`[data-element-id="${element.id}"]`);
|
|
180
|
+
return node ? rasterizeToCanvas(node) : null; // application or library implementation
|
|
181
|
+
},
|
|
182
|
+
onHtmlError: ({ elementId, reason }) => {
|
|
183
|
+
console.warn(`Could not export HTML element ${elementId}: ${reason}`);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const png = await viewport.exportImage(options);
|
|
188
|
+
const svg = await viewport.exportSVG(options);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Without `renderHtml`, embeds remain omitted and can be observed through `onHtmlError`.
|
|
170
192
|
|
|
171
193
|
## Performance Monitoring
|
|
172
194
|
|
package/dist/index.cjs
CHANGED
|
@@ -4921,6 +4921,57 @@ function createDomLayer() {
|
|
|
4921
4921
|
});
|
|
4922
4922
|
return el;
|
|
4923
4923
|
}
|
|
4924
|
+
function createPaintStack() {
|
|
4925
|
+
const el = createDomLayer();
|
|
4926
|
+
el.dataset["fieldnotesPaintStack"] = "";
|
|
4927
|
+
el.style.transformOrigin = "";
|
|
4928
|
+
return el;
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
// src/canvas/hybrid-render-surface.ts
|
|
4932
|
+
var HybridRenderSurface = class {
|
|
4933
|
+
constructor(root) {
|
|
4934
|
+
this.root = root;
|
|
4935
|
+
}
|
|
4936
|
+
canvases = /* @__PURE__ */ new Map();
|
|
4937
|
+
beginFrame(activeOrders, width, height) {
|
|
4938
|
+
for (const [order, canvas] of this.canvases) {
|
|
4939
|
+
if (!activeOrders.has(order)) {
|
|
4940
|
+
canvas.remove();
|
|
4941
|
+
this.canvases.delete(order);
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
for (const order of activeOrders) {
|
|
4945
|
+
const canvas = this.getCanvas(order);
|
|
4946
|
+
if (canvas.width !== width) canvas.width = width;
|
|
4947
|
+
if (canvas.height !== height) canvas.height = height;
|
|
4948
|
+
canvas.style.zIndex = String(order);
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4951
|
+
getContext(order) {
|
|
4952
|
+
return this.getCanvas(order).getContext("2d");
|
|
4953
|
+
}
|
|
4954
|
+
clear() {
|
|
4955
|
+
for (const canvas of this.canvases.values()) canvas.remove();
|
|
4956
|
+
this.canvases.clear();
|
|
4957
|
+
}
|
|
4958
|
+
getCanvas(order) {
|
|
4959
|
+
let canvas = this.canvases.get(order);
|
|
4960
|
+
if (canvas) return canvas;
|
|
4961
|
+
canvas = document.createElement("canvas");
|
|
4962
|
+
canvas.dataset["paintOrder"] = String(order);
|
|
4963
|
+
Object.assign(canvas.style, {
|
|
4964
|
+
position: "absolute",
|
|
4965
|
+
inset: "0",
|
|
4966
|
+
width: "100%",
|
|
4967
|
+
height: "100%",
|
|
4968
|
+
pointerEvents: "none"
|
|
4969
|
+
});
|
|
4970
|
+
this.root.appendChild(canvas);
|
|
4971
|
+
this.canvases.set(order, canvas);
|
|
4972
|
+
return canvas;
|
|
4973
|
+
}
|
|
4974
|
+
};
|
|
4924
4975
|
|
|
4925
4976
|
// src/elements/arrow-label-editor.ts
|
|
4926
4977
|
var ArrowLabelEditor = class {
|
|
@@ -5442,6 +5493,67 @@ function renderTextOnCanvas(ctx, text) {
|
|
|
5442
5493
|
ctx.restore();
|
|
5443
5494
|
}
|
|
5444
5495
|
|
|
5496
|
+
// src/canvas/html-export.ts
|
|
5497
|
+
var DEFAULT_HTML_TIMEOUT_MS = 1e4;
|
|
5498
|
+
var TIMEOUT = /* @__PURE__ */ Symbol("html-export-timeout");
|
|
5499
|
+
function validateHtmlExportOptions(options) {
|
|
5500
|
+
const timeoutMs = options.htmlTimeoutMs ?? DEFAULT_HTML_TIMEOUT_MS;
|
|
5501
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
5502
|
+
throw new RangeError("htmlTimeoutMs must be a finite number greater than 0");
|
|
5503
|
+
}
|
|
5504
|
+
return timeoutMs;
|
|
5505
|
+
}
|
|
5506
|
+
async function renderHtmlElements(elements, options) {
|
|
5507
|
+
const timeoutMs = validateHtmlExportOptions(options);
|
|
5508
|
+
const sources = /* @__PURE__ */ new Map();
|
|
5509
|
+
await Promise.all(
|
|
5510
|
+
elements.map(async (element) => {
|
|
5511
|
+
if (!options.renderHtml) {
|
|
5512
|
+
options.onHtmlError?.({
|
|
5513
|
+
elementId: element.id,
|
|
5514
|
+
htmlType: element.htmlType,
|
|
5515
|
+
reason: "unsupported"
|
|
5516
|
+
});
|
|
5517
|
+
return;
|
|
5518
|
+
}
|
|
5519
|
+
let timer;
|
|
5520
|
+
try {
|
|
5521
|
+
const timeout = new Promise((resolve) => {
|
|
5522
|
+
timer = setTimeout(() => resolve(TIMEOUT), timeoutMs);
|
|
5523
|
+
});
|
|
5524
|
+
const result = await Promise.race([Promise.resolve(options.renderHtml(element)), timeout]);
|
|
5525
|
+
if (result === TIMEOUT) {
|
|
5526
|
+
options.onHtmlError?.({
|
|
5527
|
+
elementId: element.id,
|
|
5528
|
+
htmlType: element.htmlType,
|
|
5529
|
+
reason: "timeout"
|
|
5530
|
+
});
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5533
|
+
if (!result) {
|
|
5534
|
+
options.onHtmlError?.({
|
|
5535
|
+
elementId: element.id,
|
|
5536
|
+
htmlType: element.htmlType,
|
|
5537
|
+
reason: "render"
|
|
5538
|
+
});
|
|
5539
|
+
return;
|
|
5540
|
+
}
|
|
5541
|
+
sources.set(element.id, result);
|
|
5542
|
+
} catch (cause) {
|
|
5543
|
+
options.onHtmlError?.({
|
|
5544
|
+
elementId: element.id,
|
|
5545
|
+
htmlType: element.htmlType,
|
|
5546
|
+
reason: "render",
|
|
5547
|
+
cause
|
|
5548
|
+
});
|
|
5549
|
+
} finally {
|
|
5550
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5551
|
+
}
|
|
5552
|
+
})
|
|
5553
|
+
);
|
|
5554
|
+
return sources;
|
|
5555
|
+
}
|
|
5556
|
+
|
|
5445
5557
|
// src/canvas/export-image.ts
|
|
5446
5558
|
var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
|
|
5447
5559
|
var DEFAULT_MAX_DIMENSION = 16384;
|
|
@@ -5646,6 +5758,7 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5646
5758
|
const scale = positiveOption(options.scale, 2, "scale");
|
|
5647
5759
|
const padding = nonNegativeOption(options.padding, 0, "padding");
|
|
5648
5760
|
validateExportResourceOptions(options);
|
|
5761
|
+
validateHtmlExportOptions(options);
|
|
5649
5762
|
const background = options.background ?? "#ffffff";
|
|
5650
5763
|
const filter = options.filter;
|
|
5651
5764
|
const allElements = store.getAll();
|
|
@@ -5659,6 +5772,8 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5659
5772
|
const height = Math.ceil(bounds.h * scale);
|
|
5660
5773
|
assertExportSize(width, height, options);
|
|
5661
5774
|
const imageCache = await loadImages(visibleElements, options);
|
|
5775
|
+
const htmlElements = visibleElements.filter((el) => el.type === "html");
|
|
5776
|
+
const htmlSources = await renderHtmlElements(htmlElements, options);
|
|
5662
5777
|
const canvas = document.createElement("canvas");
|
|
5663
5778
|
canvas.width = width;
|
|
5664
5779
|
canvas.height = height;
|
|
@@ -5683,6 +5798,24 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5683
5798
|
return;
|
|
5684
5799
|
}
|
|
5685
5800
|
if (el.type === "html") {
|
|
5801
|
+
const source = htmlSources.get(el.id);
|
|
5802
|
+
if (!source) return;
|
|
5803
|
+
const b = getElementBounds(el);
|
|
5804
|
+
try {
|
|
5805
|
+
withRotation(
|
|
5806
|
+
target,
|
|
5807
|
+
el,
|
|
5808
|
+
b ? center(b) : el.position,
|
|
5809
|
+
() => target.drawImage(source, el.position.x, el.position.y, el.size.w, el.size.h)
|
|
5810
|
+
);
|
|
5811
|
+
} catch (cause) {
|
|
5812
|
+
options.onHtmlError?.({
|
|
5813
|
+
elementId: el.id,
|
|
5814
|
+
htmlType: el.htmlType,
|
|
5815
|
+
reason: "render",
|
|
5816
|
+
cause
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5686
5819
|
return;
|
|
5687
5820
|
}
|
|
5688
5821
|
if (el.type === "image") {
|
|
@@ -5838,7 +5971,7 @@ function emitArrow(arrow, store) {
|
|
|
5838
5971
|
return out;
|
|
5839
5972
|
}
|
|
5840
5973
|
function emitImage(image, dataUri) {
|
|
5841
|
-
const href = dataUri ?? image.src;
|
|
5974
|
+
const href = dataUri ?? ("src" in image ? image.src : "");
|
|
5842
5975
|
if (!href) return "";
|
|
5843
5976
|
const { x, y } = image.position;
|
|
5844
5977
|
const { w, h } = image.size;
|
|
@@ -6042,6 +6175,7 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
6042
6175
|
const padding = nonNegativeOption(options.padding, 0, "padding");
|
|
6043
6176
|
const rasterScale = positiveOption(options.rasterScale, 2, "rasterScale");
|
|
6044
6177
|
validateExportResourceOptions(options);
|
|
6178
|
+
validateHtmlExportOptions(options);
|
|
6045
6179
|
const filter = options.filter;
|
|
6046
6180
|
const allElements = store.getAll();
|
|
6047
6181
|
let visibleElements = layerManager ? allElements.filter((el) => layerManager.isLayerVisible(el.layerId)) : allElements;
|
|
@@ -6056,6 +6190,9 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
6056
6190
|
);
|
|
6057
6191
|
const imageCache = await loadImages(remoteImages, options);
|
|
6058
6192
|
const imageDataUris = encodeImages(visibleElements, imageCache, rasterScale, options);
|
|
6193
|
+
const htmlElements = visibleElements.filter((el) => el.type === "html");
|
|
6194
|
+
const htmlSources = await renderHtmlElements(htmlElements, options);
|
|
6195
|
+
const htmlDataUris = encodeHtmlElements(htmlElements, htmlSources, rasterScale, options);
|
|
6059
6196
|
const grids = visibleElements.filter((el) => el.type === "grid");
|
|
6060
6197
|
const firstGrid = grids[0];
|
|
6061
6198
|
let body = "";
|
|
@@ -6064,7 +6201,15 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
6064
6201
|
}
|
|
6065
6202
|
const layerBodies = /* @__PURE__ */ new Map();
|
|
6066
6203
|
for (const el of visibleElements) {
|
|
6067
|
-
const emitted = emitElement(
|
|
6204
|
+
const emitted = emitElement(
|
|
6205
|
+
el,
|
|
6206
|
+
imageDataUris,
|
|
6207
|
+
htmlDataUris,
|
|
6208
|
+
rasterScale,
|
|
6209
|
+
firstGrid,
|
|
6210
|
+
store,
|
|
6211
|
+
options
|
|
6212
|
+
);
|
|
6068
6213
|
layerBodies.set(el.layerId, (layerBodies.get(el.layerId) ?? "") + emitted);
|
|
6069
6214
|
}
|
|
6070
6215
|
for (const [layerId, emitted] of layerBodies) {
|
|
@@ -6078,7 +6223,7 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
6078
6223
|
}
|
|
6079
6224
|
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>`;
|
|
6080
6225
|
}
|
|
6081
|
-
function emitElement(el, imageDataUris, rasterScale, firstGrid, store, resourceOptions) {
|
|
6226
|
+
function emitElement(el, imageDataUris, htmlDataUris, rasterScale, firstGrid, store, resourceOptions) {
|
|
6082
6227
|
switch (el.type) {
|
|
6083
6228
|
case "stroke":
|
|
6084
6229
|
return withRotationSvg(el, emitStroke(el));
|
|
@@ -6097,11 +6242,62 @@ function emitElement(el, imageDataUris, rasterScale, firstGrid, store, resourceO
|
|
|
6097
6242
|
case "grid":
|
|
6098
6243
|
return "";
|
|
6099
6244
|
case "html":
|
|
6100
|
-
return
|
|
6245
|
+
return withRotationSvg(el, emitImage(el, htmlDataUris.get(el.id)));
|
|
6101
6246
|
default:
|
|
6102
6247
|
return "";
|
|
6103
6248
|
}
|
|
6104
6249
|
}
|
|
6250
|
+
function encodeHtmlElements(elements, sources, rasterScale, options) {
|
|
6251
|
+
const encoded = /* @__PURE__ */ new Map();
|
|
6252
|
+
for (const element of elements) {
|
|
6253
|
+
const source = sources.get(element.id);
|
|
6254
|
+
if (!source) continue;
|
|
6255
|
+
const width = Math.max(1, Math.ceil(element.size.w * rasterScale));
|
|
6256
|
+
const height = Math.max(1, Math.ceil(element.size.h * rasterScale));
|
|
6257
|
+
assertExportSize(width, height, options);
|
|
6258
|
+
if (typeof document === "undefined") {
|
|
6259
|
+
options.onHtmlError?.({
|
|
6260
|
+
elementId: element.id,
|
|
6261
|
+
htmlType: element.htmlType,
|
|
6262
|
+
reason: "encode"
|
|
6263
|
+
});
|
|
6264
|
+
continue;
|
|
6265
|
+
}
|
|
6266
|
+
const canvas = document.createElement("canvas");
|
|
6267
|
+
canvas.width = width;
|
|
6268
|
+
canvas.height = height;
|
|
6269
|
+
const ctx = canvas.getContext("2d");
|
|
6270
|
+
if (!ctx) {
|
|
6271
|
+
options.onHtmlError?.({
|
|
6272
|
+
elementId: element.id,
|
|
6273
|
+
htmlType: element.htmlType,
|
|
6274
|
+
reason: "encode"
|
|
6275
|
+
});
|
|
6276
|
+
continue;
|
|
6277
|
+
}
|
|
6278
|
+
try {
|
|
6279
|
+
ctx.drawImage(source, 0, 0, width, height);
|
|
6280
|
+
const dataUri = canvas.toDataURL();
|
|
6281
|
+
if (dataUri.startsWith("data:")) {
|
|
6282
|
+
encoded.set(element.id, dataUri);
|
|
6283
|
+
} else {
|
|
6284
|
+
options.onHtmlError?.({
|
|
6285
|
+
elementId: element.id,
|
|
6286
|
+
htmlType: element.htmlType,
|
|
6287
|
+
reason: "encode"
|
|
6288
|
+
});
|
|
6289
|
+
}
|
|
6290
|
+
} catch (cause) {
|
|
6291
|
+
options.onHtmlError?.({
|
|
6292
|
+
elementId: element.id,
|
|
6293
|
+
htmlType: element.htmlType,
|
|
6294
|
+
reason: "encode",
|
|
6295
|
+
cause
|
|
6296
|
+
});
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
return encoded;
|
|
6300
|
+
}
|
|
6105
6301
|
function encodeImages(elements, imageCache, rasterScale, resourceOptions) {
|
|
6106
6302
|
const out = /* @__PURE__ */ new Map();
|
|
6107
6303
|
for (const el of elements) {
|
|
@@ -6394,6 +6590,7 @@ var DoubleTapDetector = class {
|
|
|
6394
6590
|
// src/canvas/dom-node-manager.ts
|
|
6395
6591
|
var DomNodeManager = class {
|
|
6396
6592
|
domNodes = /* @__PURE__ */ new Map();
|
|
6593
|
+
strata = /* @__PURE__ */ new Map();
|
|
6397
6594
|
htmlContent = /* @__PURE__ */ new Map();
|
|
6398
6595
|
domLayer;
|
|
6399
6596
|
onEditRequest;
|
|
@@ -6402,6 +6599,7 @@ var DomNodeManager = class {
|
|
|
6402
6599
|
lastSyncedVersion = /* @__PURE__ */ new Map();
|
|
6403
6600
|
lastSyncedZIndex = /* @__PURE__ */ new Map();
|
|
6404
6601
|
lastSyncedOpacity = /* @__PURE__ */ new Map();
|
|
6602
|
+
cameraTransform = "";
|
|
6405
6603
|
constructor(deps) {
|
|
6406
6604
|
this.domLayer = deps.domLayer;
|
|
6407
6605
|
this.onEditRequest = deps.onEditRequest;
|
|
@@ -6411,6 +6609,11 @@ var DomNodeManager = class {
|
|
|
6411
6609
|
getNode(id) {
|
|
6412
6610
|
return this.domNodes.get(id);
|
|
6413
6611
|
}
|
|
6612
|
+
setCameraTransform(transform) {
|
|
6613
|
+
if (transform === this.cameraTransform) return;
|
|
6614
|
+
this.cameraTransform = transform;
|
|
6615
|
+
for (const stratum of this.strata.values()) stratum.style.transform = transform;
|
|
6616
|
+
}
|
|
6414
6617
|
storeHtmlContent(elementId, dom) {
|
|
6415
6618
|
this.htmlContent.set(elementId, dom);
|
|
6416
6619
|
}
|
|
@@ -6438,7 +6641,7 @@ var DomNodeManager = class {
|
|
|
6438
6641
|
position: "absolute",
|
|
6439
6642
|
pointerEvents: "auto"
|
|
6440
6643
|
});
|
|
6441
|
-
this.
|
|
6644
|
+
this.getStratum(zIndex).appendChild(node);
|
|
6442
6645
|
this.domNodes.set(element.id, node);
|
|
6443
6646
|
} else if (this.getVersion) {
|
|
6444
6647
|
const currentVersion = this.getVersion(element.id);
|
|
@@ -6449,6 +6652,15 @@ var DomNodeManager = class {
|
|
|
6449
6652
|
return;
|
|
6450
6653
|
}
|
|
6451
6654
|
}
|
|
6655
|
+
if (node.parentElement !== this.getStratum(zIndex)) {
|
|
6656
|
+
const previousStratum = node.parentElement;
|
|
6657
|
+
this.getStratum(zIndex).appendChild(node);
|
|
6658
|
+
if (previousStratum?.childElementCount === 0) {
|
|
6659
|
+
const previousOrder = Number(previousStratum.dataset["paintOrder"]);
|
|
6660
|
+
previousStratum.remove();
|
|
6661
|
+
this.strata.delete(previousOrder);
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6452
6664
|
if (this.getVersion) {
|
|
6453
6665
|
this.lastSyncedVersion.set(element.id, this.getVersion(element.id));
|
|
6454
6666
|
this.lastSyncedZIndex.set(element.id, zIndex);
|
|
@@ -6470,7 +6682,10 @@ var DomNodeManager = class {
|
|
|
6470
6682
|
}
|
|
6471
6683
|
hideDomNode(id) {
|
|
6472
6684
|
const node = this.domNodes.get(id);
|
|
6473
|
-
if (node)
|
|
6685
|
+
if (node) {
|
|
6686
|
+
node.style.display = "none";
|
|
6687
|
+
this.lastSyncedVersion.delete(id);
|
|
6688
|
+
}
|
|
6474
6689
|
}
|
|
6475
6690
|
removeDomNode(id) {
|
|
6476
6691
|
this.htmlContent.delete(id);
|
|
@@ -6479,8 +6694,14 @@ var DomNodeManager = class {
|
|
|
6479
6694
|
this.lastSyncedOpacity.delete(id);
|
|
6480
6695
|
const node = this.domNodes.get(id);
|
|
6481
6696
|
if (node) {
|
|
6697
|
+
const stratum = node.parentElement;
|
|
6482
6698
|
node.remove();
|
|
6483
6699
|
this.domNodes.delete(id);
|
|
6700
|
+
if (stratum?.childElementCount === 0) {
|
|
6701
|
+
const order = Number(stratum.dataset["paintOrder"]);
|
|
6702
|
+
stratum.remove();
|
|
6703
|
+
this.strata.delete(order);
|
|
6704
|
+
}
|
|
6484
6705
|
}
|
|
6485
6706
|
}
|
|
6486
6707
|
clearDomNodes() {
|
|
@@ -6490,6 +6711,27 @@ var DomNodeManager = class {
|
|
|
6490
6711
|
this.lastSyncedVersion.clear();
|
|
6491
6712
|
this.lastSyncedZIndex.clear();
|
|
6492
6713
|
this.lastSyncedOpacity.clear();
|
|
6714
|
+
for (const stratum of this.strata.values()) stratum.remove();
|
|
6715
|
+
this.strata.clear();
|
|
6716
|
+
}
|
|
6717
|
+
getStratum(order) {
|
|
6718
|
+
let stratum = this.strata.get(order);
|
|
6719
|
+
if (stratum) return stratum;
|
|
6720
|
+
stratum = document.createElement("div");
|
|
6721
|
+
stratum.dataset["paintOrder"] = String(order);
|
|
6722
|
+
Object.assign(stratum.style, {
|
|
6723
|
+
position: "absolute",
|
|
6724
|
+
inset: "0",
|
|
6725
|
+
width: "100%",
|
|
6726
|
+
height: "100%",
|
|
6727
|
+
pointerEvents: "none",
|
|
6728
|
+
transformOrigin: "0 0",
|
|
6729
|
+
transform: this.cameraTransform,
|
|
6730
|
+
zIndex: String(order)
|
|
6731
|
+
});
|
|
6732
|
+
this.domLayer.appendChild(stratum);
|
|
6733
|
+
this.strata.set(order, stratum);
|
|
6734
|
+
return stratum;
|
|
6493
6735
|
}
|
|
6494
6736
|
reattachHtmlContent(store) {
|
|
6495
6737
|
for (const el of store.getElementsByType("html")) {
|
|
@@ -6671,6 +6913,7 @@ var RenderLoop = class {
|
|
|
6671
6913
|
domNodeManager;
|
|
6672
6914
|
layerCache;
|
|
6673
6915
|
marginViewport;
|
|
6916
|
+
hybridSurface;
|
|
6674
6917
|
activeDrawingLayerId = null;
|
|
6675
6918
|
gridCacheDirty = true;
|
|
6676
6919
|
// set on recenter/viewport-change; consumed by the grid block
|
|
@@ -6690,6 +6933,7 @@ var RenderLoop = class {
|
|
|
6690
6933
|
this.domNodeManager = deps.domNodeManager;
|
|
6691
6934
|
this.layerCache = deps.layerCache;
|
|
6692
6935
|
this.marginViewport = deps.marginViewport;
|
|
6936
|
+
this.hybridSurface = deps.hybridSurface;
|
|
6693
6937
|
}
|
|
6694
6938
|
requestRender() {
|
|
6695
6939
|
this.needsRender = true;
|
|
@@ -6810,9 +7054,19 @@ var RenderLoop = class {
|
|
|
6810
7054
|
h: cullBounds.h + cullPad * 2
|
|
6811
7055
|
};
|
|
6812
7056
|
const allElements = this.store.getAll();
|
|
7057
|
+
const visibleElements = allElements.filter(
|
|
7058
|
+
(element) => this.layerManager.isLayerVisible(element.layerId)
|
|
7059
|
+
);
|
|
7060
|
+
const firstDomIndex = visibleElements.findIndex(
|
|
7061
|
+
(element) => this.renderer.isDomElement(element)
|
|
7062
|
+
);
|
|
7063
|
+
const hybridActive = firstDomIndex >= 0 && visibleElements.slice(firstDomIndex + 1).some((element) => !this.renderer.isDomElement(element));
|
|
7064
|
+
const hybridCanvasRuns = /* @__PURE__ */ new Map();
|
|
7065
|
+
const hybridOrders = /* @__PURE__ */ new Set();
|
|
7066
|
+
let activeHybridOrder = null;
|
|
6813
7067
|
this.layerGroups.clear();
|
|
6814
7068
|
const gridElements = [];
|
|
6815
|
-
let
|
|
7069
|
+
let paintOrder = 0;
|
|
6816
7070
|
for (const element of allElements) {
|
|
6817
7071
|
if (!this.layerManager.isLayerVisible(element.layerId)) {
|
|
6818
7072
|
if (this.renderer.isDomElement(element)) {
|
|
@@ -6820,16 +7074,29 @@ var RenderLoop = class {
|
|
|
6820
7074
|
}
|
|
6821
7075
|
continue;
|
|
6822
7076
|
}
|
|
7077
|
+
const order = ++paintOrder;
|
|
6823
7078
|
if (this.renderer.isDomElement(element)) {
|
|
7079
|
+
activeHybridOrder = null;
|
|
6824
7080
|
const layerOpacity = this.layerManager.getLayer?.(element.layerId)?.opacity ?? 1;
|
|
6825
7081
|
const elBounds = getElementBounds(element);
|
|
6826
7082
|
if (elBounds && !boundsIntersect(elBounds, cullingRect)) {
|
|
6827
7083
|
this.domNodeManager.hideDomNode(element.id);
|
|
6828
7084
|
} else {
|
|
6829
|
-
this.domNodeManager.syncDomNode(element,
|
|
7085
|
+
this.domNodeManager.syncDomNode(element, order, layerOpacity);
|
|
6830
7086
|
}
|
|
6831
7087
|
continue;
|
|
6832
7088
|
}
|
|
7089
|
+
if (hybridActive && paintOrder > firstDomIndex + 1 && element.type !== "grid") {
|
|
7090
|
+
activeHybridOrder ??= order;
|
|
7091
|
+
let run = hybridCanvasRuns.get(activeHybridOrder);
|
|
7092
|
+
if (!run) {
|
|
7093
|
+
run = [];
|
|
7094
|
+
hybridCanvasRuns.set(activeHybridOrder, run);
|
|
7095
|
+
hybridOrders.add(activeHybridOrder);
|
|
7096
|
+
}
|
|
7097
|
+
run.push(element);
|
|
7098
|
+
continue;
|
|
7099
|
+
}
|
|
6833
7100
|
if (element.type === "grid") {
|
|
6834
7101
|
gridElements.push(element);
|
|
6835
7102
|
continue;
|
|
@@ -6841,6 +7108,10 @@ var RenderLoop = class {
|
|
|
6841
7108
|
}
|
|
6842
7109
|
group.push(element);
|
|
6843
7110
|
}
|
|
7111
|
+
const activeTool = this.toolManager.activeTool;
|
|
7112
|
+
const overlayOrder = visibleElements.length + 1;
|
|
7113
|
+
if (hybridActive && activeTool?.renderOverlay) hybridOrders.add(overlayOrder);
|
|
7114
|
+
this.hybridSurface.beginFrame(hybridOrders, this.canvasEl.width, this.canvasEl.height);
|
|
6844
7115
|
for (const [layerId, elements] of this.layerGroups) {
|
|
6845
7116
|
const isActiveDrawingLayer = layerId === this.activeDrawingLayerId;
|
|
6846
7117
|
const layerOpacity = this.layerManager.getLayer?.(layerId)?.opacity ?? 1;
|
|
@@ -6930,9 +7201,37 @@ var RenderLoop = class {
|
|
|
6930
7201
|
} else {
|
|
6931
7202
|
this.lastGridRefs = [];
|
|
6932
7203
|
}
|
|
7204
|
+
for (const [order, elements] of hybridCanvasRuns) {
|
|
7205
|
+
const hybridCtx = this.hybridSurface.getContext(order);
|
|
7206
|
+
if (!hybridCtx) continue;
|
|
7207
|
+
hybridCtx.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);
|
|
7208
|
+
hybridCtx.save();
|
|
7209
|
+
hybridCtx.scale(dpr, dpr);
|
|
7210
|
+
hybridCtx.translate(this.camera.position.x, this.camera.position.y);
|
|
7211
|
+
hybridCtx.scale(this.camera.zoom, this.camera.zoom);
|
|
7212
|
+
for (const element of elements) {
|
|
7213
|
+
const elBounds = getElementBounds(element);
|
|
7214
|
+
if (elBounds && !boundsIntersect(elBounds, cullingRect)) continue;
|
|
7215
|
+
hybridCtx.save();
|
|
7216
|
+
hybridCtx.globalAlpha = this.layerManager.getLayer?.(element.layerId)?.opacity ?? 1;
|
|
7217
|
+
this.renderer.renderCanvasElement(hybridCtx, element);
|
|
7218
|
+
hybridCtx.restore();
|
|
7219
|
+
}
|
|
7220
|
+
hybridCtx.restore();
|
|
7221
|
+
}
|
|
6933
7222
|
const overlayT0 = performance.now();
|
|
6934
|
-
|
|
6935
|
-
|
|
7223
|
+
if (hybridActive && activeTool?.renderOverlay) {
|
|
7224
|
+
const overlayCtx = this.hybridSurface.getContext(overlayOrder);
|
|
7225
|
+
if (overlayCtx) {
|
|
7226
|
+
overlayCtx.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);
|
|
7227
|
+
overlayCtx.save();
|
|
7228
|
+
overlayCtx.scale(dpr, dpr);
|
|
7229
|
+
overlayCtx.translate(this.camera.position.x, this.camera.position.y);
|
|
7230
|
+
overlayCtx.scale(this.camera.zoom, this.camera.zoom);
|
|
7231
|
+
activeTool.renderOverlay(overlayCtx);
|
|
7232
|
+
overlayCtx.restore();
|
|
7233
|
+
}
|
|
7234
|
+
} else if (activeTool?.renderOverlay) {
|
|
6936
7235
|
activeTool.renderOverlay(ctx);
|
|
6937
7236
|
}
|
|
6938
7237
|
const overlayMs = performance.now() - overlayT0;
|
|
@@ -7683,8 +7982,11 @@ var Viewport = class {
|
|
|
7683
7982
|
});
|
|
7684
7983
|
this.wrapper = createWrapper();
|
|
7685
7984
|
this.canvasEl = createCanvas();
|
|
7985
|
+
this.paintStack = createPaintStack();
|
|
7686
7986
|
this.domLayer = createDomLayer();
|
|
7987
|
+
this.domLayer.style.zIndex = "2147483647";
|
|
7687
7988
|
this.wrapper.appendChild(this.canvasEl);
|
|
7989
|
+
this.wrapper.appendChild(this.paintStack);
|
|
7688
7990
|
this.wrapper.appendChild(this.domLayer);
|
|
7689
7991
|
this.container.appendChild(this.wrapper);
|
|
7690
7992
|
this.toolContext = {
|
|
@@ -7754,11 +8056,12 @@ var Viewport = class {
|
|
|
7754
8056
|
this.minimap.scheduleDraw();
|
|
7755
8057
|
}
|
|
7756
8058
|
this.domNodeManager = new DomNodeManager({
|
|
7757
|
-
domLayer: this.
|
|
8059
|
+
domLayer: this.paintStack,
|
|
7758
8060
|
onEditRequest: (id) => this.interactions.startEditingElement(id),
|
|
7759
8061
|
isEditingElement: (id) => this.noteEditor.isEditing && this.noteEditor.editingElementId === id,
|
|
7760
8062
|
getVersion: (id) => this.store.getVersion(id)
|
|
7761
8063
|
});
|
|
8064
|
+
this.domNodeManager.setCameraTransform(this.camera.toCSSTransform());
|
|
7762
8065
|
this.interactMode = new InteractMode({
|
|
7763
8066
|
getNode: (id) => this.domNodeManager.getNode(id)
|
|
7764
8067
|
});
|
|
@@ -7779,7 +8082,8 @@ var Viewport = class {
|
|
|
7779
8082
|
layerManager: this.layerManager,
|
|
7780
8083
|
domNodeManager: this.domNodeManager,
|
|
7781
8084
|
layerCache,
|
|
7782
|
-
marginViewport: this.marginViewport
|
|
8085
|
+
marginViewport: this.marginViewport,
|
|
8086
|
+
hybridSurface: new HybridRenderSurface(this.paintStack)
|
|
7783
8087
|
});
|
|
7784
8088
|
this.unsubCamera = this.camera.onChange(() => {
|
|
7785
8089
|
this.applyCameraTransform();
|
|
@@ -7866,6 +8170,7 @@ var Viewport = class {
|
|
|
7866
8170
|
history;
|
|
7867
8171
|
domLayer;
|
|
7868
8172
|
canvasEl;
|
|
8173
|
+
paintStack;
|
|
7869
8174
|
wrapper;
|
|
7870
8175
|
unsubCamera;
|
|
7871
8176
|
unsubToolChange;
|
|
@@ -8221,7 +8526,9 @@ var Viewport = class {
|
|
|
8221
8526
|
}
|
|
8222
8527
|
}
|
|
8223
8528
|
applyCameraTransform() {
|
|
8224
|
-
|
|
8529
|
+
const transform = this.camera.toCSSTransform();
|
|
8530
|
+
this.domLayer.style.transform = transform;
|
|
8531
|
+
this.domNodeManager.setCameraTransform(transform);
|
|
8225
8532
|
}
|
|
8226
8533
|
syncCanvasSize() {
|
|
8227
8534
|
const rect = this.container.getBoundingClientRect();
|
|
@@ -10902,7 +11209,7 @@ var LaserTool = class {
|
|
|
10902
11209
|
};
|
|
10903
11210
|
|
|
10904
11211
|
// src/index.ts
|
|
10905
|
-
var VERSION = "0.
|
|
11212
|
+
var VERSION = "0.52.1";
|
|
10906
11213
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10907
11214
|
0 && (module.exports = {
|
|
10908
11215
|
ArrowTool,
|