waygraph 0.16.2 → 0.16.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.
- package/README.md +12 -0
- package/dist/analysis/practices-check.d.ts +1 -1
- package/dist/analysis/practices-check.js +36 -0
- package/dist/beta.d.ts +6 -0
- package/dist/beta.js +5 -0
- package/dist/cli/commands/codemod-flows.js +2 -2
- package/dist/cli/commands/codemod-stubs.js +43 -1
- package/dist/cli/commands/pilot.js +3 -16
- package/dist/cli/commands/session-ops.js +26 -1
- package/dist/cli/flags.d.ts +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/usage.js +6 -0
- package/dist/demo/narration/stub-ctx.js +126 -124
- package/dist/demo/overlay/cursor.js +1 -1
- package/dist/demo/overlay/inpage/banner.js +5 -0
- package/dist/demo/overlay/inpage/core.js +1 -0
- package/dist/demo/overlay/inpage/network.js +1 -1
- package/dist/demo/overlay/inpage/todos.js +0 -15
- package/dist/demo/overlay/network-capture.js +2 -2
- package/dist/demo/overlay/overlay-css.js +18 -21
- package/dist/demo/overlay/overlay-stage.js +1 -1
- package/dist/demo/overlay/phase-fixtures.js +5 -5
- package/dist/demo/overlay/pilot/shell.js +7 -5
- package/dist/demo/overlay/ui/components/ring.js +2 -2
- package/dist/demo/overlay/ui/overlay.css +98 -35
- package/dist/demo/overlay/ui/shadow.js +69 -2
- package/dist/demo/stepper/demo-log.js +8 -8
- package/dist/demo/stepper/slides.d.ts +1 -1
- package/dist/demo/stepper/slides.js +1 -1
- package/dist/demo/stepper/stepper.js +1 -1
- package/dist/engine/block/kind.d.ts +3 -0
- package/dist/engine/block/kind.js +11 -0
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +12 -0
- package/dist/engine/read/read-block.d.ts +61 -0
- package/dist/engine/read/read-block.js +94 -0
- package/dist/engine/read/read-key.d.ts +32 -0
- package/dist/engine/read/read-key.js +34 -0
- package/dist/engine/read/read-page.d.ts +19 -0
- package/dist/engine/read/read-page.js +50 -0
- package/dist/engine/read/readers.d.ts +82 -0
- package/dist/engine/read/readers.js +43 -0
- package/dist/engine/read/run-read.d.ts +17 -0
- package/dist/engine/read/run-read.js +156 -0
- package/dist/engine/run/connect.js +3 -2
- package/dist/engine/run/events.d.ts +28 -10
- package/dist/engine/run/events.js +52 -38
- package/dist/engine/run/run.js +25 -13
- package/dist/engine/stub/stub.d.ts +26 -21
- package/dist/index.d.ts +7 -4
- package/dist/index.js +9 -4
- package/dist/live/auto/auto.js +2 -3
- package/dist/live/auto/menu.js +3 -2
- package/dist/live/auto-session/auto-session.d.ts +9 -2
- package/dist/live/auto-session/auto-session.js +23 -20
- package/dist/live/auto-session/feed.d.ts +16 -0
- package/dist/live/auto-session/feed.js +71 -0
- package/dist/live/auto-session/helpers.d.ts +7 -0
- package/dist/live/auto-session/helpers.js +9 -0
- package/dist/live/auto-session/ipc.d.ts +15 -47
- package/dist/live/auto-session/ipc.js +34 -4
- package/dist/live/auto-session/protocol.d.ts +127 -0
- package/dist/live/auto-session/protocol.js +6 -0
- package/dist/live/auto-session/read.d.ts +25 -0
- package/dist/live/auto-session/read.js +50 -0
- package/dist/live/traverse/worker.d.ts +1 -0
- package/dist/live/traverse/worker.js +6 -1
- package/dist/manifest/manifest.d.ts +22 -2
- package/dist/manifest/manifest.js +27 -1
- package/dist/manifest/project.d.ts +10 -1
- package/dist/manifest/project.js +9 -12
- package/dist/manifest/read-blocks.d.ts +2 -0
- package/dist/manifest/read-blocks.js +23 -0
- package/dist/manifest/scan.d.ts +3 -2
- package/dist/manifest/scan.js +8 -4
- package/dist/runner/runner.d.ts +0 -1
- package/dist/runner/runner.js +6 -6
- package/dist/runtime/full-launch.d.ts +2 -1
- package/dist/runtime/full-launch.js +29 -27
- package/dist/runtime/run-log.d.ts +2 -0
- package/dist/runtime/run-log.js +13 -6
- package/examples/saucedemo/README.md +2 -2
- package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
- package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
- package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout.flow.ts +11 -11
- package/examples/saucedemo/src/flows/login.flow.ts +1 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +1 -1
- package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
- package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
- package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
- package/examples/saucedemo/src/stubs/device-showcase.ts +37 -37
- package/examples/saucedemo/src/stubs/finish-order.ts +2 -2
- package/examples/saucedemo/src/stubs/login.ts +5 -5
- package/package.json +5 -1
- package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
- package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout.flow.ts +11 -11
- package/templates/quickstart/src/flows/login.flow.ts +1 -1
- package/templates/quickstart/src/flows/shop.flow.ts +1 -1
- package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
- package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
- package/templates/quickstart/src/stubs/device-showcase.ts +37 -37
- package/templates/quickstart/src/stubs/finish-order.ts +2 -2
- package/templates/quickstart/src/stubs/login.ts +5 -5
- package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
- package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
- package/templates/scaffold/src/flows/README.md +1 -1
- package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
- package/templates/scaffold/src/flows/home/hello.flow.ts +8 -8
- package/templates/skills/waygraph-convention.skill.md +10 -2
- package/templates/skills/waygraph-pilot.skill.md +7 -0
|
@@ -209,6 +209,7 @@ export function installCore({ envAutoplay, todoDockUi }) {
|
|
|
209
209
|
continue;
|
|
210
210
|
const cur = dock.dataset.pos === "right" ? "right" : "left";
|
|
211
211
|
dock.dataset.pos = cur === "right" ? "left" : "right";
|
|
212
|
+
__wgAdd(dock);
|
|
212
213
|
try {
|
|
213
214
|
localStorage.setItem("wg-todo-pos", dock.dataset.pos);
|
|
214
215
|
}
|
|
@@ -145,7 +145,7 @@ export function addNetworkEntry(entry) {
|
|
|
145
145
|
const countEl = panel.querySelector(".wg-net-count");
|
|
146
146
|
if (countEl)
|
|
147
147
|
countEl.textContent = String(w.__wgNetworkEntries.length);
|
|
148
|
-
// Always captured, only shown when explicitly turned on (ctx.showNetworkTab(); default hidden -
|
|
148
|
+
// Always captured, only shown when explicitly turned on (ctx.ui.showNetworkTab(); default hidden -
|
|
149
149
|
// see StubCtx's own doc comment) - the DOM/row still gets built either way, so toggling visible
|
|
150
150
|
// later shows the full history immediately, not just entries captured after that point.
|
|
151
151
|
if (w.__wgNetworkTabVisible) {
|
|
@@ -38,19 +38,6 @@ export function installTodos({ todoPos }) {
|
|
|
38
38
|
// Drop any in-panel leftover (old builds put #wg-todos in .wg-body).
|
|
39
39
|
__wgQA("#wg-panel #wg-todos").forEach((el) => el.remove());
|
|
40
40
|
const allDocks = () => [...__wgQA(".wg-todo-dock, #wg-todo-dock")].filter((el, i, arr) => arr.indexOf(el) === i);
|
|
41
|
-
const relayoutTodoDocks = () => {
|
|
42
|
-
const docks = allDocks();
|
|
43
|
-
// Must match .wg-todo-dock/#wg-todo-dock's own CSS `top` (demo/overlay/overlay-css.ts,
|
|
44
|
-
// ui/css/dock.css) - this inline style OVERRIDES that CSS the instant more than one
|
|
45
|
-
// dock exists (multi-todo stacking), so a CSS-only change here is silently underwater;
|
|
46
|
-
// real bug this was: the CSS top was bumped 72 -> 104 for banner clearance, but this
|
|
47
|
-
// hardcoded 72 kept winning at runtime and nothing actually moved.
|
|
48
|
-
let top = 104;
|
|
49
|
-
for (const el of docks) {
|
|
50
|
-
el.style.top = top + "px";
|
|
51
|
-
top += Math.max(48, el.getBoundingClientRect().height) + 10;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
41
|
const findDock = (key) => {
|
|
55
42
|
if (key === "_default") {
|
|
56
43
|
return (__wgQ('.wg-todo-dock[data-wg-todo-key="_default"]') ||
|
|
@@ -88,7 +75,6 @@ export function installTodos({ todoPos }) {
|
|
|
88
75
|
else {
|
|
89
76
|
allDocks().forEach((el) => el.remove());
|
|
90
77
|
}
|
|
91
|
-
relayoutTodoDocks();
|
|
92
78
|
return;
|
|
93
79
|
}
|
|
94
80
|
if (sync === "set" && replace) {
|
|
@@ -285,7 +271,6 @@ export function installTodos({ todoPos }) {
|
|
|
285
271
|
});
|
|
286
272
|
}
|
|
287
273
|
dock._wgLastPayload = syncPayload;
|
|
288
|
-
relayoutTodoDocks();
|
|
289
274
|
if (window.__wgStampModal) {
|
|
290
275
|
window.__wgStampModal(dock, "todos", { ready: true });
|
|
291
276
|
}
|
|
@@ -55,6 +55,8 @@ export async function restoreNetworkHistory(page) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
export function installNetworkCapture(page) {
|
|
58
|
+
// defineApiBlock reports its calls through the engine's hook; show them in the same panel.
|
|
59
|
+
setApiCallRecorder((p, entry) => recordNetworkEntry(p, entry));
|
|
58
60
|
if (page.__wgNetworkCaptureInstalled)
|
|
59
61
|
return;
|
|
60
62
|
page.__wgNetworkCaptureInstalled = true;
|
|
@@ -123,5 +125,3 @@ export function installNetworkCapture(page) {
|
|
|
123
125
|
});
|
|
124
126
|
});
|
|
125
127
|
}
|
|
126
|
-
// defineApiBlock reports its calls through core's hook; show them in the same panel.
|
|
127
|
-
setApiCallRecorder((page, entry) => recordNetworkEntry(page, entry));
|
|
@@ -15,12 +15,12 @@ import { SURFACE } from "./ui/tokens.js";
|
|
|
15
15
|
// never collides with that bundle.
|
|
16
16
|
export const RUNNER_EXTRA_CSS =
|
|
17
17
|
// Spotlight: dim everything except the highlight target (focus: true).
|
|
18
|
-
"#wg-focus-veil{position:fixed;z-index:
|
|
18
|
+
"#wg-focus-veil{position:fixed;z-index:var(--wg-z-focus);pointer-events:none;" +
|
|
19
19
|
"border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);" +
|
|
20
20
|
"opacity:0;transition:opacity .3s ease,left .3s ease,top .3s ease,width .3s ease,height .3s ease;}" +
|
|
21
21
|
"#wg-focus-veil.wg-in{opacity:1;}" +
|
|
22
22
|
// Touch swipe trail (mobile/tablet orientation + device theater).
|
|
23
|
-
"#wg-swipe-layer{position:fixed;inset:0;z-index:
|
|
23
|
+
"#wg-swipe-layer{position:fixed;inset:0;z-index:var(--wg-z-cursor);pointer-events:none;" +
|
|
24
24
|
"overflow:hidden;}" +
|
|
25
25
|
"#wg-swipe-layer .wg-swipe-dot{position:absolute;width:14px;height:14px;margin:-7px 0 0 -7px;" +
|
|
26
26
|
"border-radius:50%;background:rgba(147,197,253,.55);border:2px solid rgba(255,255,255,.85);" +
|
|
@@ -34,8 +34,8 @@ export const RUNNER_EXTRA_CSS =
|
|
|
34
34
|
"border:1px solid rgba(56,189,248,.55);font:700 12px/1.2 system-ui,sans-serif;" +
|
|
35
35
|
"letter-spacing:.04em;text-transform:uppercase;opacity:0;transition:opacity .25s ease;}" +
|
|
36
36
|
"#wg-swipe-layer.wg-in .wg-swipe-label{opacity:1;}" +
|
|
37
|
-
"#wg-panel{
|
|
38
|
-
"
|
|
37
|
+
"#wg-panel{z-index:var(--wg-z-top);" +
|
|
38
|
+
"width:min(92vw,640px);overflow-y:auto;box-sizing:border-box;" +
|
|
39
39
|
"background:" + SURFACE + ";color:#fff;border-radius:14px;" +
|
|
40
40
|
"padding:16px 20px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35);" +
|
|
41
41
|
// .06s was tuned back when the panel was removed and recreated on
|
|
@@ -78,16 +78,14 @@ export const RUNNER_EXTRA_CSS =
|
|
|
78
78
|
// Todo checklist(s) float outside #wg-panel so --mini / Hide never hide them.
|
|
79
79
|
// Multiple docks (one per todoId) are supported - stacked on the same side.
|
|
80
80
|
// Click a dock to slide left <-> right (WAYGRAPH_TODO_POS / --todo-left|right).
|
|
81
|
-
".wg-todo-dock,#wg-todo-dock{
|
|
82
|
-
"width:min(280px,42vw);
|
|
81
|
+
".wg-todo-dock,#wg-todo-dock{z-index:var(--wg-z-dock);" +
|
|
82
|
+
"width:min(280px,42vw);overflow:auto;box-sizing:border-box;" +
|
|
83
83
|
"padding:10px 12px;background:" + SURFACE + ";color:#fff;border-radius:12px;" +
|
|
84
84
|
"border:1px solid rgba(124,58,237,.45);box-shadow:0 8px 24px rgba(0,0,0,.35);" +
|
|
85
85
|
"pointer-events:auto;cursor:pointer;" +
|
|
86
|
-
"transition:
|
|
87
|
-
".wg-todo-dock[data-pos=right],#wg-todo-dock[data-pos=right]{transform:translateX(calc(100vw - 100% - 28px));}" +
|
|
88
|
-
".wg-todo-dock[data-pos=left],#wg-todo-dock[data-pos=left]{transform:translateX(0);}" +
|
|
86
|
+
"transition:opacity .25s ease,z-index 0s;}" +
|
|
89
87
|
/* While a highlight ring is up: sit under the ring so captions stay readable. */
|
|
90
|
-
".wg-todo-dock.wg-todo-behind,#wg-todo-dock.wg-todo-behind{z-index:
|
|
88
|
+
".wg-todo-dock.wg-todo-behind,#wg-todo-dock.wg-todo-behind{z-index:var(--wg-z-focus);opacity:.42;}" +
|
|
91
89
|
/* Collision tuck: shrink so a mid-page ring can still breathe. */
|
|
92
90
|
".wg-todo-dock[data-tucked=\"1\"],#wg-todo-dock[data-tucked=\"1\"]{max-height:min(28vh,220px);}" +
|
|
93
91
|
/* Compact + cap: fold overflow rows; hover / data-expanded reveals more. */
|
|
@@ -103,7 +101,7 @@ export const RUNNER_EXTRA_CSS =
|
|
|
103
101
|
// Always-on zoom HUD - bottom-right (top-left is crowded: todos + banners). Violet, matching the
|
|
104
102
|
// rest of the overlay's design tokens - this used to be a lone amber/yellow badge, the one
|
|
105
103
|
// surface that never got pulled into the shared palette when everything else did.
|
|
106
|
-
"#wg-zoom-badge{
|
|
104
|
+
"#wg-zoom-badge{z-index:var(--wg-z-chrome);" +
|
|
107
105
|
"display:flex;align-items:center;gap:7px;padding:6px 11px 6px 8px;" +
|
|
108
106
|
"border-radius:999px;background:" + SURFACE + ";color:#f0e8ff;" +
|
|
109
107
|
"border:1px solid rgba(124,58,237,.55);box-shadow:0 6px 18px rgba(0,0,0,.4);" +
|
|
@@ -115,16 +113,16 @@ export const RUNNER_EXTRA_CSS =
|
|
|
115
113
|
"#wg-zoom-badge .wg-zoom-val{color:#c9a6ff;font-variant-numeric:tabular-nums;font-weight:800;min-width:3.2em;}" +
|
|
116
114
|
"#wg-zoom-badge[data-zoomed=\"1\"]{border-color:#7C3AED;}" +
|
|
117
115
|
// Typing chip (method fills: username / password / etc.)
|
|
118
|
-
"#wg-typing-badge{
|
|
116
|
+
"#wg-typing-badge{z-index:var(--wg-z-chrome);" +
|
|
119
117
|
"display:flex;align-items:center;gap:8px;padding:8px 14px;" +
|
|
120
118
|
"border-radius:999px;background:rgba(20,10,40,.95);color:#e0f2fe;" +
|
|
121
119
|
"border:1px solid rgba(56,189,248,.55);box-shadow:0 8px 22px rgba(0,0,0,.4);" +
|
|
122
120
|
"font:700 12px/1.2 system-ui,sans-serif;pointer-events:none;" +
|
|
123
|
-
"opacity:0;transition
|
|
121
|
+
"opacity:0;transition-duration:.2s;}" +
|
|
124
122
|
"#wg-typing-badge.wg-in{opacity:1;}" +
|
|
125
123
|
"#wg-typing-badge .wg-ty-dots{letter-spacing:.15em;color:#7dd3fc;}" +
|
|
126
124
|
// Device toast + chip (0.13.2+) - icon toast on fixture change, then compact chip.
|
|
127
|
-
"#wg-device-toast{
|
|
125
|
+
"#wg-device-toast{z-index:var(--wg-z-toast);" +
|
|
128
126
|
"display:flex;align-items:center;gap:10px;min-width:200px;max-width:min(92vw,320px);" +
|
|
129
127
|
"padding:12px 14px;border-radius:14px;background:rgba(20,10,40,.96);color:#f0e8ff;" +
|
|
130
128
|
"border:1px solid rgba(124,58,237,.55);box-shadow:0 10px 28px rgba(0,0,0,.4);" +
|
|
@@ -146,20 +144,21 @@ export const RUNNER_EXTRA_CSS =
|
|
|
146
144
|
"#wg-device-toast .wg-dev-title{font:800 13px/1.2 system-ui,sans-serif;color:#fff;}" +
|
|
147
145
|
"#wg-device-toast .wg-dev-sub{margin-top:3px;font:600 11px/1.3 system-ui,sans-serif;" +
|
|
148
146
|
"color:#c9a6ff;letter-spacing:.02em;}" +
|
|
149
|
-
"#wg-device-badge{
|
|
147
|
+
"#wg-device-badge{z-index:var(--wg-z-chrome);" +
|
|
150
148
|
"display:inline-flex;align-items:center;gap:6px;padding:6px 10px 6px 8px;" +
|
|
151
149
|
"border-radius:999px;background:rgba(20,10,40,.92);color:#f0e8ff;" +
|
|
152
150
|
"border:1px solid rgba(124,58,237,.5);font:700 11px/1.2 system-ui,sans-serif;" +
|
|
153
151
|
"letter-spacing:.04em;text-transform:uppercase;pointer-events:none;" +
|
|
154
152
|
"box-shadow:0 4px 14px rgba(0,0,0,.3);opacity:0;transform:translateY(-4px);" +
|
|
155
|
-
"transition:
|
|
153
|
+
"transition-duration:.35s;transition-timing-function:cubic-bezier(.22,1,.36,1);}" +
|
|
156
154
|
"#wg-device-badge.wg-in{opacity:1;transform:translateY(0);}" +
|
|
157
155
|
"#wg-device-badge[data-preset=mobile]{border-color:#3B82F6;}" +
|
|
158
156
|
"#wg-device-badge[data-preset=tablet]{border-color:#22C55E;}" +
|
|
159
157
|
"#wg-device-badge[data-preset=desktop]{border-color:#9CA3AF;}" +
|
|
160
158
|
"#wg-device-badge .wg-dev-icon{width:16px;height:16px;display:flex;align-items:center;justify-content:center;}" +
|
|
161
159
|
"#wg-device-badge .wg-dev-icon svg{width:14px;height:14px;display:block;}" +
|
|
162
|
-
"#wg-device-badge
|
|
160
|
+
"#wg-device-badge{overflow:hidden;}" +
|
|
161
|
+
"#wg-device-badge .wg-dev-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}" +
|
|
163
162
|
"#wg-cursor[data-touch=1]{width:28px;height:28px;}" +
|
|
164
163
|
"#wg-todos,.wg-todos-list{list-style:none;margin:0 0 12px;padding:8px 10px;background:#0f0620;border-radius:8px;" +
|
|
165
164
|
"border:1px solid #3a2a60;}" +
|
|
@@ -270,16 +269,14 @@ export const RUNNER_EXTRA_CSS =
|
|
|
270
269
|
"#wg-panel button.wg-mini-next{background:#2a9d6f;border-color:#22855e;color:#fff;display:none;}" +
|
|
271
270
|
"#wg-panel button.wg-mini-next:hover{background:#22855e;}" +
|
|
272
271
|
"#wg-panel button.wg-mini-next:disabled{opacity:.55;cursor:default;}" +
|
|
273
|
-
"#wg-panel.wg-collapsed{width:auto;
|
|
272
|
+
"#wg-panel.wg-collapsed{width:auto;padding:8px 12px;overflow:hidden;}" +
|
|
274
273
|
"#wg-panel.wg-collapsed .wg-body{display:none;}" +
|
|
275
274
|
"#wg-panel.wg-collapsed .wg-chrome{margin:0;}" +
|
|
276
275
|
"#wg-panel.wg-collapsed .wg-chrome-title{font:700 13px/1.25 system-ui,sans-serif;color:#fff;" +
|
|
277
276
|
"letter-spacing:0;text-transform:none;}" +
|
|
278
277
|
"#wg-panel.wg-collapsed button.wg-mini-next.wg-mini-next-show{display:inline-block;}" +
|
|
279
278
|
"@media (max-width:640px){" +
|
|
280
|
-
"#wg-panel{
|
|
281
|
-
"max-height:min(55vh,calc(100vh - 16px));padding:12px 14px;border-radius:12px;}" +
|
|
282
|
-
"#wg-panel.wg-collapsed{left:50%;right:auto;transform:translateX(-50%);width:auto;}" +
|
|
279
|
+
"#wg-panel{width:auto;padding:12px 14px;border-radius:12px;}" +
|
|
283
280
|
"#wg-episodes{overflow-x:auto;-webkit-overflow-scrolling:touch;flex-wrap:nowrap;}" +
|
|
284
281
|
"#wg-panel .wg-narration{font-size:13px;}" +
|
|
285
282
|
"#wg-panel button{width:100%;}" +
|
|
@@ -49,7 +49,7 @@ export function createOverlayStage(page, { todoDockRef, deviceRef }) {
|
|
|
49
49
|
if (!docks.length) {
|
|
50
50
|
// Empty registry: do NOT clear the DOM. renderBefore/After may have
|
|
51
51
|
// just painted from lastTodoDock (keep). Clearing here made todos
|
|
52
|
-
// vanish until the next ctx.todos() set (PIA #15).
|
|
52
|
+
// vanish until the next ctx.ui.todos() set (PIA #15).
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
await page
|
|
@@ -43,17 +43,17 @@ export function phaseToOverlayFixtures(phase, holdMs = 6000) {
|
|
|
43
43
|
export function describeOverlayFixtures(tag, f) {
|
|
44
44
|
const out = [];
|
|
45
45
|
if (f.title)
|
|
46
|
-
out.push(`${tag} ctx.title(${JSON.stringify(f.title)})${f.titlePos ? ` pos=${f.titlePos}` : ""}`);
|
|
46
|
+
out.push(`${tag} ctx.ui.title(${JSON.stringify(f.title)})${f.titlePos ? ` pos=${f.titlePos}` : ""}`);
|
|
47
47
|
if (f.bannerUi)
|
|
48
|
-
out.push(`${tag} ctx.bannerUi(${JSON.stringify(f.bannerUi)})`);
|
|
48
|
+
out.push(`${tag} ctx.ui.bannerUi(${JSON.stringify(f.bannerUi)})`);
|
|
49
49
|
if (f.todos?.length)
|
|
50
|
-
out.push(`${tag} ctx.todos n=${f.todos.length} index=${f.todoIndex ?? 0}${f.todoPos ? ` pos=${f.todoPos}` : ""}`);
|
|
50
|
+
out.push(`${tag} ctx.ui.todos n=${f.todos.length} index=${f.todoIndex ?? 0}${f.todoPos ? ` pos=${f.todoPos}` : ""}`);
|
|
51
51
|
if (f.todoUi)
|
|
52
52
|
out.push(`${tag} todoUi ${JSON.stringify(f.todoUi)}`);
|
|
53
53
|
if (f.device)
|
|
54
|
-
out.push(`${tag} ctx.device(${typeof f.device === "string" ? f.device : f.device.preset ?? "custom"})`);
|
|
54
|
+
out.push(`${tag} ctx.ui.device(${typeof f.device === "string" ? f.device : f.device.preset ?? "custom"})`);
|
|
55
55
|
if (f.zoom)
|
|
56
|
-
out.push(`${tag} ctx.zoom(${f.zoom})`);
|
|
56
|
+
out.push(`${tag} ctx.ui.zoom(${f.zoom})`);
|
|
57
57
|
for (const r of f.rings ?? [])
|
|
58
58
|
out.push(`${tag} ring ${r.selector} tone=${r.tone ?? "planned"} label=${JSON.stringify(r.label)}`);
|
|
59
59
|
return out;
|
|
@@ -13,8 +13,10 @@ import { ignorePageGone } from "../../../engine/page-gone.js";
|
|
|
13
13
|
// re-invented) - genuinely the same visual language, not just the ring CSS
|
|
14
14
|
// this file already shared via WAYGRAPH_RING_CSS.
|
|
15
15
|
const OVERLAY_CSS = `
|
|
16
|
+
/* Every element here sits in a slot of the overlay layout (ui/css/layout.css) - bottom-right, bottom-left, top-right -
|
|
17
|
+
or on its stage (the spotlight); none is positioned by hand. */
|
|
16
18
|
#wg-pilot-overlay {
|
|
17
|
-
|
|
19
|
+
z-index: var(--wg-z-top); display: flex; flex-direction: column; align-items: flex-end;
|
|
18
20
|
font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
|
|
19
21
|
color: #fff; pointer-events: auto;
|
|
20
22
|
}
|
|
@@ -56,7 +58,7 @@ const OVERLAY_CSS = `
|
|
|
56
58
|
#wg-pilot-panel-graph .wg-pilot-graph-edge { padding: 2px 0 2px 14px; border-left: 2px solid rgba(255,255,255,.15); font-size: 11px; }
|
|
57
59
|
#wg-pilot-panel-graph .wg-pilot-graph-edge .wg-pilot-kind { color: #c9a6ff; }
|
|
58
60
|
#wg-pilot-activity {
|
|
59
|
-
|
|
61
|
+
z-index: var(--wg-z-chrome);
|
|
60
62
|
font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
|
|
61
63
|
background: ${SURFACE}; color: #fff; padding: 6px 10px; border-radius: 8px;
|
|
62
64
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25); white-space: nowrap; pointer-events: none;
|
|
@@ -68,12 +70,12 @@ const OVERLAY_CSS = `
|
|
|
68
70
|
#wg-banner all come from the ONE shared bundle ensureShadowRoot installs (ui/css/*.css) - not
|
|
69
71
|
redefined here. */
|
|
70
72
|
/* Spotlight (demo focus:true) - dim page, cutout around target. */
|
|
71
|
-
#wg-pilot-fx-focus{position:fixed;z-index:
|
|
73
|
+
#wg-pilot-fx-focus{position:fixed;z-index:var(--wg-z-focus);pointer-events:none;
|
|
72
74
|
border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);
|
|
73
75
|
opacity:0;transition:opacity .3s ease,left .3s ease,top .3s ease,width .3s ease,height .3s ease;}
|
|
74
76
|
#wg-pilot-fx-focus.wg-in{opacity:1;}
|
|
75
77
|
/* Zoom HUD (demo parity - badge only; no CSS scale of the page). */
|
|
76
|
-
#wg-pilot-fx-zoom{
|
|
78
|
+
#wg-pilot-fx-zoom{z-index:var(--wg-z-chrome);
|
|
77
79
|
display:flex;align-items:center;gap:7px;padding:6px 11px 6px 8px;
|
|
78
80
|
border-radius:999px;background:${SURFACE};color:#f0e8ff;
|
|
79
81
|
border:1px solid rgba(250,204,21,.6);box-shadow:0 6px 18px rgba(0,0,0,.4);
|
|
@@ -83,7 +85,7 @@ const OVERLAY_CSS = `
|
|
|
83
85
|
#wg-pilot-fx-zoom .wg-pilot-fx-zoom-ico svg{width:14px;height:14px;display:block;}
|
|
84
86
|
#wg-pilot-fx-zoom .wg-pilot-fx-zoom-val{color:#fde68a;font-variant-numeric:tabular-nums;font-weight:800;min-width:3.2em;}
|
|
85
87
|
#wg-pilot-fx-zoom[data-zoomed="1"]{border-color:#fbbf24;}
|
|
86
|
-
#wg-pilot-fx-device{
|
|
88
|
+
#wg-pilot-fx-device{z-index:var(--wg-z-chrome);
|
|
87
89
|
padding:8px 12px;border-radius:10px;background:${SURFACE};color:#f0e8ff;
|
|
88
90
|
border:1px solid rgba(124,58,237,.55);font:700 12px/1.2 system-ui,sans-serif;
|
|
89
91
|
pointer-events:none;box-shadow:0 6px 18px rgba(0,0,0,.35);}
|
|
@@ -11,13 +11,13 @@ function toneRules(sel, decl, only) {
|
|
|
11
11
|
/** The highlight ring + its label (`#wg-ring`, `#wg-ring-label`): tone / size / weight variants. */
|
|
12
12
|
export function ringCss() {
|
|
13
13
|
const p = TONES.planned;
|
|
14
|
-
return ("#wg-ring{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
14
|
+
return ("#wg-ring{position:fixed;z-index:var(--wg-z-ring,2147483646);pointer-events:none;opacity:0;" +
|
|
15
15
|
`border:2.5px solid ${p.ring};border-radius:10px;` +
|
|
16
16
|
// Opacity fade only - NEVER transition border-color/box-shadow: tone swaps must snap.
|
|
17
17
|
`box-shadow:0 0 0 4px ${p.glow};transition:opacity .3s ease;}` +
|
|
18
18
|
toneRules("#wg-ring", (t) => `border-color:${t.ring};box-shadow:0 0 0 4px ${t.glow};`) +
|
|
19
19
|
// A real element (not ::after) so JS can measure it and clamp it back onto the screen.
|
|
20
|
-
"#wg-ring-label{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
20
|
+
"#wg-ring-label{position:fixed;z-index:var(--wg-z-ring,2147483646);pointer-events:none;opacity:0;" +
|
|
21
21
|
`max-width:min(360px,70vw);white-space:normal;padding:6px 10px;border-radius:7px;background:${p.labelBg};color:${p.labelFg};` +
|
|
22
22
|
"font:600 12px/1.35 system-ui,sans-serif;transition:opacity .3s ease;}" +
|
|
23
23
|
"#wg-ring-label::after{content:attr(data-label);}" +
|
|
@@ -31,16 +31,21 @@
|
|
|
31
31
|
--wg-font: system-ui, sans-serif;
|
|
32
32
|
--wg-font-mono: ui-monospace, "SF Mono", Consolas, monospace;
|
|
33
33
|
|
|
34
|
-
/* Stacking
|
|
35
|
-
|
|
36
|
-
--wg-z-
|
|
37
|
-
--wg-z-
|
|
38
|
-
--wg-z-
|
|
39
|
-
--wg-z-
|
|
40
|
-
--wg-z-
|
|
41
|
-
/*
|
|
42
|
-
--wg-z-
|
|
43
|
-
|
|
34
|
+
/* Stacking. All overlay UI lives in one fixed parent (#wg-ui, layout.css) that sits above the host page with the one
|
|
35
|
+
max z-index; these values only order the layers inside it, lowest first. */
|
|
36
|
+
--wg-z-ui: var(--layer-important, 2147483647);
|
|
37
|
+
--wg-z-focus: 1; /* the spotlight veil; chrome stepping back while a ring is the focus */
|
|
38
|
+
--wg-z-chrome: 2; /* banner, todo docks, caption, chips, network tab */
|
|
39
|
+
--wg-z-dock: 2;
|
|
40
|
+
--wg-z-banner: 2;
|
|
41
|
+
--wg-z-fx: 4; /* rings and their labels: a highlight is never covered by chrome */
|
|
42
|
+
--wg-z-ring: 4;
|
|
43
|
+
--wg-z-top: 6; /* the step panel and other controls a person clicks */
|
|
44
|
+
--wg-z-toast: 7; /* short-lived notices (device change) */
|
|
45
|
+
--wg-z-cursor: 8; /* the cursor, click pulse and swipe trail: the pointer is always on top */
|
|
46
|
+
/* Spacing of the chrome layout: between items in a slot, and from the viewport edge. */
|
|
47
|
+
--wg-gap: 10px;
|
|
48
|
+
--wg-edge: 14px;
|
|
44
49
|
--wg-ease: var(--ease-3, cubic-bezier(.22,1,.36,1));
|
|
45
50
|
--wg-shadow-dock: var(--shadow-3, 0 2px 10px rgba(0,0,0,.3));
|
|
46
51
|
--wg-shadow-banner: var(--shadow-4, 0 8px 20px rgba(0,0,0,.3));
|
|
@@ -50,6 +55,74 @@
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
|
|
58
|
+
/*
|
|
59
|
+
* The overlay's one layout. Every piece of waygraph UI lives in #wg-ui, a fixed layer over the page, in one of two
|
|
60
|
+
* places:
|
|
61
|
+
*
|
|
62
|
+
* #wg-stage things anchored to the page: ring, label, cursor, click pulse, spotlight veil, swipe trail. They follow
|
|
63
|
+
* a target's coordinates (position: fixed against the viewport).
|
|
64
|
+
* #wg-chrome everything else, placed in a slot. Nothing in it is positioned by hand: a slot is a flex column with
|
|
65
|
+
* a gap, so what shares a place stacks instead of overlapping, and a slot never grows past its row.
|
|
66
|
+
*
|
|
67
|
+
* top-left | top-center | top-right banner (by its side) · device chip / toast
|
|
68
|
+
* left | | right todo docks (by side)
|
|
69
|
+
* bottom-left | bottom-center | bottom-right network tab · typing, caption, step panel · zoom
|
|
70
|
+
*
|
|
71
|
+
* The slot an element goes in is decided once, in ui/shadow.ts (by id and data-pos), and a data-pos change moves it.
|
|
72
|
+
* Each row lays out on its own, so a wide step panel never squeezes the side docks.
|
|
73
|
+
*/
|
|
74
|
+
#wg-ui { position: fixed; inset: 0; z-index: var(--wg-z-ui); pointer-events: none; }
|
|
75
|
+
#wg-stage { display: contents; }
|
|
76
|
+
#wg-chrome {
|
|
77
|
+
position: absolute; inset: 0; box-sizing: border-box;
|
|
78
|
+
display: flex; flex-direction: column; gap: var(--wg-gap); padding: var(--wg-edge);
|
|
79
|
+
}
|
|
80
|
+
.wg-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: var(--wg-gap); min-height: 0; }
|
|
81
|
+
.wg-row-top { flex: 0 0 auto; align-items: start; }
|
|
82
|
+
.wg-row-middle { flex: 1 1 0; display: flex; justify-content: space-between; align-items: stretch; }
|
|
83
|
+
.wg-row-bottom { flex: 0 1 auto; align-items: end; max-height: 62vh; }
|
|
84
|
+
.wg-slot {
|
|
85
|
+
display: flex; flex-direction: column; gap: var(--wg-gap);
|
|
86
|
+
min-width: 0; min-height: 0; max-height: 100%; max-width: 100%;
|
|
87
|
+
}
|
|
88
|
+
.wg-slot[data-slot$="left"] { align-items: flex-start; }
|
|
89
|
+
.wg-slot[data-slot$="center"] { align-items: center; }
|
|
90
|
+
.wg-slot[data-slot$="right"] { align-items: flex-end; }
|
|
91
|
+
.wg-row-bottom .wg-slot { justify-content: flex-end; }
|
|
92
|
+
/* A slotted element shrinks (and scrolls inside) rather than spilling out of its slot: a min-width of its own (the
|
|
93
|
+
network tab's 260px) must not push it past the slot into its neighbour. */
|
|
94
|
+
.wg-slot > * { position: relative; flex: 0 1 auto; box-sizing: border-box; pointer-events: auto; }
|
|
95
|
+
/* The slot's limits win over an element's own sizing (an #id rule's min-width / max-height), hence the id here. */
|
|
96
|
+
#wg-chrome .wg-slot > * { min-width: 0; min-height: 0; max-width: 100%; max-height: 100%; }
|
|
97
|
+
|
|
98
|
+
/* Order inside a shared slot: what is being typed, what the narration says, then the controls. */
|
|
99
|
+
#wg-typing-badge { order: 1; }
|
|
100
|
+
#wg-caption-bar { order: 2; }
|
|
101
|
+
#wg-panel, #wg-auto-panel { order: 3; }
|
|
102
|
+
#wg-device-toast { order: 1; }
|
|
103
|
+
#wg-device-badge, #wg-pilot-fx-device { order: 2; }
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Chrome that comes and goes takes no space while hidden (an empty gap above the step panel would read as a bug), and
|
|
107
|
+
* still fades: display switches with the transition (allow-discrete), and @starting-style is where the fade starts.
|
|
108
|
+
*/
|
|
109
|
+
#wg-caption-bar, #wg-typing-badge, #wg-network-log, #wg-device-badge {
|
|
110
|
+
transition-property: opacity, transform, display; transition-behavior: allow-discrete;
|
|
111
|
+
}
|
|
112
|
+
#wg-caption-bar:not(.wg-in), #wg-typing-badge:not(.wg-in), #wg-network-log:not(.wg-in), #wg-device-badge:not(.wg-in) {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
@starting-style {
|
|
116
|
+
#wg-caption-bar.wg-in, #wg-typing-badge.wg-in, #wg-network-log.wg-in, #wg-device-badge.wg-in { opacity: 0; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Narrow viewports: the bottom row's three slots stack (network, then the step panel full width, then zoom). */
|
|
120
|
+
@media (max-width: 640px) {
|
|
121
|
+
.wg-row-bottom { grid-template-columns: minmax(0, 1fr); }
|
|
122
|
+
.wg-slot[data-slot="bottom-center"] { align-items: stretch; }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
53
126
|
/*
|
|
54
127
|
* ONE ring implementation, shared by every surface (demo/auto's singleton #wg-ring, and Pilot's
|
|
55
128
|
* multiple simultaneous fixture rings) - `.wg-ring-el`/`.wg-ring-el-label` is the real class every
|
|
@@ -110,14 +183,14 @@
|
|
|
110
183
|
|
|
111
184
|
/* Travelling mouse cursor + click ripple (#wg-cursor, #wg-click-pulse). */
|
|
112
185
|
#wg-cursor {
|
|
113
|
-
position: fixed; z-index: var(--wg-z-
|
|
186
|
+
position: fixed; z-index: var(--wg-z-cursor); width: 24px; height: 24px; pointer-events: none;
|
|
114
187
|
left: 0; top: 0; opacity: 0; margin: 0;
|
|
115
188
|
/* Travel time is JS-driven via --wg-cursor-ms (a hardcoded duration would make speed a no-op). */
|
|
116
189
|
transition: transform var(--wg-cursor-ms, 600ms) var(--wg-ease), opacity .2s ease;
|
|
117
190
|
filter: drop-shadow(0 2px 4px rgba(12,12,26,.4));
|
|
118
191
|
}
|
|
119
192
|
#wg-click-pulse {
|
|
120
|
-
position: fixed; z-index: var(--wg-z-
|
|
193
|
+
position: fixed; z-index: var(--wg-z-cursor); width: 14px; height: 14px;
|
|
121
194
|
margin-left: -7px; margin-top: -7px; border-radius: 50%; pointer-events: none; opacity: 0;
|
|
122
195
|
border: 2px solid var(--wg-tone-ring, var(--wg-tone-planned-ring)); background: var(--wg-tone-pulse, var(--wg-tone-planned-glow));
|
|
123
196
|
}
|
|
@@ -130,19 +203,16 @@
|
|
|
130
203
|
@keyframes wg-pulse { 0% { opacity: .9; transform: scale(.4); } 100% { opacity: 0; transform: scale(2.4); } }
|
|
131
204
|
|
|
132
205
|
|
|
133
|
-
/* Top title banner (#wg-banner)
|
|
134
|
-
* card
|
|
135
|
-
*
|
|
206
|
+
/* Top title banner (#wg-banner), in the top-left / top-center / top-right slot by its data-pos (layout.css). Sits
|
|
207
|
+
* below rings and labels (--wg-z-banner): a title card never shadows a highlight. Moves to another slot when a ring
|
|
208
|
+
* overlaps it (inpage/banner.ts's __wgBannerAvoidRing) unless authored off. */
|
|
136
209
|
#wg-banner {
|
|
137
|
-
|
|
210
|
+
z-index: var(--wg-z-banner);
|
|
138
211
|
background: var(--wg-surface); color: #fff; border-radius: var(--wg-radius-lg); padding: 10px 16px;
|
|
139
212
|
font: 14px/1.4 var(--wg-font); box-shadow: var(--wg-shadow-banner);
|
|
140
|
-
border: 1px solid rgba(124,58,237,.4); cursor: pointer;
|
|
141
|
-
transition:
|
|
213
|
+
border: 1px solid rgba(124,58,237,.4); cursor: pointer; max-width: min(92vw, 520px);
|
|
214
|
+
transition: opacity .3s var(--wg-ease);
|
|
142
215
|
}
|
|
143
|
-
#wg-banner[data-pos=left] { left: 14px; right: auto; transform: none; }
|
|
144
|
-
#wg-banner[data-pos=center] { left: 50%; right: auto; transform: translateX(-50%); }
|
|
145
|
-
#wg-banner[data-pos=right] { right: 14px; left: auto; transform: none; }
|
|
146
216
|
/* Same "auto opacity" step-back the todo dock already has (.wg-todo-behind) while a ring caption
|
|
147
217
|
is the current focus - added via __wgBannerSetBehind, not just the collision-driven move. */
|
|
148
218
|
#wg-banner.wg-banner-behind { opacity: .42; }
|
|
@@ -161,14 +231,11 @@
|
|
|
161
231
|
* duplicate of this file's own rules, is consolidated.
|
|
162
232
|
*/
|
|
163
233
|
#wg-pilot-fx-todos {
|
|
164
|
-
|
|
165
|
-
overflow: auto; font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; color: #fff;
|
|
234
|
+
z-index: var(--wg-z-dock); width: min(280px, 42vw); overflow: auto; font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; color: #fff;
|
|
166
235
|
background: var(--wg-surface); border-radius: var(--wg-radius-md); padding: 10px 12px;
|
|
167
236
|
box-shadow: var(--wg-shadow-dock); pointer-events: auto; transition: opacity .25s ease;
|
|
168
237
|
}
|
|
169
238
|
#wg-pilot-fx-todos.wg-todo-behind { z-index: var(--wg-z-focus); opacity: .42; pointer-events: none; }
|
|
170
|
-
#wg-pilot-fx-todos[data-pos=left] { left: 14px; right: auto; }
|
|
171
|
-
#wg-pilot-fx-todos[data-pos=right] { right: 14px; left: auto; }
|
|
172
239
|
#wg-pilot-fx-todos .wg-pilot-fx-todo-title {
|
|
173
240
|
font: 700 11px/1.2 system-ui, sans-serif; color: var(--wg-accent-soft);
|
|
174
241
|
letter-spacing: .04em; text-transform: uppercase; margin: 0 0 8px;
|
|
@@ -199,29 +266,25 @@
|
|
|
199
266
|
* chrome, this narrates the PAGE itself so it reads as a caption card, not more chrome).
|
|
200
267
|
*/
|
|
201
268
|
#wg-caption-bar {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
z-index:2147483647 - always on top) - without it the pill sat right over the caption card,
|
|
205
|
-
both centered at the bottom of the viewport (see #wg-typing-badge's own bottom:88px, same
|
|
206
|
-
reason). */
|
|
207
|
-
padding: 0 6% 64px; display: flex; justify-content: center;
|
|
269
|
+
/* In the bottom-center slot, above the step panel (layout.css): the slot's gap keeps them apart. */
|
|
270
|
+
z-index: var(--wg-z-chrome); display: flex; justify-content: center; max-width: min(820px, 92vw);
|
|
208
271
|
pointer-events: none; opacity: 0; transform: translateY(6px);
|
|
209
|
-
transition:
|
|
272
|
+
transition-duration: .3s; transition-timing-function: ease;
|
|
210
273
|
}
|
|
211
274
|
#wg-caption-bar.wg-in { opacity: 1; transform: translateY(0); }
|
|
212
275
|
#wg-caption-bar .wg-caption-text {
|
|
213
276
|
font: 500 15px/1.4 var(--wg-font); color: #1c1917; text-align: center;
|
|
214
|
-
|
|
277
|
+
text-wrap: balance;
|
|
215
278
|
background: #fff; border-radius: var(--wg-radius-lg);
|
|
216
279
|
padding: 10px 18px; box-shadow: var(--wg-shadow-banner);
|
|
217
280
|
}
|
|
218
281
|
|
|
219
282
|
|
|
220
283
|
/* The network tab: real page traffic (network-capture.ts) and defineApiBlock's own calls, bottom-left (the one corner
|
|
221
|
-
* free of the zoom HUD and the typing chip). Hidden until ctx.showNetworkTab(); stays up once shown. In the shared
|
|
284
|
+
* free of the zoom HUD and the typing chip). Hidden until ctx.ui.showNetworkTab(); stays up once shown. In the shared
|
|
222
285
|
* bundle, not the runner CSS: a navigation rebuilds the panel the moment the shadow root is back, before any later
|
|
223
286
|
* stylesheet, and without its CSS it painted unstyled and visible, then faded out. */
|
|
224
|
-
#wg-network-log{
|
|
287
|
+
#wg-network-log{z-index:var(--wg-z-chrome);min-width:260px;max-width:min(92vw,420px);max-height:min(60vh,420px);display:flex;flex-direction:column;border-radius:10px;background:var(--wg-surface);color:#f0e8ff;border:1px solid rgba(124,58,237,.45);box-shadow:0 8px 22px rgba(0,0,0,.4);font:12px/1.4 ui-monospace,"SF Mono",Consolas,monospace;opacity:0;transform:translateY(8px);transition-duration:.25s;transition-timing-function:ease;overflow:hidden;}
|
|
225
288
|
#wg-network-log.wg-in{opacity:1;transform:translateY(0);}
|
|
226
289
|
#wg-network-log.wg-net-collapsed .wg-net-list{display:none;}
|
|
227
290
|
#wg-network-log .wg-net-header{display:flex;align-items:center;gap:6px;padding:8px 10px;border-bottom:1px solid rgba(124,58,237,.25);cursor:default;flex:0 0 auto;}
|
|
@@ -15,18 +15,85 @@ export function installShadowRoot(overlaySheet) {
|
|
|
15
15
|
host?.remove();
|
|
16
16
|
host = document.createElement("div");
|
|
17
17
|
host.id = "wg-root";
|
|
18
|
-
// No box of its own:
|
|
18
|
+
// No box of its own: everything inside is in the fixed #wg-ui layer, so the host never affects page layout.
|
|
19
19
|
host.style.cssText = "display:contents";
|
|
20
20
|
host.attachShadow({ mode: "open" });
|
|
21
21
|
document.documentElement.appendChild(host);
|
|
22
22
|
}
|
|
23
23
|
return host.shadowRoot;
|
|
24
24
|
};
|
|
25
|
+
// The one layout (ui/css/layout.css): #wg-ui holds the stage (anchored things) and the chrome's slots. Where a chrome
|
|
26
|
+
// element goes is decided here, once, by its id and data-pos; anything not in the table is anchored (the stage).
|
|
27
|
+
const SLOTS = ["top-left", "top-center", "top-right", "left", "right", "bottom-left", "bottom-center", "bottom-right"];
|
|
28
|
+
const SLOT_OF = {
|
|
29
|
+
"wg-typing-badge": "bottom-center",
|
|
30
|
+
"wg-caption-bar": "bottom-center",
|
|
31
|
+
"wg-panel": "bottom-center",
|
|
32
|
+
"wg-auto-panel": "bottom-center",
|
|
33
|
+
"wg-network-log": "bottom-left",
|
|
34
|
+
"wg-pilot-activity": "bottom-left",
|
|
35
|
+
"wg-zoom-badge": "bottom-right",
|
|
36
|
+
"wg-pilot-fx-zoom": "bottom-right",
|
|
37
|
+
"wg-pilot-overlay": "bottom-right",
|
|
38
|
+
"wg-device-toast": "top-right",
|
|
39
|
+
"wg-device-badge": "top-right",
|
|
40
|
+
"wg-pilot-fx-device": "top-right",
|
|
41
|
+
};
|
|
42
|
+
const slotOf = (el) => {
|
|
43
|
+
if (!(el instanceof HTMLElement))
|
|
44
|
+
return undefined;
|
|
45
|
+
const pos = el.dataset.pos;
|
|
46
|
+
if (el.id === "wg-banner")
|
|
47
|
+
return pos === "center" || pos === "right" ? `top-${pos}` : "top-left";
|
|
48
|
+
if (el.classList.contains("wg-todo-dock") || el.id === "wg-todo-dock" || el.id === "wg-pilot-fx-todos")
|
|
49
|
+
return pos === "right" ? "right" : "left";
|
|
50
|
+
return SLOT_OF[el.id];
|
|
51
|
+
};
|
|
52
|
+
const ui = () => {
|
|
53
|
+
const r = root();
|
|
54
|
+
let layer = r.getElementById("wg-ui");
|
|
55
|
+
if (layer)
|
|
56
|
+
return layer;
|
|
57
|
+
layer = document.createElement("div");
|
|
58
|
+
layer.id = "wg-ui";
|
|
59
|
+
const stage = document.createElement("div");
|
|
60
|
+
stage.id = "wg-stage";
|
|
61
|
+
const chrome = document.createElement("div");
|
|
62
|
+
chrome.id = "wg-chrome";
|
|
63
|
+
for (const [row, slots] of [["top", SLOTS.slice(0, 3)], ["middle", SLOTS.slice(3, 5)], ["bottom", SLOTS.slice(5)]]) {
|
|
64
|
+
const rowEl = document.createElement("div");
|
|
65
|
+
rowEl.className = `wg-row wg-row-${row}`;
|
|
66
|
+
for (const name of slots) {
|
|
67
|
+
const slot = document.createElement("div");
|
|
68
|
+
slot.className = "wg-slot";
|
|
69
|
+
slot.dataset.slot = name;
|
|
70
|
+
rowEl.appendChild(slot);
|
|
71
|
+
}
|
|
72
|
+
chrome.appendChild(rowEl);
|
|
73
|
+
}
|
|
74
|
+
layer.append(stage, chrome);
|
|
75
|
+
r.appendChild(layer);
|
|
76
|
+
// A data-pos change (banner click, a dock flipping away from a ring) moves the element to its new slot.
|
|
77
|
+
new MutationObserver((records) => {
|
|
78
|
+
for (const rec of records)
|
|
79
|
+
if (rec.target instanceof Element && rec.target.parentElement?.classList.contains("wg-slot"))
|
|
80
|
+
place(rec.target);
|
|
81
|
+
}).observe(chrome, { subtree: true, attributes: true, attributeFilter: ["data-pos"] });
|
|
82
|
+
return layer;
|
|
83
|
+
};
|
|
84
|
+
const place = (node) => {
|
|
85
|
+
const layer = ui();
|
|
86
|
+
const slot = node instanceof Element ? slotOf(node) : undefined;
|
|
87
|
+
const parent = slot ? layer.querySelector(`[data-slot="${slot}"]`) : layer.querySelector("#wg-stage");
|
|
88
|
+
if (node.parentNode !== parent)
|
|
89
|
+
parent.appendChild(node);
|
|
90
|
+
return node;
|
|
91
|
+
};
|
|
25
92
|
w.__wgRoot = root;
|
|
26
93
|
w.__wgById = (id) => root().getElementById(id) || document.getElementById(id);
|
|
27
94
|
w.__wgQ = (sel) => root().querySelector(sel) || document.querySelector(sel);
|
|
28
95
|
w.__wgQA = (sel) => Array.from(root().querySelectorAll(sel)).concat(Array.from(document.querySelectorAll(sel)));
|
|
29
|
-
w.__wgAdd =
|
|
96
|
+
w.__wgAdd = place;
|
|
30
97
|
w.__wgCss = (css, key) => {
|
|
31
98
|
const r = root();
|
|
32
99
|
let style = Array.from(r.children).find((c) => c.tagName === "STYLE" && c.getAttribute("data-key") === key);
|
|
@@ -177,13 +177,13 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
177
177
|
return;
|
|
178
178
|
const tag = "stub." + phaseName;
|
|
179
179
|
if (phase.title)
|
|
180
|
-
demoFixture(tag, "ctx.title(...)", JSON.stringify(phase.title));
|
|
180
|
+
demoFixture(tag, "ctx.ui.title(...)", JSON.stringify(phase.title));
|
|
181
181
|
if (phase.titlePos)
|
|
182
|
-
demoFixture(tag, "ctx.titlePos(...)", JSON.stringify(phase.titlePos));
|
|
182
|
+
demoFixture(tag, "ctx.ui.titlePos(...)", JSON.stringify(phase.titlePos));
|
|
183
183
|
if (phase.bannerUi)
|
|
184
|
-
demoFixture(tag, "ctx.bannerUi(...)", JSON.stringify(phase.bannerUi));
|
|
184
|
+
demoFixture(tag, "ctx.ui.bannerUi(...)", JSON.stringify(phase.bannerUi));
|
|
185
185
|
if (phase.deviceSync && phase.deviceSync !== "keep") {
|
|
186
|
-
demoFixture(tag, phase.deviceSync === "clear" ? "ctx.hideDevice()" : "ctx.device(...)", phase.device
|
|
186
|
+
demoFixture(tag, phase.deviceSync === "clear" ? "ctx.ui.hideDevice()" : "ctx.ui.device(...)", phase.device
|
|
187
187
|
? {
|
|
188
188
|
preset: phase.device.preset,
|
|
189
189
|
w: phase.device.viewport && phase.device.viewport.width,
|
|
@@ -194,11 +194,11 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
194
194
|
: phase.deviceSync);
|
|
195
195
|
}
|
|
196
196
|
if (phase.zoom != null)
|
|
197
|
-
demoFixture(tag, "ctx.zoom(" + phase.zoom + ")", phase.zoom + "x");
|
|
197
|
+
demoFixture(tag, "ctx.ui.zoom(" + phase.zoom + ")", phase.zoom + "x");
|
|
198
198
|
if (phase.zoomOut !== undefined)
|
|
199
|
-
demoFixture(tag, "ctx.zoomOut(" + !!phase.zoomOut + ")", !!phase.zoomOut);
|
|
199
|
+
demoFixture(tag, "ctx.ui.zoomOut(" + !!phase.zoomOut + ")", !!phase.zoomOut);
|
|
200
200
|
if (phase.todoSync && phase.todoSync !== "keep") {
|
|
201
|
-
demoFixture(tag, phase.todoSync === "clear" ? "ctx.hideTodos() / clear" : "ctx.todos(...)", phase.todoDock
|
|
201
|
+
demoFixture(tag, phase.todoSync === "clear" ? "ctx.ui.hideTodos() / clear" : "ctx.ui.todos(...)", phase.todoDock
|
|
202
202
|
? {
|
|
203
203
|
id: phase.todoDock.id,
|
|
204
204
|
pos: phase.todoDock.pos,
|
|
@@ -208,7 +208,7 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
208
208
|
: phase.todoSync);
|
|
209
209
|
}
|
|
210
210
|
phase.highlights.forEach((h, i) => {
|
|
211
|
-
demoFixture(tag, "ctx.ring[" + i + "]", {
|
|
211
|
+
demoFixture(tag, "ctx.ui.ring[" + i + "]", {
|
|
212
212
|
selector: h.selector,
|
|
213
213
|
label: h.label,
|
|
214
214
|
detail: h.detail,
|
|
@@ -22,5 +22,5 @@ export interface SlidesOptions {
|
|
|
22
22
|
* doing nothing. In-page data-wg-acked + autoplay poll is the source of truth.
|
|
23
23
|
*/
|
|
24
24
|
export declare function presentSlides(page: WgPage, slides: readonly WaygraphSlide[], opts?: SlidesOptions): Promise<void>;
|
|
25
|
-
/** Plays a stub phase's `ctx.slides(...)` for the step on screen, in the flow's highlight style. */
|
|
25
|
+
/** Plays a stub phase's `ctx.ui.slides(...)` for the step on screen, in the flow's highlight style. */
|
|
26
26
|
export declare function showSlides(ctx: StepperContext, view: StepView, slides: readonly WaygraphSlide[]): Promise<void>;
|