mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
d2bec266ed2c2c01167a15c910c028cc3bfa17fa
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d2bec266ed | v1.59.0-beta.10: unify device visuals and wall refinements | ||
|
|
232c4807fd |
Nothing paints over a marker that says it is a curtain
An explicit «Открыть/закрыть» marker is the strongest statement the card has about what a marker IS, so its cover now decides the plate BEFORE the bound `controls` and before a lit light of the same device — audit DEV-1DA1-01. Until now the cover came third, and the owner's contract «у штор не должно быть жёлтой подложки НИКОГДА» had two holes: a mixed device (a lamp that also ships a blind) told «Открыть/закрыть» went yellow off its own lit light, and a curtain marker with a bound wall switch went yellow off `controls`. The early `return 'on'` never reached the cover branch, so the travelling curtain lost its breathing ring as well — and in glow fill, where the renderer strips `on` from a shining source, it was left with no indicator at all, while the tap still drove the cover. Everything else keeps the old precedence: the same mixed device WITHOUT the explicit action is yellow again, a wall switch still mirrors its controls, and a «cover» marker whose device carries no cover.* at all falls back to its primary. docs/FILTERING.md «What a marker SHOWS» is renumbered accordingly. Regression: demo/smoke_cover_plate_precedence.mjs (the auditor's two markers, every cover state, class AND resolved plate colour). |
||
|
|
1da1aba625 |
Curtains never wear a coloured plate
Owner's contract, 2026-08-04, verbatim: «у штор не должно быть жёлтой подложки
никогда, индикация открыто/закрыто за счёт морфинга иконки».
WHAT 'open' WAS. `.dev.open` is not a border — it is the badge FILLED with
--hp-open (#ff9f43), border and glyph colour included: a solid orange plate,
one step down from the yellow «включено» one. Covers shared a branch with
`valve` and took it in `open` AND `opening`, so a travelling curtain wore the
orange plate UNDER the breathing ring the owner approved a day earlier — the
plate he had just said should stay neutral while it moves, kept for the state
it stopped in. Since
|
||
|
|
de53d530fa |
A curtain marker shows the cover it opens
Owner, 2026-08-04, on his own curtains: «нет ни дышащего кольца во время хода, ни рамки "открыто", ни морфинга иконки». Same device and the same cause as the tap fix two commits before this branch: his Aqara «Roller shade driver E1» ships the `cover.*` hidden by the integration and a visible `switch.*_reverse_direction`, so `primaryEntity` picks the service switch — and `_stateClass`, the state-morphed icon and the ripple all read `d.primary`. The plan reported the state of the reverse-direction option: a yellow «включено» plate whenever it was on, and nothing at all while the curtain actually travelled. `coverEntityOf` already knew where the cover was; the indication now asks it through one helper, `_coverIndicator` — the device's cover when the marker's tap action is explicitly «Открыть/закрыть», null otherwise — and `_actEntity` (`_coverIndicator || primary`) is what the tap path and the marker presentation now share. Same entity offered in the dialog, driven by the tap and shown on the plan. THE RULE, and why it is the least surprising one (docs/FILTERING.md «What a marker SHOWS»): picking «Открыть/закрыть» is the only statement the card has that means «this marker IS the curtain», and the dialog offers it exactly for the devices that own a cover. Hanging the indication on «the device has a cover somewhere» would have re-decided, silently, what a mixed marker is — a lamp that also owns a blind would stop showing the lamp. The precedence in `_stateClass` is unchanged above it: bound controls first, then a lit light (the glow spot and the badge may never disagree), then the cover, then the primary — so even with the action chosen a shining lamp keeps its yellow. The price is that a curtain left on «Инфо-карточка» still speaks for its primary; that is one click in the dialog, and it is the honest reading of what the marker has been told it is. smoke_cover_not_primary.mjs grows an indication section on the owner's device: closed / open / opening / closing give no class, `open`, `covermove`, `covermove`, the icon morphs `mdi:curtains-closed` <-> `mdi:curtains`, and reverse-direction ON never lights the marker again. The rule's boundary is asserted from both sides (take the action away — the primary speaks again; give it back — the cover does), a lit lamp with a travelling cover keeps its yellow and its own icon, and the auditor's own DEV-2C947-04 shape (both entities VISIBLE) is pinned for the tap as well. Eight checks are red on the parent commit. |
||
|
|
f4ad843619 |
A hidden device no longer stretches the plan's frame
Audit dev@2c947f4, DEV-2C947-01 (P2). One visible room and one marker with a saved position 90 canvases out, then the marker is hidden: the auditor's probe measured a frame 112.375x wider than the room it drew — the house opened as a dot in the corner of empty canvas. The same on `houseplan-space-card`. Both cards filtered the devices for RENDERING and framed the unfiltered list. The full card's `_contentItems` walked `_devices` without looking at `hidden`, while the renderer a few lines later drew `!d.hidden`; `space-render.ts` said it out loud — `devs = spaceDevs.filter(d => !d.hidden)` for the markers, `spaceDevs` for the frame. The frame is PRESENTATION (docs/CANVAS.md §4), so it follows what is drawn. Hidden devices keep everything the filtering contract gives them: they are still built, still counted by room LQI, still hold their cell in the auto-grid roster (so hiding one does not move a visible neighbour) — they are simply not content items. The device editor's ghosts are not items either: reaching a ghost is what the §5 pan slack is for, and making the frame follow a local, ephemeral editor toggle would have made the opening view depend on which tab had it switched on. demo/smoke_canvas_frame.mjs is the auditor's probe, both cards: with the marker visible the frame holds it (2 items is below MIN_VOTERS, so the outlier vote cannot quietly rescue the test); hidden, the marker is gone from the DOM, the frame is exactly the room's 60..940 and the room fills the stage. Three of its checks are red on the parent commit. |
||
|
|
694e1e9a3b |
filtering: hiding is an explicit per-device flag (docs/FILTERING.md)
Agreed with the owner: whether a device is on the plan is a CHECKBOX
('Hide device from plan', every kind incl. virtual), not a runtime
algorithm. The old filter survives only as the SEEDER of those flags.
- marker.hidden is the flag; hidden devices are BUILT (room LQI counts
them — owner's decision) but rendered only in the device editor with
'Show hidden' on, ghosted. They cast no glow and no light fill: an
invisible device casts no visible light (owner's decision).
- seedHiddenBindings(): non-physical devices (excluded domains, Group,
scene, bridge, myheat children, grouped lamps) in bound areas WITHOUT a
marker. The editing client materialises them into hidden:true stub
markers, sets settings.filter_seeded, retires settings.show_all, and
strips fresh-hidden ids from the red-dot list. Unticking the checkbox
keeps a hidden:false marker — the seeder never revisits a marked device,
so the user's decision is final. New non-physical devices hide silently;
physical ones keep the red-dot flow.
- legacy configs (no filter_seeded) keep the OLD behaviour verbatim —
runtime filter, shared show_all, hidden-means-gone — until an editing
client materialises them, so a read-only tablet never sees a half-state.
- 'Show all' is renamed 'Show hidden' and is LOCAL to the tab; the shared
settings.show_all retires with the runtime filter.
- 'Remove from plan' disappears for auto/entity devices (the checkbox is
the way); a virtual device's Delete remains a real deletion.
- docs/FILTERING.md is the source of truth for the mechanism.
Tests: seeder/seeded/legacy/lights units (146), smoke_hidden_flag with 12
assertions (68 smokes). Inventory: 146 / 51 / 43 / 68.
|