CI / Typecheck, lint, tests, e2e (push) Failing after 3m51s
- Title screen over a real-time voxel office rendered at low resolution and quantized to a rotating 32-colour palette with Bayer dithering; day part and season re-light the scene and every surface. - 16-bit UI kit: name-plate dialogs, menu cursor, inventory, XP bar, badges, NPC speech, GAME OVER with CONTINUE, hand-placed 12x12 icons, Fusion Pixel font (Latin subset preloaded). - Sections as a game: quest log (onboarding), character select (AI CEO / AI Co-Founder), inventory (features), party roster (providers), live download XP, save point (all v1.5.1 builds), FAQ as NPC dialog, credits. - Downloads and source point to the Gitea release; the download function only redirects to git.zacharyzhang.com. - Tailwind and the old components removed; unit tests for platform/downloads, rewritten e2e suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
import type { CSSProperties } from "react";
|
||
import { PIXEL_ICONS } from "../../lib/pixel-icons";
|
||
import styles from "./pixel.module.css";
|
||
|
||
export type IconName = keyof typeof PIXEL_ICONS;
|
||
|
||
/** Each row's lit runs become one path, so an icon is a single crisp shape. */
|
||
function bitmapPath(bitmap: string): string {
|
||
return bitmap.split(" ").flatMap((row, y) =>
|
||
[...row.matchAll(/#+/g)].map((run) => `M${run.index} ${y}h${run[0].length}v1h-${run[0].length}z`),
|
||
).join("");
|
||
}
|
||
|
||
const PATHS = Object.fromEntries(
|
||
Object.entries(PIXEL_ICONS).map(([name, bitmap]) => [name, bitmapPath(bitmap)]),
|
||
) as Record<IconName, string>;
|
||
|
||
/** A hand-placed 12×12 icon at a whole-pixel scale; labelled icons are images, unlabelled ones are decoration. */
|
||
export function Icon({ name, size = 1, label }: { name: IconName; size?: 1 | 2 | 3 | 4; label?: string }) {
|
||
return (
|
||
<svg
|
||
viewBox="0 0 12 12"
|
||
className={styles.icon}
|
||
style={{ "--icon-scale": size } as CSSProperties}
|
||
fill="currentColor"
|
||
shapeRendering="crispEdges"
|
||
focusable="false"
|
||
{...(label ? { role: "img", "aria-label": label } : { "aria-hidden": true })}
|
||
>
|
||
<path d={PATHS[name]} />
|
||
</svg>
|
||
);
|
||
}
|