Nine scenes of 6-7 s, one slow camera move each, soft dissolves, one plain-English caption per scene at the top (on screen at least 4 s): the stores' to-do list and one rejection mail, `init` in an empty folder, the agent prompt and `release plan` (25 steps), store text and renders in 7 sizes x 4 languages, `play products --dry` and `catalog diff`, the scan's declarations proposal with the ask-owner line, three findings with plain reasons turning green, the desktop release timeline and Store readiness "No blockers", Submit and the end card. Every CLI line is real output: tests/teaser.test.ts runs each command (store answers from tests/helpers/teaser-store-mock.ts, preloaded through bunfig.toml, never the network) and checks scene lengths, caption holds, the 55-65 s total and 16:9 only. The vertical output is gone; the web version targets 4.5 MB. Calmer music (D major pads, soft pluck, bells on scene changes). desktop.ts adds the Store readiness frame.
77 lines
5.9 KiB
TypeScript
77 lines
5.9 KiB
TypeScript
#!/usr/bin/env bun
|
|
// Vendors icon files, copied byte for byte from their packages, plus the licences:
|
|
// site/img/icons/ Phosphor (@phosphor-icons/core, MIT, "regular" weight) for the site's icons and the
|
|
// App Store / Google Play marks from simple-icons (CC0 data)
|
|
// apps/desktop/src/ui/store-marks the same two store marks for the desktop app (the other desktop icons come from
|
|
// @phosphor-icons/react)
|
|
// media/teaser/icons Phosphor icons for the teaser video (the mouse cursor, the inbox, finding and fix icons)
|
|
// templates/web the two store marks for the landing page `storebound web init --landing` writes
|
|
// Both show them as CSS masks (site/style.css .ic-*, desktop styles.css .store-mark), so they take the text colour in
|
|
// both themes and nothing is loaded from outside. The store marks only name the store next to its name; they are
|
|
// trademarks of Apple Inc. and Google LLC (docs/desktop.md, the site footer).
|
|
// bun tools/site-icons.ts copy (after a package bump or a new icon in the lists below)
|
|
// bun tools/site-icons.ts --check fail when a vendored file differs from its package (CI, when installed)
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
export const SITE_ICONS = ['caret-right', 'play', 'user', 'squares-four', 'briefcase'] as const;
|
|
export const STORE_MARKS = ['appstore', 'googleplay'] as const;
|
|
/** Teaser icons: [Phosphor weight, icon name], vendored as <name>-<weight>.svg (the package's own file name). */
|
|
export const TEASER_ICONS = [['fill', 'navigation-arrow'], ['bold', 'check'], ['fill', 'x-circle'], ['fill', 'check-circle'], ['bold', 'tray'],
|
|
['fill', 'warning-octagon'], ['fill', 'paper-plane-tilt'], ['bold', 'terminal-window']] as const;
|
|
|
|
const root = new URL('..', import.meta.url).pathname;
|
|
const phosphor = join(root, 'node_modules/@phosphor-icons/core');
|
|
const simple = join(root, 'node_modules/simple-icons');
|
|
export const ICON_DIR = join(root, 'site/img/icons');
|
|
export const MARK_DIR = join(root, 'apps/desktop/src/ui/store-marks');
|
|
export const TEASER_DIR = join(root, 'media/teaser/icons');
|
|
export const WEB_DIR = join(root, 'templates/web');
|
|
|
|
/** [vendored dir, file name, package file] for every vendored icon. */
|
|
function plan(): [string, string, string][] {
|
|
const marks = STORE_MARKS.map(n => [`${n}.svg`, join(simple, 'icons', `${n}.svg`)] as const);
|
|
return [
|
|
...SITE_ICONS.map(n => [ICON_DIR, `${n}.svg`, join(phosphor, 'assets/regular', `${n}.svg`)] as [string, string, string]),
|
|
...marks.map(([f, src]) => [ICON_DIR, f, src] as [string, string, string]),
|
|
...marks.map(([f, src]) => [MARK_DIR, f, src] as [string, string, string]),
|
|
...marks.map(([f, src]) => [WEB_DIR, f, src] as [string, string, string]),
|
|
...TEASER_ICONS.map(([w, n]) => [TEASER_DIR, `${n}-${w}.svg`, join(phosphor, 'assets', w, `${n}-${w}.svg`)] as [string, string, string]),
|
|
];
|
|
}
|
|
const rel = (p: string) => p.slice(root.length);
|
|
|
|
/** Problems of the vendored folders against the packages ([] when they match, or when the packages are not installed). */
|
|
export function checkSiteIcons(): string[] {
|
|
if (!existsSync(phosphor) || !existsSync(simple)) return [];
|
|
const out: string[] = [];
|
|
const p = plan();
|
|
for (const [dir, f, src] of p) {
|
|
const at = join(dir, f);
|
|
if (!existsSync(at)) out.push(`${rel(at)} missing (bun tools/site-icons.ts)`);
|
|
else if (readFileSync(at, 'utf8') !== readFileSync(src, 'utf8')) out.push(`${rel(at)} is not ${rel(src).replace('node_modules/', '')}`);
|
|
}
|
|
for (const dir of [ICON_DIR, MARK_DIR, TEASER_DIR, WEB_DIR]) {
|
|
if (!existsSync(dir)) continue;
|
|
for (const f of readdirSync(dir)) if (f.endsWith('.svg') && !p.some(([d, n]) => d === dir && n === f)) out.push(`${rel(join(dir, f))} is not a vendored icon`);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const version = (dir: string) => JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version as string;
|
|
const simpleNote = () => `App Store and Google Play marks: simple-icons ${version(simple)} (https://simpleicons.org), copied unchanged from\nthe simple-icons package. The icon data is CC0; the marks are trademarks of Apple Inc. and Google LLC and are used\nonly to name the store next to its name. storebound is not affiliated with Apple or Google.\n\n${readFileSync(join(simple, 'LICENSE.md'), 'utf8')}`;
|
|
|
|
if (import.meta.main) {
|
|
if (!existsSync(phosphor) || !existsSync(simple)) { console.error('site-icons: @phosphor-icons/core or simple-icons is not installed (bun install)'); process.exit(1); }
|
|
if (process.argv.includes('--check')) {
|
|
const bad = checkSiteIcons();
|
|
for (const b of bad) console.error(`site-icons: ${b}`);
|
|
process.exit(bad.length ? 1 : 0);
|
|
}
|
|
for (const dir of [ICON_DIR, MARK_DIR, TEASER_DIR, WEB_DIR]) mkdirSync(dir, { recursive: true });
|
|
for (const [dir, f, src] of plan()) writeFileSync(join(dir, f), readFileSync(src));
|
|
writeFileSync(join(ICON_DIR, 'LICENSE.txt'), `Icons in this folder, copied unchanged by tools/site-icons.ts:\n\nPhosphor ${version(phosphor)} (https://phosphoricons.com), regular weight, from the @phosphor-icons/core\npackage.\n\n${readFileSync(join(phosphor, 'LICENSE'), 'utf8')}\n\n${simpleNote()}`);
|
|
writeFileSync(join(TEASER_DIR, 'LICENSE.txt'), `Icons in this folder, copied unchanged by tools/site-icons.ts:\n\nPhosphor ${version(phosphor)} (https://phosphoricons.com), from the @phosphor-icons/core package.\n\n${readFileSync(join(phosphor, 'LICENSE'), 'utf8')}`);
|
|
writeFileSync(join(MARK_DIR, 'LICENSE.txt'), `Store marks in this folder, copied unchanged by tools/site-icons.ts.\n\n${simpleNote()}`);
|
|
console.log(`site-icons: ${SITE_ICONS.length} Phosphor icons ${version(phosphor)} and ${STORE_MARKS.length} simple-icons ${version(simple)} store marks and ${TEASER_ICONS.length} teaser icons -> site/img/icons/, apps/desktop/src/ui/store-marks/, media/teaser/icons/, templates/web/`);
|
|
}
|