openvibers/openvibe.games Commits

A fold says what it holds, counted, not "tap to open" (#252)

OP OpenVibers committed 11 days ago · a60bd547d3 · 3 files, +19 −3

* A fold says what it holds, counted, not "tap to open" (owner's rule, 2026-10-31)

* Merge branch 'main' into design/counted-folds

Changes

ddiff --git a/apps/client/index.html b/apps/client/index.html
iindex c436afd..908080d 100644
---- a/apps/client/index.html
++++ b/apps/client/index.html
@@@ -1313,7 +1313,7 @@
shipped.js, which the shared footer loads), the same on every OpenVibe site. A fold on a phone:
closed there, open where there is room, and the list stays in the page. -->
<details class="gm-fold gm-fold-block" id="home-shipped" open>
-- <summary><h2>Recently shipped on OpenVibe.Games</h2><span>tap to open</span></summary>
++ <summary><h2>Recently shipped on OpenVibe.Games</h2><span>latest 5</span></summary>
<section
class="ov-shipped-home"
aria-label="What shipped"
ddiff --git a/apps/server/src/net/shortHome.test.ts b/apps/server/src/net/shortHome.test.ts
iindex 5b5fd28..bfe37e1 100644
---- a/apps/server/src/net/shortHome.test.ts
++++ b/apps/server/src/net/shortHome.test.ts
@@@ -37,7 +37,7 @@ describe('the home on a phone', () => {
})
expect(html).toContain('<details class="gm-fold gm-fold-block" id="home-faq" open>')
expect(html).toContain(
-- '<summary><h2 id="home-faq">Questions</h2><span>tap to open</span></summary>',
++ '<summary><h2 id="home-faq">Questions</h2></summary>',
)
expect(html).toContain('the answers')
expect(html).toContain('document.getElementById("home-faq")')
ddiff --git a/apps/server/src/net/shortHome.ts b/apps/server/src/net/shortHome.ts
iindex 4e5705c..ea86d8f 100644
---- a/apps/server/src/net/shortHome.ts
++++ b/apps/server/src/net/shortHome.ts
@@@ -42,11 +42,27 @@ export function pickBlock(o: {
}
++/**
++ * What a fold holds, counted, for its title row (the owner, 2026-10-31: a fold that shortens a page shows its count):
++ * "6 questions", "12 items", "latest 5" for the shipped list, or nothing when there is nothing to count. Replaces the
++ * hint the fold used to show instead.
++ */
++export function foldMeta(body: string, cls = ''): string {
++ const s = String(body ?? '')
++ const n = (re: RegExp): number => (s.match(re) ?? []).length
++ const shipped = /data-ov-shipped="list"[^>]*?data-limit="(\d+)"|data-limit="(\d+)"[^>]*?data-ov-shipped="list"/.exec(s)
++ const q = n(/<details[\s>]/g)
++ const li = n(/<li[\s>]/g)
++ const a = n(/<a\s/g)
++ const text = shipped ? `latest ${shipped[1] || shipped[2]}` : q > 1 ? `${q} questions` : li > 1 ? `${li} items` : a > 1 ? `${a} links` : ''
++ return text ? `<span${cls ? ` class="${cls}"` : ''}>${text}</span>` : ''
++}
++
/**
* A fold. The head is the heading the block had; the html is what it holds.
*/
export function foldBlock(o: { id: string; head: string; html: string }): string {
-- return `<details class="gm-fold gm-fold-block" id="${esc(o.id)}" open><summary>${o.head}<span>tap to open</span></summary>${o.html}</details>${foldScript(o.id)}`
++ return `<details class="gm-fold gm-fold-block" id="${esc(o.id)}" open><summary>${o.head}${foldMeta(o.html)}</summary>${o.html}</details>${foldScript(o.id)}`
}
/** The script a fold carries: closed on a phone unless the reader opened it before, and it remembers that. */