screengraft 0.54.2 → 0.54.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/ui/index.html +22 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screengraft",
3
- "version": "0.54.2",
3
+ "version": "0.54.3",
4
4
  "description": "Put a UI screenshot or screen recording onto a photographed device screen with the perspective exactly right \u2014 a homography you confirm by hand, not a generative guess.",
5
5
  "keywords": [
6
6
  "mockup",
package/ui/index.html CHANGED
@@ -1472,14 +1472,29 @@ const recall = (k,d) => { try{ const v = localStorage.getItem('sg.'+k); return v
1472
1472
  The dark casing underneath is NOT optional and stays: flat accent measures
1473
1473
  ~1.06:1 against mid-tone photography, which is the failure the casing was
1474
1474
  introduced to fix. CORE_IDLE stays white for the strip's idle rule. */
1475
- const CASE_A = 'rgba(0,0,0,.62)', CORE_IDLE = 'rgba(255,255,255,.92)';
1475
+ const CASE_A = 'rgba(0,0,0,.45)', CORE_IDLE = 'rgba(255,255,255,.92)';
1476
1476
  const CORE_ACTIVE = '#f5623d';
1477
1477
  const CORE_QUAD = '#f23b0d';
1478
+ // Line weights for the fit overlay, in one place. Thinned 13 Sep 2026 on
1479
+ // request: quad 1 -> 0.8, active edge 2 -> 1.5, casing +2.5 -> +1.6 at 45%
1480
+ // black instead of 62%. The casing is still there -- it is the part that
1481
+ // survives an arbitrary photograph -- just lighter.
1482
+ // There is no native rotate cursor; this is a 22px circular arrow, white
1483
+ // with a dark outline so it reads on any photograph, hotspot at its centre.
1484
+ const ROTATE_CURSOR = 'url("data:image/svg+xml;utf8,' + encodeURIComponent(
1485
+ '<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">' +
1486
+ '<path d="M17.5 11a6.5 6.5 0 1 1-2.2-4.9" fill="none" stroke="rgba(0,0,0,.85)" stroke-width="4.2" stroke-linecap="round"/>' +
1487
+ '<path d="M17.5 11a6.5 6.5 0 1 1-2.2-4.9" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round"/>' +
1488
+ '<path d="M15.6 2.6l0.4 4.2-4.2 0.3" fill="none" stroke="rgba(0,0,0,.85)" stroke-width="4.2" stroke-linecap="round" stroke-linejoin="round"/>' +
1489
+ '<path d="M15.6 2.6l0.4 4.2-4.2 0.3" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>' +
1490
+ '</svg>') + '") 11 11, crosshair';
1491
+ const QUAD_W = 0.8, QUAD_ACTIVE_W = 1.5, GUIDE_W = 0.8, CASE_PAD = 1.6;
1492
+ const GUIDE_DASH = [10, 7];
1478
1493
  function cased(c, path, coreW, coreColor, dash){
1479
1494
  c.save();
1480
1495
  if (dash) c.setLineDash(dash);
1481
1496
  c.lineCap = 'round'; c.lineJoin = 'round';
1482
- c.strokeStyle = CASE_A; c.lineWidth = coreW + 2.5; path(); c.stroke();
1497
+ c.strokeStyle = CASE_A; c.lineWidth = coreW + CASE_PAD; path(); c.stroke();
1483
1498
  c.strokeStyle = coreColor; c.lineWidth = coreW; path(); c.stroke();
1484
1499
  c.restore();
1485
1500
  }
@@ -1488,8 +1503,8 @@ function cased(c, path, coreW, coreColor, dash){
1488
1503
  readability guarantee that makes markers survive an arbitrary photograph. */
1489
1504
  function casedDisc(c, x, y, r, coreColor){
1490
1505
  c.save();
1491
- c.strokeStyle = CASE_A; c.lineWidth = 2.5;
1492
- c.beginPath(); c.arc(x, y, r + 0.75, 0, Math.PI*2); c.stroke();
1506
+ c.strokeStyle = CASE_A; c.lineWidth = 1.8;
1507
+ c.beginPath(); c.arc(x, y, r + 0.6, 0, Math.PI*2); c.stroke();
1493
1508
  c.fillStyle = coreColor;
1494
1509
  c.beginPath(); c.arc(x, y, r, 0, Math.PI*2); c.fill();
1495
1510
  c.restore();
@@ -2285,7 +2300,7 @@ function draw(){
2285
2300
  const a=P[active.i], b=P[(active.i+1)%4], d=unit(sub(b,a)), big=4000;
2286
2301
  cased(ctx, () => { ctx.beginPath();
2287
2302
  ctx.moveTo(a[0]-d[0]*big, a[1]-d[1]*big); ctx.lineTo(b[0]+d[0]*big, b[1]+d[1]*big); },
2288
- 0.8, CORE_ACTIVE, [2,6]);
2303
+ GUIDE_W, CORE_ACTIVE, GUIDE_DASH);
2289
2304
  }
2290
2305
 
2291
2306
  for (let i=0;i<4;i++){
@@ -2300,7 +2315,7 @@ function draw(){
2300
2315
  const s1 = add(a, mul(d, Math.max(0, L - gap)));
2301
2316
  if (L > 1){
2302
2317
  cased(ctx, () => { ctx.beginPath(); ctx.moveTo(s0[0],s0[1]); ctx.lineTo(s1[0],s1[1]); },
2303
- on ? 2 : 1, CORE_QUAD);
2318
+ on ? QUAD_ACTIVE_W : QUAD_W, CORE_QUAD);
2304
2319
  }
2305
2320
  // Pivot pips on the active edge keep their meaning: grab near an end to
2306
2321
  // rotate. Filled, like the corners, but deliberately smaller — size is what
@@ -2382,7 +2397,7 @@ cv.onpointermove = e => {
2382
2397
  const h = hitTest(p);
2383
2398
  const changed = JSON.stringify(h) !== JSON.stringify(hover);
2384
2399
  hover = h;
2385
- cv.style.cursor = h ? (h.kind==='corner' ? 'grab' : (h.mode==='translate' ? 'move' : 'crosshair')) : 'default';
2400
+ cv.style.cursor = h ? (h.kind==='corner' ? 'move' : (h.mode==='translate' ? 'move' : ROTATE_CURSOR)) : 'default';
2386
2401
  if (changed){ draw(); drawStrip(); }
2387
2402
  return;
2388
2403
  }