waygraph 0.12.37 → 0.12.39
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/dist/cli.js
CHANGED
|
@@ -712,6 +712,7 @@ async function installOverlay(page, title) {
|
|
|
712
712
|
if (!cursor) return;
|
|
713
713
|
const travel = ms || 600;
|
|
714
714
|
window.__wgCursorPos = { x: Number(x) || 0, y: Number(y) || 0 };
|
|
715
|
+
window.__wgCursorTravelUntil = Date.now() + (instant ? 0 : travel) + 40;
|
|
715
716
|
cursor.style.setProperty("--wg-cursor-ms", travel + "ms");
|
|
716
717
|
if (instant) {
|
|
717
718
|
const prev = cursor.style.transition;
|
|
@@ -812,10 +813,8 @@ async function installOverlay(page, title) {
|
|
|
812
813
|
let settled = zoom <= 1;
|
|
813
814
|
const settleAt = Date.now() + camMs;
|
|
814
815
|
const style = { size, weight, focus, color };
|
|
815
|
-
// Screen Studio / Screenify: dead zone
|
|
816
|
-
|
|
817
|
-
// on every mousemove (that fights like same-pole magnets).
|
|
818
|
-
const deadFrac = 0.4;
|
|
816
|
+
// Screen Studio / Screenify: dead zone follow (see followTick).
|
|
817
|
+
const deadFrac = 0.5;
|
|
819
818
|
let lastPanAt = 0;
|
|
820
819
|
const readCursorViewport = () => {
|
|
821
820
|
const cursor = document.getElementById("wg-cursor");
|
|
@@ -833,6 +832,11 @@ async function installOverlay(page, title) {
|
|
|
833
832
|
if (!window.__wgSetCamera || !window.__wgViewportToDoc) return;
|
|
834
833
|
const cam = window.__wgCamera;
|
|
835
834
|
if (!cam || !(cam.scale > 1.001)) return;
|
|
835
|
+
// Skip while the demo cursor is mid CSS travel - reading a
|
|
836
|
+
// mid-lerp rect makes the camera chase noise and teleport.
|
|
837
|
+
if (window.__wgCursorTravelUntil && Date.now() < window.__wgCursorTravelUntil) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
836
840
|
const pos = readCursorViewport();
|
|
837
841
|
if (!pos) return;
|
|
838
842
|
const vw = window.innerWidth;
|
|
@@ -849,12 +853,13 @@ async function installOverlay(page, title) {
|
|
|
849
853
|
else if (dy < -deadY) oy = dy + deadY;
|
|
850
854
|
if (ox === 0 && oy === 0) return;
|
|
851
855
|
const now = Date.now();
|
|
852
|
-
if (now - lastPanAt <
|
|
856
|
+
if (now - lastPanAt < 120) return;
|
|
853
857
|
lastPanAt = now;
|
|
854
|
-
//
|
|
855
|
-
const
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
+
// Partial gain - never yank the full overflow in one pan.
|
|
859
|
+
const gain = 0.35;
|
|
860
|
+
const nx = cam.cx + (ox / cam.scale) * gain;
|
|
861
|
+
const ny = cam.cy + (oy / cam.scale) * gain;
|
|
862
|
+
window.__wgSetCamera(nx, ny, cam.scale, 280);
|
|
858
863
|
};
|
|
859
864
|
const tick = () => {
|
|
860
865
|
const el = document.querySelector(sel);
|
|
@@ -887,9 +892,13 @@ async function installOverlay(page, title) {
|
|
|
887
892
|
};
|
|
888
893
|
window.__wgCameraLock = true;
|
|
889
894
|
window.__wgSetCamera(pt.x, pt.y, zoom, dur);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
895
|
+
// Unlock for dead-zone follow only when explicitly allowed.
|
|
896
|
+
// zoomOut:false / cart-edge targets stay locked through dwell+click.
|
|
897
|
+
if (followMouse && opts.allowFollow) {
|
|
898
|
+
setTimeout(() => {
|
|
899
|
+
window.__wgCameraLock = false;
|
|
900
|
+
}, dur + 40);
|
|
901
|
+
}
|
|
893
902
|
}
|
|
894
903
|
}
|
|
895
904
|
window.__wgRingRaf = requestAnimationFrame(tick);
|
|
@@ -1413,6 +1422,25 @@ async function renderBeforeStep(page, info) {
|
|
|
1413
1422
|
|
|
1414
1423
|
async function ensureSelectorInView(page, selector) {
|
|
1415
1424
|
try {
|
|
1425
|
+
// Body-scale camera + scrollIntoView = teleport / "outside viewport" loop.
|
|
1426
|
+
// When zoomed, pan the camera to the target instead of scrolling the document.
|
|
1427
|
+
const zoomed = await page
|
|
1428
|
+
.evaluate(() => !!(window.__wgCamera && window.__wgCamera.scale > 1.001))
|
|
1429
|
+
.catch(() => false);
|
|
1430
|
+
if (zoomed) {
|
|
1431
|
+
await page
|
|
1432
|
+
.evaluate((sel) => {
|
|
1433
|
+
const el = document.querySelector(sel);
|
|
1434
|
+
if (!el || !window.__wgSetCamera || !window.__wgElDocPoint) return;
|
|
1435
|
+
const cam = window.__wgCamera;
|
|
1436
|
+
const pt = window.__wgElDocPoint(el);
|
|
1437
|
+
window.__wgCameraLock = true;
|
|
1438
|
+
window.__wgSetCamera(pt.x, pt.y, cam.scale > 1 ? cam.scale : 1.4, 320);
|
|
1439
|
+
}, selector)
|
|
1440
|
+
.catch(() => {});
|
|
1441
|
+
await new Promise((res) => setTimeout(res, 340));
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1416
1444
|
const loc = page.locator(selector).first();
|
|
1417
1445
|
await loc.scrollIntoViewIfNeeded().catch(() => {});
|
|
1418
1446
|
await page
|
|
@@ -1636,7 +1664,7 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1636
1664
|
// body scale (html cursor is outside the transform).
|
|
1637
1665
|
await page
|
|
1638
1666
|
.evaluate(
|
|
1639
|
-
({ sel, label, tone, size, weight, focus, color, zoom, followMouse, camMs }) => {
|
|
1667
|
+
({ sel, label, tone, size, weight, focus, color, zoom, followMouse, camMs, allowFollow }) => {
|
|
1640
1668
|
if (window.__wgStartHighlightTrack) {
|
|
1641
1669
|
window.__wgStartHighlightTrack({
|
|
1642
1670
|
sel,
|
|
@@ -1649,6 +1677,7 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1649
1677
|
zoom,
|
|
1650
1678
|
followMouse,
|
|
1651
1679
|
camMs,
|
|
1680
|
+
allowFollow,
|
|
1652
1681
|
});
|
|
1653
1682
|
}
|
|
1654
1683
|
},
|
|
@@ -1663,6 +1692,9 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1663
1692
|
zoom: z,
|
|
1664
1693
|
followMouse,
|
|
1665
1694
|
camMs,
|
|
1695
|
+
// Follow only while zoomed-out-between-rings is OK. zoomOut:false
|
|
1696
|
+
// means hold the frame for the upcoming click - no chase.
|
|
1697
|
+
allowFollow: followMouse && h.zoomOut !== false,
|
|
1666
1698
|
},
|
|
1667
1699
|
)
|
|
1668
1700
|
.catch(() => {});
|
|
@@ -1708,6 +1740,14 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1708
1740
|
const last = list.length > 0 ? list[list.length - 1] : null;
|
|
1709
1741
|
if (!last || last.zoomOut !== false) {
|
|
1710
1742
|
await resetCameraZoom(page);
|
|
1743
|
+
} else {
|
|
1744
|
+
// Hold lock into the upcoming act click so follow/scroll cannot yank
|
|
1745
|
+
// the camera off the already-centered target (cart teleport bug).
|
|
1746
|
+
await page
|
|
1747
|
+
.evaluate(() => {
|
|
1748
|
+
window.__wgCameraLock = true;
|
|
1749
|
+
})
|
|
1750
|
+
.catch(() => {});
|
|
1711
1751
|
}
|
|
1712
1752
|
if (list.length === 0) {
|
|
1713
1753
|
await hideRing(page);
|
|
@@ -2444,10 +2484,11 @@ async function teardownOverlay(page) {
|
|
|
2444
2484
|
.catch(() => {});
|
|
2445
2485
|
}
|
|
2446
2486
|
|
|
2447
|
-
async function moveCursorTo(page, box, ms) {
|
|
2487
|
+
async function moveCursorTo(page, box, ms, opts) {
|
|
2448
2488
|
const x = box.x + box.width / 2;
|
|
2449
2489
|
const y = box.y + box.height / 2;
|
|
2450
2490
|
const travel = Math.max(0, Number(ms) || 0);
|
|
2491
|
+
const keepLock = !!(opts && opts.keepLock);
|
|
2451
2492
|
// Freeze camera follow while the demo cursor travels - otherwise dead-zone
|
|
2452
2493
|
// pans fight the click target (esp. right-edge cart chrome).
|
|
2453
2494
|
await page
|
|
@@ -2462,11 +2503,13 @@ async function moveCursorTo(page, box, ms) {
|
|
|
2462
2503
|
if (travel > 0) {
|
|
2463
2504
|
await new Promise((res) => setTimeout(res, travel));
|
|
2464
2505
|
}
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2506
|
+
if (!keepLock) {
|
|
2507
|
+
await page
|
|
2508
|
+
.evaluate(() => {
|
|
2509
|
+
window.__wgCameraLock = false;
|
|
2510
|
+
})
|
|
2511
|
+
.catch(() => {});
|
|
2512
|
+
}
|
|
2470
2513
|
return { x, y };
|
|
2471
2514
|
}
|
|
2472
2515
|
|
|
@@ -2594,6 +2637,7 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2594
2637
|
proto.click = async function (options) {
|
|
2595
2638
|
let clickPoint = null;
|
|
2596
2639
|
let clickTone = "auto";
|
|
2640
|
+
let keptCameraLock = false;
|
|
2597
2641
|
try {
|
|
2598
2642
|
await installOverlay(page);
|
|
2599
2643
|
// Wait until the target is visible BEFORE measuring - otherwise
|
|
@@ -2604,6 +2648,20 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2604
2648
|
state: "visible",
|
|
2605
2649
|
timeout: (options && options.timeout) || 30000,
|
|
2606
2650
|
}).catch(() => {});
|
|
2651
|
+
// Body-scale camera + Playwright scrollIntoView = teleport storm.
|
|
2652
|
+
// Lock the camera for the whole click when zoomed; force-click so
|
|
2653
|
+
// Playwright does not try to scroll the transformed page.
|
|
2654
|
+
const zoomed = await page
|
|
2655
|
+
.evaluate(() => !!(window.__wgCamera && window.__wgCamera.scale > 1.001))
|
|
2656
|
+
.catch(() => false);
|
|
2657
|
+
if (zoomed) {
|
|
2658
|
+
await page
|
|
2659
|
+
.evaluate(() => {
|
|
2660
|
+
window.__wgCameraLock = true;
|
|
2661
|
+
})
|
|
2662
|
+
.catch(() => {});
|
|
2663
|
+
keptCameraLock = true;
|
|
2664
|
+
}
|
|
2607
2665
|
const box = await this.boundingBox();
|
|
2608
2666
|
const narrated = box ? await wasJustNarrated(page) : false;
|
|
2609
2667
|
if (box && !narrated) {
|
|
@@ -2625,8 +2683,18 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2625
2683
|
const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
2626
2684
|
const cap = await captionForLocator(page, this, stubs, fallback);
|
|
2627
2685
|
clickTone = cap.tone;
|
|
2628
|
-
|
|
2629
|
-
|
|
2686
|
+
// Already centered from stubBefore zoom: short cursor, keep lock.
|
|
2687
|
+
const travel = zoomed ? cursorMs(160) : cursorMs(600);
|
|
2688
|
+
clickPoint = await moveCursorTo(page, box, travel, {
|
|
2689
|
+
keepLock: zoomed,
|
|
2690
|
+
});
|
|
2691
|
+
// Re-measure after cursor settle - stale pre-travel box teleports the ring.
|
|
2692
|
+
const liveBox = (await this.boundingBox()) || box;
|
|
2693
|
+
clickPoint = {
|
|
2694
|
+
x: liveBox.x + liveBox.width / 2,
|
|
2695
|
+
y: liveBox.y + liveBox.height / 2,
|
|
2696
|
+
};
|
|
2697
|
+
await showRing(page, liveBox, cap.label, cap.tone, {
|
|
2630
2698
|
size: cap.size,
|
|
2631
2699
|
weight: cap.weight,
|
|
2632
2700
|
});
|
|
@@ -2638,7 +2706,10 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2638
2706
|
} else if (box) {
|
|
2639
2707
|
// Ring/caption already handled by narrate() - still move the
|
|
2640
2708
|
// cursor + pulse the click point, just skip re-showing the ring.
|
|
2641
|
-
|
|
2709
|
+
const travel = zoomed ? cursorMs(120) : cursorMs(200);
|
|
2710
|
+
clickPoint = await moveCursorTo(page, box, travel, {
|
|
2711
|
+
keepLock: zoomed,
|
|
2712
|
+
});
|
|
2642
2713
|
clickTone = "planned";
|
|
2643
2714
|
}
|
|
2644
2715
|
} catch {
|
|
@@ -2654,10 +2725,43 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2654
2725
|
// mock click visually completes before the real one fires.
|
|
2655
2726
|
await new Promise((res) => setTimeout(res, skipTheater() ? 0 : 500));
|
|
2656
2727
|
}
|
|
2657
|
-
const
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2728
|
+
const zoomedClick = await page
|
|
2729
|
+
.evaluate(() => !!(window.__wgCamera && window.__wgCamera.scale > 1.001))
|
|
2730
|
+
.catch(() => false);
|
|
2731
|
+
try {
|
|
2732
|
+
let result;
|
|
2733
|
+
if (zoomedClick || keptCameraLock) {
|
|
2734
|
+
// Playwright scrollIntoView + body-scale camera = "outside viewport"
|
|
2735
|
+
// retry storm. Click the element in-page with no scrolling.
|
|
2736
|
+
const handle = await this.elementHandle().catch(() => null);
|
|
2737
|
+
if (handle) {
|
|
2738
|
+
await handle.evaluate((el) => {
|
|
2739
|
+
if (el && typeof el.click === "function") el.click();
|
|
2740
|
+
});
|
|
2741
|
+
await handle.dispose().catch(() => {});
|
|
2742
|
+
result = undefined;
|
|
2743
|
+
} else {
|
|
2744
|
+
result = await originalClick.call(this, {
|
|
2745
|
+
...(options || {}),
|
|
2746
|
+
force: true,
|
|
2747
|
+
timeout: (options && options.timeout) || 5000,
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
} else {
|
|
2751
|
+
result = await originalClick.call(this, options);
|
|
2752
|
+
}
|
|
2753
|
+
await new Promise((res) => setTimeout(res, clickPostPop()));
|
|
2754
|
+
await hideRing(page);
|
|
2755
|
+
return result;
|
|
2756
|
+
} finally {
|
|
2757
|
+
if (keptCameraLock) {
|
|
2758
|
+
await page
|
|
2759
|
+
.evaluate(() => {
|
|
2760
|
+
window.__wgCameraLock = false;
|
|
2761
|
+
})
|
|
2762
|
+
.catch(() => {});
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2661
2765
|
};
|
|
2662
2766
|
}
|
|
2663
2767
|
|
|
@@ -30,7 +30,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
30
30
|
color: "#c9a6ff",
|
|
31
31
|
zoom: 1.55,
|
|
32
32
|
zoomOut: false,
|
|
33
|
-
followMouse:
|
|
33
|
+
followMouse: false,
|
|
34
34
|
weight: "bold",
|
|
35
35
|
},
|
|
36
36
|
});
|
|
@@ -45,7 +45,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
45
45
|
focus: true,
|
|
46
46
|
color: "#86efac",
|
|
47
47
|
zoom: 1.4,
|
|
48
|
-
followMouse:
|
|
48
|
+
followMouse: false,
|
|
49
49
|
duration: true,
|
|
50
50
|
});
|
|
51
51
|
},
|
package/package.json
CHANGED
|
@@ -30,7 +30,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
30
30
|
color: "#c9a6ff",
|
|
31
31
|
zoom: 1.55,
|
|
32
32
|
zoomOut: false,
|
|
33
|
-
followMouse:
|
|
33
|
+
followMouse: false,
|
|
34
34
|
weight: "bold",
|
|
35
35
|
},
|
|
36
36
|
});
|
|
@@ -45,7 +45,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
45
45
|
focus: true,
|
|
46
46
|
color: "#86efac",
|
|
47
47
|
zoom: 1.4,
|
|
48
|
-
followMouse:
|
|
48
|
+
followMouse: false,
|
|
49
49
|
duration: true,
|
|
50
50
|
});
|
|
51
51
|
},
|