@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.d.cts
CHANGED
|
@@ -1123,6 +1123,6 @@ declare class LaserTool implements Tool {
|
|
|
1123
1123
|
private notifyOptionsChange;
|
|
1124
1124
|
}
|
|
1125
1125
|
|
|
1126
|
-
declare const VERSION = "0.50.
|
|
1126
|
+
declare const VERSION = "0.50.8";
|
|
1127
1127
|
|
|
1128
1128
|
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.d.ts
CHANGED
|
@@ -1123,6 +1123,6 @@ declare class LaserTool implements Tool {
|
|
|
1123
1123
|
private notifyOptionsChange;
|
|
1124
1124
|
}
|
|
1125
1125
|
|
|
1126
|
-
declare const VERSION = "0.50.
|
|
1126
|
+
declare const VERSION = "0.50.8";
|
|
1127
1127
|
|
|
1128
1128
|
export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, type ElementChangeMeta, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.js
CHANGED
|
@@ -152,6 +152,17 @@ function sanitizeAttributes(el, tag) {
|
|
|
152
152
|
|
|
153
153
|
// src/core/state-serializer.ts
|
|
154
154
|
var CURRENT_VERSION = 2;
|
|
155
|
+
var ELEMENT_TYPES = [
|
|
156
|
+
"stroke",
|
|
157
|
+
"note",
|
|
158
|
+
"arrow",
|
|
159
|
+
"image",
|
|
160
|
+
"html",
|
|
161
|
+
"text",
|
|
162
|
+
"shape",
|
|
163
|
+
"grid",
|
|
164
|
+
"template"
|
|
165
|
+
];
|
|
155
166
|
function exportState(elements, camera, layers = [], activeLayerId) {
|
|
156
167
|
const state = {
|
|
157
168
|
version: CURRENT_VERSION,
|
|
@@ -177,37 +188,42 @@ function parseState(json) {
|
|
|
177
188
|
return data;
|
|
178
189
|
}
|
|
179
190
|
function validateState(data) {
|
|
180
|
-
if (!data
|
|
191
|
+
if (!isRecord(data)) {
|
|
181
192
|
throw new Error("Invalid state: expected an object");
|
|
182
193
|
}
|
|
183
194
|
const obj = data;
|
|
184
|
-
if (
|
|
195
|
+
if (!Number.isInteger(obj["version"]) || obj["version"] < 1) {
|
|
185
196
|
throw new Error("Invalid state: missing or invalid version");
|
|
186
197
|
}
|
|
187
|
-
if (
|
|
198
|
+
if (obj["version"] > CURRENT_VERSION) {
|
|
199
|
+
throw new Error(`Invalid state: unsupported version ${String(obj["version"])}`);
|
|
200
|
+
}
|
|
201
|
+
if (!isRecord(obj["camera"])) {
|
|
188
202
|
throw new Error("Invalid state: missing camera");
|
|
189
203
|
}
|
|
190
204
|
const cam = obj["camera"];
|
|
191
|
-
if (!cam["position"]
|
|
205
|
+
if (!isRecord(cam["position"])) {
|
|
192
206
|
throw new Error("Invalid state: missing camera.position");
|
|
193
207
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
throw new Error("Invalid state: camera.position must have x and y numbers");
|
|
208
|
+
if (!isPoint(cam["position"])) {
|
|
209
|
+
throw new Error("Invalid state: camera.position must have finite x and y numbers");
|
|
197
210
|
}
|
|
198
|
-
if (
|
|
199
|
-
throw new Error("Invalid state:
|
|
211
|
+
if (!isFiniteNumber(cam["zoom"]) || cam["zoom"] <= 0) {
|
|
212
|
+
throw new Error("Invalid state: camera.zoom must be a positive finite number");
|
|
200
213
|
}
|
|
201
214
|
if (!Array.isArray(obj["elements"])) {
|
|
202
215
|
throw new Error("Invalid state: elements must be an array");
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
migrateElement(el);
|
|
217
|
+
if (obj["layers"] !== void 0 && !Array.isArray(obj["layers"])) {
|
|
218
|
+
throw new Error("Invalid state: layers must be an array");
|
|
207
219
|
}
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
220
|
+
const elements = obj["elements"];
|
|
221
|
+
const hasLayers = Array.isArray(obj["layers"]) && obj["layers"].length > 0;
|
|
222
|
+
for (const el of elements) {
|
|
223
|
+
if (!isRecord(el)) throw new Error("Invalid element: expected an object");
|
|
224
|
+
migrateElement(el, !hasLayers);
|
|
225
|
+
}
|
|
226
|
+
if (!hasLayers) {
|
|
211
227
|
obj["layers"] = [
|
|
212
228
|
{
|
|
213
229
|
id: "default-layer",
|
|
@@ -219,31 +235,73 @@ function validateState(data) {
|
|
|
219
235
|
}
|
|
220
236
|
];
|
|
221
237
|
}
|
|
238
|
+
const layers = obj["layers"];
|
|
239
|
+
const layerIds = /* @__PURE__ */ new Set();
|
|
240
|
+
for (const layer of layers) {
|
|
241
|
+
validateLayer(layer);
|
|
242
|
+
if (layerIds.has(layer.id)) throw new Error(`Invalid state: duplicate layer id "${layer.id}"`);
|
|
243
|
+
layerIds.add(layer.id);
|
|
244
|
+
}
|
|
245
|
+
const elementIds = /* @__PURE__ */ new Set();
|
|
246
|
+
for (const el of elements) {
|
|
247
|
+
validateElement(el);
|
|
248
|
+
if (elementIds.has(el.id)) throw new Error(`Invalid state: duplicate element id "${el.id}"`);
|
|
249
|
+
elementIds.add(el.id);
|
|
250
|
+
if (!layerIds.has(el.layerId)) {
|
|
251
|
+
throw new Error(`Invalid element "${el.id}": unknown layerId "${el.layerId}"`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (obj["activeLayerId"] !== void 0) {
|
|
255
|
+
if (typeof obj["activeLayerId"] !== "string" || !layerIds.has(obj["activeLayerId"])) {
|
|
256
|
+
throw new Error("Invalid state: activeLayerId must reference an existing layer");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
cleanBindings(elements);
|
|
222
260
|
}
|
|
223
|
-
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
224
|
-
"stroke",
|
|
225
|
-
"note",
|
|
226
|
-
"arrow",
|
|
227
|
-
"image",
|
|
228
|
-
"html",
|
|
229
|
-
"text",
|
|
230
|
-
"shape",
|
|
231
|
-
"grid",
|
|
232
|
-
"template"
|
|
233
|
-
]);
|
|
234
261
|
function validateElement(el) {
|
|
235
|
-
if (!el
|
|
262
|
+
if (!isRecord(el)) {
|
|
236
263
|
throw new Error("Invalid element: expected an object");
|
|
237
264
|
}
|
|
238
|
-
|
|
239
|
-
if (typeof obj["id"] !== "string") {
|
|
265
|
+
if (typeof el["id"] !== "string" || el["id"].length === 0) {
|
|
240
266
|
throw new Error("Invalid element: missing id");
|
|
241
267
|
}
|
|
242
|
-
if (
|
|
243
|
-
throw new Error(`Invalid element: unknown type "${String(
|
|
268
|
+
if (!isEnum(el["type"], ELEMENT_TYPES)) {
|
|
269
|
+
throw new Error(`Invalid element: unknown type "${String(el["type"])}"`);
|
|
270
|
+
}
|
|
271
|
+
if (!isFiniteNumber(el["zIndex"])) {
|
|
272
|
+
throw new Error(`Invalid element "${el["id"]}": missing or invalid zIndex`);
|
|
244
273
|
}
|
|
245
|
-
if (typeof
|
|
246
|
-
throw new Error(
|
|
274
|
+
if (!isPoint(el["position"]) || typeof el["locked"] !== "boolean" || typeof el["layerId"] !== "string" || !isOptional(el["groupId"], isString) || !isOptional(el["rotation"], isFiniteNumber)) {
|
|
275
|
+
throw new Error(`Invalid element "${el["id"]}": invalid base fields or geometry`);
|
|
276
|
+
}
|
|
277
|
+
const valid = validateTypeFields(el, el["type"]);
|
|
278
|
+
if (!valid) throw new Error(`Invalid element "${el["id"]}": malformed ${el["type"]} data`);
|
|
279
|
+
}
|
|
280
|
+
function validateTypeFields(el, type) {
|
|
281
|
+
switch (type) {
|
|
282
|
+
case "stroke":
|
|
283
|
+
return Array.isArray(el["points"]) && el["points"].every(isStrokePoint) && isString(el["color"]) && isFiniteNumber(el["width"]) && isFiniteNumber(el["opacity"]) && isOptionalEnum(el["blendMode"], ["multiply"]);
|
|
284
|
+
case "note":
|
|
285
|
+
return isSize(el["size"]) && isString(el["text"]) && isString(el["backgroundColor"]) && isString(el["textColor"]) && isOptional(el["fontSize"], isFiniteNumber);
|
|
286
|
+
case "arrow":
|
|
287
|
+
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"]);
|
|
288
|
+
case "image":
|
|
289
|
+
return isSize(el["size"]) && isString(el["src"]);
|
|
290
|
+
case "html":
|
|
291
|
+
return isSize(el["size"]) && isOptional(el["domId"], isString) && isOptional(el["interactive"], isBoolean) && isOptional(el["htmlType"], isString) && isOptional(el["data"], isRecord);
|
|
292
|
+
case "text":
|
|
293
|
+
return isSize(el["size"]) && isString(el["text"]) && isFiniteNumber(el["fontSize"]) && isString(el["color"]) && isEnum(el["textAlign"], ["left", "center", "right"]);
|
|
294
|
+
case "shape":
|
|
295
|
+
return isEnum(el["shape"], ["rectangle", "ellipse", "line"]) && isSize(el["size"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isString(el["fillColor"]) && isOptional(el["flip"], isBoolean);
|
|
296
|
+
case "grid":
|
|
297
|
+
return isEnum(el["gridType"], ["square", "hex"]) && isEnum(el["hexOrientation"], ["pointy", "flat"]) && isFiniteNumber(el["cellSize"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isFiniteNumber(el["opacity"]);
|
|
298
|
+
case "template":
|
|
299
|
+
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"]);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function validateLayer(layer) {
|
|
303
|
+
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) {
|
|
304
|
+
throw new Error("Invalid state: malformed layer");
|
|
247
305
|
}
|
|
248
306
|
}
|
|
249
307
|
function cleanBindings(elements) {
|
|
@@ -260,30 +318,63 @@ function cleanBindings(elements) {
|
|
|
260
318
|
}
|
|
261
319
|
}
|
|
262
320
|
}
|
|
263
|
-
function migrateElement(obj) {
|
|
264
|
-
if (
|
|
321
|
+
function migrateElement(obj, useDefaultLayer) {
|
|
322
|
+
if (obj["layerId"] === void 0 || useDefaultLayer && obj["layerId"] === "") {
|
|
265
323
|
obj["layerId"] = "default-layer";
|
|
266
324
|
}
|
|
267
|
-
if (obj["type"] === "arrow" &&
|
|
325
|
+
if (obj["type"] === "arrow" && obj["bend"] === void 0) {
|
|
268
326
|
obj["bend"] = 0;
|
|
269
327
|
}
|
|
270
328
|
if (obj["type"] === "stroke" && Array.isArray(obj["points"])) {
|
|
271
329
|
for (const pt of obj["points"]) {
|
|
272
|
-
if (
|
|
330
|
+
if (pt["pressure"] === void 0) {
|
|
273
331
|
pt["pressure"] = 0.5;
|
|
274
332
|
}
|
|
275
333
|
}
|
|
276
334
|
}
|
|
277
|
-
if (obj["type"] === "shape" &&
|
|
335
|
+
if (obj["type"] === "shape" && obj["shape"] === void 0) {
|
|
278
336
|
obj["shape"] = "rectangle";
|
|
279
337
|
}
|
|
280
|
-
if (obj["type"] === "note" &&
|
|
338
|
+
if (obj["type"] === "note" && obj["textColor"] === void 0) {
|
|
281
339
|
obj["textColor"] = "#000000";
|
|
282
340
|
}
|
|
283
341
|
if ((obj["type"] === "note" || obj["type"] === "text") && typeof obj["text"] === "string") {
|
|
284
342
|
obj["text"] = sanitizeNoteHtml(obj["text"]);
|
|
285
343
|
}
|
|
286
344
|
}
|
|
345
|
+
function isRecord(value) {
|
|
346
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
347
|
+
}
|
|
348
|
+
function isFiniteNumber(value) {
|
|
349
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
350
|
+
}
|
|
351
|
+
function isString(value) {
|
|
352
|
+
return typeof value === "string";
|
|
353
|
+
}
|
|
354
|
+
function isBoolean(value) {
|
|
355
|
+
return typeof value === "boolean";
|
|
356
|
+
}
|
|
357
|
+
function isOptional(value, validate) {
|
|
358
|
+
return value === void 0 || validate(value);
|
|
359
|
+
}
|
|
360
|
+
function isEnum(value, values) {
|
|
361
|
+
return typeof value === "string" && values.includes(value);
|
|
362
|
+
}
|
|
363
|
+
function isOptionalEnum(value, values) {
|
|
364
|
+
return value === void 0 || isEnum(value, values);
|
|
365
|
+
}
|
|
366
|
+
function isPoint(value) {
|
|
367
|
+
return isRecord(value) && isFiniteNumber(value["x"]) && isFiniteNumber(value["y"]);
|
|
368
|
+
}
|
|
369
|
+
function isSize(value) {
|
|
370
|
+
return isRecord(value) && isFiniteNumber(value["w"]) && isFiniteNumber(value["h"]);
|
|
371
|
+
}
|
|
372
|
+
function isStrokePoint(value) {
|
|
373
|
+
return isRecord(value) && isPoint(value) && isFiniteNumber(value["pressure"]);
|
|
374
|
+
}
|
|
375
|
+
function isBinding(value) {
|
|
376
|
+
return isRecord(value) && typeof value["elementId"] === "string";
|
|
377
|
+
}
|
|
287
378
|
|
|
288
379
|
// src/core/storage/local-storage-adapter.ts
|
|
289
380
|
var LocalStorageAdapter = class {
|
|
@@ -6415,7 +6506,7 @@ var RenderLoop = class {
|
|
|
6415
6506
|
layerGroups = /* @__PURE__ */ new Map();
|
|
6416
6507
|
gridCacheCanvas = null;
|
|
6417
6508
|
gridCacheCtx = null;
|
|
6418
|
-
|
|
6509
|
+
lastGridRef = null;
|
|
6419
6510
|
constructor(deps) {
|
|
6420
6511
|
this.canvasEl = deps.canvasEl;
|
|
6421
6512
|
this.camera = deps.camera;
|
|
@@ -6615,8 +6706,8 @@ var RenderLoop = class {
|
|
|
6615
6706
|
}
|
|
6616
6707
|
if (gridElements.length > 0) {
|
|
6617
6708
|
const gridT0 = performance.now();
|
|
6618
|
-
const
|
|
6619
|
-
const gridDirty = this.gridCacheDirty ||
|
|
6709
|
+
const gridRef = gridElements[0];
|
|
6710
|
+
const gridDirty = this.gridCacheDirty || gridRef !== this.lastGridRef;
|
|
6620
6711
|
if (gridDirty) {
|
|
6621
6712
|
this.ensureGridCache();
|
|
6622
6713
|
if (this.gridCacheCtx && this.gridCacheCanvas) {
|
|
@@ -6644,7 +6735,7 @@ var RenderLoop = class {
|
|
|
6644
6735
|
}
|
|
6645
6736
|
}
|
|
6646
6737
|
this.gridCacheDirty = false;
|
|
6647
|
-
this.
|
|
6738
|
+
this.lastGridRef = gridRef;
|
|
6648
6739
|
}
|
|
6649
6740
|
if (this.gridCacheCanvas) {
|
|
6650
6741
|
const offset = this.marginViewport.compositeOffset(
|
|
@@ -6664,8 +6755,6 @@ var RenderLoop = class {
|
|
|
6664
6755
|
}
|
|
6665
6756
|
}
|
|
6666
6757
|
gridMs = performance.now() - gridT0;
|
|
6667
|
-
} else {
|
|
6668
|
-
this.lastGridRefs = [];
|
|
6669
6758
|
}
|
|
6670
6759
|
const overlayT0 = performance.now();
|
|
6671
6760
|
const activeTool = this.toolManager.activeTool;
|
|
@@ -10639,7 +10728,7 @@ var LaserTool = class {
|
|
|
10639
10728
|
};
|
|
10640
10729
|
|
|
10641
10730
|
// src/index.ts
|
|
10642
|
-
var VERSION = "0.50.
|
|
10731
|
+
var VERSION = "0.50.8";
|
|
10643
10732
|
export {
|
|
10644
10733
|
ArrowTool,
|
|
10645
10734
|
AutoSave,
|