tuffgal 0.2.0-alpha.3 → 0.2.0-alpha.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/CHANGELOG.md +34 -20
- package/README.md +6 -6
- package/dist/cli.d.ts +5 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +6 -6
- package/dist/commands/supervise.d.ts +3 -3
- package/dist/commands/supervise.js +8 -4
- package/dist/commands/supervise.js.map +1 -1
- package/dist/config.d.ts +29 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -2
- package/dist/config.js.map +1 -1
- package/dist/coverage/screens.d.ts +10 -5
- package/dist/coverage/screens.d.ts.map +1 -1
- package/dist/coverage/screens.js +18 -8
- package/dist/coverage/screens.js.map +1 -1
- package/dist/reporter/assets/report.css +11 -11
- package/dist/reporter/assets/report.js +23 -23
- package/dist/reporter/template.js +48 -46
- package/dist/reporter/template.js.map +1 -1
- package/dist/runner/approve.d.ts +5 -5
- package/dist/runner/approve.js +5 -5
- package/dist/runner/approveFrom.d.ts +8 -8
- package/dist/runner/approveFrom.js +32 -31
- package/dist/runner/approveFrom.js.map +1 -1
- package/dist/runner/breakpointPasses.d.ts +16 -9
- package/dist/runner/breakpointPasses.d.ts.map +1 -1
- package/dist/runner/breakpointPasses.js +17 -9
- package/dist/runner/breakpointPasses.js.map +1 -1
- package/dist/runner/bridges/devServers.d.ts +10 -1
- package/dist/runner/bridges/devServers.d.ts.map +1 -1
- package/dist/runner/bridges/devServers.js +16 -8
- package/dist/runner/bridges/devServers.js.map +1 -1
- package/dist/runner/coverage.d.ts +3 -3
- package/dist/runner/coverage.js +5 -5
- package/dist/runner/coverage.js.map +1 -1
- package/dist/runner/exitCode.d.ts +7 -7
- package/dist/runner/exitCode.js +7 -7
- package/dist/runner/interpolate.js +1 -1
- package/dist/runner/interpolate.js.map +1 -1
- package/dist/runner/manifest.d.ts +16 -16
- package/dist/runner/manifest.js +12 -12
- package/dist/runner/mode.d.ts +2 -2
- package/dist/runner/mode.js +4 -4
- package/dist/runner/orphanScan.d.ts +3 -3
- package/dist/runner/orphanScan.js +5 -5
- package/dist/runner/orphanScan.js.map +1 -1
- package/dist/runner/resolveLocator.d.ts +5 -5
- package/dist/runner/resolveLocator.js +6 -6
- package/dist/runner/resolveLocator.js.map +1 -1
- package/dist/runner/run.d.ts +55 -18
- package/dist/runner/run.d.ts.map +1 -1
- package/dist/runner/run.js +118 -67
- package/dist/runner/run.js.map +1 -1
- package/dist/runner/runAction.d.ts +4 -4
- package/dist/runner/runAction.d.ts.map +1 -1
- package/dist/runner/runAction.js +72 -34
- package/dist/runner/runAction.js.map +1 -1
- package/dist/runner/runStory.d.ts +29 -18
- package/dist/runner/runStory.d.ts.map +1 -1
- package/dist/runner/runStory.js +38 -42
- package/dist/runner/runStory.js.map +1 -1
- package/dist/runner/scheduler.d.ts +11 -0
- package/dist/runner/scheduler.d.ts.map +1 -1
- package/dist/runner/scheduler.js +1 -1
- package/dist/runner/scheduler.js.map +1 -1
- package/dist/runner/steps/intercept.d.ts +6 -4
- package/dist/runner/steps/intercept.d.ts.map +1 -1
- package/dist/runner/steps/intercept.js.map +1 -1
- package/dist/runner/steps/navigate.d.ts +5 -2
- package/dist/runner/steps/navigate.d.ts.map +1 -1
- package/dist/runner/steps/navigate.js +17 -3
- package/dist/runner/steps/navigate.js.map +1 -1
- package/dist/runner/steps/read.d.ts +1 -1
- package/dist/runner/steps/read.js +1 -1
- package/dist/runner/steps/scroll.d.ts +1 -1
- package/dist/runner/steps/scroll.d.ts.map +1 -1
- package/dist/runner/steps/scroll.js +3 -2
- package/dist/runner/steps/scroll.js.map +1 -1
- package/dist/schema/action.d.ts +2 -2
- package/dist/schema/action.d.ts.map +1 -1
- package/dist/schema/action.js +58 -13
- package/dist/schema/action.js.map +1 -1
- package/dist/schema/load.js +1 -1
- package/dist/schema/load.js.map +1 -1
- package/dist/schema/result.d.ts +22 -22
- package/dist/schema/result.d.ts.map +1 -1
- package/dist/schema/result.js +1 -1
- package/dist/schema/story.d.ts +1 -1
- package/dist/schema/story.js +5 -5
- package/dist/screenshots/baselineStore.d.ts +44 -21
- package/dist/screenshots/baselineStore.d.ts.map +1 -1
- package/dist/screenshots/baselineStore.js +59 -28
- package/dist/screenshots/baselineStore.js.map +1 -1
- package/dist/screenshots/capture.d.ts +35 -4
- package/dist/screenshots/capture.d.ts.map +1 -1
- package/dist/screenshots/capture.js +61 -9
- package/dist/screenshots/capture.js.map +1 -1
- package/dist/screenshots/diff.d.ts +57 -8
- package/dist/screenshots/diff.d.ts.map +1 -1
- package/dist/screenshots/diff.js +78 -28
- package/dist/screenshots/diff.js.map +1 -1
- package/dist/util.d.ts +23 -2
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +42 -2
- package/dist/util.js.map +1 -1
- package/package.json +4 -8
package/dist/config.js
CHANGED
|
@@ -30,6 +30,7 @@ export const BREAKPOINTS = {
|
|
|
30
30
|
};
|
|
31
31
|
const DEFAULTS = {
|
|
32
32
|
captureMode: 'viewport',
|
|
33
|
+
maxFullPagePixels: 30_000_000,
|
|
33
34
|
defaultTimeoutMs: 10_000,
|
|
34
35
|
navigationTimeoutMs: 15_000,
|
|
35
36
|
frozenTime: '2026-01-15T12:00:00.000Z',
|
|
@@ -75,7 +76,7 @@ export async function loadConfig(cwd) {
|
|
|
75
76
|
* dereferences it. The config module is dynamically imported, so TypeScript
|
|
76
77
|
* cannot guarantee it actually matches `TuffgalConfig`; without this a missing
|
|
77
78
|
* `paths` or a non-string `baseUrl` throws an opaque TypeError inside
|
|
78
|
-
* `resolveConfig`. Validation covers the fields `resolveConfig` reads
|
|
79
|
+
* `resolveConfig`. Validation covers the fields `resolveConfig` reads. The
|
|
79
80
|
* `database`/`devServers` bridges hold functions and are left to fail at their
|
|
80
81
|
* own call sites. `source` is the config file path, surfaced in every message.
|
|
81
82
|
*/
|
|
@@ -109,6 +110,7 @@ export function assertValidConfig(input, source) {
|
|
|
109
110
|
'defaultTimeoutMs',
|
|
110
111
|
'navigationTimeoutMs',
|
|
111
112
|
'workers',
|
|
113
|
+
'maxFullPagePixels',
|
|
112
114
|
]) {
|
|
113
115
|
const value = config[key];
|
|
114
116
|
if (value !== undefined && (typeof value !== 'number' || value <= 0)) {
|
|
@@ -173,6 +175,7 @@ function resolveConfig(input, rootDir) {
|
|
|
173
175
|
storageStatePins: input.storageStatePins ?? [],
|
|
174
176
|
breakpoints,
|
|
175
177
|
captureMode: input.captureMode ?? DEFAULTS.captureMode,
|
|
178
|
+
maxFullPagePixels: input.maxFullPagePixels ?? DEFAULTS.maxFullPagePixels,
|
|
176
179
|
defaultTimeoutMs: input.defaultTimeoutMs ?? DEFAULTS.defaultTimeoutMs,
|
|
177
180
|
navigationTimeoutMs: input.navigationTimeoutMs ?? DEFAULTS.navigationTimeoutMs,
|
|
178
181
|
frozenTime: input.frozenTime ?? DEFAULTS.frozenTime,
|
|
@@ -223,7 +226,7 @@ export function resolveSelectorList(selectors) {
|
|
|
223
226
|
}
|
|
224
227
|
/**
|
|
225
228
|
* Resolves `config.breakpoints` to an always-non-empty list: the explicit
|
|
226
|
-
* selectors when set (order preserved, duplicate names dropped
|
|
229
|
+
* selectors when set (order preserved, duplicate names dropped, first wins),
|
|
227
230
|
* else a single `desktop` breakpoint (1280x800). `assertValidConfig` has
|
|
228
231
|
* already rejected unknown/empty `breakpoints`, so a non-empty input dedupes to
|
|
229
232
|
* at least one entry; the cast restores the non-empty tuple type that lets
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import type { CoverageMetric } from '../schema/result.ts';
|
|
2
2
|
/**
|
|
3
3
|
* Walks every `visit-*.json` action under `<actions>/screens/` and reports how
|
|
4
|
-
* many have
|
|
5
|
-
* action
|
|
6
|
-
*
|
|
4
|
+
* many have at least one baseline screenshot under
|
|
5
|
+
* `<baselineRoot>/<action-name>/`. The runner keys baselines by breakpoint
|
|
6
|
+
* (`<breakpoint>.png`, e.g. `desktop.png`) and a project baselined before that
|
|
7
|
+
* feature keeps a single legacy `0.png`; either counts, so a screen is covered
|
|
8
|
+
* when its action directory holds any `.png` file (non-PNG siblings such as the
|
|
9
|
+
* `<breakpoint>.a11y.yaml` snapshots are ignored). The action name is taken from
|
|
10
|
+
* the filename (minus the `.json` suffix), which by convention matches the
|
|
11
|
+
* action's declared `action` field.
|
|
7
12
|
*
|
|
8
13
|
* `baselineRoot` is the set the run compares against: committed `paths.baselines`
|
|
9
|
-
* in CI mode, the per-machine `paths.localCache` in local mode (the caller
|
|
10
|
-
* `coverageComparisonRoot
|
|
14
|
+
* in CI mode, the per-machine `paths.localCache` in local mode (the caller, see
|
|
15
|
+
* `coverageComparisonRoot`, picks it by mode so a local run never reads
|
|
11
16
|
* `paths.baselines`). The metric's meaning is the same either way: how many
|
|
12
17
|
* screens have a baseline to diff against.
|
|
13
18
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screens.d.ts","sourceRoot":"","sources":["../../src/coverage/screens.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D
|
|
1
|
+
{"version":3,"file":"screens.d.ts","sourceRoot":"","sources":["../../src/coverage/screens.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,CAAC,CAoCzB"}
|
package/dist/coverage/screens.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
const SCREENS_SUBDIR = 'screens';
|
|
4
4
|
/**
|
|
5
5
|
* Walks every `visit-*.json` action under `<actions>/screens/` and reports how
|
|
6
|
-
* many have
|
|
7
|
-
* action
|
|
8
|
-
*
|
|
6
|
+
* many have at least one baseline screenshot under
|
|
7
|
+
* `<baselineRoot>/<action-name>/`. The runner keys baselines by breakpoint
|
|
8
|
+
* (`<breakpoint>.png`, e.g. `desktop.png`) and a project baselined before that
|
|
9
|
+
* feature keeps a single legacy `0.png`; either counts, so a screen is covered
|
|
10
|
+
* when its action directory holds any `.png` file (non-PNG siblings such as the
|
|
11
|
+
* `<breakpoint>.a11y.yaml` snapshots are ignored). The action name is taken from
|
|
12
|
+
* the filename (minus the `.json` suffix), which by convention matches the
|
|
13
|
+
* action's declared `action` field.
|
|
9
14
|
*
|
|
10
15
|
* `baselineRoot` is the set the run compares against: committed `paths.baselines`
|
|
11
|
-
* in CI mode, the per-machine `paths.localCache` in local mode (the caller
|
|
12
|
-
* `coverageComparisonRoot
|
|
16
|
+
* in CI mode, the per-machine `paths.localCache` in local mode (the caller, see
|
|
17
|
+
* `coverageComparisonRoot`, picks it by mode so a local run never reads
|
|
13
18
|
* `paths.baselines`). The metric's meaning is the same either way: how many
|
|
14
19
|
* screens have a baseline to diff against.
|
|
15
20
|
*/
|
|
@@ -28,11 +33,16 @@ export async function computeScreenCoverage(actionsDir, baselineRoot) {
|
|
|
28
33
|
.sort();
|
|
29
34
|
const missing = [];
|
|
30
35
|
for (const name of screenNames) {
|
|
31
|
-
const
|
|
36
|
+
const actionDir = join(baselineRoot, name);
|
|
37
|
+
let hasBaseline = false;
|
|
32
38
|
try {
|
|
33
|
-
await
|
|
39
|
+
const baselineFiles = await readdir(actionDir);
|
|
40
|
+
hasBaseline = baselineFiles.some((file) => file.toLowerCase().endsWith('.png'));
|
|
34
41
|
}
|
|
35
42
|
catch {
|
|
43
|
+
hasBaseline = false;
|
|
44
|
+
}
|
|
45
|
+
if (!hasBaseline) {
|
|
36
46
|
missing.push(name);
|
|
37
47
|
}
|
|
38
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screens.js","sourceRoot":"","sources":["../../src/coverage/screens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"screens.js","sourceRoot":"","sources":["../../src/coverage/screens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,cAAc,GAAG,SAAS,CAAC;AAEjC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,YAAoB;IAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACrD,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzD,CAAC;IACD,MAAM,WAAW,GAAG,OAAO;SACxB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;SAC3C,IAAI,EAAE,CAAC;IACV,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;YAC/C,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACxC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CACpC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,WAAW,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC;IACjC,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IACvC,OAAO;QACL,KAAK;QACL,OAAO;QACP,KAAK,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK;QACxC,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -347,7 +347,7 @@ h2 {
|
|
|
347
347
|
color: var(--new-fg);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
/* "all" stays NEUTRAL
|
|
350
|
+
/* "all" stays NEUTRAL; never a status color, never blue. Pressed is a filled
|
|
351
351
|
neutral pill (it is pressed-by-default at load, so this is the resting view). */
|
|
352
352
|
.summary-item[data-status='all'] .summary-filter {
|
|
353
353
|
color: var(--text);
|
|
@@ -364,7 +364,7 @@ h2 {
|
|
|
364
364
|
outline-offset: 2px;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
/* Breakpoint filter
|
|
367
|
+
/* Breakpoint filter; the SECOND, independent single-select filter dimension,
|
|
368
368
|
rendered below the status totals inside the summary region (only for
|
|
369
369
|
multi-breakpoint runs). A role="group" row of neutral pills; report.js ANDs it
|
|
370
370
|
with the status filter. The row sits on the page --bg with a top gap that also
|
|
@@ -376,15 +376,15 @@ h2 {
|
|
|
376
376
|
margin-top: 0.75rem;
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
/* Neutral pills
|
|
379
|
+
/* Neutral pills; no status hue, so the two filter rows read as siblings by
|
|
380
380
|
geometry, not color. Same pill geometry/radius as the status filter.
|
|
381
381
|
Default is an OUTLINE pill: transparent bg, --text-muted border (≥3:1 vs --bg
|
|
382
382
|
in both themes → 1.4.11), --text label (≥4.5:1 → 1.4.3). Hover brightens the
|
|
383
383
|
border + adds a faint neutral tint. Pressed is a FILLED pill: --text fill,
|
|
384
|
-
--bg label, bold weight
|
|
384
|
+
--bg label, bold weight; the ONLY filled + bold pill, so the pressed state is
|
|
385
385
|
never conveyed by color alone (1.4.1); the inverse fill clears 4.5:1 in both
|
|
386
386
|
themes. Focus ring is --accent 2px / offset 2px, painted on the page --bg
|
|
387
|
-
outside the pill
|
|
387
|
+
outside the pill; identical to the status filter. Target size ≥24×24 via
|
|
388
388
|
min-height/min-width (2.5.8). Stays a <button>: it filters in place. */
|
|
389
389
|
.breakpoint-filter {
|
|
390
390
|
display: inline-flex;
|
|
@@ -497,7 +497,7 @@ h2 {
|
|
|
497
497
|
font-size: 0.8125rem;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
-
/* Link-styled verbs ("Expand" / "Collapse")
|
|
500
|
+
/* Link-styled verbs ("Expand" / "Collapse"); still <button>s, they act in
|
|
501
501
|
place. The underline is the affordance; verbs exceed 24px wide intrinsically,
|
|
502
502
|
so min-height alone carries 2.5.8. */
|
|
503
503
|
.story-bulk-toggle-button {
|
|
@@ -527,7 +527,7 @@ h2 {
|
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
/* Disabled verb: removing the underline is the color-independent cue (no
|
|
530
|
-
opacity
|
|
530
|
+
opacity; it can compound below the contrast floor). Disabled controls are
|
|
531
531
|
1.4.3/1.4.11 exempt, but the state stays perceptible without color. */
|
|
532
532
|
.story-bulk-toggle-button:disabled {
|
|
533
533
|
color: var(--text-subtle);
|
|
@@ -931,7 +931,7 @@ summary.action-row:focus-visible {
|
|
|
931
931
|
chip focus ring is never clipped by the stage overflow nor painted over
|
|
932
932
|
screenshot pixels. The chips reuse `.chip--toggle` (border `--border-bright`,
|
|
933
933
|
`--accent` focus ring) and the same `.shot-radio-label` base styles as the
|
|
934
|
-
radio-tab viewer
|
|
934
|
+
radio-tab viewer; permanently visible, no interactiveMode-specific chip
|
|
935
935
|
styling. Visible chips are the mouse/touch path to every variant (including
|
|
936
936
|
Diff, which the press gesture does not cover) and double as the viewer's
|
|
937
937
|
only visible affordance; the press-and-hold blink-compare in report.js is
|
|
@@ -946,9 +946,9 @@ summary.action-row:focus-visible {
|
|
|
946
946
|
border: 0;
|
|
947
947
|
}
|
|
948
948
|
|
|
949
|
-
/* Persistent caption naming the DISPLAYED variant
|
|
949
|
+
/* Persistent caption naming the DISPLAYED variant; the committed one, or the
|
|
950
950
|
press-and-hold preview while it is held (report.js keeps it in sync). Text on
|
|
951
|
-
`--text` (≥4.5:1), so the active variant is never conveyed by color alone
|
|
951
|
+
`--text` (≥4.5:1), so the active variant is never conveyed by color alone ;
|
|
952
952
|
it pairs the checked chip's accent border with this readable label.
|
|
953
953
|
aria-hidden keeps it visual only: the checked radio is the single accessible
|
|
954
954
|
source of committed state. */
|
|
@@ -964,7 +964,7 @@ summary.action-row:focus-visible {
|
|
|
964
964
|
|
|
965
965
|
/* The stage clips the screenshot to the viewport height. Its border uses
|
|
966
966
|
`--border-bright` (3:1 non-text contrast). overflow:hidden only ever clips
|
|
967
|
-
the image
|
|
967
|
+
the image; the controls live in the sibling fieldset above. */
|
|
968
968
|
.shot-stage {
|
|
969
969
|
margin-top: 0.5rem;
|
|
970
970
|
margin-left: 2.2rem;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
//
|
|
19
19
|
// Both writers use `textContent` (not `innerHTML`). If the new message
|
|
20
20
|
// equals the current `textContent`, some screen readers will not re-announce
|
|
21
|
-
|
|
21
|
+
//; this is a documented platform limitation, not worked around here. A
|
|
22
22
|
// future caller that needs forced re-announcement would add a separate
|
|
23
23
|
// `forceWrite()`; out of scope for this helper.
|
|
24
24
|
//
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
});
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
// Selection lives in the DOM, so a collapse only HIDES it
|
|
118
|
+
// Selection lives in the DOM, so a collapse only HIDES it; a prior
|
|
119
119
|
// Baseline/Diff pick would otherwise still be checked on the next expand.
|
|
120
120
|
// Reset on COLLAPSE, not expand: the `toggle` event fires AFTER the state
|
|
121
121
|
// change, so resetting on open flashes a visible baseline→actual jump.
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
|
|
139
139
|
// Interactive screenshot viewer (rendered when the report is built with
|
|
140
140
|
// interactiveMode). Each action shows ONE shared <img>. A native radio group
|
|
141
|
-
// (always-visible chips) is the committed-state source of truth
|
|
141
|
+
// (always-visible chips) is the committed-state source of truth; keyboard,
|
|
142
142
|
// touch, and AT operate it exactly like the radio-tab viewer, and the chips
|
|
143
143
|
// are the only path to Diff. On top of that, the mouse gesture is a STATELESS
|
|
144
144
|
// blink-compare layered on the same <img>:
|
|
@@ -150,14 +150,14 @@
|
|
|
150
150
|
// There is deliberately NO hover behavior: hover swapped the image the moment
|
|
151
151
|
// the pointer wandered in, so the resting state under the cursor was the OLD
|
|
152
152
|
// screenshot. Rest = committed; press = intentional compare. Diff is off the
|
|
153
|
-
// gesture too (secondary forensic view
|
|
153
|
+
// gesture too (secondary forensic view; its chip covers it).
|
|
154
154
|
// The preview rewrites img.src plus the sight-only (aria-hidden) caption, so
|
|
155
155
|
// the "Showing: {variant}" line always names the DISPLAYED image. It never
|
|
156
156
|
// changes the checked radio, the img alt, any ARIA attribute, or any live
|
|
157
|
-
// region
|
|
157
|
+
// region; so pressing announces nothing by construction. The caption and the
|
|
158
158
|
// checked radio are two different truths on purpose: the caption tracks what
|
|
159
159
|
// is displayed (preview included), the checked radio tracks the committed
|
|
160
|
-
// state AT operates on; during a press they diverge briefly
|
|
160
|
+
// state AT operates on; during a press they diverge briefly; both remain
|
|
161
161
|
// accurate. Do NOT "fix" that by moving radio.checked during preview: checked
|
|
162
162
|
// is the single accessible source of committed state, and press-flapping it
|
|
163
163
|
// would announce to AT and leave revert() nothing stable to revert to.
|
|
@@ -193,12 +193,12 @@
|
|
|
193
193
|
if (src !== null) sources[variant] = src;
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
-
// `committed` mirrors the checked radio
|
|
196
|
+
// `committed` mirrors the checked radio; the ONLY state the keyboard /
|
|
197
197
|
// touch / AT path mutates. Mouse preview reverts here on release, leave,
|
|
198
198
|
// and dragstart.
|
|
199
199
|
var committed = null;
|
|
200
200
|
|
|
201
|
-
// Caption always names the DISPLAYED variant
|
|
201
|
+
// Caption always names the DISPLAYED variant; committed or previewed.
|
|
202
202
|
// The equality check is a cheap idempotence guard: writes are skipped
|
|
203
203
|
// when the label is already showing (e.g. revert after a no-op press).
|
|
204
204
|
function setCaption(variant) {
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
stage.addEventListener('mouseleave', revert);
|
|
252
252
|
// A press that turns into a native image drag can swallow the mouseup
|
|
253
253
|
// (the pointer never "leaves" the stage), which would leave the preview
|
|
254
|
-
// stuck. Native drag itself stays enabled
|
|
254
|
+
// stuck. Native drag itself stays enabled; save-image is a platform
|
|
255
255
|
// feature, not ours to suppress.
|
|
256
256
|
stage.addEventListener('dragstart', revert);
|
|
257
257
|
}
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
// `pass`): a story is status-eligible iff its own rollup `data-status`
|
|
270
270
|
// (emitted on the .story element) matches the active status, or the status
|
|
271
271
|
// filter is "all". Within an eligible story, non-matching actions are then
|
|
272
|
-
// PRUNED (hidden) so e.g. the `changed` view shows only the changed rows
|
|
272
|
+
// PRUNED (hidden) so e.g. the `changed` view shows only the changed rows ;
|
|
273
273
|
// and expand-all opens only those rows' screenshots. `skipped` actions are
|
|
274
274
|
// exempt from pruning: `skipped` exists on actions but not in the story
|
|
275
275
|
// rollup vocabulary, so pruning it could empty a story the pill counts (a
|
|
@@ -289,7 +289,7 @@
|
|
|
289
289
|
//
|
|
290
290
|
// The two axes are ANDed: a story shows iff it is status-eligible (story
|
|
291
291
|
// rollup) AND breakpoint-eligible (has a matching container). apply() is a
|
|
292
|
-
// single top-down pass
|
|
292
|
+
// single top-down pass; story → [data-breakpoint] container; that reads BOTH
|
|
293
293
|
// active tokens, so re-running it after either axis changes yields the current
|
|
294
294
|
// intersection (no stale hides carried between axes). The breakpoint pills
|
|
295
295
|
// render only for multi-breakpoint runs, so on a single-breakpoint run this
|
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
|
|
327
327
|
// Cached for the zero-match disable + focus-rescue logic in apply(). The
|
|
328
328
|
// scope word is now static markup ("screenshots" visible, " all screenshots"
|
|
329
|
-
// sr-only), so the buttons are never relabelled per filter
|
|
329
|
+
// sr-only), so the buttons are never relabelled per filter; the filter's own
|
|
330
330
|
// live-region announcement already covers the context change.
|
|
331
331
|
var expandButton = document.querySelector('[data-bulk-toggle="expand"]');
|
|
332
332
|
var collapseButton = document.querySelector(
|
|
@@ -359,18 +359,18 @@
|
|
|
359
359
|
// reveal starts from a clean slate.
|
|
360
360
|
// 3. STATUS pruning is action-level. Inside each breakpoint-eligible
|
|
361
361
|
// container, an action is hidden when statusFilter is active and the
|
|
362
|
-
// action's own data-status differs
|
|
362
|
+
// action's own data-status differs; EXCEPT `skipped` actions, which
|
|
363
363
|
// are never pruned. `skipped` is not a story-rollup status, so pruning
|
|
364
364
|
// it could empty a story the pill counts (a dependency-blocked story
|
|
365
365
|
// is rollup `failed` with only `skipped` actions); skipped rows are
|
|
366
366
|
// context for the failure, not mismatches.
|
|
367
|
-
// 4. A container pruned to zero visible actions is hidden
|
|
367
|
+
// 4. A container pruned to zero visible actions is hidden; a captioned
|
|
368
368
|
// empty list is a dead end. The story shows iff at least one container
|
|
369
369
|
// survives with a visible action.
|
|
370
370
|
// Deliberate gate asymmetry under combined filters: the ROLLUP gates the
|
|
371
371
|
// story, the exact match prunes actions. Under "changed at mobile", a story
|
|
372
372
|
// whose rollup is `failed` stays hidden even if its mobile container holds
|
|
373
|
-
// a `changed` action
|
|
373
|
+
// a `changed` action; that matches the pill's per-story rollup semantics.
|
|
374
374
|
//
|
|
375
375
|
// Recompute invariant: apply() re-runs this for EVERY story on every filter
|
|
376
376
|
// change, unconditionally recomputing action.hidden in every breakpoint-
|
|
@@ -462,7 +462,7 @@
|
|
|
462
462
|
trigger.focus();
|
|
463
463
|
}
|
|
464
464
|
// Disable both bulk-toggle buttons when the intersection matches zero
|
|
465
|
-
// stories
|
|
465
|
+
// stories; there is nothing to expand/collapse. Runs on EVERY apply()
|
|
466
466
|
// call, so a filter change that re-reveals stories re-enables them.
|
|
467
467
|
if (expandButton) expandButton.disabled = hasNone;
|
|
468
468
|
if (collapseButton) collapseButton.disabled = hasNone;
|
|
@@ -524,7 +524,7 @@
|
|
|
524
524
|
var token = button.getAttribute('data-filter');
|
|
525
525
|
var isActive = button.getAttribute('aria-pressed') === 'true';
|
|
526
526
|
// Re-clicking the active, non-"all" filter reverts that axis to "all".
|
|
527
|
-
// Focus stays on the clicked button
|
|
527
|
+
// Focus stays on the clicked button; it is in the summary row and never
|
|
528
528
|
// hidden by its own action.
|
|
529
529
|
if (isActive && token !== 'all') {
|
|
530
530
|
setPressed(statusButtons, statusAllButton);
|
|
@@ -561,9 +561,9 @@
|
|
|
561
561
|
// Bulk-toggle (expand all / collapse all) for the screenshot <details> panels.
|
|
562
562
|
// Acts on the moment: only visible rows (.story:not([hidden])) are affected,
|
|
563
563
|
// so the filter scope wins. Each <details> retains its own open state per
|
|
564
|
-
// native HTML semantics
|
|
564
|
+
// native HTML semantics; newly-revealed stories after a later filter change
|
|
565
565
|
// stay closed by default (their default state). A future maintainer might be
|
|
566
|
-
// tempted to "fix" this by tracking a global expanded mode; don't
|
|
566
|
+
// tempted to "fix" this by tracking a global expanded mode; don't; the lack
|
|
567
567
|
// of persistence is the agreed accessibility-lead decision (act-on-the-moment).
|
|
568
568
|
//
|
|
569
569
|
// We use `details.open = true/false` rather than `details.click()` so the
|
|
@@ -587,7 +587,7 @@
|
|
|
587
587
|
function apply(mode) {
|
|
588
588
|
var shouldOpen = mode === 'expand';
|
|
589
589
|
// Collapse-all focus rescue: if focus sits inside a disclosure that is
|
|
590
|
-
// about to collapse, move it to that disclosure's <summary> first
|
|
590
|
+
// about to collapse, move it to that disclosure's <summary> first; the
|
|
591
591
|
// summary stays visible after collapse, so focus is never dropped into a
|
|
592
592
|
// hidden subtree. Expand-all reveals content, so it needs no rescue.
|
|
593
593
|
if (!shouldOpen) {
|
|
@@ -607,7 +607,7 @@
|
|
|
607
607
|
// axis prunes non-matching actions ([hidden] on the .action) and the
|
|
608
608
|
// breakpoint axis hides whole containers, so an action is "visible"
|
|
609
609
|
// iff neither it nor its container is hidden. Excluding both keeps a
|
|
610
|
-
// pruned row's panels closed
|
|
610
|
+
// pruned row's panels closed; otherwise they would open here and
|
|
611
611
|
// later appear pre-expanded when a filter change reveals them,
|
|
612
612
|
// breaking the default-closed contract.
|
|
613
613
|
var panels = Array.prototype.slice.call(
|
|
@@ -683,14 +683,14 @@
|
|
|
683
683
|
// Deep-link support for the CI comment. A URL like `…/index.html#story-3`
|
|
684
684
|
// (built by tuffgal-action from the same `results.json` story ordinal that
|
|
685
685
|
// template.ts renders as `id="story-<index>"`) lands the reader ON that story
|
|
686
|
-
// with its screenshots already expanded
|
|
686
|
+
// with its screenshots already expanded; no scrolling or clicking through a
|
|
687
687
|
// 34-row report to find the one that changed.
|
|
688
688
|
//
|
|
689
689
|
// On a matching hash we:
|
|
690
690
|
// 1. Expand every VISIBLE screenshot disclosure in that story, reusing the
|
|
691
691
|
// bulk-toggle's own visibility predicate so a filtered-out row is not
|
|
692
692
|
// force-opened (default-closed contract). Setting `details.open` directly
|
|
693
|
-
//
|
|
693
|
+
// ; not `.click()`; mirrors the bulk-toggle and avoids a toggle cascade.
|
|
694
694
|
// 2. Move focus to the story `<li>` (tabindex="-1") so assistive tech
|
|
695
695
|
// announces the story prose rather than dropping the reader mid-list.
|
|
696
696
|
// `preventScroll` keeps focus from yanking the viewport before our own
|