mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-11 21:53:48 +01:00
Sidebar paints a glyph and digit per section, the palette button a magnifier and ⌃K; page titles, subtitles and the Actions heading are spoken only. web/scripts/visible_words.mjs lists chrome words in the built pages (146 -> 4, the command-button labels). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
1.9 KiB
JavaScript
51 lines
1.9 KiB
JavaScript
// Textgate check over the built site: list every chrome word a page paints
|
|
// before its custom elements hydrate (layout, page heads, command-button
|
|
// labels). Spoken-only text (.sr-only, aria-hidden, the focus-only skip link)
|
|
// is ignored. Exit 1 when any page paints a word, so it works as a gate.
|
|
//
|
|
// Usage: node scripts/visible_words.mjs [distDir] (run `pnpm build` first)
|
|
|
|
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
import { join, relative } from 'node:path'
|
|
import { JSDOM } from 'jsdom'
|
|
|
|
const WORD_RE = /[A-Za-z]{2,}/
|
|
const SPOKEN = '.sr-only, [aria-hidden="true"], .skip, script, style, template'
|
|
|
|
/** @param {string} dir @returns {string[]} */
|
|
function htmlFiles(dir) {
|
|
return readdirSync(dir).flatMap((name) => {
|
|
const p = join(dir, name)
|
|
if (statSync(p).isDirectory()) return htmlFiles(p)
|
|
return name.endsWith('.html') ? [p] : []
|
|
})
|
|
}
|
|
|
|
/** @param {string} html @returns {string[]} */
|
|
export function visibleWords(html) {
|
|
const { document } = new JSDOM(html).window
|
|
for (const el of document.querySelectorAll(SPOKEN)) el.remove()
|
|
const out = []
|
|
const walker = document.createTreeWalker(document.body, 4 /* SHOW_TEXT */)
|
|
for (let n = walker.nextNode(); n; n = walker.nextNode()) {
|
|
const t = n.textContent?.trim() ?? ''
|
|
if (WORD_RE.test(t)) out.push(t)
|
|
}
|
|
// command-button paints its label attribute once it hydrates.
|
|
for (const el of document.querySelectorAll('command-button[label]')) {
|
|
const t = el.getAttribute('label') ?? ''
|
|
if (WORD_RE.test(t)) out.push(`[button] ${t}`)
|
|
}
|
|
return out
|
|
}
|
|
|
|
const dist = process.argv[2] ?? 'dist'
|
|
let total = 0
|
|
for (const file of htmlFiles(dist).sort()) {
|
|
const words = visibleWords(readFileSync(file, 'utf8'))
|
|
total += words.length
|
|
if (words.length) console.log(`${relative(dist, file)} (${words.length}): ${words.join(' | ')}`)
|
|
}
|
|
console.log(`total: ${total}`)
|
|
process.exit(total ? 1 : 0)
|