screengraft 0.62.4 → 0.63.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screengraft",
3
- "version": "0.62.4",
3
+ "version": "0.63.0",
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",
@@ -5,7 +5,7 @@ description: Injects a UI screenshot OR a screen recording onto a photographed d
5
5
 
6
6
  # Inject a screenshot onto a photographed device
7
7
 
8
- **What ships (v0.62):** a local browser UI (`scripts/ui.py`) that walks the designer through the whole job — pick the photo and the screen source, which may be an image **or a video** (the images you have used before, drag-drop, browse, path, or a **Figma frame link**), auto-detect the screen as a starting position — and when detection cannot tell which region is a screen, **Point at screen**: one click inside it and the detector uses that point — or, if this photograph has been fitted before, **the fit it was saved with comes back** as the starting position instead of a detection, recognised by the photo's own pixels so a rename or a drag-drop still match — and every save also writes a **portable `.fit.json` beside the mockup** that can be dropped back onto the page later, which is how a fit survives a re-export, another machine, or someone else's hands — then **match the four edges** (drag an edge's middle to slide it, near an end to pivot; corners still draggable) with canvas navigation that follows the usual conventions — **hold ⌘ and scroll to zoom to the pointer, hold space and drag to pan** — and a rectified strip loupe. The fit and the composite sit **side by side and always have** — the result pane re-renders as you drag, which is how a corner gets judged, so it is the layout rather than a mode you can switch off. Then an on-by-default realism pass that colour-matches the source to the photo's light, **Save** (or **Render**, for a video) into the project folder (`--out-dir`), and a **Send to Claude** button that reaches you through the plugin's own MCP server. The UI is a hand port of the project's Figma design file — dark only.
8
+ **What ships (v0.63):** a local browser UI (`scripts/ui.py`) that walks the designer through the whole job — pick the photo and the screen source, which may be an image **or a video** (the images you have used before, drag-drop, browse, path, or a **Figma frame link**), auto-detect the screen as a starting position — and when detection cannot tell which region is a screen, **Point at screen**: one click inside it and the detector uses that point — or, if this photograph has been fitted before, **the fit it was saved with comes back** as the starting position instead of a detection, recognised by the photo's own pixels so a rename or a drag-drop still match — and every save also writes a **portable `.fit.json` beside the mockup** that can be dropped back onto the page later, which is how a fit survives a re-export, another machine, or someone else's hands — then **match the four edges** (drag an edge's middle to slide it, near an end to pivot; corners still draggable) with canvas navigation that follows the usual conventions — **hold ⌘ and scroll to zoom to the pointer, hold space and drag to pan** — and a rectified strip loupe. The fit and the composite sit **side by side and always have** — the result pane re-renders as you drag, which is how a corner gets judged, so it is the layout rather than a mode you can switch off. Then an on-by-default realism pass that colour-matches the source to the photo's light, **Save** (or **Render**, for a video) into the project folder (`--out-dir`), and a **Send to Claude** button that reaches you through the plugin's own MCP server. The UI is a hand port of the project's Figma design file — dark only.
9
9
 
10
10
  The geometry is exact (`warp.py`); the detection is advisory (`detect.py`) and the human corrects it. **When a detection is wrong and you want to know why**, ask for the candidate list: `POST /api/detect {"trace": true}` writes `<session>/candidates.json`, or run `python3 scripts/detect.py --photo P --out-corners /tmp/c.json --trace /tmp/t.json` (add `--click X,Y`). Every candidate quad is in there with its score and whether it was accepted, rejected, never reached, or filtered out by the click — which is what separates "the screen was never proposed" from "it was proposed and something else won".
11
11
 
package/ui/index.html CHANGED
@@ -88,8 +88,10 @@
88
88
  /* button-group/default/border — a 20% cool grey, the ONLY translucent
89
89
  stroke in the system; it sits over the photograph, so it has to read on
90
90
  any tone. The two shadows are the Button Group's and the Status pill's. */
91
- --group-edge:rgba(194,195,203,.2);
92
- --group-shadow:0 2px 4px rgba(0,0,0,.03), 0 3px 10px rgba(0,0,0,.08), 0 4px 24px rgba(0,0,0,.25);
91
+ /* Re-read from the Canvas well frame (7:36) on 14 Sep 2026: a 55% mid
92
+ grey stroke and a slightly heavier shadow than the first read. */
93
+ --group-edge:rgba(139,139,139,.55);
94
+ --group-shadow:0 2px 4px rgba(0,0,0,.08), 0 3px 10px rgba(0,0,0,.12), 0 4px 24px rgba(0,0,0,.25);
93
95
  --pill-shadow:0 2px 2px rgba(0,0,0,.03), 0 3px 5px rgba(0,0,0,.08), 0 4px 12px rgba(0,0,0,.25);
94
96
 
95
97
  --warn:#f0c375;
@@ -561,6 +563,7 @@
561
563
  .bgroup{display:inline-flex;flex-direction:column;align-items:stretch;flex:none;
562
564
  background:var(--raise);border:var(--bw) solid var(--group-edge);
563
565
  border-radius:var(--r-xs);box-shadow:var(--group-shadow);overflow:hidden}
566
+ .bgroup.row{flex-direction:row}
564
567
  .segbtn,.segbtn:hover,.segbtn:active{border:0;border-radius:0;box-shadow:none;transform:none}
565
568
  /* Segment Button re-read 14 Sep 2026: 32px tall, 32px minimum, 8px side
566
569
  padding; a text label sits in its own 6px-padded box, so a text button
@@ -574,10 +577,11 @@
574
577
  .segbtn[disabled],.segbtn[disabled]:hover{background:var(--raise-low);color:var(--mute)}
575
578
  .segbtn:focus-visible{outline-offset:-2px}
576
579
  .segbtn svg{width:16px;height:16px;display:block;flex:none}
577
- .lrail{position:absolute;left:24px;top:48px;z-index:3;display:flex;flex-direction:column;gap:var(--s2)}
580
+ .lrail{position:absolute;left:16px;top:48px;z-index:3;display:flex;flex-direction:column;gap:var(--s2)}
578
581
  .lrail .corners{margin-top:var(--s6)} /* 8 + 24 = the design's 32px between rails */
579
- .brail{position:absolute;left:0;right:0;bottom:8px;z-index:3;display:flex;justify-content:center;
580
- align-items:center;gap:4px;pointer-events:none;flex-wrap:wrap;padding:0 var(--s4)}
582
+ /* One horizontal group, centred, 20px off the bottom edge (7:36, 14 Sep). */
583
+ .brail{position:absolute;left:0;right:0;bottom:20px;z-index:3;display:flex;justify-content:center;
584
+ align-items:center;pointer-events:none;padding:0 var(--s4)}
581
585
  .brail .bgroup{pointer-events:auto}
582
586
  /* The status pill (Status pill 5:7 / 5:10): floats top-centre, its own
583
587
  recessed surface and the group stroke, hidden while it has nothing to say.
@@ -1154,15 +1158,18 @@
1154
1158
  when it lived over the canvas it slid out of view the moment you
1155
1159
  zoomed in. Anchored to the pane it stays put while the picture moves
1156
1160
  underneath, which is what a floating control should do. -->
1157
- <!-- Bottom rail (Figma 113:2851): the actions on the FIT as a whole, each
1158
- its own one-button group, centred. Corner selection moved to the left
1159
- rail — it is navigation, not an action on the fit. Rotate is not in
1160
- the design frame and is kept until it is ruled on. -->
1161
+ <!-- Bottom rail (Figma 7:36, 14 Sep): the actions on the FIT as a whole
1162
+ as ONE horizontal Button Group of four segments, centred 20px off the
1163
+ bottom. Corner selection lives on the left rail — it is navigation,
1164
+ not an action on the fit. The frame's last two segments are
1165
+ placeholders; Rotate and Reset fill them (SG103). -->
1161
1166
  <div class="brail" id="cvbar">
1162
- <div class="bgroup"><button class="segbtn txt" id="redetect">Re-detect</button></div>
1163
- <div class="bgroup"><button class="segbtn txt" id="pointat" title="Click once inside the screen and the detector will use that point. The detectors usually do find the screen — they just cannot tell which region IS one, and that is the part you can answer instantly.">Point at screen</button></div>
1164
- <div class="bgroup"><button class="segbtn txt" id="rotatequad" title="Turn the screenshot inside the same four edges. The edges stay where they are; only which one is the top changes. A portrait screenshot fits a phone only two ways, so on a phone this is a half turn; a quarter turn where the screen is near-square or the screenshot fits the other way.">Rotate</button></div>
1165
- <div class="bgroup"><button class="segbtn txt" id="resetquad" title="Put the four edges back to a rectangle in the middle of the photo. Use this if a corner has ended up off the picture where you cannot grab it.">Reset</button></div>
1167
+ <div class="bgroup row" role="group" aria-label="Fit actions">
1168
+ <button class="segbtn txt" id="redetect">Re-detect</button>
1169
+ <button class="segbtn txt" id="pointat" title="Click once inside the screen and the detector will use that point. The detectors usually do find the screen — they just cannot tell which region IS one, and that is the part you can answer instantly.">Point at screen</button>
1170
+ <button class="segbtn txt" id="rotatequad" title="Turn the screenshot inside the same four edges. The edges stay where they are; only which one is the top changes. A portrait screenshot fits a phone only two ways, so on a phone this is a half turn; a quarter turn where the screen is near-square or the screenshot fits the other way.">Rotate</button>
1171
+ <button class="segbtn txt" id="resetquad" title="Put the four edges back to a rectangle in the middle of the photo. Use this if a corner has ended up off the picture where you cannot grab it.">Reset</button>
1172
+ </div>
1166
1173
  </div>
1167
1174
  </section>
1168
1175