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>
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
|
|
import { dayPartAt, paletteCssVars, paletteFor, seasonAt, type DayPart, type Season } from "../../lib/palette";
|
|
import { applyPalette, overrideFrom } from "./palette-boot";
|
|
|
|
type Override = { part: DayPart | null; season: Season | null };
|
|
|
|
export type PaletteClockValue = {
|
|
dayPart: DayPart;
|
|
season: Season;
|
|
colours: string[];
|
|
setOverride: (part: DayPart | null, season: Season | null) => void;
|
|
};
|
|
|
|
const PaletteClockContext = createContext<PaletteClockValue | null>(null);
|
|
const TICK_MS = 60_000;
|
|
|
|
export function PaletteClockProvider({ children }: { children: ReactNode }) {
|
|
const [now, setNow] = useState<Date | null>(null);
|
|
const [override, setOverrideState] = useState<Override>({ part: null, season: null });
|
|
|
|
useEffect(() => {
|
|
setOverrideState(overrideFrom(window.location.search));
|
|
setNow(new Date());
|
|
const timer = window.setInterval(() => setNow(new Date()), TICK_MS);
|
|
return () => window.clearInterval(timer);
|
|
}, []);
|
|
|
|
const dayPart = override.part ?? (now ? dayPartAt(now) : "day");
|
|
const season = override.season ?? (now ? seasonAt(now) : "summer");
|
|
const colours = useMemo(() => paletteFor(dayPart, season), [dayPart, season]);
|
|
|
|
useEffect(() => {
|
|
if (now) applyPalette(paletteCssVars(colours), dayPart, season);
|
|
}, [now, colours, dayPart, season]);
|
|
|
|
const setOverride = useCallback((part: DayPart | null, nextSeason: Season | null) => {
|
|
setOverrideState({ part, season: nextSeason });
|
|
}, []);
|
|
|
|
const value = useMemo(() => ({ dayPart, season, colours, setOverride }), [dayPart, season, colours, setOverride]);
|
|
return <PaletteClockContext.Provider value={value}>{children}</PaletteClockContext.Provider>;
|
|
}
|
|
|
|
export function usePaletteClock(): PaletteClockValue {
|
|
const value = useContext(PaletteClockContext);
|
|
if (!value) throw new Error("usePaletteClock must be used inside <PaletteClock>");
|
|
return value;
|
|
}
|