screengraft 0.63.6 → 0.63.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/package.json +1 -1
- package/ui/index.html +19 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screengraft",
|
|
3
|
-
"version": "0.63.
|
|
3
|
+
"version": "0.63.8",
|
|
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
|
@@ -667,8 +667,15 @@
|
|
|
667
667
|
display:inline-flex;line-height:20px}
|
|
668
668
|
#detSt:empty,#outSt:empty{display:none}
|
|
669
669
|
.lbl{color:var(--ink);text-transform:uppercase;letter-spacing:.06em;font-size:11px;font-weight:600}
|
|
670
|
+
/* overscroll-behavior:none on both wells (14 Sep 2026): a trackpad pinch is a
|
|
671
|
+
wheel event we zoom on, but macOS still owns the gesture's elastic bounce.
|
|
672
|
+
While the fingers were moving the browser let the well rubber-band past
|
|
673
|
+
the picture's bottom edge, so the last rows of the photo sat under a band
|
|
674
|
+
of well for as long as the gesture lasted -- the "photo cropped at the
|
|
675
|
+
bottom on zoom" report of 14 Sep 2026, seen in both panes. Not
|
|
676
|
+
reproducible from script, because scripted scrolls clamp. */
|
|
670
677
|
.scroller{overflow:auto;background:var(--well);display:grid;
|
|
671
|
-
min-height:0;min-width:0;
|
|
678
|
+
min-height:0;min-width:0;overscroll-behavior:none;
|
|
672
679
|
box-shadow:inset 0 1px 3px rgba(0,0,0,.5)}
|
|
673
680
|
/* tabindex -1, not 0: the canvas is not a tab stop — it takes focus only when
|
|
674
681
|
you actually click it, which is what makes space mean PAN rather than
|
|
@@ -785,7 +792,7 @@
|
|
|
785
792
|
button.switch:focus-visible .sw-track{box-shadow:0 0 0 3px var(--acc-soft);border-color:var(--acc)}
|
|
786
793
|
|
|
787
794
|
#outPane{display:grid}
|
|
788
|
-
#outWrap{overflow:auto;background:var(--well);display:grid;box-shadow:inset 0 1px 3px rgba(0,0,0,.5)}
|
|
795
|
+
#outWrap{overflow:auto;overscroll-behavior:none;background:var(--well);display:grid;box-shadow:inset 0 1px 3px rgba(0,0,0,.5)}
|
|
789
796
|
/* The depth-of-field gizmo: an SVG laid exactly over the composite, drawn
|
|
790
797
|
in PHOTO pixels (its viewBox is the photo) so every coordinate is the
|
|
791
798
|
engine's own. Always at full opacity: the fade-when-idle was tried and
|
|
@@ -2565,15 +2572,20 @@ function ringColour(rgb){ let b = rgb.map(v => v/255);
|
|
|
2565
2572
|
return 'rgb(' + b.map(v => Math.round(Math.max(0, Math.min(1, v))*255)).join(',') + ')'; }
|
|
2566
2573
|
// One small copy of each pane's picture to sample from (a readback per draw,
|
|
2567
2574
|
// not per pixel). Fit pane: the canvas; result pane: the composite <img>.
|
|
2575
|
+
const WELL_RGB = [10,10,11]; // --well, what is under a rail that is not over the picture
|
|
2568
2576
|
const _sampleCv = document.createElement('canvas'), _sampleCx = _sampleCv.getContext('2d', {willReadFrequently: true});
|
|
2569
2577
|
function _backdropSampler(source, rect){
|
|
2570
2578
|
const W = 256, H = Math.max(1, Math.round(256 * rect.height / Math.max(rect.width, 1)));
|
|
2571
2579
|
_sampleCv.width = W; _sampleCv.height = H;
|
|
2572
2580
|
try { _sampleCx.drawImage(source, 0, 0, W, H); } catch (e) { return null; }
|
|
2573
2581
|
const data = _sampleCx.getImageData(0, 0, W, H).data;
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2582
|
+
// A point outside the picture is over the well, not over the picture's
|
|
2583
|
+
// nearest edge -- clamping put the left rail on the photo's bright edge
|
|
2584
|
+
// column and painted it light (Safari, 14 Sep 2026).
|
|
2585
|
+
return (x, y) => { if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) return WELL_RGB;
|
|
2586
|
+
const px = Math.max(0, Math.min(W-1, Math.floor((x - rect.left) * W / rect.width)));
|
|
2587
|
+
const py = Math.max(0, Math.min(H-1, Math.floor((y - rect.top) * H / rect.height)));
|
|
2588
|
+
const i = (py*W + px) * 4; return data[i+3] ? [data[i], data[i+1], data[i+2]] : WELL_RGB; };
|
|
2577
2589
|
}
|
|
2578
2590
|
function _sideColours(sample, r, out){
|
|
2579
2591
|
const N = 9, sides = {top: [], right: [], bottom: [], left: []};
|
|
@@ -2581,7 +2593,7 @@ function _sideColours(sample, r, out){
|
|
|
2581
2593
|
sides.top.push(sample(r.left + r.width*t, r.top - 1)); sides.bottom.push(sample(r.left + r.width*t, r.bottom + 1));
|
|
2582
2594
|
sides.left.push(sample(r.left - 1, r.top + r.height*t)); sides.right.push(sample(r.right + 1, r.top + r.height*t)); }
|
|
2583
2595
|
for (const [side, pts] of Object.entries(sides)){ const ok = pts.filter(Boolean);
|
|
2584
|
-
const avg = ok.length ? [0,1,2].map(i => ok.reduce((a, p) => a + p[i], 0) / ok.length) :
|
|
2596
|
+
const avg = ok.length ? [0,1,2].map(i => ok.reduce((a, p) => a + p[i], 0) / ok.length) : WELL_RGB;
|
|
2585
2597
|
out.style['border' + side[0].toUpperCase() + side.slice(1) + 'Color'] = ringColour(avg); }
|
|
2586
2598
|
}
|
|
2587
2599
|
let _ringsQueued = false;
|
|
@@ -2597,7 +2609,7 @@ function paintRingsFallback(){
|
|
|
2597
2609
|
const sample = ok && rect.width > 0 ? _backdropSampler(src, rect) : null;
|
|
2598
2610
|
pane.querySelectorAll('.bgroup > .ring.lift, .pillwrap > .ring.lift').forEach(ring => {
|
|
2599
2611
|
const r = ring.parentElement.getBoundingClientRect();
|
|
2600
|
-
if (sample) _sideColours(sample, r, ring); else ['Top','Right','Bottom','Left'].forEach(sd => ring.style['border' + sd + 'Color'] = ringColour(
|
|
2612
|
+
if (sample) _sideColours(sample, r, ring); else ['Top','Right','Bottom','Left'].forEach(sd => ring.style['border' + sd + 'Color'] = ringColour(WELL_RGB));
|
|
2601
2613
|
});
|
|
2602
2614
|
}
|
|
2603
2615
|
});
|