@fieldnotes/core 0.57.0 → 0.59.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 +348 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -2
- package/dist/index.d.ts +107 -2
- package/dist/index.js +347 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
MEASURE_PRESENCE_KIND: () => MEASURE_PRESENCE_KIND,
|
|
38
38
|
MeasureTool: () => MeasureTool,
|
|
39
39
|
MemoryAdapter: () => MemoryAdapter,
|
|
40
|
+
MinimapController: () => MinimapController,
|
|
40
41
|
NoteTool: () => NoteTool,
|
|
41
42
|
PING_PRESENCE_KIND: () => PING_PRESENCE_KIND,
|
|
42
43
|
PencilTool: () => PencilTool,
|
|
@@ -4796,88 +4797,247 @@ function miniToWorld(t, p) {
|
|
|
4796
4797
|
return { x: (p.x - t.offsetX) / t.scale, y: (p.y - t.offsetY) / t.scale };
|
|
4797
4798
|
}
|
|
4798
4799
|
|
|
4799
|
-
// src/canvas/minimap.ts
|
|
4800
|
-
var
|
|
4801
|
-
var
|
|
4802
|
-
var
|
|
4803
|
-
var
|
|
4800
|
+
// src/canvas/minimap-controller.ts
|
|
4801
|
+
var DEFAULT_WIDTH = 200;
|
|
4802
|
+
var DEFAULT_HEIGHT = 140;
|
|
4803
|
+
var DEFAULT_PADDING = 8;
|
|
4804
|
+
var DEFAULT_DEBOUNCE_MS2 = 200;
|
|
4805
|
+
var DEFAULT_VIEWPORT_STROKE = "#3b82f6";
|
|
4804
4806
|
var NEUTRAL = "rgba(100,116,139,0.6)";
|
|
4805
|
-
var
|
|
4807
|
+
var DOM_FALLBACK_TYPES = /* @__PURE__ */ new Set(["note", "text", "html"]);
|
|
4806
4808
|
function elementColor(el) {
|
|
4807
4809
|
return "color" in el && typeof el.color === "string" ? el.color : NEUTRAL;
|
|
4808
4810
|
}
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
Object.assign(canvas.style, {
|
|
4816
|
-
position: "absolute",
|
|
4817
|
-
right: `${MARGIN}px`,
|
|
4818
|
-
bottom: `${MARGIN}px`,
|
|
4819
|
-
width: `${WIDTH}px`,
|
|
4820
|
-
height: `${HEIGHT}px`,
|
|
4821
|
-
background: "rgba(255,255,255,0.85)",
|
|
4822
|
-
border: "1px solid rgba(0,0,0,0.15)",
|
|
4823
|
-
borderRadius: "4px",
|
|
4824
|
-
touchAction: "none",
|
|
4825
|
-
cursor: "pointer",
|
|
4826
|
-
zIndex: "10"
|
|
4827
|
-
});
|
|
4828
|
-
canvas.addEventListener("pointerdown", this.onPointerDown);
|
|
4829
|
-
canvas.addEventListener("pointermove", this.onPointerMove);
|
|
4830
|
-
canvas.addEventListener("pointerup", this.onPointerUp);
|
|
4831
|
-
this.deps.container.appendChild(canvas);
|
|
4811
|
+
function sameBounds(a, b) {
|
|
4812
|
+
return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
4813
|
+
}
|
|
4814
|
+
var MinimapController = class {
|
|
4815
|
+
constructor(viewport, canvas, options = {}) {
|
|
4816
|
+
this.viewport = viewport;
|
|
4832
4817
|
this.canvas = canvas;
|
|
4818
|
+
this.width = options.width ?? DEFAULT_WIDTH;
|
|
4819
|
+
this.height = options.height ?? DEFAULT_HEIGHT;
|
|
4820
|
+
this.padding = options.padding ?? DEFAULT_PADDING;
|
|
4821
|
+
this.background = options.background ?? null;
|
|
4822
|
+
this.viewportStroke = options.viewportStroke ?? DEFAULT_VIEWPORT_STROKE;
|
|
4823
|
+
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS2;
|
|
4824
|
+
this.interactive = options.interactive !== false;
|
|
4825
|
+
this.requestFrame = options.requestFrame ?? ((cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0);
|
|
4826
|
+
this.cancelFrame = options.cancelFrame ?? ((id) => {
|
|
4827
|
+
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
|
|
4828
|
+
});
|
|
4829
|
+
this.renderer.setStore(viewport.store);
|
|
4830
|
+
this.renderer.setOnImageLoad(() => this.markSceneDirty());
|
|
4831
|
+
this.applyCanvasSize();
|
|
4832
|
+
const onScene = () => this.markSceneDirty();
|
|
4833
|
+
this.unsubs.push(
|
|
4834
|
+
viewport.store.on("add", onScene),
|
|
4835
|
+
viewport.store.on("remove", onScene),
|
|
4836
|
+
viewport.store.on("update", onScene),
|
|
4837
|
+
viewport.store.on("clear", onScene),
|
|
4838
|
+
viewport.layerManager.on("change", onScene),
|
|
4839
|
+
viewport.camera.onChange(() => this.onViewChanged()),
|
|
4840
|
+
viewport.onResize(() => this.onViewChanged())
|
|
4841
|
+
);
|
|
4842
|
+
if (this.interactive) {
|
|
4843
|
+
canvas.style.touchAction = "none";
|
|
4844
|
+
canvas.style.cursor = "pointer";
|
|
4845
|
+
canvas.addEventListener("pointerdown", this.onPointerDown);
|
|
4846
|
+
canvas.addEventListener("pointermove", this.onPointerMove);
|
|
4847
|
+
canvas.addEventListener("pointerup", this.onPointerUp);
|
|
4848
|
+
canvas.addEventListener("pointercancel", this.onPointerEnd);
|
|
4849
|
+
canvas.addEventListener("lostpointercapture", this.onPointerEnd);
|
|
4850
|
+
}
|
|
4851
|
+
this.renderScene();
|
|
4852
|
+
this.requestDraw();
|
|
4833
4853
|
}
|
|
4834
|
-
|
|
4835
|
-
|
|
4854
|
+
width;
|
|
4855
|
+
height;
|
|
4856
|
+
padding;
|
|
4857
|
+
background;
|
|
4858
|
+
viewportStroke;
|
|
4859
|
+
debounceMs;
|
|
4860
|
+
interactive;
|
|
4861
|
+
requestFrame;
|
|
4862
|
+
cancelFrame;
|
|
4863
|
+
renderer = new ElementRenderer();
|
|
4864
|
+
scene = null;
|
|
4865
|
+
frameId = null;
|
|
4866
|
+
debounceTimer = null;
|
|
4836
4867
|
dragging = false;
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4868
|
+
disposed = false;
|
|
4869
|
+
unsubs = [];
|
|
4870
|
+
setSize(width, height) {
|
|
4871
|
+
if (this.disposed) return;
|
|
4872
|
+
this.width = width;
|
|
4873
|
+
this.height = height;
|
|
4874
|
+
this.applyCanvasSize();
|
|
4875
|
+
this.clearDebounce();
|
|
4876
|
+
this.renderScene();
|
|
4877
|
+
this.requestDraw();
|
|
4840
4878
|
}
|
|
4841
|
-
|
|
4842
|
-
if (this.
|
|
4843
|
-
|
|
4844
|
-
|
|
4879
|
+
requestDraw() {
|
|
4880
|
+
if (this.disposed || this.frameId !== null) return;
|
|
4881
|
+
this.frameId = this.requestFrame(this.draw);
|
|
4882
|
+
}
|
|
4883
|
+
dispose() {
|
|
4884
|
+
if (this.disposed) return;
|
|
4885
|
+
this.disposed = true;
|
|
4886
|
+
this.clearDebounce();
|
|
4887
|
+
if (this.frameId !== null) {
|
|
4888
|
+
this.cancelFrame(this.frameId);
|
|
4889
|
+
this.frameId = null;
|
|
4890
|
+
}
|
|
4891
|
+
for (const unsub of this.unsubs.splice(0)) unsub();
|
|
4892
|
+
if (this.interactive) {
|
|
4893
|
+
this.canvas.removeEventListener("pointerdown", this.onPointerDown);
|
|
4894
|
+
this.canvas.removeEventListener("pointermove", this.onPointerMove);
|
|
4895
|
+
this.canvas.removeEventListener("pointerup", this.onPointerUp);
|
|
4896
|
+
this.canvas.removeEventListener("pointercancel", this.onPointerEnd);
|
|
4897
|
+
this.canvas.removeEventListener("lostpointercapture", this.onPointerEnd);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
clearDebounce() {
|
|
4901
|
+
if (this.debounceTimer !== null) {
|
|
4902
|
+
clearTimeout(this.debounceTimer);
|
|
4903
|
+
this.debounceTimer = null;
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
dpr() {
|
|
4907
|
+
return typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
4908
|
+
}
|
|
4909
|
+
applyCanvasSize() {
|
|
4910
|
+
const dpr = this.dpr();
|
|
4911
|
+
this.canvas.width = Math.max(1, Math.round(this.width * dpr));
|
|
4912
|
+
this.canvas.height = Math.max(1, Math.round(this.height * dpr));
|
|
4913
|
+
}
|
|
4914
|
+
// Single source for both mapping bounds and rendering: layer-visible,
|
|
4915
|
+
// grids excluded. (getElementBounds already returns null for grids, so they
|
|
4916
|
+
// cannot extend the bounding box today — this filter makes the invariant
|
|
4917
|
+
// structural instead of relying on that special case.)
|
|
4918
|
+
sceneElements() {
|
|
4919
|
+
return this.viewport.store.getAll().filter((el) => el.type !== "grid" && this.viewport.layerManager.isLayerVisible(el.layerId));
|
|
4920
|
+
}
|
|
4921
|
+
currentMapping() {
|
|
4922
|
+
const viewportRect = this.viewport.getVisibleRect();
|
|
4923
|
+
const content = getElementsBoundingBox(this.sceneElements());
|
|
4924
|
+
return content ? unionBounds(content, viewportRect) : viewportRect;
|
|
4925
|
+
}
|
|
4926
|
+
onViewChanged() {
|
|
4927
|
+
if (this.disposed) return;
|
|
4928
|
+
const mapping = this.currentMapping();
|
|
4929
|
+
if (!this.scene || !sameBounds(mapping, this.scene.mapping)) {
|
|
4930
|
+
this.markSceneDirty();
|
|
4845
4931
|
}
|
|
4846
|
-
this.
|
|
4847
|
-
this.canvas.removeEventListener("pointermove", this.onPointerMove);
|
|
4848
|
-
this.canvas.removeEventListener("pointerup", this.onPointerUp);
|
|
4849
|
-
this.canvas.remove();
|
|
4932
|
+
this.requestDraw();
|
|
4850
4933
|
}
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4934
|
+
markSceneDirty() {
|
|
4935
|
+
if (this.disposed) return;
|
|
4936
|
+
this.clearDebounce();
|
|
4937
|
+
this.debounceTimer = setTimeout(() => {
|
|
4938
|
+
this.debounceTimer = null;
|
|
4939
|
+
this.renderScene();
|
|
4940
|
+
this.requestDraw();
|
|
4941
|
+
}, this.debounceMs);
|
|
4942
|
+
}
|
|
4943
|
+
renderScene() {
|
|
4944
|
+
if (this.disposed) return;
|
|
4945
|
+
const dpr = this.dpr();
|
|
4946
|
+
const mapping = this.currentMapping();
|
|
4947
|
+
const transform = computeMinimapTransform(mapping, this.width, this.height, this.padding);
|
|
4948
|
+
const sceneCanvas = document.createElement("canvas");
|
|
4949
|
+
sceneCanvas.width = Math.max(1, Math.round(this.width * dpr));
|
|
4950
|
+
sceneCanvas.height = Math.max(1, Math.round(this.height * dpr));
|
|
4951
|
+
const ctx = sceneCanvas.getContext("2d");
|
|
4952
|
+
if (!ctx) return;
|
|
4953
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
4954
|
+
for (const el of this.sceneElements()) {
|
|
4955
|
+
let list = byLayer.get(el.layerId);
|
|
4956
|
+
if (!list) {
|
|
4957
|
+
list = [];
|
|
4958
|
+
byLayer.set(el.layerId, list);
|
|
4959
|
+
}
|
|
4960
|
+
list.push(el);
|
|
4961
|
+
}
|
|
4962
|
+
for (const layer of this.viewport.layerManager.getLayers()) {
|
|
4963
|
+
const els = byLayer.get(layer.id);
|
|
4964
|
+
if (!els || els.length === 0) continue;
|
|
4965
|
+
const opacity = layer.opacity;
|
|
4966
|
+
if (opacity >= 1) {
|
|
4967
|
+
this.renderLayerElements(ctx, els, transform, dpr);
|
|
4968
|
+
continue;
|
|
4969
|
+
}
|
|
4970
|
+
const layerCanvas = document.createElement("canvas");
|
|
4971
|
+
layerCanvas.width = sceneCanvas.width;
|
|
4972
|
+
layerCanvas.height = sceneCanvas.height;
|
|
4973
|
+
const layerCtx = layerCanvas.getContext("2d");
|
|
4974
|
+
if (!layerCtx) continue;
|
|
4975
|
+
this.renderLayerElements(layerCtx, els, transform, dpr);
|
|
4976
|
+
ctx.save();
|
|
4977
|
+
ctx.globalAlpha = opacity;
|
|
4978
|
+
ctx.drawImage(layerCanvas, 0, 0);
|
|
4979
|
+
ctx.restore();
|
|
4980
|
+
}
|
|
4981
|
+
this.scene = { canvas: sceneCanvas, transform, mapping };
|
|
4982
|
+
}
|
|
4983
|
+
renderLayerElements(ctx, elements, t, dpr) {
|
|
4984
|
+
for (const el of elements) {
|
|
4985
|
+
if (DOM_FALLBACK_TYPES.has(el.type)) {
|
|
4986
|
+
const b = getElementBounds(el);
|
|
4987
|
+
if (!b) continue;
|
|
4988
|
+
const tl = worldToMini(t, { x: b.x, y: b.y });
|
|
4989
|
+
ctx.save();
|
|
4990
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
4991
|
+
ctx.fillStyle = elementColor(el);
|
|
4992
|
+
ctx.fillRect(tl.x, tl.y, Math.max(1, b.w * t.scale), Math.max(1, b.h * t.scale));
|
|
4993
|
+
ctx.restore();
|
|
4994
|
+
continue;
|
|
4995
|
+
}
|
|
4996
|
+
ctx.save();
|
|
4997
|
+
ctx.setTransform(dpr * t.scale, 0, 0, dpr * t.scale, dpr * t.offsetX, dpr * t.offsetY);
|
|
4998
|
+
this.renderer.renderCanvasElement(ctx, el);
|
|
4999
|
+
ctx.restore();
|
|
5000
|
+
}
|
|
4856
5001
|
}
|
|
4857
5002
|
draw = () => {
|
|
4858
|
-
this.
|
|
5003
|
+
this.frameId = null;
|
|
5004
|
+
if (this.disposed) return;
|
|
4859
5005
|
const ctx = this.canvas.getContext("2d");
|
|
4860
5006
|
if (!ctx) return;
|
|
4861
|
-
const
|
|
4862
|
-
|
|
4863
|
-
ctx.clearRect(0, 0,
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
const
|
|
4872
|
-
|
|
5007
|
+
const dpr = this.dpr();
|
|
5008
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
5009
|
+
ctx.clearRect(0, 0, this.width, this.height);
|
|
5010
|
+
if (this.background) {
|
|
5011
|
+
ctx.fillStyle = this.background;
|
|
5012
|
+
ctx.fillRect(0, 0, this.width, this.height);
|
|
5013
|
+
}
|
|
5014
|
+
const scene = this.scene;
|
|
5015
|
+
if (!scene) return;
|
|
5016
|
+
ctx.drawImage(scene.canvas, 0, 0, this.width, this.height);
|
|
5017
|
+
const viewportRect = this.viewport.getVisibleRect();
|
|
5018
|
+
const tl = worldToMini(scene.transform, { x: viewportRect.x, y: viewportRect.y });
|
|
5019
|
+
ctx.strokeStyle = this.viewportStroke;
|
|
4873
5020
|
ctx.lineWidth = 1.5;
|
|
4874
|
-
ctx.strokeRect(
|
|
5021
|
+
ctx.strokeRect(
|
|
5022
|
+
tl.x,
|
|
5023
|
+
tl.y,
|
|
5024
|
+
viewportRect.w * scene.transform.scale,
|
|
5025
|
+
viewportRect.h * scene.transform.scale
|
|
5026
|
+
);
|
|
4875
5027
|
};
|
|
5028
|
+
navTransform() {
|
|
5029
|
+
return this.scene?.transform ?? computeMinimapTransform(this.currentMapping(), this.width, this.height, this.padding);
|
|
5030
|
+
}
|
|
4876
5031
|
navigateFromEvent(e) {
|
|
4877
5032
|
const rect = this.canvas.getBoundingClientRect();
|
|
4878
|
-
const
|
|
4879
|
-
const
|
|
4880
|
-
|
|
5033
|
+
const scaleX = rect.width > 0 ? this.width / rect.width : 1;
|
|
5034
|
+
const scaleY = rect.height > 0 ? this.height / rect.height : 1;
|
|
5035
|
+
const point = {
|
|
5036
|
+
x: (e.clientX - rect.left) * scaleX,
|
|
5037
|
+
y: (e.clientY - rect.top) * scaleY
|
|
5038
|
+
};
|
|
5039
|
+
const world = miniToWorld(this.navTransform(), point);
|
|
5040
|
+
this.viewport.centerCameraAt(world);
|
|
4881
5041
|
}
|
|
4882
5042
|
onPointerDown = (e) => {
|
|
4883
5043
|
e.stopPropagation();
|
|
@@ -4898,6 +5058,45 @@ var Minimap = class {
|
|
|
4898
5058
|
} catch {
|
|
4899
5059
|
}
|
|
4900
5060
|
};
|
|
5061
|
+
// pointercancel / lostpointercapture: a browser gesture or interrupted touch
|
|
5062
|
+
// can end the interaction without a pointerup — the drag must not stick.
|
|
5063
|
+
onPointerEnd = () => {
|
|
5064
|
+
this.dragging = false;
|
|
5065
|
+
};
|
|
5066
|
+
};
|
|
5067
|
+
|
|
5068
|
+
// src/canvas/minimap.ts
|
|
5069
|
+
var WIDTH = 200;
|
|
5070
|
+
var HEIGHT = 140;
|
|
5071
|
+
var MARGIN = 16;
|
|
5072
|
+
var Minimap = class {
|
|
5073
|
+
canvas;
|
|
5074
|
+
controller;
|
|
5075
|
+
constructor(container, viewport) {
|
|
5076
|
+
const canvas = document.createElement("canvas");
|
|
5077
|
+
Object.assign(canvas.style, {
|
|
5078
|
+
position: "absolute",
|
|
5079
|
+
right: `${MARGIN}px`,
|
|
5080
|
+
bottom: `${MARGIN}px`,
|
|
5081
|
+
width: `${WIDTH}px`,
|
|
5082
|
+
height: `${HEIGHT}px`,
|
|
5083
|
+
background: "rgba(255,255,255,0.85)",
|
|
5084
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
5085
|
+
borderRadius: "4px",
|
|
5086
|
+
zIndex: "10"
|
|
5087
|
+
});
|
|
5088
|
+
canvas.dataset.fieldnotesMinimap = "true";
|
|
5089
|
+
container.appendChild(canvas);
|
|
5090
|
+
this.canvas = canvas;
|
|
5091
|
+
this.controller = new MinimapController(viewport, canvas, { width: WIDTH, height: HEIGHT });
|
|
5092
|
+
}
|
|
5093
|
+
scheduleDraw() {
|
|
5094
|
+
this.controller.requestDraw();
|
|
5095
|
+
}
|
|
5096
|
+
destroy() {
|
|
5097
|
+
this.controller.dispose();
|
|
5098
|
+
this.canvas.remove();
|
|
5099
|
+
}
|
|
4901
5100
|
};
|
|
4902
5101
|
|
|
4903
5102
|
// src/canvas/viewport-dom.ts
|
|
@@ -5656,6 +5855,19 @@ function computeBounds(elements, padding) {
|
|
|
5656
5855
|
h: maxY - minY + padding * 2
|
|
5657
5856
|
};
|
|
5658
5857
|
}
|
|
5858
|
+
function resolveExportBounds(region, elements, padding) {
|
|
5859
|
+
if (!region) return computeBounds(elements, padding);
|
|
5860
|
+
const finite = Number.isFinite(region.x) && Number.isFinite(region.y) && Number.isFinite(region.w) && Number.isFinite(region.h);
|
|
5861
|
+
if (!finite || region.w <= 0 || region.h <= 0) {
|
|
5862
|
+
throw new RangeError("region must have finite coordinates and positive w/h");
|
|
5863
|
+
}
|
|
5864
|
+
return {
|
|
5865
|
+
x: region.x - padding,
|
|
5866
|
+
y: region.y - padding,
|
|
5867
|
+
w: region.w + padding * 2,
|
|
5868
|
+
h: region.h + padding * 2
|
|
5869
|
+
};
|
|
5870
|
+
}
|
|
5659
5871
|
function renderGridForBounds(ctx, grid, bounds) {
|
|
5660
5872
|
const visibleBounds = {
|
|
5661
5873
|
minX: bounds.x,
|
|
@@ -5698,6 +5910,29 @@ function nonNegativeOption(value, fallback, name) {
|
|
|
5698
5910
|
}
|
|
5699
5911
|
return resolved;
|
|
5700
5912
|
}
|
|
5913
|
+
function fitExportScale(bounds, requestedScale, options) {
|
|
5914
|
+
const maxDimension = positiveOption(options.maxDimension, DEFAULT_MAX_DIMENSION, "maxDimension");
|
|
5915
|
+
const maxPixels = positiveOption(options.maxPixels, DEFAULT_MAX_PIXELS, "maxPixels");
|
|
5916
|
+
const fits = (scale) => {
|
|
5917
|
+
const w = Math.ceil(bounds.w * scale);
|
|
5918
|
+
const h = Math.ceil(bounds.h * scale);
|
|
5919
|
+
return w <= maxDimension && h <= maxDimension && w * h <= maxPixels;
|
|
5920
|
+
};
|
|
5921
|
+
if (fits(requestedScale)) return requestedScale;
|
|
5922
|
+
const analytical = Math.min(
|
|
5923
|
+
maxDimension / Math.max(bounds.w, bounds.h),
|
|
5924
|
+
Math.sqrt(maxPixels) / Math.sqrt(bounds.w) / Math.sqrt(bounds.h)
|
|
5925
|
+
);
|
|
5926
|
+
let hi = Math.min(requestedScale, analytical);
|
|
5927
|
+
if (fits(hi)) return hi;
|
|
5928
|
+
let lo = 0;
|
|
5929
|
+
for (let i = 0; i < 40; i++) {
|
|
5930
|
+
const mid = (lo + hi) / 2;
|
|
5931
|
+
if (fits(mid)) lo = mid;
|
|
5932
|
+
else hi = mid;
|
|
5933
|
+
}
|
|
5934
|
+
return lo;
|
|
5935
|
+
}
|
|
5701
5936
|
function assertExportSize(width, height, options) {
|
|
5702
5937
|
const maxDimension = positiveOption(options.maxDimension, DEFAULT_MAX_DIMENSION, "maxDimension");
|
|
5703
5938
|
const maxPixels = positiveOption(options.maxPixels, DEFAULT_MAX_PIXELS, "maxPixels");
|
|
@@ -5772,10 +6007,23 @@ function loadImages(elements, options = {}) {
|
|
|
5772
6007
|
});
|
|
5773
6008
|
}
|
|
5774
6009
|
async function exportImage(store, options = {}, layerManager) {
|
|
5775
|
-
const
|
|
6010
|
+
const requestedScale = positiveOption(options.scale, 2, "scale");
|
|
6011
|
+
const scaleMode = options.scaleMode ?? "exact";
|
|
6012
|
+
if (scaleMode !== "exact" && scaleMode !== "fit") {
|
|
6013
|
+
throw new RangeError(`scaleMode must be 'exact' or 'fit'`);
|
|
6014
|
+
}
|
|
5776
6015
|
const padding = nonNegativeOption(options.padding, 0, "padding");
|
|
5777
6016
|
validateExportResourceOptions(options);
|
|
5778
6017
|
validateHtmlExportOptions(options);
|
|
6018
|
+
const format = options.format ?? "png";
|
|
6019
|
+
if (format !== "png" && format !== "jpeg") {
|
|
6020
|
+
throw new RangeError(`format must be 'png' or 'jpeg'`);
|
|
6021
|
+
}
|
|
6022
|
+
if (options.quality !== void 0) {
|
|
6023
|
+
if (!Number.isFinite(options.quality) || options.quality <= 0 || options.quality > 1) {
|
|
6024
|
+
throw new RangeError("quality must be a finite number in (0, 1]");
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
5779
6027
|
const background = options.background ?? "#ffffff";
|
|
5780
6028
|
const filter = options.filter;
|
|
5781
6029
|
const allElements = store.getAll();
|
|
@@ -5783,8 +6031,9 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5783
6031
|
if (filter) {
|
|
5784
6032
|
visibleElements = visibleElements.filter(filter);
|
|
5785
6033
|
}
|
|
5786
|
-
const bounds =
|
|
6034
|
+
const bounds = resolveExportBounds(options.region, visibleElements, padding);
|
|
5787
6035
|
if (!bounds) return null;
|
|
6036
|
+
const scale = scaleMode === "fit" ? fitExportScale(bounds, requestedScale, options) : requestedScale;
|
|
5788
6037
|
const width = Math.ceil(bounds.w * scale);
|
|
5789
6038
|
const height = Math.ceil(bounds.h * scale);
|
|
5790
6039
|
assertExportSize(width, height, options);
|
|
@@ -5886,8 +6135,9 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
5886
6135
|
renderGridForBounds(ctx, grid, bounds);
|
|
5887
6136
|
ctx.restore();
|
|
5888
6137
|
}
|
|
6138
|
+
const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
|
|
5889
6139
|
return new Promise((resolve) => {
|
|
5890
|
-
canvas.toBlob((blob) => resolve(blob),
|
|
6140
|
+
canvas.toBlob((blob) => resolve(blob), mimeType, options.quality);
|
|
5891
6141
|
});
|
|
5892
6142
|
}
|
|
5893
6143
|
|
|
@@ -8079,25 +8329,7 @@ var Viewport = class {
|
|
|
8079
8329
|
}
|
|
8080
8330
|
this.unsubToolChange = this.toolManager.onChange(() => this.contextMenu?.close());
|
|
8081
8331
|
if (options.minimap) {
|
|
8082
|
-
|
|
8083
|
-
this.minimap = new Minimap({
|
|
8084
|
-
container: this.wrapper,
|
|
8085
|
-
getElements: visibleEls,
|
|
8086
|
-
getContentBounds: () => getElementsBoundingBox(visibleEls()),
|
|
8087
|
-
getViewportRect: () => this.camera.getVisibleRect(this.canvasEl.clientWidth, this.canvasEl.clientHeight),
|
|
8088
|
-
navigateTo: (w) => {
|
|
8089
|
-
const z = this.camera.zoom;
|
|
8090
|
-
this.camera.moveTo(
|
|
8091
|
-
this.canvasEl.clientWidth / 2 - w.x * z,
|
|
8092
|
-
this.canvasEl.clientHeight / 2 - w.y * z
|
|
8093
|
-
);
|
|
8094
|
-
},
|
|
8095
|
-
requestFrame: (cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0,
|
|
8096
|
-
cancelFrame: (id) => {
|
|
8097
|
-
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
|
|
8098
|
-
}
|
|
8099
|
-
});
|
|
8100
|
-
this.minimap.scheduleDraw();
|
|
8332
|
+
this.minimap = new Minimap(this.wrapper, this);
|
|
8101
8333
|
}
|
|
8102
8334
|
this.domNodeManager = new DomNodeManager({
|
|
8103
8335
|
domLayer: this.paintStack,
|
|
@@ -8133,7 +8365,6 @@ var Viewport = class {
|
|
|
8133
8365
|
this.applyCameraTransform();
|
|
8134
8366
|
this.noteEditor.updateToolbarPosition();
|
|
8135
8367
|
this.contextMenu?.close();
|
|
8136
|
-
this.minimap?.scheduleDraw();
|
|
8137
8368
|
this.requestRender();
|
|
8138
8369
|
});
|
|
8139
8370
|
this.gridController = new GridController({
|
|
@@ -8148,7 +8379,6 @@ var Viewport = class {
|
|
|
8148
8379
|
this.store.on("add", (el) => {
|
|
8149
8380
|
if (el.type === "grid") this.gridController.syncContext();
|
|
8150
8381
|
this.renderLoop.markLayerDirty(el.layerId);
|
|
8151
|
-
this.minimap?.scheduleDraw();
|
|
8152
8382
|
this.requestRender();
|
|
8153
8383
|
}),
|
|
8154
8384
|
this.store.on("remove", (el) => {
|
|
@@ -8156,7 +8386,6 @@ var Viewport = class {
|
|
|
8156
8386
|
this.unbindArrowsFrom(el);
|
|
8157
8387
|
this.domNodeManager.removeDomNode(el.id);
|
|
8158
8388
|
this.renderLoop.markLayerDirty(el.layerId);
|
|
8159
|
-
this.minimap?.scheduleDraw();
|
|
8160
8389
|
this.requestRender();
|
|
8161
8390
|
}),
|
|
8162
8391
|
this.store.on("update", ({ previous, current }) => {
|
|
@@ -8165,21 +8394,18 @@ var Viewport = class {
|
|
|
8165
8394
|
if (previous.layerId !== current.layerId) {
|
|
8166
8395
|
this.renderLoop.markLayerDirty(previous.layerId);
|
|
8167
8396
|
}
|
|
8168
|
-
this.minimap?.scheduleDraw();
|
|
8169
8397
|
this.requestRender();
|
|
8170
8398
|
}),
|
|
8171
8399
|
this.store.on("clear", () => {
|
|
8172
8400
|
this.domNodeManager.clearDomNodes();
|
|
8173
8401
|
this.renderLoop.markAllLayersDirty();
|
|
8174
8402
|
this.gridController.syncContext();
|
|
8175
|
-
this.minimap?.scheduleDraw();
|
|
8176
8403
|
this.requestRender();
|
|
8177
8404
|
})
|
|
8178
8405
|
];
|
|
8179
8406
|
this.layerManager.on("change", () => {
|
|
8180
8407
|
this.toolContext.activeLayerId = this.layerManager.activeLayerId;
|
|
8181
8408
|
this.renderLoop.markAllLayersDirty();
|
|
8182
|
-
this.minimap?.scheduleDraw();
|
|
8183
8409
|
this.requestRender();
|
|
8184
8410
|
});
|
|
8185
8411
|
this.interactions = new ViewportInteractions({
|
|
@@ -8243,6 +8469,7 @@ var Viewport = class {
|
|
|
8243
8469
|
contextMenu = null;
|
|
8244
8470
|
minimap = null;
|
|
8245
8471
|
htmlRenderers = /* @__PURE__ */ new Map();
|
|
8472
|
+
resizeListeners = /* @__PURE__ */ new Set();
|
|
8246
8473
|
get ctx() {
|
|
8247
8474
|
return this.canvasEl.getContext("2d");
|
|
8248
8475
|
}
|
|
@@ -8267,6 +8494,27 @@ var Viewport = class {
|
|
|
8267
8494
|
if (!bbox) return;
|
|
8268
8495
|
this.camera.fitToContent(bbox, this.wrapper.clientWidth, this.wrapper.clientHeight, padding);
|
|
8269
8496
|
}
|
|
8497
|
+
/** World-space rectangle currently visible through the canvas. */
|
|
8498
|
+
getVisibleRect() {
|
|
8499
|
+
return this.camera.getVisibleRect(this.canvasEl.clientWidth, this.canvasEl.clientHeight);
|
|
8500
|
+
}
|
|
8501
|
+
/** Centers the camera on a world point without changing zoom. */
|
|
8502
|
+
centerCameraAt(world) {
|
|
8503
|
+
const z = this.camera.zoom;
|
|
8504
|
+
this.camera.moveTo(
|
|
8505
|
+
this.canvasEl.clientWidth / 2 - world.x * z,
|
|
8506
|
+
this.canvasEl.clientHeight / 2 - world.y * z
|
|
8507
|
+
);
|
|
8508
|
+
}
|
|
8509
|
+
/**
|
|
8510
|
+
* Notifies after the host container resizes (ResizeObserver-driven). A resize
|
|
8511
|
+
* changes the visible world rect without a camera event; overlays such as the
|
|
8512
|
+
* minimap subscribe to stay current. Returns an idempotent unsubscribe.
|
|
8513
|
+
*/
|
|
8514
|
+
onResize(listener) {
|
|
8515
|
+
this.resizeListeners.add(listener);
|
|
8516
|
+
return () => this.resizeListeners.delete(listener);
|
|
8517
|
+
}
|
|
8270
8518
|
requestRender() {
|
|
8271
8519
|
this.renderLoop.requestRender();
|
|
8272
8520
|
}
|
|
@@ -8571,6 +8819,7 @@ var Viewport = class {
|
|
|
8571
8819
|
this.unsubStore.forEach((fn) => fn());
|
|
8572
8820
|
this.resizeObserver?.disconnect();
|
|
8573
8821
|
this.resizeObserver = null;
|
|
8822
|
+
this.resizeListeners.clear();
|
|
8574
8823
|
this.wrapper.remove();
|
|
8575
8824
|
}
|
|
8576
8825
|
stopInteracting() {
|
|
@@ -8620,6 +8869,7 @@ var Viewport = class {
|
|
|
8620
8869
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
8621
8870
|
this.renderLoop.setCanvasSize(rect.width * dpr, rect.height * dpr);
|
|
8622
8871
|
this.requestRender();
|
|
8872
|
+
this.resizeListeners.forEach((fn) => fn());
|
|
8623
8873
|
}
|
|
8624
8874
|
observeResize() {
|
|
8625
8875
|
if (typeof ResizeObserver === "undefined") return;
|
|
@@ -8659,7 +8909,7 @@ function toLaserTrailPresence(emission) {
|
|
|
8659
8909
|
};
|
|
8660
8910
|
}
|
|
8661
8911
|
var DEFAULT_COLOR = "#ff3b30";
|
|
8662
|
-
var
|
|
8912
|
+
var DEFAULT_WIDTH2 = 4;
|
|
8663
8913
|
var DEFAULT_FADE_MS = 1200;
|
|
8664
8914
|
var DEFAULT_MAX_POINTS = 512;
|
|
8665
8915
|
var RemoteLaserOverlay = class {
|
|
@@ -8675,7 +8925,7 @@ var RemoteLaserOverlay = class {
|
|
|
8675
8925
|
constructor(host, options = {}) {
|
|
8676
8926
|
this.host = host;
|
|
8677
8927
|
this.color = options.color ?? DEFAULT_COLOR;
|
|
8678
|
-
this.width = options.width ??
|
|
8928
|
+
this.width = options.width ?? DEFAULT_WIDTH2;
|
|
8679
8929
|
this.fadeMs = options.fadeMs ?? DEFAULT_FADE_MS;
|
|
8680
8930
|
this.maxPointsPerSender = options.maxPointsPerSender ?? DEFAULT_MAX_POINTS;
|
|
8681
8931
|
this.unregister = host.registerOverlay((ctx) => this.renderTrails(ctx));
|
|
@@ -11904,7 +12154,7 @@ var TemplateTool = class {
|
|
|
11904
12154
|
|
|
11905
12155
|
// src/tools/laser-tool.ts
|
|
11906
12156
|
var DEFAULT_COLOR5 = "#ff3b30";
|
|
11907
|
-
var
|
|
12157
|
+
var DEFAULT_WIDTH3 = 4;
|
|
11908
12158
|
var DEFAULT_FADE_MS3 = 1200;
|
|
11909
12159
|
var LaserTool = class {
|
|
11910
12160
|
name;
|
|
@@ -11920,7 +12170,7 @@ var LaserTool = class {
|
|
|
11920
12170
|
constructor(options = {}) {
|
|
11921
12171
|
this.name = options.name ?? "laser";
|
|
11922
12172
|
this.color = options.color ?? DEFAULT_COLOR5;
|
|
11923
|
-
this.width = options.width ??
|
|
12173
|
+
this.width = options.width ?? DEFAULT_WIDTH3;
|
|
11924
12174
|
this.fadeMs = options.fadeMs ?? DEFAULT_FADE_MS3;
|
|
11925
12175
|
}
|
|
11926
12176
|
now() {
|
|
@@ -12167,7 +12417,7 @@ var PingTool = class {
|
|
|
12167
12417
|
};
|
|
12168
12418
|
|
|
12169
12419
|
// src/index.ts
|
|
12170
|
-
var VERSION = "0.
|
|
12420
|
+
var VERSION = "0.59.0";
|
|
12171
12421
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12172
12422
|
0 && (module.exports = {
|
|
12173
12423
|
ArrowTool,
|
|
@@ -12187,6 +12437,7 @@ var VERSION = "0.57.0";
|
|
|
12187
12437
|
MEASURE_PRESENCE_KIND,
|
|
12188
12438
|
MeasureTool,
|
|
12189
12439
|
MemoryAdapter,
|
|
12440
|
+
MinimapController,
|
|
12190
12441
|
NoteTool,
|
|
12191
12442
|
PING_PRESENCE_KIND,
|
|
12192
12443
|
PencilTool,
|