From 3da976bd0287ebb632b0d8475f2e780d1b10e1b8 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 18 Jul 2026 07:56:44 -0500 Subject: feat(gallery): white-ink default, 85ms base rate, ID badges as anchors The split-flap's default skin becomes the white ink, listed first in the chip row, with the kit cream one click away. The base flap rate moves to 85ms, and the probe pins both defaults so a drive-by change can't silently flip them. Every card's ID badge is now a real anchor to its own card, so a click deep-links the URL and a right-click copies it. One shared rule keeps the badge look. The refactor sweep for extraction-readiness closes out: the flip duration and the ms clamp each live in one place, grid normalization is one helper shared by setText and the silent first paint (which now records itself as the last commanded grid, ending the null fallback), the state declarations sit above everything that references them (the same declared-later shape that caused a load crash earlier in the branch), and a stale two-skin comment caught up with the four skins. Behavior-preserving: all 79 probe checks stay green. --- tests/gallery-probes/probe.mjs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'tests/gallery-probes/probe.mjs') diff --git a/tests/gallery-probes/probe.mjs b/tests/gallery-probes/probe.mjs index 7b9ba5e..051d575 100644 --- a/tests/gallery-probes/probe.mjs +++ b/tests/gallery-probes/probe.mjs @@ -1000,28 +1000,29 @@ try { const h = document.querySelector('#card-N20').gw; if (typeof h.setStyle !== 'function' || !GW.splitFlap.STYLES || !GW.splitFlap.STYLES.skin) return 'no skin axis'; const skins = Object.keys(GW.splitFlap.STYLES.skin); + if (skins[0] !== 'white') return 'white must list first, got: ' + skins.join(','); if (!['dark','white','light','paper'].every(k => skins.includes(k))) return 'skins are: ' + skins.join(','); const ink = () => getComputedStyle(document.querySelector('#card-N20 .fh')).color; const lum = c => { const m = c.match(/[0-9]+/g).map(Number); return m[0] + m[1] + m[2]; }; - const dark = ink(); - h.setStyle('skin', 'white'); - const white = ink(); + const white = ink(); // the card's default skin is white ink + if (white !== 'rgb(255, 255, 255)') return 'default ink is not white: ' + white; + h.setStyle('skin', 'dark'); + const cream = ink(); h.setStyle('skin', 'light'); const light = ink(); const gotClass = document.querySelector('#card-N20 .flap').classList.contains('flap-light'); h.setStyle('skin', 'paper'); const paper = ink(); const paperClass = document.querySelector('#card-N20 .flap').classList.contains('flap-paper'); - h.setStyle('skin', 'dark'); + h.setStyle('skin', 'white'); const back = ink(); if (!gotClass) return 'light skin class missing'; if (!paperClass) return 'paper skin class missing'; - if (white !== 'rgb(255, 255, 255)') return 'white skin ink is ' + white; - if (!(lum(dark) > 400 && lum(dark) < 760 && lum(light) < 300 && lum(paper) < 300)) return 'inks wrong: ' + dark + ' / ' + light + ' / ' + paper; - if (back !== dark) return 'did not restore dark: ' + back; + if (!(lum(cream) > 400 && lum(cream) < 760 && lum(light) < 300 && lum(paper) < 300)) return 'inks wrong: ' + cream + ' / ' + light + ' / ' + paper; + if (back !== white) return 'did not restore the white default: ' + back; return 'ok'; })()`); - ok('N20 skins: cream dark, white ink, ivory board, white board — all switch and restore', n20e === 'ok', n20e); + ok('N20 skins: white default first, cream, ivory board, white board — all switch and restore', n20e === 'ok', n20e); const n20fnt = await evl(`(() => { const h = document.querySelector('#card-N20').gw; @@ -1057,6 +1058,7 @@ try { const h = document.querySelector('#card-N20').gw; if (typeof h.setFlapMs !== 'function' || typeof h.flapMs !== 'function') return 'no speed api'; const before = h.flapMs(); + if (before !== 85) return 'default flap rate is ' + before + ', want 85'; h.setFlapMs(120); if (h.flapMs() !== 120) return 'setFlapMs did not take: ' + h.flapMs(); const slider = document.querySelector('#card-N20 input[type=range]'); @@ -1117,6 +1119,18 @@ try { ok('N20 rests under reduced motion (no self-advancing dwell chain)', rm1 === rm2.r && rm2.anims === 0, JSON.stringify({ before: rm1, after: rm2.r, anims: rm2.anims })); + // Every card's ID badge is its own anchor: click it and the URL carries + // the card's deep link. + const anchors = await evl(`(() => { + const bad = [...document.querySelectorAll('.card .wname .no')] + .filter(n => n.tagName !== 'A' || n.getAttribute('href') !== '#' + n.closest('.card').id) + .map(n => n.textContent); + const count = document.querySelectorAll('.card .wname a.no').length; + if (bad.length) return 'not anchors: ' + bad.slice(0, 5).join(' '); + return count === document.querySelectorAll('.card').length ? 'ok' : 'anchor count ' + count; + })()`); + ok('every card ID badge links to its own anchor', anchors === 'ok', anchors); + // late exceptions from interactions const errs2 = events.filter(e => e.method === 'Runtime.exceptionThrown'); ok('no exceptions after interaction', errs2.length === 0); -- cgit v1.2.3