waygraph 0.12.34 → 0.12.35

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
@@ -829,11 +829,15 @@ async function installOverlay(page, title) {
829
829
  const el0 = document.querySelector(sel);
830
830
  if (el0) {
831
831
  const r0 = el0.getBoundingClientRect();
832
+ const already =
833
+ window.__wgCamera && window.__wgCamera.scale > 1.001;
834
+ // Already zoomed (zoomOut:false chain): short pan, not full zoom-in.
835
+ const dur = already ? Math.min(camMs, 420) : camMs;
832
836
  window.__wgSetCamera(
833
837
  r0.left + r0.width / 2,
834
838
  r0.top + r0.height / 2,
835
839
  zoom,
836
- camMs,
840
+ dur,
837
841
  );
838
842
  }
839
843
  }
@@ -1590,7 +1594,11 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1590
1594
  },
1591
1595
  )
1592
1596
  .catch(() => {});
1593
- await new Promise((res) => setTimeout(res, camMs));
1597
+ const alreadyZoomed = await page
1598
+ .evaluate(() => !!(window.__wgCamera && window.__wgCamera.scale > 1.001))
1599
+ .catch(() => false);
1600
+ const waitMs = alreadyZoomed ? Math.min(camMs, 420) : camMs;
1601
+ await new Promise((res) => setTimeout(res, waitMs));
1594
1602
 
1595
1603
  // Appear settle
1596
1604
  await new Promise((res) => setTimeout(res, fadeMs));
@@ -1599,11 +1607,12 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1599
1607
  const holdMs =
1600
1608
  authored != null ? authored : defaultHoldMs != null ? defaultHoldMs : legacyMs;
1601
1609
  await new Promise((res) => setTimeout(res, holdMs));
1602
- // Disappear before next (respect fade)
1610
+ // Disappear before next. zoomOut:false keeps camera for the next ring.
1603
1611
  await stopLiveFollow();
1604
1612
  await hideRing(page);
1605
- await resetCameraZoom(page);
1606
- await new Promise((res) => setTimeout(res, fadeMs));
1613
+ const doZoomOut = h.zoomOut !== false;
1614
+ if (doZoomOut) await resetCameraZoom(page);
1615
+ await new Promise((res) => setTimeout(res, doZoomOut ? fadeMs : 120));
1607
1616
  } catch {
1608
1617
  /* best-effort */
1609
1618
  }
@@ -2939,6 +2948,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2939
2948
  focus: styled.focus,
2940
2949
  color: styled.color,
2941
2950
  cursor: styled.cursor,
2951
+ zoomOut: styled.zoomOut,
2942
2952
  };
2943
2953
  });
2944
2954
  await cycleHighlightRings(page, beforeRings, !!pacing.gatesFast, {
@@ -3019,6 +3029,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
3019
3029
  focus: styled.focus,
3020
3030
  color: styled.color,
3021
3031
  cursor: styled.cursor,
3032
+ zoomOut: styled.zoomOut,
3022
3033
  };
3023
3034
  });
3024
3035
  await cycleHighlightRings(page, errHighlights, !!pacing.gatesFast, {
@@ -3113,6 +3124,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
3113
3124
  focus: styled.focus,
3114
3125
  color: styled.color,
3115
3126
  cursor: styled.cursor,
3127
+ zoomOut: styled.zoomOut,
3116
3128
  };
3117
3129
  });
