mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 12:49:56 +00:00
At dawn, dusk and night the environment shows the moon in the top-left corner of the scene, behind the plan: computed in the card from the home coordinates and the browser clock (short Meeus series + topocentric parallax, within 1.4° / 1.8 pp of JPL Horizons), one designer image under a continuous phase mask with the lit side always on the left (owner 2026-09-29), a feathered terminator, 3°/3 % thresholds and the 2 s fade of the window rays. Everything but a small gate lives in the lazy moon-runtime chunk, with its own 30 s ticker. General settings: "Sun" becomes "Sun and Moon" with one switch, on for new installations (DEFAULT_CONFIG), off for existing ones. The initial View graph sat 728 B under its budget: the gate is paid for by moving fifteen dialog-only strings of General settings into the lazy settings dictionary (#459) and by one build fingerprint literal instead of three, so the graph ends 4 B above dev. Golden: two new moon scenes, and the two General settings help frames show «Sun and Moon»; the WSL artifact test fixture now models scenes whose first capture awaits acceptance. Issue: #661 User-Visible: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
51 lines
2.4 KiB
TypeScript
51 lines
2.4 KiB
TypeScript
/**
|
|
* The moon's only foothold in the initial View graph (#661, C7): whether the
|
|
* lazy `moon-runtime` chunk is wanted, and one load per page for every card on
|
|
* it. It is wanted when the moon is switched on, the environment exists (the
|
|
* effective background follows the sun, on a View surface) and it is not
|
|
* daytime. Everything else — astronomy, art, element, its 30 s ticker — lives
|
|
* in the chunk.
|
|
*
|
|
* The contract of the other lazy runtimes, in the fewest bytes (the initial
|
|
* graph sits at its budget): a chunk from another build is never installed and
|
|
* never asked for again; a failed load leaves no moon and is retried at most
|
|
* every 30 s, on the next render, from the content-hashed URL with a fresh
|
|
* query so the browser's cached failure is not replayed.
|
|
*/
|
|
import { nothing, type TemplateResult } from 'lit';
|
|
import { ENTRY_BUILD_FINGERPRINT } from './editor-runtime-loader';
|
|
import type { DayCycleState } from './sun';
|
|
|
|
type MoonRuntime = typeof import('./moon-runtime');
|
|
export type MoonHost = import('./moon-runtime').MoonHost;
|
|
|
|
const MOON_RETRY_ASSET = '__HOUSEPLAN_MOON_RETRY_ASSET__';
|
|
let runtime: MoonRuntime | null = null;
|
|
/** Earliest next attempt; 0 before the first, Infinity after a foreign build. */
|
|
let nextAttempt = 0;
|
|
|
|
/**
|
|
* The moon element for `renderDayCycleEnvironment`. `state` is the
|
|
* environment (null: no environment, so no moon); `settings` the global
|
|
* settings, where only an explicit `moon: true` counts (C1). Nothing until the
|
|
* chunk is here; the card that asked re-renders when it arrives.
|
|
*/
|
|
export function moonLayer(
|
|
host: MoonHost | undefined, settings: unknown, state: DayCycleState | null,
|
|
): TemplateResult | typeof nothing {
|
|
if (!host || !state || (settings as { moon?: unknown } | null | undefined)?.moon !== true) return nothing;
|
|
if (!runtime && state.phase !== 'day' && Date.now() >= nextAttempt) {
|
|
const retry = nextAttempt > 0;
|
|
nextAttempt = Date.now() + 30_000;
|
|
(retry
|
|
? import(/* @vite-ignore */ new URL(`${MOON_RETRY_ASSET}?${nextAttempt}`, import.meta.url).href) as Promise<MoonRuntime>
|
|
: import('./moon-runtime')
|
|
).then((module) => {
|
|
if (module.MOON_RUNTIME_FINGERPRINT !== ENTRY_BUILD_FINGERPRINT) { nextAttempt = Infinity; return; }
|
|
runtime = module;
|
|
host.requestUpdate();
|
|
}, () => undefined);
|
|
}
|
|
return runtime?.renderMoon(host, settings, state.phase) ?? nothing;
|
|
}
|