@kaydash9999/banker-plugins 0.22.0 → 0.23.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +41 -0
- package/PRIVACY.md +1 -0
- package/README.md +39 -13
- package/bin/banker.js +118 -22
- package/bin/lib/codex-hooks.mjs +116 -18
- package/bin/lib/codex-names.mjs +4 -0
- package/bin/lib/codex-routes.mjs +93 -1
- package/bin/lib/command-skill.cjs +37 -6
- package/codex/command-skills/front-qa/SKILL.md +1 -1
- package/codex/command-skills/setup/SKILL.md +4 -4
- package/codex/hooks.json +5 -5
- package/codex/manifest.json +18 -15
- package/codex/skills/progress/scripts/progress.mjs +44 -14
- package/codex/skills/teamwork/SKILL.md +137 -0
- package/codex/skills/teamwork/scripts/teamwork.mjs +957 -0
- package/codex/transform-matrix.md +5 -3
- package/hooks/obsidize-hook.mjs +75 -32
- package/hooks/telemetry-count-codex.mjs +238 -57
- package/hooks/update-fetch.mjs +2 -1
- package/hooks/update-notify.mjs +82 -20
- package/package.json +3 -1
- package/skills/3d-intro-build/SKILL.md +101 -20
- package/skills/3d-intro-build/references/NOTICE.md +9 -2
- package/skills/3d-intro-build/references/assemble.mjs +113 -22
- package/skills/3d-intro-build/references/azure-adapter.mjs +110 -24
- package/skills/3d-intro-build/references/clip-post.mjs +443 -0
- package/skills/3d-intro-build/references/dive-mode.mjs +299 -0
- package/skills/3d-intro-build/references/intro-fixes.css +11 -0
- package/skills/3d-intro-build/references/prompts.md +51 -4
- package/skills/3d-intro-build/references/scrub-engine.js +1 -0
- package/skills/3d-intro-build/references/step-nav.css +29 -0
- package/skills/3d-intro-build/references/step-nav.js +277 -106
- package/skills/3d-intro-build/references/verify-intro.mjs +373 -32
- package/skills/3d-intro-build/references/verify.md +6 -0
- package/skills/3d-intro-build/references/video-pool.mjs +142 -12
- package/skills/3d-intro-setup/SKILL.md +20 -7
- package/skills/3d-intro-setup/references/azure-adapter.mjs +110 -24
- package/skills/3d-intro-setup/references/video-pool.mjs +142 -12
- package/skills/all-in-one/SKILL.md +125 -86
- package/skills/omc-patch/SKILL.md +1 -1
- package/skills/payload-mon/SKILL.md +1 -1
- package/skills/play-qa/SKILL.md +6 -4
- package/skills/progress-mon/README.md +162 -0
- package/skills/progress-mon/SKILL.md +165 -0
- package/skills/progress-mon/assets/fixture/README.md +1 -0
- package/skills/progress-mon/assets/fixture/arch.json +33 -0
- package/skills/progress-mon/assets/fixture/commits.json +7 -0
- package/skills/progress-mon/assets/fixture/config.json +30 -0
- package/skills/progress-mon/assets/fixture/events.jsonl +7 -0
- package/skills/progress-mon/assets/fixture/flow.json +44 -0
- package/skills/progress-mon/assets/fixture/items.json +14 -0
- package/skills/progress-mon/assets/fixture/req-spec.json +31 -0
- package/skills/progress-mon/assets/fixture/reqs.json +11 -0
- package/skills/progress-mon/assets/fixture/variants/arch-auto/arch.json +58 -0
- package/skills/progress-mon/assets/fixture/variants/check-auto/arch.json +15 -0
- package/skills/progress-mon/assets/fixture/variants/check-overlap/arch.json +14 -0
- package/skills/progress-mon/assets/fixture/variants/history-manual/config.json +13 -0
- package/skills/progress-mon/assets/fixture/variants/history-manual/items.json +7 -0
- package/skills/progress-mon/assets/fixture/variants/workflow-wide/flow.json +35 -0
- package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a01.jsonl +1 -0
- package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a02.jsonl +1 -0
- package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a03.jsonl +1 -0
- package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a04.jsonl +1 -0
- package/skills/progress-mon/assets/fixture/workflows/wf_ex1/journal.jsonl +7 -0
- package/skills/progress-mon/references/arch.md +280 -0
- package/skills/progress-mon/references/authoring.md +199 -0
- package/skills/progress-mon/references/config.md +115 -0
- package/skills/progress-mon/references/flow.md +75 -0
- package/skills/progress-mon/references/inventory.md +58 -0
- package/skills/progress-mon/references/items.md +74 -0
- package/skills/progress-mon/references/req-spec.md +74 -0
- package/skills/progress-mon/references/reqs.md +30 -0
- package/skills/progress-mon/scripts/index.js +31 -0
- package/skills/progress-mon/scripts/pm-check.mjs +526 -0
- package/skills/progress-mon/scripts/pm-config.mjs +341 -0
- package/skills/progress-mon/scripts/pm-data.mjs +394 -0
- package/skills/progress-mon/scripts/pm-html.mjs +77 -0
- package/skills/progress-mon/scripts/pm-memory.mjs +308 -0
- package/skills/progress-mon/scripts/pm-render.mjs +1644 -0
- package/skills/progress-mon/scripts/pm-server.mjs +735 -0
- package/skills/progress-mon/scripts/pm-sources.mjs +501 -0
- package/skills/progress-mon/scripts/progress-mon.mjs +167 -0
- package/skills/ralph-qa/SKILL.md +104 -15
- package/skills/ralph-qa/references/gate-evidence.mjs +457 -0
- package/skills/ralph-qa/references/payload-scan.mjs +19 -2
- package/skills/ralph-qa/references/run-folder.mjs +299 -0
- package/skills/ralph-qa/references/seat-payload.mjs +60 -13
- package/skills/ralph-qa/references/seat-run.mjs +159 -155
- package/skills/remains/SKILL.md +1 -0
- package/skills/remains/scripts/boxes.mjs +6 -2
- package/skills/setup-bypass-permissions/scripts/codex-config.mjs +19 -8
- package/skills/setup-mcp/SKILL.md +1 -1
- package/skills/setup-omc-hud/SKILL.md +2 -1
- package/skills/setup-omc-hud/assets/codex-config.mjs +19 -8
- package/skills/ultra-chain/SKILL.md +32 -14
- package/skills/ultra-chain/references/chain-ledger.mjs +111 -3
- package/skills/ultra-init/SKILL.md +30 -22
- package/skills/ultraqa/NOTICE.md +34 -0
- package/skills/ultraqa/SKILL.md +196 -0
- package/skills/ultraqa/references/gate-evidence.mjs +457 -0
- package/skills/ultraqa/references/run-folder.mjs +299 -0
- package/skills/ultraqa/references/tmux-drive.md +52 -0
- package/skills/ultraqa/references/uqa-run.mjs +339 -0
- package/skills/update-banker/SKILL.md +1 -1
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Scene-by-scene navigation for the scroll-world intro (
|
|
2
|
+
* Scene-by-scene navigation for the scroll-world intro (on by default; intro.json "stepNav": false
|
|
3
|
+
* turns it off).
|
|
3
4
|
*
|
|
4
5
|
* The vendored engine maps the window's scroll position to clip time and stays as shipped; this
|
|
5
|
-
* script only moves the scroll position.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* script only moves the scroll position. Stops: each scene's start (its dive's first frame; an exit
|
|
7
|
+
* clip is the way to the next scene), the end of the last scene, then the top of the page body
|
|
8
|
+
* (`end`, when the intro sits on top of a site). Between two stops lies a half point.
|
|
9
|
+
* wheel one notch moves half way to the next stop, two notches a whole scene. Pixel, line
|
|
10
|
+
* and page deltas, and a touchpad's small ones, add up to notches (notchPx, linePx);
|
|
11
|
+
* one gesture moves at most maxNotches
|
|
12
|
+
* arrow keys up/down and left/right as the wheel (up and left go back)
|
|
13
|
+
* PageUp/PageDown, Space (shift goes back), a swipe over swipePx one scene
|
|
14
|
+
* Home, End the first scene's start, the last scene's end, at once (no animation)
|
|
15
|
+
* a route dot its scene's start, at once (a click, or Enter or Space on the focused dot)
|
|
16
|
+
* Fields and editable places keep their keys. An input during a move retargets it from where that
|
|
17
|
+
* move was going. When the page comes to rest anywhere else above the body (a scrollbar, a touchpad,
|
|
18
|
+
* an anchor), it settles on the nearest stop after snapMs. Below the body top the browser scrolls as
|
|
19
|
+
* usual. At a stop or a half point, the copy of the scene in view is shown (class isn-copy, faded by
|
|
20
|
+
* step-nav.css; the engine's own fade takes over again when a move starts).
|
|
11
21
|
*
|
|
12
22
|
* Options (intro.json "stepNav", all optional; values are the defaults below):
|
|
13
23
|
* end selector of the body start; omit on a standalone intro page. It is looked up at
|
|
@@ -18,8 +28,14 @@
|
|
|
18
28
|
* swipePx a touch swipe longer than this is one step
|
|
19
29
|
* durBaseMs, durPerVhMs, durMaxMs a move takes min(durMax, durBase + distance in screen heights
|
|
20
30
|
* x durPerVh) ms with a cosine ease; 0 under prefers-reduced-motion
|
|
21
|
-
*
|
|
22
|
-
*
|
|
31
|
+
* notchPx, linePx, maxNotches wheel pixels to a notch, pixels in a line delta, notches per gesture
|
|
32
|
+
* snapMs how long the page must rest off the stops before it settles on the nearest one
|
|
33
|
+
* decodeCapMs, instantDecodeCapMs the longest wait for the posters to decode before a move, and before
|
|
34
|
+
* an instant one (nothing hides an undecoded poster there, so it waits longer)
|
|
35
|
+
* loadingDelayMs, loadingLabel a wait for the posters (either kind of move) longer than loadingDelayMs
|
|
36
|
+
* shows a small loading mark mid-screen with this text (a status region, read by a screen
|
|
37
|
+
* reader; it takes no focus and no input, and a new input drops it with the old move). It
|
|
38
|
+
* goes as the move starts. Under reduced motion the mark does not spin
|
|
23
39
|
* prefetch download the clips at load so the first move already plays video; on a phone or
|
|
24
40
|
* with Save-Data only the first three in playback order
|
|
25
41
|
* hintLabel, skipLabel, mainLabel texts of the mouse hint, the skip link and the main button
|
|
@@ -37,7 +53,8 @@
|
|
|
37
53
|
|
|
38
54
|
const DEFAULTS = Object.freeze({
|
|
39
55
|
end: null, gestureGapMs: 220, minWheel: 2, swipePx: 30,
|
|
40
|
-
durBaseMs: 2600, durPerVhMs: 760, durMaxMs: 5600,
|
|
56
|
+
durBaseMs: 2600, durPerVhMs: 760, durMaxMs: 5600, decodeCapMs: 300, prefetch: true,
|
|
57
|
+
notchPx: 100, linePx: 40, maxNotches: 2, snapMs: 400, instantDecodeCapMs: 1500, loadingDelayMs: 150, loadingLabel: 'Loading',
|
|
41
58
|
hintLabel: 'Scroll or press the down key', skipLabel: 'Skip intro', mainLabel: 'Go to main content',
|
|
42
59
|
});
|
|
43
60
|
|
|
@@ -63,16 +80,64 @@
|
|
|
63
80
|
/** The scroll length of the segments in screen heights (the engine's track adds one screen). */
|
|
64
81
|
function totalWeight(config) { return spansOf(config).total; }
|
|
65
82
|
|
|
66
|
-
/** Stop positions in px:
|
|
83
|
+
/** Stop positions in px: each scene's start, the last scene's end, then the body. */
|
|
67
84
|
function stopPositions(config, vh, endTop) {
|
|
68
85
|
const spans = spansOf(config).spans;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
86
|
+
// one pixel into the scene: the engine's current segment is the last whose start is at or above the
|
|
87
|
+
// page, and its starts carry float error (2.2 x 800 = 1760.0000000000002), so a stop at the rounded
|
|
88
|
+
// start would leave the segment before on top; an exit ending on a background plate showed the plate
|
|
89
|
+
const stops = spans.map(function (span, i) { return i ? Math.floor(span[0] * vh) + 1 : 0; });
|
|
90
|
+
if (spans.length) stops.push(Math.round(spans[spans.length - 1][1] * vh));
|
|
73
91
|
return endTop == null ? stops : stops.concat([endTop]);
|
|
74
92
|
}
|
|
75
93
|
|
|
94
|
+
/** The stops with the half point between each two: where a wheel notch or an arrow key can land. */
|
|
95
|
+
function gridOf(stops) {
|
|
96
|
+
const g = [];
|
|
97
|
+
stops.forEach(function (v, i) { if (i) g.push(Math.round((stops[i - 1] + v) / 2)); g.push(v); });
|
|
98
|
+
return g;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Where n points of `list` (signed: + goes down) take the page from `base`; from a place between
|
|
103
|
+
* points the first one is the next point that way. null when there is none that way.
|
|
104
|
+
*/
|
|
105
|
+
function stepFrom(list, base, n) {
|
|
106
|
+
if (!n) return null;
|
|
107
|
+
let i;
|
|
108
|
+
if (n > 0) i = list.findIndex(function (v) { return v > base + 1; });
|
|
109
|
+
else for (i = list.length - 1; i >= 0 && !(list[i] < base - 1); i--);
|
|
110
|
+
if (i < 0) return null;
|
|
111
|
+
return list[Math.max(0, Math.min(list.length - 1, i + (n > 0 ? n - 1 : n + 1)))];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The scene whose copy is in view at y: the dive holding it, or across an exit the nearer scene. */
|
|
115
|
+
function sceneAt(config, vh, y) {
|
|
116
|
+
const spans = spansOf(config).spans;
|
|
117
|
+
for (let i = spans.length - 1; i >= 0; i--) {
|
|
118
|
+
if (y >= spans[i][0] * vh - 1) {
|
|
119
|
+
const end = spans[i][1] * vh;
|
|
120
|
+
const next = spans[i + 1];
|
|
121
|
+
return y <= end + 1 || !next ? i : (y - end > (next[0] * vh - end) / 2 ? i + 1 : i);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The stop a page resting at y settles on (the nearest up to the last scene's end); -1 at a stop or a half point. */
|
|
128
|
+
function settleTarget(stops, lastStop, y) {
|
|
129
|
+
if (gridOf(stops).some(function (v) { return Math.abs(v - y) <= 1; })) return -1;
|
|
130
|
+
return nearest(stops.slice(0, lastStop + 1), y);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Wheel notches in one event: pixel, line and page deltas in pixels, added to the gesture's remainder. */
|
|
134
|
+
function wheelNotches(e, carry, o) {
|
|
135
|
+
const unit = e.deltaMode === 1 ? o.linePx : e.deltaMode === 2 ? 2 * o.notchPx : 1;
|
|
136
|
+
const total = carry + (e.deltaY || 0) * unit;
|
|
137
|
+
const n = Math.trunc(total / o.notchPx);
|
|
138
|
+
return { n: n, carry: total - n * o.notchPx };
|
|
139
|
+
}
|
|
140
|
+
|
|
76
141
|
function moveDuration(distance, vh, o, reduce) {
|
|
77
142
|
if (reduce) return 0;
|
|
78
143
|
return Math.min(o.durMaxMs, o.durBaseMs + (Math.abs(distance) / vh) * o.durPerVhMs);
|
|
@@ -133,67 +198,49 @@
|
|
|
133
198
|
return best;
|
|
134
199
|
};
|
|
135
200
|
|
|
136
|
-
//
|
|
137
|
-
// next
|
|
138
|
-
function targetOf(stops,
|
|
139
|
-
if (
|
|
140
|
-
if (input
|
|
141
|
-
|
|
142
|
-
return -1;
|
|
201
|
+
// Where an input asks to go from `base`: { to: i } a stop, { steps: n } n grid points, { scenes: n } the
|
|
202
|
+
// next stop that way (n is +1 or -1). null when there is nothing that way.
|
|
203
|
+
function targetOf(stops, base, input) {
|
|
204
|
+
if (input.to != null) return stops[Math.max(0, Math.min(stops.length - 1, input.to))];
|
|
205
|
+
if (input.steps) return stepFrom(gridOf(stops), base, input.steps);
|
|
206
|
+
return stepFrom(stops, base, input.scenes);
|
|
143
207
|
}
|
|
144
208
|
|
|
145
209
|
/**
|
|
146
|
-
* Moves between stops. input(
|
|
147
|
-
*
|
|
148
|
-
* onArrive(
|
|
210
|
+
* Moves between stops and half points. input(spec) starts a move from where the page stands (sync(y)),
|
|
211
|
+
* or during a move retargets it from where it was going. move(fromY, toY, done) animates from the
|
|
212
|
+
* page's place (where()); onArrive(y) runs when a move ends; onLeave() when one starts.
|
|
149
213
|
*/
|
|
150
214
|
function createNavigator(opt) {
|
|
151
|
-
let index = 0;
|
|
152
215
|
let pos = 0;
|
|
153
|
-
let target =
|
|
154
|
-
let queued = null;
|
|
155
|
-
let guardUntil = -Infinity;
|
|
216
|
+
let target = null;
|
|
156
217
|
let generation = 0;
|
|
218
|
+
let arrivals = 0;
|
|
157
219
|
const list = function () { return opt.stops(); };
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
target
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (to < 0 || Math.abs(list()[to] - pos) <= 1) return;
|
|
171
|
-
if (input === 'down' && index === opt.lastScene && opt.now() < guardUntil) return;
|
|
172
|
-
const from = index;
|
|
173
|
-
const gen = generation;
|
|
174
|
-
target = to;
|
|
175
|
-
opt.move(pos, list()[to], function () { if (gen === generation) arrive(from, to); });
|
|
220
|
+
function go(y, instant) {
|
|
221
|
+
if (y == null || Math.abs(y - (target == null ? pos : target)) <= 1) return;
|
|
222
|
+
const gen = ++generation;
|
|
223
|
+
if (target == null && opt.onLeave) opt.onLeave(instant);
|
|
224
|
+
target = y;
|
|
225
|
+
opt.move(opt.where ? opt.where() : pos, y, function () {
|
|
226
|
+
if (gen !== generation) return;
|
|
227
|
+
pos = y;
|
|
228
|
+
target = null;
|
|
229
|
+
arrivals++;
|
|
230
|
+
if (opt.onArrive) opt.onArrive(y);
|
|
231
|
+
}, Boolean(instant));
|
|
176
232
|
}
|
|
177
233
|
return {
|
|
178
|
-
input: function (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
},
|
|
183
|
-
sync: function (y) {
|
|
184
|
-
if (target !== -1) return;
|
|
185
|
-
pos = y;
|
|
186
|
-
index = nearest(list(), y);
|
|
187
|
-
},
|
|
188
|
-
// Drops the move in flight and the queued input; the move's end, when it comes, starts nothing.
|
|
189
|
-
cancel: function () {
|
|
190
|
-
generation++;
|
|
191
|
-
target = -1;
|
|
192
|
-
queued = null;
|
|
193
|
-
},
|
|
234
|
+
input: function (spec) { go(targetOf(list(), target == null ? pos : target, spec), spec.instant); },
|
|
235
|
+
sync: function (y) { if (target == null) pos = y; },
|
|
236
|
+
// Drops the move in flight; its end, when it comes, does nothing.
|
|
237
|
+
cancel: function () { generation++; target = null; },
|
|
194
238
|
state: function () {
|
|
195
|
-
const
|
|
196
|
-
|
|
239
|
+
const stops = list();
|
|
240
|
+
const at = target == null ? pos : target;
|
|
241
|
+
const index = nearest(stops, at);
|
|
242
|
+
return { index: index, y: at, moving: target != null, arrivals: arrivals, onStop: Math.abs(stops[index] - at) <= 1,
|
|
243
|
+
atBody: stops.length - 1 > opt.lastStop && index === stops.length - 1 && Math.abs(stops[index] - at) <= 1 };
|
|
197
244
|
},
|
|
198
245
|
};
|
|
199
246
|
}
|
|
@@ -207,15 +254,22 @@
|
|
|
207
254
|
const pressable = function (t) {
|
|
208
255
|
return Boolean(t) && (/^(BUTTON|A|SUMMARY)$/.test(t.tagName || '') || /^(button|link|checkbox|switch|menuitem|tab|option)$/.test(role(t)));
|
|
209
256
|
};
|
|
210
|
-
const KEYS = { ArrowDown:
|
|
257
|
+
const KEYS = { ArrowDown: { steps: 1 }, ArrowRight: { steps: 1 }, ArrowUp: { steps: -1 }, ArrowLeft: { steps: -1 },
|
|
258
|
+
PageDown: { scenes: 1 }, PageUp: { scenes: -1 }, Home: { to: 0, instant: true }, End: { to: 'last', instant: true } };
|
|
211
259
|
|
|
212
|
-
/**
|
|
260
|
+
/** What a key asks for ({ steps }, { scenes }, { to }; End's to is 'last'), or null when the key is not the intro's. */
|
|
213
261
|
function keyInput(e) {
|
|
214
262
|
if (e.altKey || e.ctrlKey || e.metaKey || editable(e.target)) return null;
|
|
215
|
-
if (e.key === ' ') return pressable(e.target) ? null :
|
|
263
|
+
if (e.key === ' ') return pressable(e.target) ? null : { scenes: e.shiftKey ? -1 : 1 };
|
|
216
264
|
return KEYS[e.key] || null;
|
|
217
265
|
}
|
|
218
266
|
|
|
267
|
+
/** The direction of an input from `base`: 'down', 'up', or null when it asks for no move. */
|
|
268
|
+
function dirOf(stops, base, input) {
|
|
269
|
+
const y = targetOf(stops, base, input);
|
|
270
|
+
return y == null || Math.abs(y - base) <= 1 ? null : (y > base ? 'down' : 'up');
|
|
271
|
+
}
|
|
272
|
+
|
|
219
273
|
// ---- browser wiring -----------------------------------------------------------
|
|
220
274
|
|
|
221
275
|
const ease = function (p) { return 0.5 - 0.5 * Math.cos(Math.PI * p); };
|
|
@@ -226,9 +280,46 @@
|
|
|
226
280
|
try { window.scrollTo({ top: y, behavior: 'instant' }); } catch (e) { window.scrollTo(0, y); }
|
|
227
281
|
}
|
|
228
282
|
|
|
283
|
+
// A scene a move shows paints nothing until its poster is decoded (the engine's posters decode async):
|
|
284
|
+
// a jump showed an empty background for 0.1-0.35 s. Decode every poster first, waiting at most capMs.
|
|
285
|
+
function postersReady(world, capMs, then) {
|
|
286
|
+
let fired = false;
|
|
287
|
+
const go = function () { if (!fired) { fired = true; then(); } };
|
|
288
|
+
const stills = Array.prototype.slice.call(world.querySelectorAll('.sw-scene__still'));
|
|
289
|
+
Promise.all(stills.map(function (i) { return i.decode ? i.decode().catch(function () {}) : null; })).then(go);
|
|
290
|
+
setTimeout(go, capMs);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The loading mark of the poster waits: begin(token) shows it after delayMs unless end(token) came first;
|
|
295
|
+
* a newer begin() or cancel() drops the older wait and its mark. show() and hide() draw it; timers injectable.
|
|
296
|
+
*/
|
|
297
|
+
function createLoader(delayMs, show, hide, timers) {
|
|
298
|
+
// wrapped: a browser refuses setTimeout called as a method of another object (Illegal invocation)
|
|
299
|
+
const t = timers || { set: function (f, ms) { return setTimeout(f, ms); }, clear: function (id) { clearTimeout(id); } };
|
|
300
|
+
let current = null;
|
|
301
|
+
let timer = null;
|
|
302
|
+
let shown = false;
|
|
303
|
+
function drop() {
|
|
304
|
+
if (timer !== null) t.clear(timer);
|
|
305
|
+
timer = null;
|
|
306
|
+
if (shown) { shown = false; hide(); }
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
begin: function (token) {
|
|
310
|
+
drop();
|
|
311
|
+
current = token;
|
|
312
|
+
timer = t.set(function () { timer = null; if (current === token) { shown = true; show(); } }, delayMs);
|
|
313
|
+
},
|
|
314
|
+
end: function (token) { if (token === current) { drop(); current = null; } },
|
|
315
|
+
cancel: function () { drop(); current = null; },
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
229
319
|
// The running animation; a newer one, or stopAnimation(), ends it between frames.
|
|
230
320
|
let animation = 0;
|
|
231
|
-
|
|
321
|
+
let pending = 0; // a move waiting for its posters; a newer move or stopAnimation() drops it
|
|
322
|
+
function stopAnimation() { animation++; pending++; }
|
|
232
323
|
|
|
233
324
|
function animate(from, to, duration, done) {
|
|
234
325
|
const mine = ++animation;
|
|
@@ -321,23 +412,39 @@
|
|
|
321
412
|
document.body.insertBefore(skip, document.body.firstChild);
|
|
322
413
|
document.body.appendChild(hint);
|
|
323
414
|
if (o.end) document.body.appendChild(main);
|
|
324
|
-
|
|
415
|
+
// a status region: its text is set while a poster wait shows, so a screen reader hears it; no focus taken
|
|
416
|
+
const loading = element('div', 'isn-loading');
|
|
417
|
+
loading.setAttribute('role', 'status');
|
|
418
|
+
loading.setAttribute('aria-live', 'polite');
|
|
419
|
+
const spin = element('i', 'isn-loading__spin');
|
|
420
|
+
spin.setAttribute('aria-hidden', 'true');
|
|
421
|
+
loading.appendChild(spin);
|
|
422
|
+
loading.appendChild(element('span', 'isn-loading__label'));
|
|
423
|
+
document.body.appendChild(loading);
|
|
424
|
+
return { hint: hint, skip: skip, main: main, loading: loading };
|
|
325
425
|
}
|
|
326
426
|
|
|
327
|
-
/**
|
|
427
|
+
/**
|
|
428
|
+
* Wires the navigation to the page. Returns { goTo(i), hold(on), state(), stops(), lastScene, lastStop, end };
|
|
429
|
+
* hold(true) stops the settling on a stop (a measuring script that parks the page between stops).
|
|
430
|
+
*/
|
|
328
431
|
function attach(world, config, options) {
|
|
329
432
|
const o = Object.assign({}, DEFAULTS, options || {});
|
|
330
433
|
const ctx = makeContext(world, config, o);
|
|
331
434
|
ctx.ui = buildUi(o);
|
|
435
|
+
const label = ctx.ui.loading.querySelector('.isn-loading__label');
|
|
436
|
+
ctx.loader = createLoader(o.loadingDelayMs,
|
|
437
|
+
function () { label.textContent = o.loadingLabel; ctx.ui.loading.classList.add('is-on'); },
|
|
438
|
+
function () { ctx.ui.loading.classList.remove('is-on'); label.textContent = ''; });
|
|
332
439
|
listenWheel(ctx);
|
|
333
440
|
listenKeys(ctx);
|
|
334
441
|
listenTouch(ctx);
|
|
335
442
|
listenClicks(ctx);
|
|
336
443
|
listenScroll(ctx);
|
|
337
444
|
ctx.nav.sync(window.scrollY);
|
|
338
|
-
ctx.mark(
|
|
339
|
-
const handle = { goTo: function (i) { ctx
|
|
340
|
-
state: function () { return ctx.nav.state(); }, stops: ctx.stops, lastScene: ctx.lastScene, end: o.end };
|
|
445
|
+
ctx.mark();
|
|
446
|
+
const handle = { goTo: function (i) { step(ctx, { to: i }); }, hold: function (on) { ctx.held = Boolean(on); },
|
|
447
|
+
state: function () { return ctx.nav.state(); }, stops: ctx.stops, lastScene: ctx.lastScene, lastStop: ctx.lastStop, end: o.end };
|
|
341
448
|
// The navigation on this page, for scripts and for verify-intro.mjs.
|
|
342
449
|
window.IntroStepNav.current = handle;
|
|
343
450
|
return handle;
|
|
@@ -346,7 +453,7 @@
|
|
|
346
453
|
function makeContext(world, config, o) {
|
|
347
454
|
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
348
455
|
const lastScene = (config.sections || []).length - 1;
|
|
349
|
-
const ctx = { o: o, world: world, lastScene: lastScene, focusEnd: false, warned: false };
|
|
456
|
+
const ctx = { o: o, world: world, config: config, lastScene: lastScene, lastStop: lastScene + 1, focusEnd: false, warned: false };
|
|
350
457
|
ctx.endEl = function () { return endElement(ctx); };
|
|
351
458
|
ctx.endTop = function () { const e = ctx.endEl(); return e ? Math.round(e.getBoundingClientRect().top + window.scrollY) : null; };
|
|
352
459
|
// The engine sizes its track (segments + one screen) from the height it last laid out at, so the
|
|
@@ -357,11 +464,24 @@
|
|
|
357
464
|
return h > 0 ? h / (totalWeight(config) + 1) : window.innerHeight;
|
|
358
465
|
};
|
|
359
466
|
ctx.stops = function () { return stopPositions(config, ctx.vh(), ctx.endTop()); };
|
|
360
|
-
ctx.ours = function (y, dir) { return belongsToIntro(y, dir, ctx.stops()[
|
|
361
|
-
ctx.mark = function (
|
|
362
|
-
ctx.nav = createNavigator({ stops: ctx.stops,
|
|
363
|
-
|
|
364
|
-
|
|
467
|
+
ctx.ours = function (y, dir) { return belongsToIntro(y, dir, ctx.stops()[ctx.lastStop], ctx.endTop()); };
|
|
468
|
+
ctx.mark = function () { mark(ctx); };
|
|
469
|
+
ctx.nav = createNavigator({ stops: ctx.stops, lastStop: ctx.lastStop, where: function () { return window.scrollY; },
|
|
470
|
+
onLeave: function (instant) { instantCopy(instant); showCopy(ctx, -1); },
|
|
471
|
+
onArrive: function () { ctx.mark(); requestAnimationFrame(function () { requestAnimationFrame(function () { instantCopy(false); }); }); },
|
|
472
|
+
move: function (from, to, done, instant) {
|
|
473
|
+
const mine = ++pending;
|
|
474
|
+
// an instant move swaps the copy at once even when it retargets a move already under way (no onLeave then)
|
|
475
|
+
instantCopy(instant);
|
|
476
|
+
ctx.loader.begin(mine);
|
|
477
|
+
// from where the page is once the posters are ready: an earlier move may have run on while waiting
|
|
478
|
+
postersReady(world, instant ? o.instantDecodeCapMs : o.decodeCapMs, function () {
|
|
479
|
+
if (mine !== pending) return;
|
|
480
|
+
ctx.loader.end(mine);
|
|
481
|
+
const at = window.scrollY;
|
|
482
|
+
animate(at, to, instant ? 0 : moveDuration(to - at, ctx.vh(), o, reduce), done);
|
|
483
|
+
});
|
|
484
|
+
} });
|
|
365
485
|
const fine = window.matchMedia('(hover: hover) and (pointer: fine)').matches;
|
|
366
486
|
document.documentElement.classList.add('isn-on', fine ? 'isn-mouse' : 'isn-touch');
|
|
367
487
|
return ctx;
|
|
@@ -387,44 +507,75 @@
|
|
|
387
507
|
e.focus({ preventScroll: true });
|
|
388
508
|
}
|
|
389
509
|
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
//
|
|
394
|
-
|
|
510
|
+
// An instant move (Home, End, a dot) swaps the copy without the fade (class isn-instant, step-nav.css).
|
|
511
|
+
function instantCopy(on) { document.documentElement.classList.toggle('isn-instant', Boolean(on)); }
|
|
512
|
+
|
|
513
|
+
// The copy of scene k shows over the engine's own fade (class isn-copy, see step-nav.css); -1 shows none.
|
|
514
|
+
// The same element: nothing is copied, so a screen reader and the focus meet each text once.
|
|
515
|
+
function showCopy(ctx, k) {
|
|
516
|
+
ctx.world.querySelectorAll('.sw-copy').forEach(function (c, i) { c.classList.toggle('isn-copy', i === k); });
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Classes the CSS reads: isn-last at the last scene's end; isn-ready there once its clip has played
|
|
520
|
+
// to the end (under reduced motion that stop is reached at once and the engine plays no clip);
|
|
521
|
+
// isn-body at or below the body top. At a stop or a half point the copy in view shows. Moves the
|
|
522
|
+
// focus to the body after the main button.
|
|
523
|
+
function mark(ctx) {
|
|
395
524
|
const c = document.documentElement.classList;
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
525
|
+
const y = window.scrollY;
|
|
526
|
+
const stops = ctx.stops();
|
|
527
|
+
const atLast = Math.abs(y - stops[ctx.lastStop]) <= 1;
|
|
528
|
+
const body = stops.length - 1 > ctx.lastStop && y >= stops[stops.length - 1] - 1;
|
|
529
|
+
c.toggle('isn-last', atLast);
|
|
530
|
+
c.toggle('isn-ready', Boolean(ctx.endEl()) && atLast);
|
|
531
|
+
c.toggle('isn-body', body);
|
|
532
|
+
const onGrid = gridOf(stops).some(function (v) { return Math.abs(v - y) <= 1; });
|
|
533
|
+
showCopy(ctx, onGrid && !body ? sceneAt(ctx.config, ctx.vh(), y) : -1);
|
|
534
|
+
if (ctx.focusEnd && body) focusBody(ctx);
|
|
401
535
|
}
|
|
402
536
|
|
|
403
|
-
|
|
537
|
+
// An input ({ steps }, { scenes }, { to }, End's { to: 'last' }; instant: no animation) from where the page stands.
|
|
538
|
+
function step(ctx, input) {
|
|
404
539
|
ctx.nav.sync(window.scrollY);
|
|
405
|
-
ctx.nav.input(
|
|
540
|
+
ctx.nav.input(input.to === 'last' ? { to: ctx.lastStop, instant: input.instant } : input);
|
|
406
541
|
}
|
|
407
542
|
|
|
543
|
+
// The direction an input takes from where the page (or the move in flight) is going.
|
|
544
|
+
const dirFrom = function (ctx, input) {
|
|
545
|
+
const st = ctx.nav.state();
|
|
546
|
+
return dirOf(ctx.stops(), st.moving ? st.y : window.scrollY, input.to === 'last' ? { to: ctx.lastStop, instant: input.instant } : input);
|
|
547
|
+
};
|
|
548
|
+
|
|
408
549
|
function listenWheel(ctx) {
|
|
409
550
|
const gesture = createWheelGesture(ctx.o);
|
|
551
|
+
let carry = 0;
|
|
552
|
+
let used = 0;
|
|
410
553
|
window.addEventListener('wheel', function (e) {
|
|
411
554
|
if (wheelIgnored(e)) return;
|
|
412
|
-
|
|
413
|
-
if (!ctx.ours(window.scrollY, dir)) return;
|
|
555
|
+
if (!ctx.ours(window.scrollY, e.deltaY > 0 ? 'down' : 'up')) return;
|
|
414
556
|
e.preventDefault();
|
|
415
|
-
|
|
557
|
+
const kind = gesture.feed(e, e.timeStamp);
|
|
558
|
+
if (kind === 'ignore') return;
|
|
559
|
+
if (kind === 'new') { carry = 0; used = 0; }
|
|
560
|
+
const w = wheelNotches(e, carry, ctx.o);
|
|
561
|
+
carry = w.carry;
|
|
562
|
+
const n = Math.sign(w.n) * Math.min(Math.abs(w.n), ctx.o.maxNotches - used);
|
|
563
|
+
if (!n) return;
|
|
564
|
+
used += Math.abs(n);
|
|
565
|
+
step(ctx, { steps: n });
|
|
416
566
|
}, { passive: false });
|
|
417
567
|
}
|
|
418
568
|
|
|
419
569
|
function listenKeys(ctx) {
|
|
420
570
|
window.addEventListener('keydown', function (e) {
|
|
421
|
-
const
|
|
571
|
+
const input = keyInput(e);
|
|
572
|
+
if (!input) return;
|
|
573
|
+
const dir = dirFrom(ctx, input);
|
|
422
574
|
if (!dir || !ctx.ours(window.scrollY, dir)) return;
|
|
423
575
|
e.preventDefault();
|
|
424
|
-
if (!e.repeat) step(ctx,
|
|
576
|
+
if (!e.repeat) step(ctx, input);
|
|
425
577
|
});
|
|
426
578
|
}
|
|
427
|
-
|
|
428
579
|
function listenTouch(ctx) {
|
|
429
580
|
let y0 = null;
|
|
430
581
|
window.addEventListener('touchstart', function (e) { y0 = e.touches.length === 1 ? e.touches[0].clientY : null; }, { passive: true });
|
|
@@ -438,7 +589,7 @@
|
|
|
438
589
|
const dy = y0 - e.changedTouches[0].clientY;
|
|
439
590
|
const dir = dy > 0 ? 'down' : 'up';
|
|
440
591
|
y0 = null;
|
|
441
|
-
if (Math.abs(dy) > ctx.o.swipePx && ctx.ours(window.scrollY, dir)) step(ctx, dir);
|
|
592
|
+
if (Math.abs(dy) > ctx.o.swipePx && ctx.ours(window.scrollY, dir)) step(ctx, { scenes: dir === 'down' ? 1 : -1 });
|
|
442
593
|
}, { passive: true });
|
|
443
594
|
}
|
|
444
595
|
|
|
@@ -447,10 +598,11 @@
|
|
|
447
598
|
function skip(ctx) {
|
|
448
599
|
ctx.nav.cancel();
|
|
449
600
|
stopAnimation();
|
|
601
|
+
ctx.loader.cancel();
|
|
450
602
|
const top = ctx.endTop();
|
|
451
|
-
jump(top == null ? ctx.stops()[ctx.
|
|
603
|
+
jump(top == null ? ctx.stops()[ctx.lastStop] : top);
|
|
452
604
|
ctx.nav.sync(window.scrollY);
|
|
453
|
-
ctx.mark(
|
|
605
|
+
ctx.mark();
|
|
454
606
|
focusBody(ctx);
|
|
455
607
|
}
|
|
456
608
|
|
|
@@ -473,30 +625,49 @@
|
|
|
473
625
|
if (!dot && !control) return;
|
|
474
626
|
e.preventDefault();
|
|
475
627
|
e.stopImmediatePropagation();
|
|
476
|
-
if (dot) return step(ctx, { to: Array.prototype.indexOf.call(dot.parentNode.children, dot) });
|
|
628
|
+
if (dot) return step(ctx, { to: Array.prototype.indexOf.call(dot.parentNode.children, dot), instant: true });
|
|
477
629
|
return control.classList.contains('isn-skip') ? skip(ctx) : toBody(ctx);
|
|
478
630
|
}, true);
|
|
479
631
|
}
|
|
480
632
|
|
|
481
|
-
// Scrolling by other means (scrollbar, find in page,
|
|
633
|
+
// Scrolling by other means (scrollbar, a touchpad, find in page, an anchor) moves the navigator's place
|
|
634
|
+
// too; a page that comes to rest off the stops and half points, above the body, settles on the nearest stop.
|
|
482
635
|
function listenScroll(ctx) {
|
|
483
636
|
let queued = false;
|
|
637
|
+
let rest = 0;
|
|
638
|
+
const later = function () { clearTimeout(rest); rest = setTimeout(function () { settle(ctx); }, ctx.o.snapMs); };
|
|
639
|
+
// while a pointer holds the page or its scrollbar, nothing settles; snapMs after it lets go, it may
|
|
640
|
+
window.addEventListener('pointerdown', function () { ctx.pressed = true; clearTimeout(rest); }, true);
|
|
641
|
+
// a press can end without a pointerup: a context menu, a release outside the window, a lost capture, the tab hidden
|
|
642
|
+
const release = function () { if (!ctx.pressed) return; ctx.pressed = false; later(); };
|
|
643
|
+
['pointerup', 'pointercancel', 'lostpointercapture', 'contextmenu', 'blur'].forEach(function (k) { window.addEventListener(k, release, true); });
|
|
644
|
+
document.addEventListener('visibilitychange', function () { if (document.hidden) release(); });
|
|
484
645
|
window.addEventListener('scroll', function () {
|
|
485
|
-
if (
|
|
646
|
+
if (ctx.nav.state().moving) return;
|
|
647
|
+
later();
|
|
648
|
+
if (queued) return;
|
|
486
649
|
queued = true;
|
|
487
650
|
requestAnimationFrame(function () {
|
|
488
651
|
queued = false;
|
|
489
652
|
if (ctx.nav.state().moving) return;
|
|
490
653
|
ctx.nav.sync(window.scrollY);
|
|
491
|
-
ctx.mark(
|
|
654
|
+
ctx.mark();
|
|
492
655
|
});
|
|
493
656
|
}, { passive: true });
|
|
494
657
|
}
|
|
495
658
|
|
|
659
|
+
function settle(ctx) {
|
|
660
|
+
const y = window.scrollY;
|
|
661
|
+
if (ctx.held || ctx.pressed || ctx.nav.state().moving || !ctx.ours(y, 'down')) return;
|
|
662
|
+
const to = settleTarget(ctx.stops(), ctx.lastStop, y);
|
|
663
|
+
if (to >= 0) step(ctx, { to: to });
|
|
664
|
+
}
|
|
665
|
+
|
|
496
666
|
const api = {
|
|
497
667
|
DEFAULTS: DEFAULTS, stopPositions: stopPositions, totalWeight: totalWeight, moveDuration: moveDuration,
|
|
498
668
|
wheelIgnored: wheelIgnored, createWheelGesture: createWheelGesture, belongsToIntro: belongsToIntro,
|
|
499
|
-
createNavigator: createNavigator, keyInput: keyInput, prefetch: prefetch, attach: attach,
|
|
669
|
+
createNavigator: createNavigator, keyInput: keyInput, prefetch: prefetch, attach: attach, postersReady: postersReady,
|
|
670
|
+
gridOf: gridOf, stepFrom: stepFrom, createLoader: createLoader, sceneAt: sceneAt, wheelNotches: wheelNotches, dirOf: dirOf, settleTarget: settleTarget,
|
|
500
671
|
};
|
|
501
672
|
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
502
673
|
else window.IntroStepNav = api;
|