SUN: white day — the brightest moment of the day is white (owner 2026-08-03)

- BG_STOPS: +10deg #e8ddcf (morning light), +30..90deg #ffffff; night half
  of the scale untouched (-4 #131a28, -12..-90 #070c14)
- drawn-plan paper vs white sky: all paper shapes now sit in one
  .hp-paperg group; in daynight mode the group gets a subtle
  drop-shadow so the sheet contour stays readable at high sun
  (static mode and night unaffected)
- pinned colors updated: test/sun.test.mjs, demo/smoke_sun.mjs;
  smoke_bg_color paperUnderneath follows the .hp-paperg wrapper
- docs/SUN.md: explicit BG_STOPS table
- demo/shot_daynight.mjs: noon/sunset/night stills of the scale
This commit is contained in:
Matysh
2026-08-03 09:56:36 +03:00
parent c024c6d75a
commit 3a6a819dec
11 changed files with 104 additions and 20 deletions
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
// Three stills of the daynight scale (noon/sunset/night) for the white-day review.
import { launch } from './serve.mjs';
const outDir = process.argv[2] || '/tmp';
const { page, browser } = await launch({ width: 700, height: 700 }, 1);
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.evaluate(async () => {
const c = window.__card;
const sp = c._serverCfg.spaces.find((s) => s.id === 'f1');
sp.openings = [
{ id: 'wN', type: 'window', x: 0.30, y: 0.14, angle: 0, length: 0.08 },
{ id: 'wE', type: 'window', x: 0.96, y: 0.60, angle: 90, length: 0.08 },
{ id: 'wW', type: 'window', x: 0.04, y: 0.30, angle: 90, length: 0.08 },
];
sp.plan_url = ''; // hand-drawn plan: white paper — the risky case for a white day
sp.settings = { ...(sp.settings || {}), show_borders: true, fill_mode: 'glow' };
c._serverCfg.settings = { ...(c._serverCfg.settings || {}), north_deg: 0, sun_rays: true, bg_mode: 'daynight' };
c._cfgRev = (c._cfgRev || 0) + 1;
});
const shot = async (az, el, name) => {
await page.evaluate(async ([az, el]) => {
const c = window.__card;
c.hass = { ...c.hass, states: { ...c.hass.states, 'sun.sun': {
entity_id: 'sun.sun', state: el > 0 ? 'above_horizon' : 'below_horizon',
attributes: { azimuth: az, elevation: el },
} } };
c.requestUpdate(); await c.updateComplete;
}, [az, el]);
await page.waitForTimeout(300);
const stage = await page.evaluateHandle(() => window.__card.shadowRoot.querySelector('.stage'));
await stage.asElement().screenshot({ path: `${outDir}/${name}.png` });
};
await shot(180, 60, 'dn_noon');
await shot(265, 3, 'dn_sunset');
await shot(0, -20, 'dn_night');
await browser.close();
console.log('shots done');
+4 -2
View File
@@ -231,9 +231,11 @@ const drawn = await page.evaluate(async () => {
const cs = getComputedStyle(p);
return cs.stroke === 'none' || cs.strokeOpacity === '0' || cs.strokeWidth === '0px';
});
// paper first, everything else on top of it
// paper first, everything else on top of it (one .hp-paperg group wraps
// all paper shapes so the daynight drop shadow composites without seams)
const first = sr().querySelector('.stage.noplan svg').firstElementChild;
out.paperUnderneath = !!first && first.classList.contains('hp-paper');
out.paperUnderneath = !!first && first.classList.contains('hp-paperg')
&& !!first.firstElementChild && first.firstElementChild.classList.contains('hp-paper');
// live resize preview: drag the L's right wall 0.625 -> 0.6875 — the paper
// must move WITH the room, not lag behind until the drop
const pv = JSON.parse(JSON.stringify(sp));
+2 -2
View File
@@ -90,11 +90,11 @@ const res = await page.evaluate(async () => {
// 8) day/night background: daynight breathes, static does not
await setSun(180, 60);
out.staticBgAtNoon = !stageStyle().includes('background:#5a6673');
out.staticBgAtNoon = !stageStyle().includes('background:#ffffff');
cfg().settings.bg_mode = 'daynight';
touchCfg();
await setSun(180, 60);
out.daynightNoonBg = stageStyle().includes('background:#5a6673');
out.daynightNoonBg = stageStyle().includes('background:#ffffff');
out.daynightClass = !!sr().querySelector('.stage.daynight');
await setSun(180, -20);
out.daynightNightBg = stageStyle().includes('background:#070c14');
File diff suppressed because one or more lines are too long
+10 -2
View File
File diff suppressed because one or more lines are too long
+13 -2
View File
@@ -50,8 +50,19 @@ only — no entities are created, no services are called.
- `'static'` — the existing `bg_color` behaviour, color picker and
all. Nothing changes for existing installs.
- `'daynight'` — the stage background follows the sun's elevation:
neutral by day, a warm shift in the golden hour (elevation below
~10°), cooling through dusk, deep darkening at night. The PLAN
WHITE at full day (the brightest moment of the day is white — owner,
2026-08-03), a warm bright shift in the golden hour (elevation below
~10°), cooling through dusk, deep darkening at night. The scale
(piecewise-linear between stops, `BG_STOPS` in `src/sun.ts`):
| elevation | color | phase |
| --- | --- | --- |
| −90°…−12° | `#070c14` | deep night |
| −4° | `#131a28` | dusk cools down |
| 0° | `#4a3527` | warm band right at the horizon |
| +10° | `#e8ddcf` | morning light — warm and bright |
| +30°…+90° | `#ffffff` | plain day, white |
The PLAN
itself dims only ~10% at night (`filter: brightness(.9)`), so the
daytime room fills stay readable. Transitions are a CSS
background/filter transition tens of seconds long;
+6 -3
View File
@@ -5071,15 +5071,18 @@ class HouseplanCard extends LitElement {
// L-shaped house — the scene bg reaches the exterior walls).
// `space` comes from _renderCfg, so a live resize preview
// (_rszPreview) moves the paper together with the rooms.
// One <g> around ALL paper shapes: the daynight drop shadow
// (styles.ts) is composited once for the whole sheet, so
// adjacent rooms never cast seams onto each other's paper.
if (space.bg) {
const pp = this._paperRect(space)!;
return svg`<rect class="hp-paper" x="${pp.x}" y="${pp.y}" width="${pp.w}" height="${pp.h}" pointer-events="none"></rect>`;
return svg`<g class="hp-paperg"><rect class="hp-paper" x="${pp.x}" y="${pp.y}" width="${pp.w}" height="${pp.h}" pointer-events="none"></rect></g>`;
}
return paperRoomShapes(space.rooms).map((sh) =>
return svg`<g class="hp-paperg">${paperRoomShapes(space.rooms).map((sh) =>
'poly' in sh
? svg`<polygon class="hp-paper" points="${sh.poly}" pointer-events="none"></polygon>`
: svg`<rect class="hp-paper" x="${sh.rect.x}" y="${sh.rect.y}" width="${sh.rect.w}" height="${sh.rect.h}" rx="${sh.rect.rx}" pointer-events="none"></rect>`,
);
)}</g>`;
})()}
${this._editing ? this._renderMarkupDefs(vb) : nothing}
${this._editing && !this._markup
+8
View File
@@ -328,6 +328,14 @@ export const cardStyles = css`
.stage.noplan .hp-paper {
fill: #ffffff;
}
/* White day (owner 2026-08-03): at high sun the daynight sky is #ffffff —
the same white as a drawn plan's paper. A whisper of a drop shadow on
the paper GROUP keeps the sheet's contour readable on the white sky
(user units on the 1000-unit canvas ≈ a few screen px). Invisible at
night against the dark sky; 'static' mode never gets it. */
.stage.daynight .hp-paperg {
filter: drop-shadow(0 2px 8px rgba(10, 16, 26, 0.28));
}
.room {
transition: 0.12s;
cursor: default; /* v1.40.1: rooms are not clickable — the label's link icon is */
+3 -3
View File
@@ -49,9 +49,9 @@ const BG_STOPS: [number, string][] = [
[-12, '#070c14'],
[-4, '#131a28'], // dusk cools down
[0, '#4a3527'], // warm band right at the horizon
[10, '#46505f'], // golden hour fades into neutral
[30, '#5a6673'], // plain day
[90, '#5a6673'],
[10, '#e8ddcf'], // morning light — warm and bright on the way to white
[30, '#ffffff'], // plain day: the brightest moment is white (owner 2026-08-03)
[90, '#ffffff'],
];
const clamp01 = (t: number) => Math.min(1, Math.max(0, t));
+2 -2
View File
@@ -50,12 +50,12 @@ test('sunDirOnPlan: compass points map to canvas vectors (y grows down)', () =>
assert.ok(near(d[0], 0, 1e-12) && near(d[1], -1, 1e-12));
});
test('dayPhase: night is dark and dim, noon is neutral, sunrise is warm', () => {
test('dayPhase: night is dark and dim, noon is white, sunrise is warm', () => {
const night = dayPhase(-20);
const dawn = dayPhase(2);
const noon = dayPhase(60);
assert.equal(night.bg, '#070c14');
assert.equal(noon.bg, '#5a6673');
assert.equal(noon.bg, '#ffffff');
assert.notEqual(dawn.bg, night.bg);
assert.notEqual(dawn.bg, noon.bg);
assert.ok(near(night.planDim, 0.1));