waygraph 0.15.31 → 0.15.33
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/auto-session/session.js +2 -1
- package/dist/highlights/run-phase.js +2 -0
- package/dist/highlights/stub-ctx.js +36 -2
- package/dist/highlights/types.d.ts +35 -3
- package/dist/index.d.ts +1 -1
- package/dist/pilot-overlay/action-ring.d.ts +16 -0
- package/dist/pilot-overlay/action-ring.js +67 -0
- package/dist/pilot-overlay/fixtures.d.ts +9 -1
- package/dist/pilot-overlay/fixtures.js +38 -1
- package/dist/pilot-overlay/overlay-fixtures.d.ts +9 -0
- package/dist/pilot-overlay/overlay-fixtures.js +45 -0
- package/dist/pilot-overlay/shell.js +4 -23
- package/dist/pilot-overlay.d.ts +2 -0
- package/dist/pilot-overlay.js +2 -0
- package/dist/runner/banner-log.js +62 -0
- package/dist/runner/demo-log.js +9 -1
- package/dist/runner/inpage/banner.js +56 -1
- package/dist/runner/inpage/core.js +2 -0
- package/dist/runner/overlay-css.js +6 -27
- package/dist/runner/overlay-install.js +7 -4
- package/dist/runner/overlay-present.d.ts +8 -0
- package/dist/runner/overlay-present.js +57 -0
- package/dist/runner/overlay-stage.js +105 -0
- package/dist/runner/step-mode.js +35 -126
- package/dist/runner/step-panels.js +3 -3
- package/dist/step-overlay.js +1 -3
- package/dist/ui/components/ring.js +5 -0
- package/dist/ui/overlay.css +209 -0
- package/dist/ui/shadow.d.ts +7 -2
- package/dist/ui/shadow.js +12 -4
- package/dist/ui/stylesheet.d.ts +7 -0
- package/dist/ui/stylesheet.js +18 -0
- package/dist/verbose-log.d.ts +1 -0
- package/dist/verbose-log.js +16 -0
- package/examples/saucedemo/.waygraph-auto/03df352d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/0a8c3719.log +0 -0
- package/examples/saucedemo/.waygraph-auto/232a1d81.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2553d107.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2c79c73a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2c94a05b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2f4f531e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/37b56184.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3bdb2f5a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/476cff76.log +0 -0
- package/examples/saucedemo/.waygraph-auto/535e8922.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5b838485.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5d13bdde.log +0 -0
- package/examples/saucedemo/.waygraph-auto/63b0f4cd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/6f05bdc1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/70279fc3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/731a14bd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/73a41843.log +0 -0
- package/examples/saucedemo/.waygraph-auto/76128a2d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/796ab578.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8598b4c0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/859a261d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a1ab5d29.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a94738b7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b3d172f3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b57921c5.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b9ab0983.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bf8693e7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d37719a6.log +0 -0
- package/examples/saucedemo/.waygraph-auto/da0a06bf.log +0 -0
- package/examples/saucedemo/.waygraph-auto/dc58eb72.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f86224c6.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fe83305d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ff616932.log +0 -0
- package/package.json +3 -2
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
|
|
2
2
|
// Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
|
|
3
|
-
import { WAYGRAPH_RING_CSS } from "../highlights.js";
|
|
4
|
-
import { cursorCss } from "../ui/components/cursor.js";
|
|
5
3
|
import { SURFACE } from "../ui/tokens.js";
|
|
6
4
|
|
|
7
5
|
// ---------------------------------------------------------------------------
|
|
@@ -13,22 +11,11 @@ import { SURFACE } from "../ui/tokens.js";
|
|
|
13
11
|
// label-under-box language, minus everything camera/narration-specific).
|
|
14
12
|
// Purely CLI-level orchestration - no engine changes, no change to the
|
|
15
13
|
// non-STEP path above.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Mouse cursor icon that travels to a target before it's acted on, plus a
|
|
22
|
-
// quick expanding ripple at the moment of a click - same idea as
|
|
23
|
-
// help-center-clip-engine's #clip-cursor/#clip-ring (video-pipeline). The
|
|
24
|
-
// shape itself is an inline SVG set as innerHTML in installOverlay below
|
|
25
|
-
// (dark fill + white stroke, same as the clip-engine's own cursor) - a
|
|
26
|
-
// plain solid-white CSS clip-path (the first attempt here) had no outline
|
|
27
|
-
// at all and all but disappeared against this app's light background.
|
|
28
|
-
// Travel duration is JS-driven per call via --wg-cursor-ms, same reason
|
|
29
|
-
// the clip engine's own comment gives: a hardcoded CSS duration would
|
|
30
|
-
// make the speed param a no-op.
|
|
31
|
-
cursorCss() +
|
|
14
|
+
// Runner-only chrome (step panel, todo-dock legacy variant, zoom/typing/device chips) - NOT
|
|
15
|
+
// ring/cursor/banner, which come from the one shared bundle every surface installs
|
|
16
|
+
// (ui/css/*.css, see ensureShadowRoot in overlay-install.js). Injected under its own key so it
|
|
17
|
+
// never collides with that bundle.
|
|
18
|
+
export const RUNNER_EXTRA_CSS =
|
|
32
19
|
// Spotlight: dim everything except the highlight target (focus: true).
|
|
33
20
|
"#wg-focus-veil{position:fixed;z-index:2147483644;pointer-events:none;" +
|
|
34
21
|
"border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);" +
|
|
@@ -225,15 +212,7 @@ export const RING_CSS =
|
|
|
225
212
|
"#wg-modules .wg-mod-done{background:#2a1650;color:#9a7ad1;}" +
|
|
226
213
|
"#wg-modules .wg-mod-current{background:#7C3AED;color:#fff;}" +
|
|
227
214
|
"#wg-modules .wg-mod-upcoming{background:transparent;color:#5a4a80;border:1px solid #3a2a60;}" +
|
|
228
|
-
|
|
229
|
-
"background:" + SURFACE + ";color:#fff;border-radius:12px;padding:10px 16px;" +
|
|
230
|
-
"font:14px/1.4 system-ui,sans-serif;box-shadow:0 8px 20px rgba(0,0,0,.3);" +
|
|
231
|
-
"border:1px solid rgba(124,58,237,.4);cursor:pointer;user-select:none;}" +
|
|
232
|
-
"#wg-banner[data-pos=left]{left:14px;right:auto;transform:none;}" +
|
|
233
|
-
"#wg-banner[data-pos=center]{left:50%;right:auto;transform:translateX(-50%);}" +
|
|
234
|
-
"#wg-banner[data-pos=right]{right:14px;left:auto;transform:none;}" +
|
|
235
|
-
"#wg-banner .wg-banner-tag{display:block;font-size:10px;font-weight:700;color:#c9a6ff;" +
|
|
236
|
-
"letter-spacing:.05em;text-transform:uppercase;margin-bottom:2px;}" +
|
|
215
|
+
// #wg-banner: comes from the shared bundle (ui/css/banner.css) now - not redefined here.
|
|
237
216
|
"#wg-panel .wg-key{margin:8px 0;}" +
|
|
238
217
|
// A live, human-readable preview of what's about to be written for this
|
|
239
218
|
// MemKey - Dan: "i want pretty to exist in memkeys too... it shows what
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
|
|
2
2
|
// Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
|
|
3
|
-
import { HOST_CSS,
|
|
3
|
+
import { HOST_CSS, RUNNER_EXTRA_CSS, WAYGRAPH_FAVICON } from "./overlay-css.js";
|
|
4
4
|
import { ensureShadowRoot } from "../ui/shadow.js";
|
|
5
5
|
import { resolveTodoDockUi } from "../highlights.js";
|
|
6
6
|
import { installCore } from "./inpage/core.js";
|
|
@@ -12,9 +12,12 @@ import { installTodos } from "./inpage/todos.js";
|
|
|
12
12
|
import { installDevice } from "./inpage/device.js";
|
|
13
13
|
import { applyVideoDeviceStage } from "./device-stage.js";
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/** `ui` = authored banner UX from the stub phase: { pos, collision, hidden } (all optional). */
|
|
16
|
+
export async function installOverlay(page, title, ui) {
|
|
17
|
+
// ring/cursor/banner/dock come from the ONE shared bundle every surface installs (ui/css/*.css).
|
|
16
18
|
await ensureShadowRoot(page);
|
|
17
|
-
|
|
19
|
+
// Runner-only chrome (step panel, zoom/typing/device chips) - separate key, no collision.
|
|
20
|
+
await page.evaluate(([css, key]) => __wgCss(css, key), [RUNNER_EXTRA_CSS, "wg-runner-css"]).catch(() => {});
|
|
18
21
|
await page.addStyleTag({ content: HOST_CSS }).catch(() => {});
|
|
19
22
|
// Default top-left; override with WAYGRAPH_TITLE_POS=left|center|right.
|
|
20
23
|
// Click cycles left -> center -> right (persisted in localStorage so a
|
|
@@ -25,7 +28,7 @@ export async function installOverlay(page, title) {
|
|
|
25
28
|
const todoPos = envTodoPos === "right" ? "right" : "left";
|
|
26
29
|
const envAutoplay = process.env.WAYGRAPH_AUTOPLAY === "1";
|
|
27
30
|
const todoDockUi = resolveTodoDockUi();
|
|
28
|
-
const args = { title, favicon: WAYGRAPH_FAVICON, bannerPos, todoPos, envAutoplay, todoDockUi };
|
|
31
|
+
const args = { title, favicon: WAYGRAPH_FAVICON, bannerPos, todoPos, envAutoplay, todoDockUi, bannerUi: ui || null };
|
|
29
32
|
await page.evaluate(installCore, args).catch(() => {});
|
|
30
33
|
await page.evaluate(installSwipeZoom, args).catch(() => {});
|
|
31
34
|
await page.evaluate(installBanner, args).catch(() => {});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Page } from "@playwright/test";
|
|
2
|
+
import type { StubPhaseResult } from "../highlights/types.js";
|
|
3
|
+
|
|
4
|
+
export interface SessionOverlay {
|
|
5
|
+
present(tag: string, phase: Partial<StubPhaseResult>): Promise<void>;
|
|
6
|
+
restore(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export function createSessionOverlay(page: Page, surface: string): SessionOverlay;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Presents a resolved stub phase ("waygraph overlay fixtures": ctx.todos / device / title / banner ...) on a LIVE page
|
|
2
|
+
// for the session surfaces (auto / browser / pilot). Same OverlayStage + same in-page installers + same verbose log
|
|
3
|
+
// lines as `waygraph demo`, so an overlay fixture behaves identically on every surface. Rings are painted separately
|
|
4
|
+
// (pilot holds them; demo cycles them) - that is the only intended difference.
|
|
5
|
+
import { installOverlay } from "./overlay-install.js";
|
|
6
|
+
import { createOverlayStage } from "./overlay-stage.js";
|
|
7
|
+
import { demoLog, logStubPhaseFixtures, logTodoDockFull, setDemoLogSurface, summarizeDevice } from "./demo-log.js";
|
|
8
|
+
import { logBannerDom, probeBanner } from "./banner-log.js";
|
|
9
|
+
import { probeTodoDocksOnPage } from "./todo-dock.js";
|
|
10
|
+
|
|
11
|
+
export function createSessionOverlay(page, surface) {
|
|
12
|
+
setDemoLogSurface(surface);
|
|
13
|
+
const stage = createOverlayStage(page, { todoDockRef: { current: undefined }, deviceRef: { current: undefined } });
|
|
14
|
+
let title = undefined;
|
|
15
|
+
let painted = false;
|
|
16
|
+
|
|
17
|
+
const paintChrome = async (phaseParallel) => {
|
|
18
|
+
await installOverlay(page, title, stage.state.lastBannerUi);
|
|
19
|
+
await stage.syncAllTodoDocks({ parallel: !!phaseParallel });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
stage,
|
|
24
|
+
/** Apply one phase (tag = before | after | error | highlight) and log the resulting overlay state. */
|
|
25
|
+
async present(tag, phase) {
|
|
26
|
+
setDemoLogSurface(surface);
|
|
27
|
+
logStubPhaseFixtures(tag, phase);
|
|
28
|
+
await stage.pushTodoDockUi(phase.todoDockUi);
|
|
29
|
+
const { appliedTodo, appliedDevice } = await stage.applyPhase(phase, { reapplyDeviceOnKeep: true });
|
|
30
|
+
if (phase.title && String(phase.title).trim()) title = String(phase.title).trim();
|
|
31
|
+
const wantsChrome = !!(title || stage.state.lastTodoDock || phase.todoSync === "set");
|
|
32
|
+
if (wantsChrome) {
|
|
33
|
+
painted = true;
|
|
34
|
+
await paintChrome(phase.todoParallel);
|
|
35
|
+
}
|
|
36
|
+
logTodoDockFull(stage.state.lastTodoDock, " " + tag);
|
|
37
|
+
demoLog(" " + tag + " device=" + summarizeDevice(stage.state.lastDevice) + " deviceSync=" + appliedDevice.sync);
|
|
38
|
+
demoLog(" " + tag + " todoSync=" + appliedTodo.sync);
|
|
39
|
+
const docks = await probeTodoDocksOnPage(page);
|
|
40
|
+
if (!docks.length && stage.state.lastTodoDock && appliedTodo.sync !== "clear") {
|
|
41
|
+
demoLog(" WARN " + tag + ": todos authored but dock-dom empty (not painted?)");
|
|
42
|
+
}
|
|
43
|
+
for (const d of docks) {
|
|
44
|
+
demoLog(
|
|
45
|
+
" " + tag + " dock-dom " + d.key + "@" + d.pos + " " + d.left + "," + d.top + " " + d.w + "x" + d.h +
|
|
46
|
+
(d.onScreen ? "" : " OFFSCREEN") + " n=" + d.n + (d.current ? ' cur="' + d.current + '"' : ""),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (title || painted) logBannerDom(tag, await probeBanner(page));
|
|
50
|
+
},
|
|
51
|
+
/** A navigation wipes the overlay DOM: rebuild what was authored (same job as the demo's post-navigation restore). */
|
|
52
|
+
async restore() {
|
|
53
|
+
if (!painted) return;
|
|
54
|
+
await paintChrome(false);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The waygraph OVERLAY stage: one owner of overlay state (todo docks, device, banner UX) and of how a resolved
|
|
2
|
+
// stub phase is applied to the page. demo / auto / browser / pilot all go through this, so a "waygraph overlay
|
|
3
|
+
// fixture" (ctx.todos / ctx.device / ctx.title / ...) means the same thing on every surface.
|
|
4
|
+
// NOT Playwright test fixtures - these paint the waygraph overlay on the page under test.
|
|
5
|
+
import { applyDevicePhase, applyTodoPhase, resolveTodoDockUi } from "../highlights.js";
|
|
6
|
+
import { applyDeviceToPage } from "./device-stage.js";
|
|
7
|
+
import { bannerUiFromPhase } from "./banner-log.js";
|
|
8
|
+
|
|
9
|
+
export function createOverlayStage(page, { todoDockRef, deviceRef }) {
|
|
10
|
+
/** Carried across steps: omitting a fixture in a later phase = keep (todos, device, banner UX). */
|
|
11
|
+
const state = { lastTodoDock: undefined, lastDevice: undefined, lastBannerUi: {} };
|
|
12
|
+
/** Author todoDockUi patches (merged); resolved against env each step. */
|
|
13
|
+
let lastTodoDockUiPatch = undefined;
|
|
14
|
+
/** Multi-todo: every dock keyed by todoId (or "_default"), survives navigation. */
|
|
15
|
+
const todoDockById = new Map();
|
|
16
|
+
|
|
17
|
+
const pushTodoDockUi = async (patch) => {
|
|
18
|
+
if (patch && typeof patch === "object") {
|
|
19
|
+
lastTodoDockUiPatch = { ...(lastTodoDockUiPatch || {}), ...patch };
|
|
20
|
+
}
|
|
21
|
+
const ui = resolveTodoDockUi(lastTodoDockUiPatch);
|
|
22
|
+
await page
|
|
23
|
+
.evaluate((u) => {
|
|
24
|
+
window.__wgTodoDockUi = u;
|
|
25
|
+
}, ui)
|
|
26
|
+
.catch(() => {});
|
|
27
|
+
};
|
|
28
|
+
const dockRegistryKey = (dock) =>
|
|
29
|
+
dock && dock.id && String(dock.id).trim() ? String(dock.id).trim() : "_default";
|
|
30
|
+
const rememberTodoDock = (dock, sync, parallel) => {
|
|
31
|
+
if (sync === "clear") {
|
|
32
|
+
todoDockById.clear();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (sync === "set" && dock) {
|
|
36
|
+
if (!parallel) todoDockById.clear();
|
|
37
|
+
todoDockById.set(dockRegistryKey(dock), dock);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// keep: refresh / re-seed so syncAllTodoDocks never sees an empty map
|
|
41
|
+
// while lastTodoDock still carries (nav wipe + mid-act advance).
|
|
42
|
+
if (sync === "keep" && dock) {
|
|
43
|
+
todoDockById.set(dockRegistryKey(dock), dock);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const syncAllTodoDocks = async (opts) => {
|
|
47
|
+
const docks = [...todoDockById.values()];
|
|
48
|
+
const parallel = !!(opts && opts.parallel);
|
|
49
|
+
if (!docks.length) {
|
|
50
|
+
// Empty registry: do NOT clear the DOM. renderBefore/After may have
|
|
51
|
+
// just painted from lastTodoDock (keep). Clearing here made todos
|
|
52
|
+
// vanish until the next ctx.todos() set (PIA #15).
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await page
|
|
56
|
+
.evaluate(
|
|
57
|
+
({ list, parallel }) => {
|
|
58
|
+
if (!window.__wgSyncTodos) return;
|
|
59
|
+
for (let i = 0; i < list.length; i++) {
|
|
60
|
+
const d = list[i];
|
|
61
|
+
window.__wgSyncTodos({
|
|
62
|
+
sync: "set",
|
|
63
|
+
dock: d,
|
|
64
|
+
todoId: (d && d.id) || null,
|
|
65
|
+
pos: (d && d.pos) || null,
|
|
66
|
+
// First dock replaces; later ones keep siblings when parallel.
|
|
67
|
+
replace: !(parallel && i > 0),
|
|
68
|
+
parallel: parallel && i > 0,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{ list: docks, parallel },
|
|
73
|
+
)
|
|
74
|
+
.catch(() => {});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Merge one resolved stub phase (banner UX, todo dock, device) into the carried state and push it to the page.
|
|
79
|
+
* `reapplyDeviceOnKeep`: also re-apply the carried device when the phase says keep (after a navigation wipe).
|
|
80
|
+
*/
|
|
81
|
+
const applyPhase = async (phase, { reapplyDeviceOnKeep = false } = {}) => {
|
|
82
|
+
state.lastBannerUi = { ...state.lastBannerUi, ...bannerUiFromPhase(phase) };
|
|
83
|
+
const appliedTodo = applyTodoPhase(state.lastTodoDock, {
|
|
84
|
+
todoSync: phase.todoSync,
|
|
85
|
+
todoDock: phase.todoDock,
|
|
86
|
+
todos: phase.todos,
|
|
87
|
+
todoPos: phase.todoPos,
|
|
88
|
+
});
|
|
89
|
+
state.lastTodoDock = appliedTodo.dock;
|
|
90
|
+
todoDockRef.current = state.lastTodoDock;
|
|
91
|
+
rememberTodoDock(appliedTodo.dock, appliedTodo.sync, !!phase.todoParallel);
|
|
92
|
+
const appliedDevice = applyDevicePhase(state.lastDevice, {
|
|
93
|
+
deviceSync: phase.deviceSync,
|
|
94
|
+
device: phase.device,
|
|
95
|
+
});
|
|
96
|
+
state.lastDevice = appliedDevice.device;
|
|
97
|
+
deviceRef.current = state.lastDevice;
|
|
98
|
+
if (appliedDevice.sync !== "keep" || (reapplyDeviceOnKeep && state.lastDevice)) {
|
|
99
|
+
await applyDeviceToPage(page, state.lastDevice, appliedDevice.sync);
|
|
100
|
+
}
|
|
101
|
+
return { appliedTodo, appliedDevice };
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return { state, pushTodoDockUi, rememberTodoDock, syncAllTodoDocks, applyPhase };
|
|
105
|
+
}
|
package/dist/runner/step-mode.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
|
|
2
2
|
// Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
|
|
3
3
|
import { instrumentInteractionHighlighting } from "./interaction-patch.js";
|
|
4
|
-
import {
|
|
4
|
+
import { applyHighlightStyleDefaults, demoPaceGateMs, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, formatHighlightCaption, hasAuthoredStubAfter, resolveSlides, resolveStepDemoPace, resolveTodoDockUi, runStubPhase } from "../highlights.js";
|
|
5
5
|
import { markStepRunning, resetPageState, teardownOverlay } from "./teardown.js";
|
|
6
6
|
import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
|
|
7
|
-
import { applyDeviceToPage } from "./device-stage.js";
|
|
8
7
|
import { extractVerifyHighlights, presentFailPanel, renderAfterStep, renderBeforeStep } from "./step-panels.js";
|
|
9
8
|
import { probeTodoDocksOnPage } from "./todo-dock.js";
|
|
9
|
+
import { logBannerDom, probeBanner } from "./banner-log.js";
|
|
10
|
+
import { createOverlayStage } from "./overlay-stage.js";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
import { presentSlides } from "./slides.js";
|
|
12
13
|
|
|
@@ -26,9 +27,9 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
26
27
|
const stepperMode = process.env.WAYGRAPH_STEPPER === "full" ? "full" : "carousel";
|
|
27
28
|
const pacing = { gatesFast: demoFast, gatesSlow: false, skipTheater: false, demoPace: "normal" };
|
|
28
29
|
const stubBeforeRef = { current: [] };
|
|
29
|
-
/** Live todo dock for Method fill/click advance (same carry as lastTodoDock). */
|
|
30
|
+
/** Live todo dock for Method fill/click advance (same carry as stage.state.lastTodoDock). */
|
|
30
31
|
const todoDockRef = { current: undefined };
|
|
31
|
-
/** Live device fixture for touch theater (same carry as lastDevice). */
|
|
32
|
+
/** Live device fixture for touch theater (same carry as stage.state.lastDevice). */
|
|
32
33
|
const deviceRef = { current: undefined };
|
|
33
34
|
instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef, deviceRef);
|
|
34
35
|
// Force the panel checkbox from this process's flags/env at run start.
|
|
@@ -158,73 +159,8 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
let result;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/** Author todoDockUi patches (merged); resolved against env each step. */
|
|
164
|
-
let lastTodoDockUiPatch = undefined;
|
|
165
|
-
/** Multi-todo: every dock keyed by todoId (or "_default"), survives navigation. */
|
|
166
|
-
const todoDockById = new Map();
|
|
167
|
-
const pushTodoDockUi = async (patch) => {
|
|
168
|
-
if (patch && typeof patch === "object") {
|
|
169
|
-
lastTodoDockUiPatch = { ...(lastTodoDockUiPatch || {}), ...patch };
|
|
170
|
-
}
|
|
171
|
-
const ui = resolveTodoDockUi(lastTodoDockUiPatch);
|
|
172
|
-
await page
|
|
173
|
-
.evaluate((u) => {
|
|
174
|
-
window.__wgTodoDockUi = u;
|
|
175
|
-
}, ui)
|
|
176
|
-
.catch(() => {});
|
|
177
|
-
};
|
|
178
|
-
const dockRegistryKey = (dock) =>
|
|
179
|
-
dock && dock.id && String(dock.id).trim() ? String(dock.id).trim() : "_default";
|
|
180
|
-
const rememberTodoDock = (dock, sync, parallel) => {
|
|
181
|
-
if (sync === "clear") {
|
|
182
|
-
todoDockById.clear();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (sync === "set" && dock) {
|
|
186
|
-
if (!parallel) todoDockById.clear();
|
|
187
|
-
todoDockById.set(dockRegistryKey(dock), dock);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
// keep: refresh / re-seed so syncAllTodoDocks never sees an empty map
|
|
191
|
-
// while lastTodoDock still carries (nav wipe + mid-act advance).
|
|
192
|
-
if (sync === "keep" && dock) {
|
|
193
|
-
todoDockById.set(dockRegistryKey(dock), dock);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const syncAllTodoDocks = async (opts) => {
|
|
197
|
-
const docks = [...todoDockById.values()];
|
|
198
|
-
const parallel = !!(opts && opts.parallel);
|
|
199
|
-
if (!docks.length) {
|
|
200
|
-
// Empty registry: do NOT clear the DOM. renderBefore/After may have
|
|
201
|
-
// just painted from lastTodoDock (keep). Clearing here made todos
|
|
202
|
-
// vanish until the next ctx.todos() set (PIA #15).
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
await page
|
|
206
|
-
.evaluate(
|
|
207
|
-
({ list, parallel }) => {
|
|
208
|
-
if (!window.__wgSyncTodos) return;
|
|
209
|
-
for (let i = 0; i < list.length; i++) {
|
|
210
|
-
const d = list[i];
|
|
211
|
-
window.__wgSyncTodos({
|
|
212
|
-
sync: "set",
|
|
213
|
-
dock: d,
|
|
214
|
-
todoId: (d && d.id) || null,
|
|
215
|
-
pos: (d && d.pos) || null,
|
|
216
|
-
// First dock replaces; later ones keep siblings when parallel.
|
|
217
|
-
replace: !(parallel && i > 0),
|
|
218
|
-
parallel: parallel && i > 0,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
{ list: docks, parallel },
|
|
223
|
-
)
|
|
224
|
-
.catch(() => {});
|
|
225
|
-
};
|
|
226
|
-
/** Carry device / touch fixture across steps (omit = keep, like todos). */
|
|
227
|
-
let lastDevice = undefined;
|
|
162
|
+
const stage = createOverlayStage(page, { todoDockRef, deviceRef });
|
|
163
|
+
const { pushTodoDockUi, rememberTodoDock, syncAllTodoDocks } = stage;
|
|
228
164
|
for (let i = 0; i < resolved.length; i++) {
|
|
229
165
|
const r = resolved[i];
|
|
230
166
|
// The block breadcrumb is scoped to THIS step's own episode, not the
|
|
@@ -322,26 +258,12 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
322
258
|
label: formatHighlightCaption(h),
|
|
323
259
|
})),
|
|
324
260
|
});
|
|
325
|
-
const appliedBefore =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
todoPos: stubBeforePhase.todoPos,
|
|
330
|
-
});
|
|
331
|
-
lastTodoDock = appliedBefore.dock;
|
|
332
|
-
todoDockRef.current = lastTodoDock;
|
|
333
|
-
rememberTodoDock(appliedBefore.dock, appliedBefore.sync, !!stubBeforePhase.todoParallel);
|
|
334
|
-
const appliedDeviceBefore = applyDevicePhase(lastDevice, {
|
|
335
|
-
deviceSync: stubBeforePhase.deviceSync,
|
|
336
|
-
device: stubBeforePhase.device,
|
|
337
|
-
});
|
|
338
|
-
lastDevice = appliedDeviceBefore.device;
|
|
339
|
-
deviceRef.current = lastDevice;
|
|
340
|
-
if (appliedDeviceBefore.sync !== "keep" || lastDevice) {
|
|
341
|
-
await applyDeviceToPage(page, lastDevice, appliedDeviceBefore.sync);
|
|
342
|
-
}
|
|
261
|
+
const { appliedTodo: appliedBefore, appliedDevice: appliedDeviceBefore } = await stage.applyPhase(
|
|
262
|
+
stubBeforePhase,
|
|
263
|
+
{ reapplyDeviceOnKeep: true },
|
|
264
|
+
);
|
|
343
265
|
const stubBeforeTodos =
|
|
344
|
-
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
266
|
+
(stage.state.lastTodoDock && stage.state.lastTodoDock.groups[0] && stage.state.lastTodoDock.groups[0].items) ||
|
|
345
267
|
stubBeforePhase.todos ||
|
|
346
268
|
[];
|
|
347
269
|
const overlayTitle =
|
|
@@ -350,7 +272,7 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
350
272
|
title ||
|
|
351
273
|
"waygraph demo";
|
|
352
274
|
const overlayTodoPos =
|
|
353
|
-
(lastTodoDock && lastTodoDock.pos) || stubBeforePhase.todoPos || undefined;
|
|
275
|
+
(stage.state.lastTodoDock && stage.state.lastTodoDock.pos) || stubBeforePhase.todoPos || undefined;
|
|
354
276
|
const isNavBlock = r.block.__waygraphKind === "nav";
|
|
355
277
|
const autoNow = await currentAutoplay();
|
|
356
278
|
// Video / --mini: compact pill. Never pass false - Hide/localStorage wins.
|
|
@@ -391,7 +313,7 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
391
313
|
" gatesFast=" +
|
|
392
314
|
!!pacing.gatesFast,
|
|
393
315
|
);
|
|
394
|
-
demoLog(" " + summarizeDevice(lastDevice));
|
|
316
|
+
demoLog(" " + summarizeDevice(stage.state.lastDevice));
|
|
395
317
|
const phaseZoom =
|
|
396
318
|
stubBeforePhase.zoom != null && Number(stubBeforePhase.zoom) > 0
|
|
397
319
|
? Number(stubBeforePhase.zoom)
|
|
@@ -404,7 +326,7 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
404
326
|
(phaseZoomOut != null ? " zoomOut=" + phaseZoomOut : "") +
|
|
405
327
|
" (StubCtx / API; live chip top-left when ring zoom >1)",
|
|
406
328
|
);
|
|
407
|
-
logTodoDockFull(lastTodoDock, " before");
|
|
329
|
+
logTodoDockFull(stage.state.lastTodoDock, " before");
|
|
408
330
|
demoLog(" todoSync=" + appliedBefore.sync);
|
|
409
331
|
}
|
|
410
332
|
await renderBeforeStep(page, {
|
|
@@ -419,22 +341,23 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
419
341
|
allEpisodes,
|
|
420
342
|
justEnteredEpisode,
|
|
421
343
|
title: overlayTitle,
|
|
344
|
+
bannerUi: stage.state.lastBannerUi,
|
|
422
345
|
todoPos: overlayTodoPos,
|
|
423
346
|
episodeNumber: r.episodeNumber,
|
|
424
347
|
episodeTitle: r.episodeTitle,
|
|
425
348
|
stepperMode,
|
|
426
349
|
forceCollapsed,
|
|
427
350
|
todos: stubBeforeTodos,
|
|
428
|
-
todoDock: lastTodoDock,
|
|
351
|
+
todoDock: stage.state.lastTodoDock,
|
|
429
352
|
todoSync: appliedBefore.sync,
|
|
430
|
-
todoId: lastTodoDock && lastTodoDock.id,
|
|
353
|
+
todoId: stage.state.lastTodoDock && stage.state.lastTodoDock.id,
|
|
431
354
|
...paceSpeak,
|
|
432
355
|
});
|
|
433
356
|
// Re-paint dock(s) - renderBeforeStep syncs the active one; this enforces replace.
|
|
434
357
|
await syncAllTodoDocks({ parallel: !!stubBeforePhase.todoParallel });
|
|
435
358
|
if (process.env.WAYGRAPH_JSON !== "1") {
|
|
436
359
|
const docks = await probeTodoDocksOnPage(page);
|
|
437
|
-
if (!docks.length && lastTodoDock) {
|
|
360
|
+
if (!docks.length && stage.state.lastTodoDock) {
|
|
438
361
|
demoLog(" WARN todos authored but dock-dom empty (not painted?)");
|
|
439
362
|
} else if (docks.length) {
|
|
440
363
|
demoLog(
|
|
@@ -462,6 +385,7 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
462
385
|
);
|
|
463
386
|
}
|
|
464
387
|
}
|
|
388
|
+
if (process.env.WAYGRAPH_JSON !== "1") logBannerDom("before", await probeBanner(page));
|
|
465
389
|
const edits = await gate();
|
|
466
390
|
for (const k of requires) {
|
|
467
391
|
if (edits[k.name] !== undefined) {
|
|
@@ -554,9 +478,9 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
554
478
|
.catch(() => {});
|
|
555
479
|
result = stepOutcome.result;
|
|
556
480
|
// Mid-act Method fill/click advances live on todoDockRef - fold that
|
|
557
|
-
// back into lastTodoDock before stubAfter / next-block keep, or the
|
|
481
|
+
// back into stage.state.lastTodoDock before stubAfter / next-block keep, or the
|
|
558
482
|
// next step reverts to the pre-act (blank / index-0) checklist.
|
|
559
|
-
if (todoDockRef.current) lastTodoDock = todoDockRef.current;
|
|
483
|
+
if (todoDockRef.current) stage.state.lastTodoDock = todoDockRef.current;
|
|
560
484
|
// withExpectedFailure last block that SUCCEEDS on the intentional fail
|
|
561
485
|
// branch (e.g. submit-login -> LoginPage + error banner). Still show
|
|
562
486
|
// stubOnError rings + amber "expected outcome" panel - branching no
|
|
@@ -621,8 +545,8 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
621
545
|
let stubAfterTodos = [];
|
|
622
546
|
let afterOverlayTitle = overlayTitle;
|
|
623
547
|
let afterTodoPos = overlayTodoPos;
|
|
624
|
-
let appliedAfter = { dock: lastTodoDock, sync: "keep" };
|
|
625
|
-
let appliedDeviceAfter = { device: lastDevice, sync: "keep" };
|
|
548
|
+
let appliedAfter = { dock: stage.state.lastTodoDock, sync: "keep" };
|
|
549
|
+
let appliedDeviceAfter = { device: stage.state.lastDevice, sync: "keep" };
|
|
626
550
|
let afterTodoParallel = false;
|
|
627
551
|
if (hasAuthoredStubAfter(r.block, result, fixturesAfter)) {
|
|
628
552
|
const afterPhase = await runStubPhase(r.block, "stubAfter", {
|
|
@@ -641,33 +565,16 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
641
565
|
}),
|
|
642
566
|
});
|
|
643
567
|
afterTodoParallel = !!afterPhase.todoParallel;
|
|
644
|
-
appliedAfter =
|
|
645
|
-
todoSync: afterPhase.todoSync,
|
|
646
|
-
todoDock: afterPhase.todoDock,
|
|
647
|
-
todos: afterPhase.todos,
|
|
648
|
-
todoPos: afterPhase.todoPos,
|
|
649
|
-
});
|
|
650
|
-
lastTodoDock = appliedAfter.dock;
|
|
651
|
-
todoDockRef.current = lastTodoDock;
|
|
652
|
-
rememberTodoDock(appliedAfter.dock, appliedAfter.sync, afterTodoParallel);
|
|
653
|
-
appliedDeviceAfter = applyDevicePhase(lastDevice, {
|
|
654
|
-
deviceSync: afterPhase.deviceSync,
|
|
655
|
-
device: afterPhase.device,
|
|
656
|
-
});
|
|
657
|
-
lastDevice = appliedDeviceAfter.device;
|
|
658
|
-
deviceRef.current = lastDevice;
|
|
659
|
-
if (appliedDeviceAfter.sync !== "keep" || (appliedDeviceAfter.sync === "set" && lastDevice)) {
|
|
660
|
-
await applyDeviceToPage(page, lastDevice, appliedDeviceAfter.sync);
|
|
661
|
-
}
|
|
568
|
+
({ appliedTodo: appliedAfter, appliedDevice: appliedDeviceAfter } = await stage.applyPhase(afterPhase));
|
|
662
569
|
stubAfterTodos =
|
|
663
|
-
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
570
|
+
(stage.state.lastTodoDock && stage.state.lastTodoDock.groups[0] && stage.state.lastTodoDock.groups[0].items) ||
|
|
664
571
|
afterPhase.todos ||
|
|
665
572
|
[];
|
|
666
573
|
if (afterPhase.title && String(afterPhase.title).trim()) {
|
|
667
574
|
afterOverlayTitle = String(afterPhase.title).trim();
|
|
668
575
|
}
|
|
669
576
|
if (afterPhase.todoPos) afterTodoPos = afterPhase.todoPos;
|
|
670
|
-
if (lastTodoDock && lastTodoDock.pos) afterTodoPos = lastTodoDock.pos;
|
|
577
|
+
if (stage.state.lastTodoDock && stage.state.lastTodoDock.pos) afterTodoPos = stage.state.lastTodoDock.pos;
|
|
671
578
|
highlights = afterPhase.highlights.map((h) => {
|
|
672
579
|
const styled = applyHighlightStyleDefaults(h, r.highlightStyle);
|
|
673
580
|
return {
|
|
@@ -707,14 +614,15 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
707
614
|
moduleIndex,
|
|
708
615
|
allEpisodes,
|
|
709
616
|
title: afterOverlayTitle,
|
|
617
|
+
bannerUi: stage.state.lastBannerUi,
|
|
710
618
|
todoPos: afterTodoPos,
|
|
711
619
|
episodeNumber: r.episodeNumber,
|
|
712
620
|
episodeTitle: r.episodeTitle,
|
|
713
621
|
stepperMode,
|
|
714
622
|
todos: stubAfterTodos,
|
|
715
|
-
todoDock: lastTodoDock,
|
|
623
|
+
todoDock: stage.state.lastTodoDock,
|
|
716
624
|
todoSync: appliedAfter.sync,
|
|
717
|
-
todoId: lastTodoDock && lastTodoDock.id,
|
|
625
|
+
todoId: stage.state.lastTodoDock && stage.state.lastTodoDock.id,
|
|
718
626
|
todoDockRef,
|
|
719
627
|
forceCollapsed:
|
|
720
628
|
!!process.env.WAYGRAPH_VIDEO ||
|
|
@@ -726,20 +634,20 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
726
634
|
await syncAllTodoDocks({ parallel: afterTodoParallel });
|
|
727
635
|
// Ring cycle / stubAfter may have advanced the dock - persist for next block.
|
|
728
636
|
if (todoDockRef.current) {
|
|
729
|
-
lastTodoDock = todoDockRef.current;
|
|
637
|
+
stage.state.lastTodoDock = todoDockRef.current;
|
|
730
638
|
rememberTodoDock(todoDockRef.current, "set", afterTodoParallel);
|
|
731
639
|
}
|
|
732
640
|
if (process.env.WAYGRAPH_JSON !== "1") {
|
|
733
641
|
demoLog(
|
|
734
642
|
" after " +
|
|
735
|
-
summarizeDevice(lastDevice) +
|
|
643
|
+
summarizeDevice(stage.state.lastDevice) +
|
|
736
644
|
" deviceSync=" +
|
|
737
645
|
appliedDeviceAfter.sync,
|
|
738
646
|
);
|
|
739
|
-
logTodoDockFull(lastTodoDock, " after");
|
|
647
|
+
logTodoDockFull(stage.state.lastTodoDock, " after");
|
|
740
648
|
demoLog(" after todoSync=" + appliedAfter.sync);
|
|
741
649
|
const docksAfter = await probeTodoDocksOnPage(page);
|
|
742
|
-
if (!docksAfter.length && lastTodoDock && appliedAfter.sync !== "clear") {
|
|
650
|
+
if (!docksAfter.length && stage.state.lastTodoDock && appliedAfter.sync !== "clear") {
|
|
743
651
|
demoLog(" WARN after: todos authored but dock-dom empty");
|
|
744
652
|
} else if (docksAfter.length) {
|
|
745
653
|
demoLog(
|
|
@@ -769,6 +677,7 @@ export async function runStepMode(engine, start, end, context, page, mem, resolv
|
|
|
769
677
|
demoLog(" after dock-dom cleared (expected)");
|
|
770
678
|
}
|
|
771
679
|
}
|
|
680
|
+
if (process.env.WAYGRAPH_JSON !== "1") logBannerDom("after", await probeBanner(page));
|
|
772
681
|
await gate();
|
|
773
682
|
}
|
|
774
683
|
await teardownOverlay(page);
|
|
@@ -6,7 +6,7 @@ import { applyHighlightStyleDefaults, formatHighlightCaption, hasAuthoredStubOnE
|
|
|
6
6
|
import { cycleHighlightRings } from "./rings.js";
|
|
7
7
|
|
|
8
8
|
export async function renderBeforeStep(page, info) {
|
|
9
|
-
await installOverlay(page, info.title);
|
|
9
|
+
await installOverlay(page, info.title, info.bannerUi);
|
|
10
10
|
// A ring left highlighting the PREVIOUS step's element (and its live
|
|
11
11
|
// resize/scroll tracker) shouldn't linger once a new step's own panel is
|
|
12
12
|
// up - only relevant when a Block's act() doesn't navigate away, since a
|
|
@@ -375,7 +375,7 @@ export async function presentFailPanel(page, opts) {
|
|
|
375
375
|
|
|
376
376
|
|
|
377
377
|
export async function renderAfterStep(page, info) {
|
|
378
|
-
await installOverlay(page, info.title);
|
|
378
|
+
await installOverlay(page, info.title, info.bannerUi);
|
|
379
379
|
// Clear any tracker from a previous highlight before cycling through this
|
|
380
380
|
// step's own.
|
|
381
381
|
await page
|
|
@@ -574,7 +574,7 @@ export async function renderAfterStep(page, info) {
|
|
|
574
574
|
* regardless - main()'s own try/finally - but not before this is visible).
|
|
575
575
|
*/
|
|
576
576
|
async function renderStepError(page, info) {
|
|
577
|
-
await installOverlay(page, info.title);
|
|
577
|
+
await installOverlay(page, info.title, info.bannerUi);
|
|
578
578
|
const errorPayload = { ...info, stepLabel: miniStepLabel(info) };
|
|
579
579
|
await page
|
|
580
580
|
.evaluate((info) => {
|
package/dist/step-overlay.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { ensureShadowRoot } from "./ui/shadow.js";
|
|
2
|
-
import { composeStepOverlay } from "./ui/compose.js";
|
|
3
|
-
const RING_CSS = composeStepOverlay();
|
|
4
2
|
const FAVICON = "data:image/svg+xml," +
|
|
5
3
|
encodeURIComponent("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><circle cx='16' cy='16' r='14' fill='#7C3AED'/></svg>");
|
|
6
4
|
export async function installDemoChrome(page, title = "waygraph auto", options = {}) {
|
|
7
5
|
const showBanner = options.banner !== false;
|
|
6
|
+
// ring/cursor/banner CSS comes from the one shared bundle - see ui/css/README.md.
|
|
8
7
|
await ensureShadowRoot(page);
|
|
9
|
-
await page.evaluate(([css, key]) => __wgCss(css, key), [RING_CSS, "wg-overlay-css"]).catch(() => { });
|
|
10
8
|
await page
|
|
11
9
|
.evaluate(({ title, favicon, showBanner }) => {
|
|
12
10
|
if (!__wgById("wg-ring")) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { TONES } from "../tokens.js";
|
|
2
|
+
/** Prefix icon for gray (auto) labels. */
|
|
3
|
+
const ROBOT = 'content:"\\1F916\\00a0";';
|
|
2
4
|
const toneNames = Object.keys(TONES);
|
|
3
5
|
/** Tone rules for `sel[data-tone=x]{...}` from the token table. */
|
|
4
6
|
function toneRules(sel, decl, only) {
|
|
@@ -17,6 +19,8 @@ export function ringCss() {
|
|
|
17
19
|
`max-width:min(360px,70vw);white-space:normal;padding:6px 10px;border-radius:7px;background:${p.labelBg};color:${p.labelFg};` +
|
|
18
20
|
"font:600 12px/1.35 system-ui,sans-serif;transition:opacity .3s ease;}" +
|
|
19
21
|
toneRules("#wg-ring-label", (t) => `background:${t.labelBg};color:${t.labelFg};`) +
|
|
22
|
+
// Gray = a real Playwright-driven instruction: mark it with a robot.
|
|
23
|
+
`#wg-ring-label[data-tone=auto]::before{${ROBOT}}` +
|
|
20
24
|
"#wg-ring[data-size=sm]{border-width:1.5px;border-radius:8px;}" +
|
|
21
25
|
"#wg-ring[data-size=lg]{border-width:4px;border-radius:12px;}" +
|
|
22
26
|
"#wg-ring-label[data-size=sm]{font-size:10px;line-height:1.25;padding:4px 7px;border-radius:5px;}" +
|
|
@@ -36,6 +40,7 @@ export function pilotFxRingCss() {
|
|
|
36
40
|
"max-width:min(360px,80vw);box-shadow:0 2px 8px rgba(0,0,0,.25);white-space:nowrap;" +
|
|
37
41
|
"overflow:hidden;text-overflow:ellipsis;transition:opacity .12s ease;}" +
|
|
38
42
|
toneRules(".wg-pilot-fx-label", (t) => `background:${t.labelBg};color:${t.labelFg};`) +
|
|
43
|
+
`.wg-pilot-fx-label[data-tone=auto]::before{${ROBOT}}` +
|
|
39
44
|
".wg-pilot-fx-ring[data-size=sm]{border-width:1.5px;border-radius:8px;}" +
|
|
40
45
|
".wg-pilot-fx-ring[data-size=lg]{border-width:4px;border-radius:12px;}" +
|
|
41
46
|
".wg-pilot-fx-label[data-size=sm]{font-size:10px;padding:4px 7px;}" +
|