Files
houseplan-card/src/day-cycle-render.ts
T
Sergey MatyuninandClaude Opus 5.5 c8c470ed57 feat(view): the moon in its phase over the "Follow the Sun" background (#661)
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
2026-09-30 12:25:46 +03:00

50 lines
2.2 KiB
TypeScript

/** Shared four-phase environment templates for full and static cards (#146). */
import { html, nothing, type TemplateResult } from 'lit';
import {
DAY_CYCLE_PALETTES, type DayCyclePhase, type DayCycleState,
} from './sun';
const PHASES: readonly DayCyclePhase[] = ['dawn', 'day', 'dusk', 'night'];
/** CSS variables owned by the stage rather than copied into its plan tree. */
export function dayCycleStageVars(state: DayCycleState | null): string {
if (!state) return '';
const palette = DAY_CYCLE_PALETTES[state.phase];
return [
`--hp-day-cycle-outline-near:${palette.outlineNear}`,
`--hp-day-cycle-outline-mid:${palette.outlineMid}`,
`--hp-day-cycle-outline-far:${palette.outlineFar}`,
].join(';');
}
/**
* Four constant layers make gradient changes genuinely cross-fade in CSS. The
* moon (#661) comes last: above the gradients and the sun glow, still inside
* the environment and therefore below the plan paper and everything else.
*/
export function renderDayCycleEnvironment(
state: DayCycleState | null,
viewWeight = 1,
moon: TemplateResult | typeof nothing = nothing,
): TemplateResult | typeof nothing {
if (!state) return nothing;
const position = [
`--hp-day-cycle-sun-x:${state.sunX.toFixed(2)}%`,
`--hp-day-cycle-sun-y:${state.sunY.toFixed(2)}%`,
`--hp-day-cycle-sun-opacity:${state.sunOpacity.toFixed(3)}`,
`opacity:${Math.min(1, Math.max(0, viewWeight)).toFixed(4)}`,
].join(';');
return html`<div class="hp-day-cycle-env" aria-hidden="true"
data-day-cycle-phase=${state.phase} data-day-cycle-source=${state.source}
style=${position}>
${PHASES.map((phase) => {
const palette = DAY_CYCLE_PALETTES[phase];
const background = `background:radial-gradient(ellipse at 50% 88%, ${palette.horizon} 0%, transparent 54%),linear-gradient(180deg, ${palette.top} 0%, ${palette.bottom} 100%);box-shadow:inset 0 0 90px ${palette.vignette}`;
return html`<div class="hp-day-cycle-bg phase-${phase} ${phase === state.phase ? 'active' : ''}"
data-day-cycle-layer=${phase} style=${background}>
<div class="hp-day-cycle-sun" style="background:radial-gradient(circle, ${palette.sun} 0%, transparent 67%)"></div>
</div>`;
})}${moon}
</div>`;
}