@littlepartytime/dev-kit 1.18.2 → 1.18.4

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.
@@ -46,6 +46,7 @@ export default function PhoneFrame({ children }: { children: React.ReactNode })
46
46
  <div style={{ width: BODY_W * scale, height: BODY_H * scale, flexShrink: 0 }}>
47
47
  {/* Phone body at original pixel size, visually scaled */}
48
48
  <div
49
+ id="devkit-phone"
49
50
  style={{
50
51
  position: 'relative',
51
52
  width: BODY_W,
@@ -1,55 +1,50 @@
1
1
  import html2canvas from 'html2canvas';
2
2
 
3
- // Must match PhoneFrame constants: SCREEN_W=390, SAFE_AREA_TOP=59, SAFE_AREA_BOTTOM=34, SCREEN_H=844
4
- const CAPTURE_W = 390;
5
- const CAPTURE_H = 751; // 844 - 59 - 34
3
+ // PhoneFrame body width (bezel included): 390 + 8*2
4
+ const PHONE_W = 406;
6
5
 
7
6
  /**
8
- * Captures the game content area (safe area only, no phone bezel).
9
- * Returns a base64-encoded PNG data URL.
7
+ * Captures the phone model (game content + bezel frame) as a PNG.
8
+ * Returns a base64-encoded data URL.
10
9
  *
11
10
  * Also exposed on window.__devkit__.captureScreen() for LLM/Playwright callers:
12
11
  * await page.evaluate(() => window.__devkit__.captureScreen())
13
12
  *
14
- * ## Why onclone?
15
- * html2canvas clones the entire document before rendering. The PhoneFrame DOM
16
- * has three CSS properties that break the capture:
13
+ * ## Strategy: capture from documentElement with visual crop
17
14
  *
18
- * - `contain: paint` on Screen div and safe-area div — html2canvas uses
19
- * getBoundingClientRect() to compute clip rects, which returns the *visual*
20
- * (post-transform) size, clipping the output to ~half height.
21
- * - `transform: scale(x)` on the phone bodysame getBoundingClientRect
22
- * mismatch; intrinsic size is 390×751 but visual rect is smaller.
23
- * - `overflow: hidden` on ancestors — once transform is removed, the full-size
24
- * phone body overflows its scaled wrapper and gets clipped.
15
+ * The phone body lives inside transform:scale(x) + contain:paint, which makes
16
+ * its getBoundingClientRect() return the *visual* (scaled) size. Previous
17
+ * attempts to target the game element directly all failed because html2canvas
18
+ * reads the target's bounding rect first, then clones the crop coordinates
19
+ * never agree with the clone's layout.
25
20
  *
26
- * Fix: in the `onclone` callback, neutralise all three properties on ancestor
27
- * elements of the *cloned* document. The original DOM is never touched.
21
+ * By targeting document.documentElement and passing the phone body's visual
22
+ * bounding rect as an explicit crop, html2canvas renders the full page as-is
23
+ * (all CSS respected) and simply cuts out the phone region. Crop coordinates
24
+ * equal visual coordinates — no mismatch possible.
28
25
  */
29
26
  export async function captureScreen(): Promise<string> {
30
- const el = document.getElementById('devkit-game-screen');
31
- if (!el) throw new Error('[devkit] #devkit-game-screen not found — is the Preview page active?');
27
+ const phone = document.getElementById('devkit-phone');
28
+ if (!phone) throw new Error('[devkit] #devkit-phone not found — is the Preview page active?');
32
29
 
33
- const canvas = await html2canvas(el, {
34
- width: CAPTURE_W,
35
- height: CAPTURE_H,
36
- scale: 2,
30
+ const rect = phone.getBoundingClientRect();
31
+ if (rect.width === 0 || rect.height === 0) {
32
+ throw new Error('[devkit] phone frame has zero dimensions');
33
+ }
34
+
35
+ // Upsample so the output matches the phone's natural size at 2× retina.
36
+ const outputScale = (PHONE_W * 2) / rect.width;
37
+
38
+ const canvas = await html2canvas(document.documentElement, {
39
+ x: rect.left + window.scrollX,
40
+ y: rect.top + window.scrollY,
41
+ width: rect.width,
42
+ height: rect.height,
43
+ scale: outputScale,
37
44
  logging: false,
38
45
  backgroundColor: null,
39
46
  useCORS: true,
40
47
  allowTaint: true,
41
- onclone: (clonedDoc: Document) => {
42
- const clonedEl = clonedDoc.getElementById('devkit-game-screen');
43
- if (!clonedEl) return;
44
-
45
- let node: HTMLElement | null = clonedEl;
46
- while (node && node !== clonedDoc.body) {
47
- node.style.contain = '';
48
- node.style.transform = 'none';
49
- node.style.overflow = 'visible';
50
- node = node.parentElement;
51
- }
52
- },
53
48
  });
54
49
 
55
50
  return canvas.toDataURL('image/png');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@littlepartytime/dev-kit",
3
- "version": "1.18.2",
3
+ "version": "1.18.4",
4
4
  "description": "Development toolkit CLI for Little Party Time game developers",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",