@fieldnotes/core 0.20.0 → 0.21.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/README.md +481 -472
- package/dist/index.cjs +86 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +86 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2719,6 +2719,7 @@ var ElementRenderer = class {
|
|
|
2719
2719
|
store = null;
|
|
2720
2720
|
imageCache = /* @__PURE__ */ new Map();
|
|
2721
2721
|
onImageLoad = null;
|
|
2722
|
+
onImageError = null;
|
|
2722
2723
|
camera = null;
|
|
2723
2724
|
canvasSize = null;
|
|
2724
2725
|
hexTileCache = null;
|
|
@@ -2729,6 +2730,9 @@ var ElementRenderer = class {
|
|
|
2729
2730
|
setOnImageLoad(callback) {
|
|
2730
2731
|
this.onImageLoad = callback;
|
|
2731
2732
|
}
|
|
2733
|
+
setOnImageError(callback) {
|
|
2734
|
+
this.onImageError = callback;
|
|
2735
|
+
}
|
|
2732
2736
|
setCamera(camera) {
|
|
2733
2737
|
this.camera = camera;
|
|
2734
2738
|
}
|
|
@@ -3087,6 +3091,10 @@ var ElementRenderer = class {
|
|
|
3087
3091
|
ctx.restore();
|
|
3088
3092
|
}
|
|
3089
3093
|
renderImage(ctx, image) {
|
|
3094
|
+
if (this.imageCache.get(image.src) === "failed") {
|
|
3095
|
+
this.renderImagePlaceholder(ctx, image);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3090
3098
|
const img = this.getImage(image.src);
|
|
3091
3099
|
if (!img) return;
|
|
3092
3100
|
ctx.drawImage(
|
|
@@ -3097,6 +3105,27 @@ var ElementRenderer = class {
|
|
|
3097
3105
|
image.size.h
|
|
3098
3106
|
);
|
|
3099
3107
|
}
|
|
3108
|
+
renderImagePlaceholder(ctx, image) {
|
|
3109
|
+
const { x, y } = image.position;
|
|
3110
|
+
const { w, h } = image.size;
|
|
3111
|
+
ctx.save();
|
|
3112
|
+
ctx.fillStyle = "#eeeeee";
|
|
3113
|
+
ctx.fillRect(x, y, w, h);
|
|
3114
|
+
ctx.strokeStyle = "#bdbdbd";
|
|
3115
|
+
ctx.lineWidth = 1;
|
|
3116
|
+
ctx.strokeRect(x, y, w, h);
|
|
3117
|
+
const glyph = Math.min(24, w / 2, h / 2);
|
|
3118
|
+
const cx = x + w / 2;
|
|
3119
|
+
const cy = y + h / 2;
|
|
3120
|
+
ctx.strokeStyle = "#9e9e9e";
|
|
3121
|
+
ctx.lineWidth = 2;
|
|
3122
|
+
ctx.beginPath();
|
|
3123
|
+
ctx.arc(cx, cy, glyph / 2, 0, Math.PI * 2);
|
|
3124
|
+
ctx.moveTo(cx - glyph / 2, cy + glyph / 2);
|
|
3125
|
+
ctx.lineTo(cx + glyph / 2, cy - glyph / 2);
|
|
3126
|
+
ctx.stroke();
|
|
3127
|
+
ctx.restore();
|
|
3128
|
+
}
|
|
3100
3129
|
getHexTile(cellSize, orientation, strokeColor, strokeWidth, opacity, scale) {
|
|
3101
3130
|
const key = `${cellSize}:${orientation}:${strokeColor}:${strokeWidth}:${opacity}:${scale}`;
|
|
3102
3131
|
if (this.hexTileCacheKey === key && this.hexTileCache) {
|
|
@@ -3112,6 +3141,7 @@ var ElementRenderer = class {
|
|
|
3112
3141
|
getImage(src) {
|
|
3113
3142
|
const cached = this.imageCache.get(src);
|
|
3114
3143
|
if (cached) {
|
|
3144
|
+
if (cached === "failed") return null;
|
|
3115
3145
|
if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
|
|
3116
3146
|
return cached;
|
|
3117
3147
|
}
|
|
@@ -3128,6 +3158,11 @@ var ElementRenderer = class {
|
|
|
3128
3158
|
});
|
|
3129
3159
|
}
|
|
3130
3160
|
};
|
|
3161
|
+
img.onerror = () => {
|
|
3162
|
+
this.imageCache.set(src, "failed");
|
|
3163
|
+
this.onImageError?.(src);
|
|
3164
|
+
this.onImageLoad?.();
|
|
3165
|
+
};
|
|
3131
3166
|
return null;
|
|
3132
3167
|
}
|
|
3133
3168
|
};
|
|
@@ -5039,6 +5074,17 @@ var Viewport = class {
|
|
|
5039
5074
|
this.renderLoop.markAllLayersDirty();
|
|
5040
5075
|
this.requestRender();
|
|
5041
5076
|
});
|
|
5077
|
+
this.renderer.setOnImageError((src) => {
|
|
5078
|
+
const elementIds = [];
|
|
5079
|
+
for (const el of this.store.getAll()) {
|
|
5080
|
+
if (el.type === "image" && el.src === src) elementIds.push(el.id);
|
|
5081
|
+
}
|
|
5082
|
+
if (options.onImageError) {
|
|
5083
|
+
options.onImageError({ src, elementIds });
|
|
5084
|
+
} else {
|
|
5085
|
+
console.warn(`[fieldnotes] image failed to load: ${src}`);
|
|
5086
|
+
}
|
|
5087
|
+
});
|
|
5042
5088
|
this.noteEditor = new NoteEditor({
|
|
5043
5089
|
fontSizePresets: options.fontSizePresets,
|
|
5044
5090
|
toolbar: options.toolbar,
|
|
@@ -5743,6 +5789,43 @@ var PencilTool = class {
|
|
|
5743
5789
|
}
|
|
5744
5790
|
};
|
|
5745
5791
|
|
|
5792
|
+
// src/elements/stroke-hit.ts
|
|
5793
|
+
function distSqToSegment(p, a, b) {
|
|
5794
|
+
const abx = b.x - a.x;
|
|
5795
|
+
const aby = b.y - a.y;
|
|
5796
|
+
const apx = p.x - a.x;
|
|
5797
|
+
const apy = p.y - a.y;
|
|
5798
|
+
const lenSq = abx * abx + aby * aby;
|
|
5799
|
+
if (lenSq === 0) {
|
|
5800
|
+
return apx * apx + apy * apy;
|
|
5801
|
+
}
|
|
5802
|
+
const t = Math.max(0, Math.min(1, (apx * abx + apy * aby) / lenSq));
|
|
5803
|
+
const dx = p.x - (a.x + t * abx);
|
|
5804
|
+
const dy = p.y - (a.y + t * aby);
|
|
5805
|
+
return dx * dx + dy * dy;
|
|
5806
|
+
}
|
|
5807
|
+
function hitTestStroke(stroke, point, radius) {
|
|
5808
|
+
const bounds = getElementBounds(stroke);
|
|
5809
|
+
if (!bounds) return false;
|
|
5810
|
+
if (point.x < bounds.x - radius || point.x > bounds.x + bounds.w + radius || point.y < bounds.y - radius || point.y > bounds.y + bounds.h + radius) {
|
|
5811
|
+
return false;
|
|
5812
|
+
}
|
|
5813
|
+
const radiusSq = radius * radius;
|
|
5814
|
+
const local = { x: point.x - stroke.position.x, y: point.y - stroke.position.y };
|
|
5815
|
+
const { segments } = getStrokeRenderData(stroke);
|
|
5816
|
+
if (segments.length === 0) {
|
|
5817
|
+
const p = stroke.points[0];
|
|
5818
|
+
if (!p) return false;
|
|
5819
|
+
const dx = p.x - local.x;
|
|
5820
|
+
const dy = p.y - local.y;
|
|
5821
|
+
return dx * dx + dy * dy <= radiusSq;
|
|
5822
|
+
}
|
|
5823
|
+
for (const seg of segments) {
|
|
5824
|
+
if (distSqToSegment(local, seg.start, seg.end) <= radiusSq) return true;
|
|
5825
|
+
}
|
|
5826
|
+
return false;
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5746
5829
|
// src/tools/eraser-tool.ts
|
|
5747
5830
|
var DEFAULT_RADIUS = 20;
|
|
5748
5831
|
function makeEraserCursor(radius) {
|
|
@@ -5801,12 +5884,7 @@ var EraserTool = class {
|
|
|
5801
5884
|
if (erased) ctx.requestRender();
|
|
5802
5885
|
}
|
|
5803
5886
|
strokeIntersects(stroke, point) {
|
|
5804
|
-
|
|
5805
|
-
return stroke.points.some((p) => {
|
|
5806
|
-
const dx = p.x + stroke.position.x - point.x;
|
|
5807
|
-
const dy = p.y + stroke.position.y - point.y;
|
|
5808
|
-
return dx * dx + dy * dy <= radiusSq;
|
|
5809
|
-
});
|
|
5887
|
+
return hitTestStroke(stroke, point, this.radius);
|
|
5810
5888
|
}
|
|
5811
5889
|
};
|
|
5812
5890
|
|
|
@@ -6486,12 +6564,7 @@ var SelectTool = class {
|
|
|
6486
6564
|
return point.x >= el.position.x && point.x <= el.position.x + s.w && point.y >= el.position.y && point.y <= el.position.y + s.h;
|
|
6487
6565
|
}
|
|
6488
6566
|
if (el.type === "stroke") {
|
|
6489
|
-
|
|
6490
|
-
return el.points.some((p) => {
|
|
6491
|
-
const dx = p.x + el.position.x - point.x;
|
|
6492
|
-
const dy = p.y + el.position.y - point.y;
|
|
6493
|
-
return dx * dx + dy * dy <= HIT_RADIUS * HIT_RADIUS;
|
|
6494
|
-
});
|
|
6567
|
+
return hitTestStroke(el, point, 10);
|
|
6495
6568
|
}
|
|
6496
6569
|
if (el.type === "arrow") {
|
|
6497
6570
|
return isNearBezier(point, el.from, el.to, el.bend, 10);
|
|
@@ -7327,7 +7400,7 @@ var TemplateTool = class {
|
|
|
7327
7400
|
};
|
|
7328
7401
|
|
|
7329
7402
|
// src/index.ts
|
|
7330
|
-
var VERSION = "0.
|
|
7403
|
+
var VERSION = "0.21.0";
|
|
7331
7404
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7332
7405
|
0 && (module.exports = {
|
|
7333
7406
|
AddElementCommand,
|