shots: feature rotate, per-language frame fallback, layout-aware capture selectors

This commit is contained in:
CRTkafa 2026-10-24 13:00:48 +08:00
parent 2e1c7cdc3c
commit 61b5fa0623
15 changed files with 355 additions and 17 deletions

View file

@ -6,7 +6,7 @@
// sublines, badge labels; plus the Noto face of a non-Latin script). A character the stack lacks gets an automatic
// Noto fallback (logged; `shots check` errors on renders that used it unless shots.fonts.fallback lists it); a
// character no fallback covers fails with the language and the characters.
// storebound shots fonts [--json] (re-run after editing the copy or the fonts)
// storebound shots fonts [--lang en,tr] [--json] (--lang limits the report to those languages; the manifest keeps all) (re-run after editing the copy or the fonts)
// Out: <project>/.storebound/fonts/*.woff2 + fonts.css + fonts.json (faces and the per-language stacks, read by render).
// The Google fonts are SIL Open Font License 1.1. A local font file in shots.fonts (local-fonts.ts) gets the same cmap
// check, is read in place (never copied) and recorded with its path, sha256 and license note.
@ -107,7 +107,10 @@ export async function run(ctx: Ctx): Promise<void> {
}
for (const f of Object.values(local)) if (!f.license) warn('shots', `local font ${f.path} has no license note: add license ('OFL-1.1', 'proprietary', ...) to its shots.fonts entry`);
let auto = 0;
for (const [lang, l] of Object.entries(manifest.langs)) for (const f of l.fallbacks) {
const only = ctx.args.list('lang');
const unknown = only.filter(l => !(l in manifest.langs));
if (unknown.length) throw new Error(`--lang: no copy or config language ${unknown.join(', ')} (known: ${Object.keys(manifest.langs).join(', ')})`);
for (const [lang, l] of Object.entries(manifest.langs)) for (const f of only.length && !only.includes(lang) ? [] : l.fallbacks) {
const ok = manifest.faces[f.face]?.chosen;
if (!ok) auto++;
const base = f.role === 'title' ? title : body;