openvibers/openvibe.vision Commits
Home: every pane shows on a phone; the chips are links, not a picker (#52)
Changes
@@@ -268,27 +268,22 @@ html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi.vi-home .sc-room-chips a:hover, .vi-home .sc-room-chips a:focus-visible { color: var(--text-primary, #eef1f6); border-color: var(--room-accent); }.vi-status { color: var(--text-primary, #eef1f6); background: color-mix(in srgb, var(--text-primary, #eef1f6) 8%, transparent); }--/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page shows one block at a time, picked with a-- row of chips. Radio inputs and :has(), no script: the chips and the one-block rule live under 640 px and only-- where :has() is understood, so a wider screen, or an older browser, shows every block and no chips. */--.pick-head { display: none; }--.pick-row { display: none; }--@supports selector(:has(*)) { @media (max-width: 640px) {-- .pick-bar { margin-top: 0; }++/* Pick: on a phone every pane is shown, stacked, each with its own heading; the chips are a row of links that++ jump to a pane (the owner's rule, 2026-10-31: never hide content to shorten). A wide screen shows the panes as++ before, with no chips. */++.pick-head, .pick-row { display: none; }++@media (max-width: 640px) {++ .pick-bar { margin-top: 30px; }.pick-head { display: block; }-- .pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px,2.4vw,28px)/1.2 var(--ov-font,'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif); letter-spacing: -.02em; }++ .pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px, 2.4vw, 28px)/1.2 var(--ov-font, 'Inter', system-ui, sans-serif); letter-spacing: -.02em; }.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }.pick-row::-webkit-scrollbar { display: none; }-- .pick-row input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }-- .pick-row label { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; cursor: pointer; }-- .pick-row input:checked + label { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); background: color-mix(in srgb, var(--accent, #3b82f6) 12%, transparent); }-- .pick-row input:focus-visible + label { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }-- .pick .pick-pane { display: none; }-- .pick .pick-list > li { display: none; }-- /* The chip names the block, so the block's own heading gives way to it; the content stays in the page for the-- crawlers and for the reader on a wide screen. */-- .pick .sc-sec > h2, .pick section > h2, .pick > h2 { display: none; }--} }++ .pick-chip { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; text-decoration: none; }++ .pick-chip:hover, .pick-chip:focus-visible { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); }++ .pick-chip:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }++ .pick .pick-pane { scroll-margin-top: 72px; }++ .pick .pick-pane + .pick-pane { margin-top: 26px; }++}/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section itreplaces: a heading and its content, with no card around it. */@@@ -2,16 +2,14 @@/*** The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.*-- * pickBlock() several blocks of the same kind, one at a time on a phone, picked with a row of chips. Radio-- * inputs and :has(), no script: every block stays in the page for crawlers, and a wider screen (or a-- * browser without :has()) shows them all and no chips.-- * pickList() a list of things of the same kind, one at a time on a phone, picked with a row of chips.++ * pickBlock() several blocks of the same kind, all shown, each with its own heading; on a phone the chips are a++ * row of links that jump to them (the owner, 2026-10-31: never hide content to shorten).++ * pickList() a list of things of the same kind under one heading, every item shown.* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow* screen and remembers the reader's choice.*-- * The CSS for both lives in this site's own stylesheet; the rule that shows the picked block is written for the-- * page, because there is one per block.++ * The CSS lives in this site's own stylesheet.*/const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');@@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {// A pane with nothing to show is left out, so the chips and the rules that show them match the page.panes = (panes || []).filter((p) => p.html);if (!panes.length) return '';-- const chips = panes.map((p, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + esc(p.id) + '" value="' + esc(p.id) + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + esc(p.id) + '">' + esc(p.label) + '</label>').join('');++ const chips = panes.map((p) => '<a class="pick-chip" href="#' + esc(p.id) + '">' + esc(p.label) + '</a>').join('');const body = panes.map((p) => '<div class="pick-pane" id="' + esc(p.id) + '">' + p.html + '</div>').join('');-- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div><div class="pick-body">' + body + '</div></div>';++ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><nav class="pick-row" aria-label="' + esc(label) + '">' + chips + '</nav></div><div class="pick-body">' + body + '</div></div>';}--/** The rule that shows the picked block of a pick block: one per block, so the CSS is written for the page. */--function pickRule(key, id) {-- return '.pick:has(#pk-' + key + '-' + id + ':checked) #' + id + '{display:block}';--}--/** The rules for every block of a pick block, in the order they appear. */--function pickRules(key, ids) {-- return ids.map((id) => pickRule(key, id)).join('');--}/**-- * A pick list: a list of things of the same kind, one at a time on a phone, picked with a row of chips. The list-- * keeps its own markup and every item stays in the page; the caller marks each item with the index its chip picks.++ * A pick list: a list of things of the same kind under one heading, every item shown (2026-10-31). The list keeps++ * its own markup; `labels` is kept for callers and no longer drawn.** pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items*/function pickList({ key, label, head = '', labels, html }) {labels = labels || [];if (!labels.length) return '';-- const chips = labels.map((l, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + i + '" value="' + i + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + i + '">' + esc(l) + '</label>').join('');-- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div>' + html + '</div>';++ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div></div>' + html + '</div>';}--/** The rule that shows the picked item of a pick list: one per item, so the CSS is written for the page. */--function pickListRule(key, i) {-- return '.pick#pick-' + key + ':has(#pk-' + key + '-' + i + ':checked) .pick-list>li[data-i="' + i + '"]{display:block}';--}--/** The rules for every item of a pick list. */--function pickListRules(key, n) {-- return Array.from({ length: n }, (_, i) => pickListRule(key, i)).join('');--}/*** A fold. The head is the heading the block had; the html is what it holds.@@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);}--module.exports = { pickBlock, pickRule, pickRules, pickList, pickListRule, pickListRules, foldBlock, foldScript, part, swap, foldAround };++module.exports = { pickBlock, pickList, foldBlock, foldScript, part, swap, foldAround };@@@ -8,7 +8,7 @@const { esc, time } = require('../render/html');const sc = require('openvibe-shared/showcase');// The short home (the owner's rule, 2026-10-30): pick blocks and folds.--const { pickBlock, pickRules, foldAround } = require('../render/short');++const { pickBlock, foldAround } = require('../render/short');const MODEL_LABEL = (a) => (a && a.model ? `${a.provider ? `${a.provider}:` : ''}${a.model}` : 'the local engine');@@@ -105,7 +105,6 @@ ${signedIn ? '' : '<p class="vi-fine">Reading, watching and the page check workreturn {body: `<div class="vi-wrap vi-home" id="vi-home">${html}</div>`,// The rule that shows the picked scene of the pick block: one per scene, written for this page.-- inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('scenes', arts.map((_, i) => `vi-scene-${i}`))}}}`],};}@@@ -11,10 +11,10 @@ const { boot, check, done } = require('./helpers/boot');const start = page.indexOf('<div class="pick" id="pick-scenes">');assert.ok(start > 0, 'the pick block is on the home');const block = page.slice(start, start + 8000);-- assert.match(block, /<div class="pick-row" role="radiogroup" aria-label="What Vision does">/);-- const chips = [...block.matchAll(/<input type="radio" name="pick-scenes" id="pk-scenes-(vi-scene-\d+)" value="(vi-scene-\d+)"( checked)?><label for="pk-scenes-vi-scene-\d+">([^<]+)<\/label>/g)];++ assert.match(block, /<nav class="pick-row" aria-label="What Vision does">/);++ const chips = [...block.matchAll(/<a class="pick-chip" href="#(?:pp-scenes-)?(vi-scene-\d+)">()()([^<]+)<\/a>/g)];assert.strictEqual(chips.length, 4, 'a chip per scene (' + chips.length + ')');-- assert.deepStrictEqual(chips.filter((m) => m[3]).map((m) => m[1]), [chips[0][1]], 'the first scene is picked');++ assert.ok(!/type="radio"/.test(block), 'no chip is a radio: nothing is picked, every pane shows');const scenes = [...block.matchAll(/<article class="sc-scene/g)];assert.strictEqual(scenes.length, chips.length, 'every scene is in the page');for (const m of chips) assert.ok(block.includes('id="' + m[1] + '"'), 'the pane of ' + m[1] + ' is in the page');@@@ -22,12 +22,8 @@ const { boot, check, done } = require('./helpers/boot');await check('the CSS guard: the chips are hidden and the scenes all show until 640 px and where :has() is understood', async () => {const r = await t.get('/');const page = r.text;-- assert.match(page, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-scenes-vi-scene-0:checked\) #vi-scene-0\{display:block\}/, 'the rule that shows the picked scene');const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'vision.css'), 'utf8');assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');-- assert.match(css, /@supports selector\(:has\(\*\)\) \{ @media \(max-width: 640px\) \{/, 'the phone rules are under :has() and 640 px');-- assert.match(css, /\.pick-row label \{[^}]*min-height: 44px/, 'a chip is a 44 px target');-- assert.match(css, /\.pick \.pick-pane \{ display: none; \}/, 'the scenes are hidden on a phone');});await check('the parts a reader rarely needs are folds, open in the page and closed on a phone', async () => {const r = await t.get('/');
@@ -268,27 +268,22 @@ html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi
@@ -268,27 +268,22 @@ html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi
.vi-home .sc-room-chips a:hover, .vi-home .sc-room-chips a:focus-visible { color: var(--text-primary, #eef1f6); border-color: var(--room-accent); }
.vi-home .sc-room-chips a:hover, .vi-home .sc-room-chips a:focus-visible { color: var(--text-primary, #eef1f6); border-color: var(--room-accent); }
.vi-status { color: var(--text-primary, #eef1f6); background: color-mix(in srgb, var(--text-primary, #eef1f6) 8%, transparent); }
.vi-status { color: var(--text-primary, #eef1f6); background: color-mix(in srgb, var(--text-primary, #eef1f6) 8%, transparent); }
-/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page shows one block at a time, picked with a
- row of chips. Radio inputs and :has(), no script: the chips and the one-block rule live under 640 px and only
- where :has() is understood, so a wider screen, or an older browser, shows every block and no chips. */
-.pick-head { display: none; }
-.pick-row { display: none; }
-@supports selector(:has(*)) { @media (max-width: 640px) {
- .pick-bar { margin-top: 0; }
+/* Pick: on a phone every pane is shown, stacked, each with its own heading; the chips are a row of links that
+ jump to a pane (the owner's rule, 2026-10-31: never hide content to shorten). A wide screen shows the panes as
+ before, with no chips. */
+.pick-head, .pick-row { display: none; }
+@media (max-width: 640px) {
+ .pick-bar { margin-top: 30px; }
.pick-head { display: block; }
.pick-head { display: block; }
- .pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px,2.4vw,28px)/1.2 var(--ov-font,'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif); letter-spacing: -.02em; }
+ .pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px, 2.4vw, 28px)/1.2 var(--ov-font, 'Inter', system-ui, sans-serif); letter-spacing: -.02em; }
.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }
.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }
.pick-row::-webkit-scrollbar { display: none; }
.pick-row::-webkit-scrollbar { display: none; }
- .pick-row input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
- .pick-row label { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; cursor: pointer; }
- .pick-row input:checked + label { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); background: color-mix(in srgb, var(--accent, #3b82f6) 12%, transparent); }
- .pick-row input:focus-visible + label { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }
- .pick .pick-pane { display: none; }
- .pick .pick-list > li { display: none; }
- /* The chip names the block, so the block's own heading gives way to it; the content stays in the page for the
- crawlers and for the reader on a wide screen. */
- .pick .sc-sec > h2, .pick section > h2, .pick > h2 { display: none; }
-} }
+ .pick-chip { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; text-decoration: none; }
+ .pick-chip:hover, .pick-chip:focus-visible { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); }
+ .pick-chip:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }
+ .pick .pick-pane { scroll-margin-top: 72px; }
+ .pick .pick-pane + .pick-pane { margin-top: 26px; }
+}
/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section it
/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section it
replaces: a heading and its content, with no card around it. */
replaces: a heading and its content, with no card around it. */
@@ -2,16 +2,14 @@
@@ -2,16 +2,14 @@
/**
/**
* The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.
* The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.
*
*
- * pickBlock() several blocks of the same kind, one at a time on a phone, picked with a row of chips. Radio
- * inputs and :has(), no script: every block stays in the page for crawlers, and a wider screen (or a
- * browser without :has()) shows them all and no chips.
- * pickList() a list of things of the same kind, one at a time on a phone, picked with a row of chips.
+ * pickBlock() several blocks of the same kind, all shown, each with its own heading; on a phone the chips are a
+ * row of links that jump to them (the owner, 2026-10-31: never hide content to shorten).
+ * pickList() a list of things of the same kind under one heading, every item shown.
* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup
* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup
* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow
* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow
* screen and remembers the reader's choice.
* screen and remembers the reader's choice.
*
*
- * The CSS for both lives in this site's own stylesheet; the rule that shows the picked block is written for the
- * page, because there is one per block.
+ * The CSS lives in this site's own stylesheet.
*/
*/
const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {
@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {
// A pane with nothing to show is left out, so the chips and the rules that show them match the page.
// A pane with nothing to show is left out, so the chips and the rules that show them match the page.
panes = (panes || []).filter((p) => p.html);
panes = (panes || []).filter((p) => p.html);
if (!panes.length) return '';
if (!panes.length) return '';
- const chips = panes.map((p, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + esc(p.id) + '" value="' + esc(p.id) + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + esc(p.id) + '">' + esc(p.label) + '</label>').join('');
+ const chips = panes.map((p) => '<a class="pick-chip" href="#' + esc(p.id) + '">' + esc(p.label) + '</a>').join('');
const body = panes.map((p) => '<div class="pick-pane" id="' + esc(p.id) + '">' + p.html + '</div>').join('');
const body = panes.map((p) => '<div class="pick-pane" id="' + esc(p.id) + '">' + p.html + '</div>').join('');
- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div><div class="pick-body">' + body + '</div></div>';
+ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><nav class="pick-row" aria-label="' + esc(label) + '">' + chips + '</nav></div><div class="pick-body">' + body + '</div></div>';
}
}
-/** The rule that shows the picked block of a pick block: one per block, so the CSS is written for the page. */
-function pickRule(key, id) {
- return '.pick:has(#pk-' + key + '-' + id + ':checked) #' + id + '{display:block}';
-}
-/** The rules for every block of a pick block, in the order they appear. */
-function pickRules(key, ids) {
- return ids.map((id) => pickRule(key, id)).join('');
-}
/**
/**
- * A pick list: a list of things of the same kind, one at a time on a phone, picked with a row of chips. The list
- * keeps its own markup and every item stays in the page; the caller marks each item with the index its chip picks.
+ * A pick list: a list of things of the same kind under one heading, every item shown (2026-10-31). The list keeps
+ * its own markup; `labels` is kept for callers and no longer drawn.
*
*
* pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items
* pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items
*/
*/
function pickList({ key, label, head = '', labels, html }) {
function pickList({ key, label, head = '', labels, html }) {
labels = labels || [];
labels = labels || [];
if (!labels.length) return '';
if (!labels.length) return '';
- const chips = labels.map((l, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + i + '" value="' + i + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + i + '">' + esc(l) + '</label>').join('');
- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div>' + html + '</div>';
+ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div></div>' + html + '</div>';
}
}
-/** The rule that shows the picked item of a pick list: one per item, so the CSS is written for the page. */
-function pickListRule(key, i) {
- return '.pick#pick-' + key + ':has(#pk-' + key + '-' + i + ':checked) .pick-list>li[data-i="' + i + '"]{display:block}';
-}
-/** The rules for every item of a pick list. */
-function pickListRules(key, n) {
- return Array.from({ length: n }, (_, i) => pickListRule(key, i)).join('');
-}
/**
/**
* A fold. The head is the heading the block had; the html is what it holds.
* A fold. The head is the heading the block had; the html is what it holds.
@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {
@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {
return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);
return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);
}
}
-module.exports = { pickBlock, pickRule, pickRules, pickList, pickListRule, pickListRules, foldBlock, foldScript, part, swap, foldAround };
+module.exports = { pickBlock, pickList, foldBlock, foldScript, part, swap, foldAround };
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
const { esc, time } = require('../render/html');
const { esc, time } = require('../render/html');
const sc = require('openvibe-shared/showcase');
const sc = require('openvibe-shared/showcase');
// The short home (the owner's rule, 2026-10-30): pick blocks and folds.
// The short home (the owner's rule, 2026-10-30): pick blocks and folds.
-const { pickBlock, pickRules, foldAround } = require('../render/short');
+const { pickBlock, foldAround } = require('../render/short');
const MODEL_LABEL = (a) => (a && a.model ? `${a.provider ? `${a.provider}:` : ''}${a.model}` : 'the local engine');
const MODEL_LABEL = (a) => (a && a.model ? `${a.provider ? `${a.provider}:` : ''}${a.model}` : 'the local engine');
@@ -105,7 +105,6 @@ ${signedIn ? '' : '<p class="vi-fine">Reading, watching and the page check work
@@ -105,7 +105,6 @@ ${signedIn ? '' : '<p class="vi-fine">Reading, watching and the page check work
return {
return {
body: `<div class="vi-wrap vi-home" id="vi-home">${html}</div>`,
body: `<div class="vi-wrap vi-home" id="vi-home">${html}</div>`,
// The rule that shows the picked scene of the pick block: one per scene, written for this page.
// The rule that shows the picked scene of the pick block: one per scene, written for this page.
- inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('scenes', arts.map((_, i) => `vi-scene-${i}`))}}}`],
};
};
}
}
@@ -11,10 +11,10 @@ const { boot, check, done } = require('./helpers/boot');
@@ -11,10 +11,10 @@ const { boot, check, done } = require('./helpers/boot');
const start = page.indexOf('<div class="pick" id="pick-scenes">');
const start = page.indexOf('<div class="pick" id="pick-scenes">');
assert.ok(start > 0, 'the pick block is on the home');
assert.ok(start > 0, 'the pick block is on the home');
const block = page.slice(start, start + 8000);
const block = page.slice(start, start + 8000);
- assert.match(block, /<div class="pick-row" role="radiogroup" aria-label="What Vision does">/);
- const chips = [...block.matchAll(/<input type="radio" name="pick-scenes" id="pk-scenes-(vi-scene-\d+)" value="(vi-scene-\d+)"( checked)?><label for="pk-scenes-vi-scene-\d+">([^<]+)<\/label>/g)];
+ assert.match(block, /<nav class="pick-row" aria-label="What Vision does">/);
+ const chips = [...block.matchAll(/<a class="pick-chip" href="#(?:pp-scenes-)?(vi-scene-\d+)">()()([^<]+)<\/a>/g)];
assert.strictEqual(chips.length, 4, 'a chip per scene (' + chips.length + ')');
assert.strictEqual(chips.length, 4, 'a chip per scene (' + chips.length + ')');
- assert.deepStrictEqual(chips.filter((m) => m[3]).map((m) => m[1]), [chips[0][1]], 'the first scene is picked');
+ assert.ok(!/type="radio"/.test(block), 'no chip is a radio: nothing is picked, every pane shows');
const scenes = [...block.matchAll(/<article class="sc-scene/g)];
const scenes = [...block.matchAll(/<article class="sc-scene/g)];
assert.strictEqual(scenes.length, chips.length, 'every scene is in the page');
assert.strictEqual(scenes.length, chips.length, 'every scene is in the page');
for (const m of chips) assert.ok(block.includes('id="' + m[1] + '"'), 'the pane of ' + m[1] + ' is in the page');
for (const m of chips) assert.ok(block.includes('id="' + m[1] + '"'), 'the pane of ' + m[1] + ' is in the page');
@@ -22,12 +22,8 @@ const { boot, check, done } = require('./helpers/boot');
@@ -22,12 +22,8 @@ const { boot, check, done } = require('./helpers/boot');
await check('the CSS guard: the chips are hidden and the scenes all show until 640 px and where :has() is understood', async () => {
await check('the CSS guard: the chips are hidden and the scenes all show until 640 px and where :has() is understood', async () => {
const r = await t.get('/');
const r = await t.get('/');
const page = r.text;
const page = r.text;
- assert.match(page, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-scenes-vi-scene-0:checked\) #vi-scene-0\{display:block\}/, 'the rule that shows the picked scene');
const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'vision.css'), 'utf8');
const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'vision.css'), 'utf8');
assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');
assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');
- assert.match(css, /@supports selector\(:has\(\*\)\) \{ @media \(max-width: 640px\) \{/, 'the phone rules are under :has() and 640 px');
- assert.match(css, /\.pick-row label \{[^}]*min-height: 44px/, 'a chip is a 44 px target');
- assert.match(css, /\.pick \.pick-pane \{ display: none; \}/, 'the scenes are hidden on a phone');
});
});
await check('the parts a reader rarely needs are folds, open in the page and closed on a phone', async () => {
await check('the parts a reader rarely needs are folds, open in the page and closed on a phone', async () => {
const r = await t.get('/');
const r = await t.get('/');