diff --git a/src/index.html b/src/index.html
index add6ef1..6808551 100644
--- a/src/index.html
+++ b/src/index.html
@@ -9999,23 +9999,39 @@
if (p.x > maxX) maxX = p.x;
if (p.y > maxY) maxY = p.y;
}
- // No-op unless we're outside [0, 100] on any side. A bit of
- // tolerance to avoid micro-jiggle during normal in-bounds drags.
+ // Layer viewBox is 0-100 × 0-vbH where vbH = 100*layer.h/layer.w
+ // — the renderer's hard-coded formula. We want nodes to fit
+ // x:[0,100] and y:[0,vbH], matching the layer's actual aspect
+ // ratio after refit. The current viewBox-y bound is implied
+ // by what the renderer would compute given new layer.w/h.
+ //
+ // No-op unless we're outside the current viewBox on any side
+ // (with a small tolerance to avoid micro-jiggle during normal
+ // in-bounds drags). The current viewBox y-max is `vbH` computed
+ // from existing layer w/h.
+ const currentVbH = layer.w > 0 ? 100 * layer.h / layer.w : 100;
const EPS = 0.5;
- if (minX > -EPS && minY > -EPS && maxX < 100 + EPS && maxY < 100 + EPS) return;
+ if (minX > -EPS && minY > -EPS && maxX < 100 + EPS && maxY < currentVbH + EPS) return;
const newW = Math.max(EPS, maxX - minX);
const newH = Math.max(EPS, maxY - minY);
+ // Layer scales: in inches, layer.w grows in proportion to
+ // newW vs current viewBox-x (always 100); layer.h grows in
+ // proportion to newH vs the existing vbH.
const sx = newW / 100;
- const sy = newH / 100;
+ const sy = newH / currentVbH;
// Layer position shifts in inches by the bbox offset scaled
// to the existing layer w/h.
layer.x = layer.x + (minX / 100) * layer.w;
- layer.y = layer.y + (minY / 100) * layer.h;
+ layer.y = layer.y + (minY / currentVbH) * layer.h;
layer.w = Math.max(0.01, layer.w * sx);
layer.h = Math.max(0.01, layer.h * sy);
+ // Uniform-scale remap so the new node range matches the new
+ // viewBox aspect exactly — no stretching when the SVG paints
+ // into the refitted layer rectangle.
+ const remapScale = 100 / newW;
const remap = (p) => ({
- x: (p.x - minX) / newW * 100,
- y: (p.y - minY) / newH * 100,
+ x: (p.x - minX) * remapScale,
+ y: (p.y - minY) * remapScale,
});
for (const n of nodes) {
const r = remap(n);
@@ -10113,8 +10129,13 @@
clearPenDraftRender();
const dims = currentCanvasDims();
const unit = Math.max(dims.w, dims.h);
- const lineW = unit * 0.003;
- const dotR = unit * 0.013;
+ // Smaller, less prominent anchors/handles — earlier sizes
+ // (0.003 stroke, 0.013 anchor) rendered ~25px dots on a
+ // 1920px-wide preview, which obscured short paths and made
+ // the canvas feel cluttered. ~50% smaller reads as deliberate
+ // editing UI without dominating.
+ const lineW = unit * 0.0018;
+ const dotR = unit * 0.0075;
const nodes = layer.shape.pen.nodes;
nodes.forEach((n, i) => {
const a = penNodeToInch(layer, n);
@@ -10469,7 +10490,14 @@
if (n.hOut) allPts.push(n.hOut);
}
const bbox = pointsBbox(allPts);
- const remap = (pt) => ({ x: (pt.x - bbox.x) / bbox.w * 100, y: (pt.y - bbox.y) / bbox.h * 100 });
+ // Uniform scaling: divide both x and y by bbox.w so nodes land
+ // in 0-100 × 0-(100*h/w) — matching the renderer's vbH formula
+ // (= 100 * layer.h / layer.w). Independent x/y scaling here
+ // forces a 0-100×0-100 node range into a non-square viewBox
+ // later, stretching curves and varying stroke widths around
+ // the path. Uniform scaling keeps strokes a constant width.
+ const scale = bbox.w > 0 ? 100 / bbox.w : 1;
+ const remap = (pt) => ({ x: (pt.x - bbox.x) * scale, y: (pt.y - bbox.y) * scale });
const normNodes = nodes.map(n => ({
x: remap(n).x, y: remap(n).y,
hIn: n.hIn ? remap(n.hIn) : null,
@@ -10515,8 +10543,10 @@
clearPenDraftRender();
const dims = currentCanvasDims();
const unit = Math.max(dims.w, dims.h);
- const lineW = unit * 0.004;
- const dotR = unit * 0.012;
+ // Sized to match renderPenEditHandles so the in-progress
+ // draw and the post-commit edit handles look consistent.
+ const lineW = unit * 0.0025;
+ const dotR = unit * 0.0075;
if (_penDraft.tool === 'pencil') {
const pts = _penDraft.points;