@fieldnotes/core 0.50.7 → 0.50.8
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 +135 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +135 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -239,6 +239,17 @@ function sanitizeAttributes(el, tag) {
|
|
|
239
239
|
|
|
240
240
|
// src/core/state-serializer.ts
|
|
241
241
|
var CURRENT_VERSION = 2;
|
|
242
|
+
var ELEMENT_TYPES = [
|
|
243
|
+
"stroke",
|
|
244
|
+
"note",
|
|
245
|
+
"arrow",
|
|
246
|
+
"image",
|
|
247
|
+
"html",
|
|
248
|
+
"text",
|
|
249
|
+
"shape",
|
|
250
|
+
"grid",
|
|
251
|
+
"template"
|
|
252
|
+
];
|
|
242
253
|
function exportState(elements, camera, layers = [], activeLayerId) {
|
|
243
254
|
const state = {
|
|
244
255
|
version: CURRENT_VERSION,
|
|
@@ -264,37 +275,42 @@ function parseState(json) {
|
|
|
264
275
|
return data;
|
|
265
276
|
}
|
|
266
277
|
function validateState(data) {
|
|
267
|
-
if (!data
|
|
278
|
+
if (!isRecord(data)) {
|
|
268
279
|
throw new Error("Invalid state: expected an object");
|
|
269
280
|
}
|
|
270
281
|
const obj = data;
|
|
271
|
-
if (
|
|
282
|
+
if (!Number.isInteger(obj["version"]) || obj["version"] < 1) {
|
|
272
283
|
throw new Error("Invalid state: missing or invalid version");
|
|
273
284
|
}
|
|
274
|
-
if (
|
|
285
|
+
if (obj["version"] > CURRENT_VERSION) {
|
|
286
|
+
throw new Error(`Invalid state: unsupported version ${String(obj["version"])}`);
|
|
287
|
+
}
|
|
288
|
+
if (!isRecord(obj["camera"])) {
|
|
275
289
|
throw new Error("Invalid state: missing camera");
|
|
276
290
|
}
|
|
277
291
|
const cam = obj["camera"];
|
|
278
|
-
if (!cam["position"]
|
|
292
|
+
if (!isRecord(cam["position"])) {
|
|
279
293
|
throw new Error("Invalid state: missing camera.position");
|
|
280
294
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
throw new Error("Invalid state: camera.position must have x and y numbers");
|
|
295
|
+
if (!isPoint(cam["position"])) {
|
|
296
|
+
throw new Error("Invalid state: camera.position must have finite x and y numbers");
|
|
284
297
|
}
|
|
285
|
-
if (
|
|
286
|
-
throw new Error("Invalid state:
|
|
298
|
+
if (!isFiniteNumber(cam["zoom"]) || cam["zoom"] <= 0) {
|
|
299
|
+
throw new Error("Invalid state: camera.zoom must be a positive finite number");
|
|
287
300
|
}
|
|
288
301
|
if (!Array.isArray(obj["elements"])) {
|
|
289
302
|
throw new Error("Invalid state: elements must be an array");
|
|
290
303
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
migrateElement(el);
|
|
304
|
+
if (obj["layers"] !== void 0 && !Array.isArray(obj["layers"])) {
|
|
305
|
+
throw new Error("Invalid state: layers must be an array");
|
|
294
306
|
}
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
307
|
+
const elements = obj["elements"];
|
|
308
|
+
const hasLayers = Array.isArray(obj["layers"]) && obj["layers"].length > 0;
|
|
309
|
+
for (const el of elements) {
|
|
310
|
+
if (!isRecord(el)) throw new Error("Invalid element: expected an object");
|
|
311
|
+
migrateElement(el, !hasLayers);
|
|
312
|
+
}
|
|
313
|
+
if (!hasLayers) {
|
|
298
314
|
obj["layers"] = [
|
|
299
315
|
{
|
|
300
316
|
id: "default-layer",
|
|
@@ -306,31 +322,73 @@ function validateState(data) {
|
|
|
306
322
|
}
|
|
307
323
|
];
|
|
308
324
|
}
|
|
325
|
+
const layers = obj["layers"];
|
|
326
|
+
const layerIds = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const layer of layers) {
|
|
328
|
+
validateLayer(layer);
|
|
329
|
+
if (layerIds.has(layer.id)) throw new Error(`Invalid state: duplicate layer id "${layer.id}"`);
|
|
330
|
+
layerIds.add(layer.id);
|
|
331
|
+
}
|
|
332
|
+
const elementIds = /* @__PURE__ */ new Set();
|
|
333
|
+
for (const el of elements) {
|
|
334
|
+
validateElement(el);
|
|
335
|
+
if (elementIds.has(el.id)) throw new Error(`Invalid state: duplicate element id "${el.id}"`);
|
|
336
|
+
elementIds.add(el.id);
|
|
337
|
+
if (!layerIds.has(el.layerId)) {
|
|
338
|
+
throw new Error(`Invalid element "${el.id}": unknown layerId "${el.layerId}"`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (obj["activeLayerId"] !== void 0) {
|
|
342
|
+
if (typeof obj["activeLayerId"] !== "string" || !layerIds.has(obj["activeLayerId"])) {
|
|
343
|
+
throw new Error("Invalid state: activeLayerId must reference an existing layer");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
cleanBindings(elements);
|
|
309
347
|
}
|
|
310
|
-
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
311
|
-
"stroke",
|
|
312
|
-
"note",
|
|
313
|
-
"arrow",
|
|
314
|
-
"image",
|
|
315
|
-
"html",
|
|
316
|
-
"text",
|
|
317
|
-
"shape",
|
|
318
|
-
"grid",
|
|
319
|
-
"template"
|
|
320
|
-
]);
|
|
321
348
|
function validateElement(el) {
|
|
322
|
-
if (!el
|
|
349
|
+
if (!isRecord(el)) {
|
|
323
350
|
throw new Error("Invalid element: expected an object");
|
|
324
351
|
}
|
|
325
|
-
|
|
326
|
-
if (typeof obj["id"] !== "string") {
|
|
352
|
+
if (typeof el["id"] !== "string" || el["id"].length === 0) {
|
|
327
353
|
throw new Error("Invalid element: missing id");
|
|
328
354
|
}
|
|
329
|
-
if (
|
|
330
|
-
throw new Error(`Invalid element: unknown type "${String(
|
|
355
|
+
if (!isEnum(el["type"], ELEMENT_TYPES)) {
|
|
356
|
+
throw new Error(`Invalid element: unknown type "${String(el["type"])}"`);
|
|
357
|
+
}
|
|
358
|
+
if (!isFiniteNumber(el["zIndex"])) {
|
|
359
|
+
throw new Error(`Invalid element "${el["id"]}": missing or invalid zIndex`);
|
|
331
360
|
}
|
|
332
|
-
if (typeof
|
|
333
|
-
throw new Error(
|
|
361
|
+
if (!isPoint(el["position"]) || typeof el["locked"] !== "boolean" || typeof el["layerId"] !== "string" || !isOptional(el["groupId"], isString) || !isOptional(el["rotation"], isFiniteNumber)) {
|
|
362
|
+
throw new Error(`Invalid element "${el["id"]}": invalid base fields or geometry`);
|
|
363
|
+
}
|
|
364
|
+
const valid = validateTypeFields(el, el["type"]);
|
|
365
|
+
if (!valid) throw new Error(`Invalid element "${el["id"]}": malformed ${el["type"]} data`);
|
|
366
|
+
}
|
|
367
|
+
function validateTypeFields(el, type) {
|
|
368
|
+
switch (type) {
|
|
369
|
+
case "stroke":
|
|
370
|
+
return Array.isArray(el["points"]) && el["points"].every(isStrokePoint) && isString(el["color"]) && isFiniteNumber(el["width"]) && isFiniteNumber(el["opacity"]) && isOptionalEnum(el["blendMode"], ["multiply"]);
|
|
371
|
+
case "note":
|
|
372
|
+
return isSize(el["size"]) && isString(el["text"]) && isString(el["backgroundColor"]) && isString(el["textColor"]) && isOptional(el["fontSize"], isFiniteNumber);
|
|
373
|
+
case "arrow":
|
|
374
|
+
return isPoint(el["from"]) && isPoint(el["to"]) && isFiniteNumber(el["bend"]) && isString(el["color"]) && isFiniteNumber(el["width"]) && isOptional(el["fromBinding"], isBinding) && isOptional(el["toBinding"], isBinding) && isOptional(el["cachedControlPoint"], isPoint) && isOptional(el["label"], isString) && isOptionalEnum(el["strokeStyle"], ["solid", "dashed", "dotted"]);
|
|
375
|
+
case "image":
|
|
376
|
+
return isSize(el["size"]) && isString(el["src"]);
|
|
377
|
+
case "html":
|
|
378
|
+
return isSize(el["size"]) && isOptional(el["domId"], isString) && isOptional(el["interactive"], isBoolean) && isOptional(el["htmlType"], isString) && isOptional(el["data"], isRecord);
|
|
379
|
+
case "text":
|
|
380
|
+
return isSize(el["size"]) && isString(el["text"]) && isFiniteNumber(el["fontSize"]) && isString(el["color"]) && isEnum(el["textAlign"], ["left", "center", "right"]);
|
|
381
|
+
case "shape":
|
|
382
|
+
return isEnum(el["shape"], ["rectangle", "ellipse", "line"]) && isSize(el["size"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isString(el["fillColor"]) && isOptional(el["flip"], isBoolean);
|
|
383
|
+
case "grid":
|
|
384
|
+
return isEnum(el["gridType"], ["square", "hex"]) && isEnum(el["hexOrientation"], ["pointy", "flat"]) && isFiniteNumber(el["cellSize"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isFiniteNumber(el["opacity"]);
|
|
385
|
+
case "template":
|
|
386
|
+
return isEnum(el["templateShape"], ["circle", "cone", "line", "square", "rectangle"]) && isFiniteNumber(el["radius"]) && isFiniteNumber(el["angle"]) && isOptional(el["width"], isFiniteNumber) && isString(el["fillColor"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isFiniteNumber(el["opacity"]) && isOptional(el["feetPerCell"], isFiniteNumber) && isOptional(el["radiusFeet"], isFiniteNumber) && isOptionalEnum(el["renderStyle"], ["cells", "geometric"]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function validateLayer(layer) {
|
|
390
|
+
if (!isRecord(layer) || typeof layer["id"] !== "string" || layer["id"].length === 0 || typeof layer["name"] !== "string" || typeof layer["visible"] !== "boolean" || typeof layer["locked"] !== "boolean" || !isFiniteNumber(layer["order"]) || !isFiniteNumber(layer["opacity"]) || layer["opacity"] < 0 || layer["opacity"] > 1) {
|
|
391
|
+
throw new Error("Invalid state: malformed layer");
|
|
334
392
|
}
|
|
335
393
|
}
|
|
336
394
|
function cleanBindings(elements) {
|
|
@@ -347,30 +405,63 @@ function cleanBindings(elements) {
|
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
407
|
}
|
|
350
|
-
function migrateElement(obj) {
|
|
351
|
-
if (
|
|
408
|
+
function migrateElement(obj, useDefaultLayer) {
|
|
409
|
+
if (obj["layerId"] === void 0 || useDefaultLayer && obj["layerId"] === "") {
|
|
352
410
|
obj["layerId"] = "default-layer";
|
|
353
411
|
}
|
|
354
|
-
if (obj["type"] === "arrow" &&
|
|
412
|
+
if (obj["type"] === "arrow" && obj["bend"] === void 0) {
|
|
355
413
|
obj["bend"] = 0;
|
|
356
414
|
}
|
|
357
415
|
if (obj["type"] === "stroke" && Array.isArray(obj["points"])) {
|
|
358
416
|
for (const pt of obj["points"]) {
|
|
359
|
-
if (
|
|
417
|
+
if (pt["pressure"] === void 0) {
|
|
360
418
|
pt["pressure"] = 0.5;
|
|
361
419
|
}
|
|
362
420
|
}
|
|
363
421
|
}
|
|
364
|
-
if (obj["type"] === "shape" &&
|
|
422
|
+
if (obj["type"] === "shape" && obj["shape"] === void 0) {
|
|
365
423
|
obj["shape"] = "rectangle";
|
|
366
424
|
}
|
|
367
|
-
if (obj["type"] === "note" &&
|
|
425
|
+
if (obj["type"] === "note" && obj["textColor"] === void 0) {
|
|
368
426
|
obj["textColor"] = "#000000";
|
|
369
427
|
}
|
|
370
428
|
if ((obj["type"] === "note" || obj["type"] === "text") && typeof obj["text"] === "string") {
|
|
371
429
|
obj["text"] = sanitizeNoteHtml(obj["text"]);
|
|
372
430
|
}
|
|
373
431
|
}
|
|
432
|
+
function isRecord(value) {
|
|
433
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
434
|
+
}
|
|
435
|
+
function isFiniteNumber(value) {
|
|
436
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
437
|
+
}
|
|
438
|
+
function isString(value) {
|
|
439
|
+
return typeof value === "string";
|
|
440
|
+
}
|
|
441
|
+
function isBoolean(value) {
|
|
442
|
+
return typeof value === "boolean";
|
|
443
|
+
}
|
|
444
|
+
function isOptional(value, validate) {
|
|
445
|
+
return value === void 0 || validate(value);
|
|
446
|
+
}
|
|
447
|
+
function isEnum(value, values) {
|
|
448
|
+
return typeof value === "string" && values.includes(value);
|
|
449
|
+
}
|
|
450
|
+
function isOptionalEnum(value, values) {
|
|
451
|
+
return value === void 0 || isEnum(value, values);
|
|
452
|
+
}
|
|
453
|
+
function isPoint(value) {
|
|
454
|
+
return isRecord(value) && isFiniteNumber(value["x"]) && isFiniteNumber(value["y"]);
|
|
455
|
+
}
|
|
456
|
+
function isSize(value) {
|
|
457
|
+
return isRecord(value) && isFiniteNumber(value["w"]) && isFiniteNumber(value["h"]);
|
|
458
|
+
}
|
|
459
|
+
function isStrokePoint(value) {
|
|
460
|
+
return isRecord(value) && isPoint(value) && isFiniteNumber(value["pressure"]);
|
|
461
|
+
}
|
|
462
|
+
function isBinding(value) {
|
|
463
|
+
return isRecord(value) && typeof value["elementId"] === "string";
|
|
464
|
+
}
|
|
374
465
|
|
|
375
466
|
// src/core/storage/local-storage-adapter.ts
|
|
376
467
|
var LocalStorageAdapter = class {
|
|
@@ -6502,7 +6593,7 @@ var RenderLoop = class {
|
|
|
6502
6593
|
layerGroups = /* @__PURE__ */ new Map();
|
|
6503
6594
|
gridCacheCanvas = null;
|
|
6504
6595
|
gridCacheCtx = null;
|
|
6505
|
-
|
|
6596
|
+
lastGridRef = null;
|
|
6506
6597
|
constructor(deps) {
|
|
6507
6598
|
this.canvasEl = deps.canvasEl;
|
|
6508
6599
|
this.camera = deps.camera;
|
|
@@ -6702,8 +6793,8 @@ var RenderLoop = class {
|
|
|
6702
6793
|
}
|
|
6703
6794
|
if (gridElements.length > 0) {
|
|
6704
6795
|
const gridT0 = performance.now();
|
|
6705
|
-
const
|
|
6706
|
-
const gridDirty = this.gridCacheDirty ||
|
|
6796
|
+
const gridRef = gridElements[0];
|
|
6797
|
+
const gridDirty = this.gridCacheDirty || gridRef !== this.lastGridRef;
|
|
6707
6798
|
if (gridDirty) {
|
|
6708
6799
|
this.ensureGridCache();
|
|
6709
6800
|
if (this.gridCacheCtx && this.gridCacheCanvas) {
|
|
@@ -6731,7 +6822,7 @@ var RenderLoop = class {
|
|
|
6731
6822
|
}
|
|
6732
6823
|
}
|
|
6733
6824
|
this.gridCacheDirty = false;
|
|
6734
|
-
this.
|
|
6825
|
+
this.lastGridRef = gridRef;
|
|
6735
6826
|
}
|
|
6736
6827
|
if (this.gridCacheCanvas) {
|
|
6737
6828
|
const offset = this.marginViewport.compositeOffset(
|
|
@@ -6751,8 +6842,6 @@ var RenderLoop = class {
|
|
|
6751
6842
|
}
|
|
6752
6843
|
}
|
|
6753
6844
|
gridMs = performance.now() - gridT0;
|
|
6754
|
-
} else {
|
|
6755
|
-
this.lastGridRefs = [];
|
|
6756
6845
|
}
|
|
6757
6846
|
const overlayT0 = performance.now();
|
|
6758
6847
|
const activeTool = this.toolManager.activeTool;
|
|
@@ -10726,7 +10815,7 @@ var LaserTool = class {
|
|
|
10726
10815
|
};
|
|
10727
10816
|
|
|
10728
10817
|
// src/index.ts
|
|
10729
|
-
var VERSION = "0.50.
|
|
10818
|
+
var VERSION = "0.50.8";
|
|
10730
10819
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10731
10820
|
0 && (module.exports = {
|
|
10732
10821
|
ArrowTool,
|