waygraph 0.12.35 → 0.12.37
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 +133 -34
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +1 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +2 -0
- package/package.json +1 -1
- package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +1 -0
- package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +2 -0
package/dist/cli.js
CHANGED
|
@@ -711,6 +711,7 @@ async function installOverlay(page, title) {
|
|
|
711
711
|
const cursor = document.getElementById("wg-cursor");
|
|
712
712
|
if (!cursor) return;
|
|
713
713
|
const travel = ms || 600;
|
|
714
|
+
window.__wgCursorPos = { x: Number(x) || 0, y: Number(y) || 0 };
|
|
714
715
|
cursor.style.setProperty("--wg-cursor-ms", travel + "ms");
|
|
715
716
|
if (instant) {
|
|
716
717
|
const prev = cursor.style.transition;
|
|
@@ -811,6 +812,50 @@ async function installOverlay(page, title) {
|
|
|
811
812
|
let settled = zoom <= 1;
|
|
812
813
|
const settleAt = Date.now() + camMs;
|
|
813
814
|
const style = { size, weight, focus, color };
|
|
815
|
+
// Screen Studio / Screenify: dead zone = center ~40% of viewport.
|
|
816
|
+
// Pan only when the demo cursor leaves that zone - never hard-center
|
|
817
|
+
// on every mousemove (that fights like same-pole magnets).
|
|
818
|
+
const deadFrac = 0.4;
|
|
819
|
+
let lastPanAt = 0;
|
|
820
|
+
const readCursorViewport = () => {
|
|
821
|
+
const cursor = document.getElementById("wg-cursor");
|
|
822
|
+
if (cursor && cursor.style.opacity !== "0") {
|
|
823
|
+
const r = cursor.getBoundingClientRect();
|
|
824
|
+
if (r.width || r.height || r.left || r.top) {
|
|
825
|
+
return { x: r.left, y: r.top };
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (window.__wgCursorPos) return window.__wgCursorPos;
|
|
829
|
+
return null;
|
|
830
|
+
};
|
|
831
|
+
const followTick = () => {
|
|
832
|
+
if (!followMouse || !settled || window.__wgCameraLock) return;
|
|
833
|
+
if (!window.__wgSetCamera || !window.__wgViewportToDoc) return;
|
|
834
|
+
const cam = window.__wgCamera;
|
|
835
|
+
if (!cam || !(cam.scale > 1.001)) return;
|
|
836
|
+
const pos = readCursorViewport();
|
|
837
|
+
if (!pos) return;
|
|
838
|
+
const vw = window.innerWidth;
|
|
839
|
+
const vh = window.innerHeight;
|
|
840
|
+
const dx = pos.x - vw / 2;
|
|
841
|
+
const dy = pos.y - vh / 2;
|
|
842
|
+
const deadX = (vw * deadFrac) / 2;
|
|
843
|
+
const deadY = (vh * deadFrac) / 2;
|
|
844
|
+
let ox = 0;
|
|
845
|
+
let oy = 0;
|
|
846
|
+
if (dx > deadX) ox = dx - deadX;
|
|
847
|
+
else if (dx < -deadX) ox = dx + deadX;
|
|
848
|
+
if (dy > deadY) oy = dy - deadY;
|
|
849
|
+
else if (dy < -deadY) oy = dy + deadY;
|
|
850
|
+
if (ox === 0 && oy === 0) return;
|
|
851
|
+
const now = Date.now();
|
|
852
|
+
if (now - lastPanAt < 80) return;
|
|
853
|
+
lastPanAt = now;
|
|
854
|
+
// Viewport overflow -> document delta (divide by scale).
|
|
855
|
+
const nx = cam.cx + ox / cam.scale;
|
|
856
|
+
const ny = cam.cy + oy / cam.scale;
|
|
857
|
+
window.__wgSetCamera(nx, ny, cam.scale, 220);
|
|
858
|
+
};
|
|
814
859
|
const tick = () => {
|
|
815
860
|
const el = document.querySelector(sel);
|
|
816
861
|
if (el && window.__wgPositionRing) {
|
|
@@ -823,32 +868,34 @@ async function installOverlay(page, title) {
|
|
|
823
868
|
);
|
|
824
869
|
}
|
|
825
870
|
if (!settled && Date.now() >= settleAt) settled = true;
|
|
871
|
+
followTick();
|
|
826
872
|
window.__wgRingRaf = requestAnimationFrame(tick);
|
|
827
873
|
};
|
|
828
874
|
if (zoom > 1 && window.__wgSetCamera) {
|
|
829
875
|
const el0 = document.querySelector(sel);
|
|
830
876
|
if (el0) {
|
|
831
|
-
const r0 = el0.getBoundingClientRect();
|
|
832
877
|
const already =
|
|
833
878
|
window.__wgCamera && window.__wgCamera.scale > 1.001;
|
|
834
|
-
// Already zoomed (zoomOut:false chain): short pan, not full zoom-in.
|
|
835
879
|
const dur = already ? Math.min(camMs, 420) : camMs;
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
880
|
+
// Focus must be document-space, not raw getBoundingClientRect
|
|
881
|
+
// (viewport) - otherwise already-zoomed pans fight the cursor.
|
|
882
|
+
const pt = window.__wgElDocPoint
|
|
883
|
+
? window.__wgElDocPoint(el0)
|
|
884
|
+
: {
|
|
885
|
+
x: el0.getBoundingClientRect().left + el0.getBoundingClientRect().width / 2,
|
|
886
|
+
y: el0.getBoundingClientRect().top + el0.getBoundingClientRect().height / 2,
|
|
887
|
+
};
|
|
888
|
+
window.__wgCameraLock = true;
|
|
889
|
+
window.__wgSetCamera(pt.x, pt.y, zoom, dur);
|
|
890
|
+
setTimeout(() => {
|
|
891
|
+
window.__wgCameraLock = false;
|
|
892
|
+
}, dur + 40);
|
|
842
893
|
}
|
|
843
894
|
}
|
|
844
895
|
window.__wgRingRaf = requestAnimationFrame(tick);
|
|
896
|
+
// Real mouse only seeds cursor pos; dead-zone follow runs in RAF.
|
|
845
897
|
const onMove = (ev) => {
|
|
846
|
-
|
|
847
|
-
if (!window.__wgSetCamera) return;
|
|
848
|
-
const cam = window.__wgCamera;
|
|
849
|
-
const scale = (cam && cam.scale > 1 ? cam.scale : zoom) || zoom;
|
|
850
|
-
if (!(scale > 1)) return;
|
|
851
|
-
window.__wgSetCamera(ev.clientX, ev.clientY, scale, 140);
|
|
898
|
+
window.__wgCursorPos = { x: ev.clientX, y: ev.clientY };
|
|
852
899
|
};
|
|
853
900
|
window.addEventListener("mousemove", onMove, { passive: true });
|
|
854
901
|
window.__wgRingTrack = onMove;
|
|
@@ -889,10 +936,38 @@ async function installOverlay(page, title) {
|
|
|
889
936
|
if (ring) ring.style.opacity = "0";
|
|
890
937
|
if (ringLabel) ringLabel.style.opacity = "0";
|
|
891
938
|
if (window.__wgHideFocusVeil) window.__wgHideFocusVeil();
|
|
892
|
-
|
|
939
|
+
// Do NOT clear camera zoom here - zoomOut:false keeps scale between
|
|
940
|
+
// rings; only resetCameraZoom / teardown call __wgClearZoom.
|
|
893
941
|
};
|
|
894
942
|
// Screen Studio-style camera: scale <body>, keep overlays on <html> fixed.
|
|
895
|
-
|
|
943
|
+
// Focus (cx,cy) is DOCUMENT space (pre-transform). Viewport mouse/rects
|
|
944
|
+
// must go through __wgViewportToDoc before SetCamera.
|
|
945
|
+
window.__wgCamera = {
|
|
946
|
+
scale: 1,
|
|
947
|
+
cx: window.innerWidth / 2,
|
|
948
|
+
cy: window.innerHeight / 2,
|
|
949
|
+
};
|
|
950
|
+
window.__wgCameraLock = false;
|
|
951
|
+
window.__wgViewportToDoc = (vx, vy) => {
|
|
952
|
+
const cam = window.__wgCamera || {
|
|
953
|
+
scale: 1,
|
|
954
|
+
cx: window.innerWidth / 2,
|
|
955
|
+
cy: window.innerHeight / 2,
|
|
956
|
+
};
|
|
957
|
+
const s = cam.scale > 1.001 ? cam.scale : 1;
|
|
958
|
+
if (s <= 1.001) return { x: vx, y: vy };
|
|
959
|
+
return {
|
|
960
|
+
x: (vx - window.innerWidth / 2) / s + cam.cx,
|
|
961
|
+
y: (vy - window.innerHeight / 2) / s + cam.cy,
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
window.__wgElDocPoint = (el) => {
|
|
965
|
+
const r = el.getBoundingClientRect();
|
|
966
|
+
return window.__wgViewportToDoc(
|
|
967
|
+
r.left + r.width / 2,
|
|
968
|
+
r.top + r.height / 2,
|
|
969
|
+
);
|
|
970
|
+
};
|
|
896
971
|
window.__wgSetCamera = (fx, fy, scale, ms) => {
|
|
897
972
|
const body = document.body;
|
|
898
973
|
if (!body) return;
|
|
@@ -960,7 +1035,7 @@ async function installOverlay(page, title) {
|
|
|
960
1035
|
'<div class="wg-todo-head">Checklist</div><ul>' + rows + '</ul>';
|
|
961
1036
|
modal.classList.add("wg-todo-on");
|
|
962
1037
|
};
|
|
963
|
-
window.__wgResetCamera = () => {
|
|
1038
|
+
window.__wgResetCamera = () => {
|
|
964
1039
|
window.__wgSetCamera(window.innerWidth / 2, window.innerHeight / 2, 1);
|
|
965
1040
|
};
|
|
966
1041
|
window.__wgClearZoom = () => {
|
|
@@ -1412,13 +1487,17 @@ async function applyCameraZoom(page, box, zoom, ms) {
|
|
|
1412
1487
|
}).catch(() => {});
|
|
1413
1488
|
return;
|
|
1414
1489
|
}
|
|
1415
|
-
const
|
|
1416
|
-
const
|
|
1490
|
+
const vx = box.x + box.width / 2;
|
|
1491
|
+
const vy = box.y + box.height / 2;
|
|
1417
1492
|
const dur = ms != null ? ms : 650;
|
|
1418
1493
|
await page
|
|
1419
|
-
.evaluate(({
|
|
1420
|
-
if (window.__wgSetCamera)
|
|
1421
|
-
|
|
1494
|
+
.evaluate(({ vx, vy, scale, dur }) => {
|
|
1495
|
+
if (!window.__wgSetCamera) return;
|
|
1496
|
+
const pt = window.__wgViewportToDoc
|
|
1497
|
+
? window.__wgViewportToDoc(vx, vy)
|
|
1498
|
+
: { x: vx, y: vy };
|
|
1499
|
+
window.__wgSetCamera(pt.x, pt.y, scale, dur);
|
|
1500
|
+
}, { vx, vy, scale: z, dur })
|
|
1422
1501
|
.catch(() => {});
|
|
1423
1502
|
}
|
|
1424
1503
|
|
|
@@ -1552,16 +1631,9 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1552
1631
|
: h.follow !== undefined
|
|
1553
1632
|
? !!h.follow
|
|
1554
1633
|
: z > 1;
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
await page
|
|
1559
|
-
.evaluate(({ cx, cy, camMs }) => {
|
|
1560
|
-
if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(cx, cy, camMs, false);
|
|
1561
|
-
}, { cx, cy, camMs })
|
|
1562
|
-
.catch(() => {});
|
|
1563
|
-
}
|
|
1564
|
-
// Track first: ring rides visual rect through the zoom (no re-adjust).
|
|
1634
|
+
// Track/zoom first, then park the demo cursor on the LIVE rect.
|
|
1635
|
+
// Placing the cursor on a pre-zoom boundingBox leaves it stranded after
|
|
1636
|
+
// body scale (html cursor is outside the transform).
|
|
1565
1637
|
await page
|
|
1566
1638
|
.evaluate(
|
|
1567
1639
|
({ sel, label, tone, size, weight, focus, color, zoom, followMouse, camMs }) => {
|
|
@@ -1599,7 +1671,21 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1599
1671
|
.catch(() => false);
|
|
1600
1672
|
const waitMs = alreadyZoomed ? Math.min(camMs, 420) : camMs;
|
|
1601
1673
|
await new Promise((res) => setTimeout(res, waitMs));
|
|
1602
|
-
|
|
1674
|
+
if (h.cursor !== false) {
|
|
1675
|
+
await page
|
|
1676
|
+
.evaluate(({ sel, ms }) => {
|
|
1677
|
+
const el = document.querySelector(sel);
|
|
1678
|
+
if (!el || !window.__wgMoveCursorTo) return;
|
|
1679
|
+
window.__wgCameraLock = true;
|
|
1680
|
+
const r = el.getBoundingClientRect();
|
|
1681
|
+
window.__wgMoveCursorTo(r.left + r.width / 2, r.top + r.height / 2, ms, false);
|
|
1682
|
+
setTimeout(() => {
|
|
1683
|
+
window.__wgCameraLock = false;
|
|
1684
|
+
}, ms + 40);
|
|
1685
|
+
}, { sel: h.selector, ms: Math.min(280, camMs) })
|
|
1686
|
+
.catch(() => {});
|
|
1687
|
+
await new Promise((res) => setTimeout(res, Math.min(280, camMs)));
|
|
1688
|
+
}
|
|
1603
1689
|
// Appear settle
|
|
1604
1690
|
await new Promise((res) => setTimeout(res, fadeMs));
|
|
1605
1691
|
const authored = resolveFixtureDwellMs(h, { gatesFast, pace });
|
|
@@ -1618,7 +1704,11 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1618
1704
|
}
|
|
1619
1705
|
}
|
|
1620
1706
|
await stopLiveFollow();
|
|
1621
|
-
|
|
1707
|
+
// Keep camera if the last ring (or phase default) asked zoomOut:false.
|
|
1708
|
+
const last = list.length > 0 ? list[list.length - 1] : null;
|
|
1709
|
+
if (!last || last.zoomOut !== false) {
|
|
1710
|
+
await resetCameraZoom(page);
|
|
1711
|
+
}
|
|
1622
1712
|
if (list.length === 0) {
|
|
1623
1713
|
await hideRing(page);
|
|
1624
1714
|
}
|
|
@@ -2345,6 +2435,7 @@ async function teardownOverlay(page) {
|
|
|
2345
2435
|
window.__wgRingTrack = null;
|
|
2346
2436
|
}
|
|
2347
2437
|
window.__wgNarrateOwnsRing = false;
|
|
2438
|
+
if (window.__wgClearZoom) window.__wgClearZoom();
|
|
2348
2439
|
for (const id of ["wg-panel", "wg-ring", "wg-ring-label", "wg-cursor", "wg-click-pulse", "wg-banner", "wg-todo-modal", "wg-focus-veil"]) {
|
|
2349
2440
|
const el = document.getElementById(id);
|
|
2350
2441
|
if (el) el.remove();
|
|
@@ -2357,8 +2448,11 @@ async function moveCursorTo(page, box, ms) {
|
|
|
2357
2448
|
const x = box.x + box.width / 2;
|
|
2358
2449
|
const y = box.y + box.height / 2;
|
|
2359
2450
|
const travel = Math.max(0, Number(ms) || 0);
|
|
2451
|
+
// Freeze camera follow while the demo cursor travels - otherwise dead-zone
|
|
2452
|
+
// pans fight the click target (esp. right-edge cart chrome).
|
|
2360
2453
|
await page
|
|
2361
2454
|
.evaluate(({ x, y, ms }) => {
|
|
2455
|
+
window.__wgCameraLock = true;
|
|
2362
2456
|
if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(x, y, ms);
|
|
2363
2457
|
}, { x, y, ms: travel })
|
|
2364
2458
|
.catch(() => {});
|
|
@@ -2368,6 +2462,11 @@ async function moveCursorTo(page, box, ms) {
|
|
|
2368
2462
|
if (travel > 0) {
|
|
2369
2463
|
await new Promise((res) => setTimeout(res, travel));
|
|
2370
2464
|
}
|
|
2465
|
+
await page
|
|
2466
|
+
.evaluate(() => {
|
|
2467
|
+
window.__wgCameraLock = false;
|
|
2468
|
+
})
|
|
2469
|
+
.catch(() => {});
|
|
2371
2470
|
return { x, y };
|
|
2372
2471
|
}
|
|
2373
2472
|
|
package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -66,6 +66,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
66
66
|
ctx.banner("In the cart");
|
|
67
67
|
ctx.todos(["Find the product", "Add to cart", "Open cart"]);
|
|
68
68
|
ctx.todoIndex(1);
|
|
69
|
+
ctx.zoomOut(false);
|
|
69
70
|
ctx.ring("badge", {
|
|
70
71
|
selector: InventorySel.cartBadge,
|
|
71
72
|
label: "Cart badge",
|
|
@@ -73,6 +74,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
73
74
|
focus: true,
|
|
74
75
|
color: "#86efac",
|
|
75
76
|
zoom: 1.6,
|
|
77
|
+
zoomOut: false,
|
|
76
78
|
followMouse: true,
|
|
77
79
|
duration: true,
|
|
78
80
|
});
|
package/package.json
CHANGED
package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -66,6 +66,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
66
66
|
ctx.banner("In the cart");
|
|
67
67
|
ctx.todos(["Find the product", "Add to cart", "Open cart"]);
|
|
68
68
|
ctx.todoIndex(1);
|
|
69
|
+
ctx.zoomOut(false);
|
|
69
70
|
ctx.ring("badge", {
|
|
70
71
|
selector: InventorySel.cartBadge,
|
|
71
72
|
label: "Cart badge",
|
|
@@ -73,6 +74,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
73
74
|
focus: true,
|
|
74
75
|
color: "#86efac",
|
|
75
76
|
zoom: 1.6,
|
|
77
|
+
zoomOut: false,
|
|
76
78
|
followMouse: true,
|
|
77
79
|
duration: true,
|
|
78
80
|
});
|