3118
3130
  } else {
@@ -50,6 +50,8 @@ export type StubPhaseFixtures = {
50
50
  * cursor. Not element CSS zoom. Typical `1.25` .. `2`.
51
51
  */
52
52
  zoom?: number;
53
+ /** Default zoomOut for rings (false = keep camera between rings). */
54
+ zoomOut?: boolean;
53
55
  /** Top demo banner title (the purple "waygraph demo" card). */
54
56
  title?: string;
55
57
  };
@@ -81,10 +83,15 @@ export interface WaygraphHighlightStub extends FixtureDurationFields {
81
83
  weight?: HighlightWeight;
82
84
  /**
83
85
  * Camera zoom while this ring is up (Screen Studio style - page scales toward
84
- * the target / cursor). Typical `1.25` .. `2`. Cleared when the ring hides.
85
- * Episode default: {@link StubCtx.zoom}.
86
+ * the target / cursor). Typical `1.25` .. `2`. Cleared when the ring hides
87
+ * unless `zoomOut: false`. Episode default: {@link StubCtx.zoom}.
86
88
  */
87
89
  zoom?: number;
90
+ /**
91
+ * When false, keep the camera zoomed after this ring (pan to the next target
92
+ * instead of zooming out). Default true. Phase default: {@link StubCtx.zoomOut}.
93
+ */
94
+ zoomOut?: boolean;
88
95
  /**
89
96
  * Spotlight: dim the rest of the page, keep this target as the focal point.
90
97
  */
@@ -109,7 +116,7 @@ export interface WaygraphHighlightStub extends FixtureDurationFields {
109
116
  cursor?: boolean;
110
117
  }
111
118
  /** Flow fixture patch: label required; selector optional (inherits from stub). */
112
- export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight" | "zoom" | "focus" | "followMouse" | "follow" | "color" | "cursor"> & FixtureDurationFields;
119
+ export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight" | "zoom" | "zoomOut" | "focus" | "followMouse" | "follow" | "color" | "cursor"> & FixtureDurationFields;
113
120
  export type HighlightStubPhase = Record<string, WaygraphHighlightStub>;
114
121
  /**
115
122
  * Open stub lifecycle context. Authors set rings + episode fixtures freely —
@@ -147,6 +154,10 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
147
154
  todoIndex(n: number): void;
148
155
  /** Default camera zoom for rings without their own zoom. */
149
156
  zoom(n: number): void;
157
+ /**
158
+ * Default zoomOut for rings in this phase. `false` = keep camera between rings.
159
+ */
160
+ zoomOut(keep: boolean): void;
150
161
  /** Top banner title (waygraph demo card). */
151
162
  title(text: string): void;
152
163
  /** Alias for {@link StubCtx.title}. */
@@ -170,6 +181,7 @@ export type StubPhaseResult = {
170
181
  todos: WaygraphTodoItem[];
171
182
  todoIndex?: number;
172
183
  zoom?: number;
184
+ zoomOut?: boolean;
173
185
  title?: string;
174
186
  };
175
187
  /**
@@ -403,6 +403,9 @@ function createStubCtx(bag, opts) {
403
403
  if (Number.isFinite(n) && n > 0)
404
404
  bag.zoom = n;
405
405
  },
406
+ zoomOut(keep) {
407
+ bag.zoomOut = !!keep;
408
+ },
406
409
  title(text) {
407
410
  bag.title = String(text ?? "");
408
411
  },
@@ -419,6 +422,8 @@ function createStubCtx(bag, opts) {
419
422
  if (partial.zoom !== undefined && Number.isFinite(partial.zoom) && partial.zoom > 0) {
420
423
  bag.zoom = partial.zoom;
421
424
  }
425
+ if (partial.zoomOut !== undefined)
426
+ bag.zoomOut = !!partial.zoomOut;
422
427
  if (partial.title !== undefined)
423
428
  bag.title = String(partial.title ?? "");
424
429
  },
@@ -443,6 +448,11 @@ function applyDefaultZoom(slots, defaultZoom) {
443
448
  return slots;
444
449
  return slots.map((h) => (h.zoom !== undefined && h.zoom > 0 ? h : { ...h, zoom: defaultZoom }));
445
450
  }
451
+ function applyDefaultZoomOut(slots, defaultZoomOut) {
452
+ if (defaultZoomOut === undefined)
453
+ return slots;
454
+ return slots.map((h) => h.zoomOut !== undefined ? h : { ...h, zoomOut: defaultZoomOut });
455
+ }
446
456
  function mergePhaseMaps(phaseMap, fixturePhase) {
447
457
  const slotIds = new Set([...Object.keys(phaseMap), ...Object.keys(fixturePhase || {})]);
448
458
  const ordered = [...slotIds].sort((a, b) => {
@@ -546,12 +556,14 @@ export async function runStubPhase(block, phase, opts) {
546
556
  const fixturePhase = opts?.fixtures?.[block.name]?.[phase];
547
557
  let highlights = mergePhaseMaps(bag.highlights, fixturePhase);
548
558
  highlights = applyDefaultZoom(highlights, bag.zoom);
559
+ highlights = applyDefaultZoomOut(highlights, bag.zoomOut);
549
560
  const todos = normalizeTodos(bag.todos, bag.todoIndex);
550
561
  return {
551
562
  highlights,
552
563
  todos,
553
564
  ...(bag.todoIndex !== undefined ? { todoIndex: bag.todoIndex } : {}),
554
565
  ...(bag.zoom !== undefined ? { zoom: bag.zoom } : {}),
566
+ ...(bag.zoomOut !== undefined ? { zoomOut: bag.zoomOut } : {}),
555
567
  ...(bag.title !== undefined && bag.title !== "" ? { title: bag.title } : {}),
556
568
  };
557
569
  }
@@ -15,7 +15,6 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
15
15
  checkpoint: "CartPage",
16
16
  click: ".shopping_cart_link",
17
17
  verify: [Trait.url({ pathname: "/cart.html" })],
18
- // Demo: zoom cart chrome, then the cart line items after landing.
19
18
  stubBefore: (ctx) => {
20
19
  ctx.title("Open cart");
21
20
  ctx.todos(["Find the product", "Add to cart", "Open cart"]);
@@ -29,6 +28,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
29
28
  focus: true,
30
29
  color: "#c9a6ff",
31
30
  zoom: 1.55,
31
+ zoomOut: false,
32
32
  followMouse: true,
33
33
  weight: "bold",
34
34
  },
@@ -36,6 +36,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
36
36
  },
37
37
  stubAfter: (ctx) => {
38
38
  ctx.banner("Your cart");
39
+ ctx.zoomOut(false);
39
40
  ctx.ring("items", {
40
41
  selector: InventorySel.cartItem,
41
42
  label: "Cart items",
@@ -33,12 +33,13 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
33
33
  },
34
34
  },
35
35
  ],
36
- // Demo path: zoom the product, then the add button, then (after) the cart badge.
36
+ // Demo: zoom product -> add (stay zoomed), then badge after act.
37
37
  stubBefore: (ctx) => {
38
38
  ctx.title("Pick a product");
39
39
  ctx.todos(["Find the product", "Add to cart", "Open cart"]);
40
40
  ctx.todoIndex(0);
41
41
  ctx.zoom(1.35);
42
+ ctx.zoomOut(false);
42
43
  ctx.highlights({
43
44
  product: {
44
45
  selector: InventorySel.title(DEMO_PRODUCT_ID),
@@ -47,6 +48,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
47
48
  focus: true,
48
49
  color: "#c9a6ff",
49
50
  zoom: 1.45,
51
+ zoomOut: false,
50
52
  followMouse: true,
51
53
  },
52
54
  add: {
@@ -55,6 +57,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
55
57
  focus: true,
56
58
  color: "#86efac",
57
59
  zoom: 1.5,
60
+ zoomOut: false,
58
61
  weight: "bold",
59
62
  },
60
63
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.12.34",
3
+ "version": "0.12.35",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",
@@ -15,7 +15,6 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
15
15
  checkpoint: "CartPage",
16
16
  click: ".shopping_cart_link",
17
17
  verify: [Trait.url({ pathname: "/cart.html" })],
18
- // Demo: zoom cart chrome, then the cart line items after landing.
19
18
  stubBefore: (ctx) => {
20
19
  ctx.title("Open cart");
21
20
  ctx.todos(["Find the product", "Add to cart", "Open cart"]);
@@ -29,6 +28,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
29
28
  focus: true,
30
29
  color: "#c9a6ff",
31
30
  zoom: 1.55,
31
+ zoomOut: false,
32
32
  followMouse: true,
33
33
  weight: "bold",
34
34
  },
@@ -36,6 +36,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
36
36
  },
37
37
  stubAfter: (ctx) => {
38
38
  ctx.banner("Your cart");
39
+ ctx.zoomOut(false);
39
40
  ctx.ring("items", {
40
41
  selector: InventorySel.cartItem,
41
42
  label: "Cart items",
@@ -33,12 +33,13 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
33
33
  },
34
34
  },
35
35
  ],
36
- // Demo path: zoom the product, then the add button, then (after) the cart badge.
36
+ // Demo: zoom product -> add (stay zoomed), then badge after act.
37
37
  stubBefore: (ctx) => {
38
38
  ctx.title("Pick a product");
39
39
  ctx.todos(["Find the product", "Add to cart", "Open cart"]);
40
40
  ctx.todoIndex(0);
41
41
  ctx.zoom(1.35);
42
+ ctx.zoomOut(false);
42
43
  ctx.highlights({
43
44
  product: {
44
45
  selector: InventorySel.title(DEMO_PRODUCT_ID),
@@ -47,6 +48,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
47
48
  focus: true,
48
49
  color: "#c9a6ff",
49
50
  zoom: 1.45,
51
+ zoomOut: false,
50
52
  followMouse: true,
51
53
  },
52
54
  add: {
@@ -55,6 +57,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
55
57
  focus: true,
56
58
  color: "#86efac",
57
59
  zoom: 1.5,
60
+ zoomOut: false,
58
61
  weight: "bold",
59
62
  },
60
63
  });