mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 04:09:17 +00:00
v1.59.0-rc.1: optimize plans and polish editor feedback
This commit is contained in:
@@ -72,8 +72,12 @@ Built from the registries (`_buildDevices`), rules carried over 1-to-1 from the
|
||||
- Temperature: an entity with device_class=temperature / °C / `_temperature$` → label on the right.
|
||||
- LQI (zigbee): the average over `*_linkquality` entities → label under the icon; color via
|
||||
`lqiColor()`: ≤40 red → ≥180 green (hsl gradient). The room average is shown in the room tooltip.
|
||||
The same tooltip includes the formatted clean-floor area (inner contour for
|
||||
thick walls), and View hover gives every room an accent/brightness highlight.
|
||||
- Icon state classes: on (yellow), open (orange: cover/valve/lock/binary_sensor
|
||||
of problem classes), unavail (transparency).
|
||||
of problem classes), unavail (transparency). Yellow remains on the marker in
|
||||
source-glow fill mode: a light pool is spatial information, not a replacement
|
||||
for the universal working-state plate.
|
||||
|
||||
## Sizes
|
||||
|
||||
@@ -92,6 +96,8 @@ resolver (`src/device-visual.ts`) for availability, steady status and activity.
|
||||
shows the icon/morph and status plate; `icon_ripple` additionally shows a finite event,
|
||||
static presence, mechanical transition or actual-work ring; `value` replaces the icon
|
||||
with the HA-formatted numeric value. A critical alarm is red in every presentation.
|
||||
Runtime baselines are seeded as soon as a rebuilt registry becomes authoritative, before
|
||||
the next HA snapshot is classified; source-key changes reset any finite effect immediately.
|
||||
Legacy `display: ripple` is read as `icon_ripple` and rewritten on the next config save;
|
||||
the backend accepts it only for compatibility. `ripple_color` and `ripple_size` remain the
|
||||
stored names for the ordinary activity effect. `size` (icon multiplier via the
|
||||
@@ -201,6 +207,8 @@ double click → properties dialog. In markup mode the "Opening" tool handles cl
|
||||
| `houseplan/config/get` | — | `{config, rev}` |
|
||||
| `houseplan/trail/get` | — | `{trails: {marker: {current, previous}}}` — vacuum runs, raw robot coords |
|
||||
| `houseplan/config/set` | `config`, `expected_rev?` | `{ok, rev}` / err `conflict`; event `houseplan_config_updated` |
|
||||
| `houseplan/plan/optimize` | `config`, `layout`, both expected revisions | crash-resumable two-store commit + one-deep backup |
|
||||
| `houseplan/plan/optimize_undo` | both expected revisions | restores backup only before any later edit |
|
||||
| `houseplan/plan/set` | `space_id`, `ext` (svg/png/jpg/webp), `data` (b64, ≤8 MB) | `{ok, url}` — writes `<space>.<token>.<ext>`, deletes nothing |
|
||||
| `houseplan/plans/list` | — | `{plans: [{name, url, size, modified, used_by}], total}` (newest 60) |
|
||||
| `houseplan/plans/delete` | `name` | `{ok, removed}` / err `in_use` |
|
||||
|
||||
+32
-12
@@ -398,13 +398,13 @@ Three things were fixed here besides the new coverage:
|
||||
older meanings too — it opts out of the opening's centre magnet, and it
|
||||
turns the compass to coarse 15° steps.
|
||||
|
||||
### 9.5 «Выровнять всё по сетке» — an ACTION, not a migration
|
||||
### 9.5 «Оптимизировать планы» — explicit whole-plan maintenance
|
||||
|
||||
Existing plans may hold coordinates between the nodes. The card does
|
||||
**not** round them on update. Instead, general settings grow a **Grid**
|
||||
group with one button; it opens a confirmation that states how many
|
||||
elements will move and by how much at most, warns that there is no undo,
|
||||
and only then writes.
|
||||
**not** round them on update. General settings contain a **Plan
|
||||
maintenance** group whose action previews and then runs all current
|
||||
lossless maintenance passes: model upgrades, grid alignment, exact
|
||||
open-span canonicalisation and wall-interval compaction.
|
||||
|
||||
Why an action rather than a silent migration:
|
||||
|
||||
@@ -416,15 +416,35 @@ Why an action rather than a silent migration:
|
||||
3. A silent migration is unattributable. When a room looks 3 cm wrong
|
||||
the owner cannot tell whether the card did it or they did.
|
||||
4. An update that touches stored geometry cannot be rolled back by
|
||||
downgrading the card. An action can simply not be pressed.
|
||||
downgrading the card. The explicit action has a one-deep snapshot and
|
||||
can also simply not be pressed.
|
||||
|
||||
`optimizePlans(config, layout)` (`src/plan-optimizer.ts`) is the pure
|
||||
orchestrator. It converts only legacy fields with an exact lossless
|
||||
mapping, materialises legacy `open_to`, calls the grid projection,
|
||||
rekeys exact wall/open-span endpoints onto the moved rooms, merges
|
||||
touching virtual spans per room pair, compacts consecutive real-wall
|
||||
intervals of equal thickness and stamps `model_version`. Unknown fields
|
||||
are preserved and every pass is idempotent.
|
||||
|
||||
The optimizer deliberately does **not** alter backdrop calibration or
|
||||
saved view boxes, delete unattached layout entries (a device may only be
|
||||
temporarily unavailable), deduplicate markers, or delete files. File
|
||||
collection remains the backend's reference-aware scheduled job.
|
||||
|
||||
`alignAllToGrid(spaces, layout)` (`src/align-grid.ts`) is pure: it
|
||||
copies its input, never mutates it, and returns the new spaces, the new
|
||||
layout and the report. The dialog therefore measures and commits the
|
||||
**same object** — the numbers it promises cannot differ from what it
|
||||
does. The write is one `config/set` plus the layout updates, in one go.
|
||||
does. The resulting config+layout pair is sent to
|
||||
`houseplan/plan/optimize`; the backend persists a durable intent before
|
||||
either store changes, commits both revisions, and retains one snapshot.
|
||||
`houseplan/plan/optimize_undo` restores it only while neither revision
|
||||
has changed since the optimization. A crash between store writes is
|
||||
completed from the intent on the next integration setup.
|
||||
|
||||
Guarantees, all covered by `test/align-grid.test.mjs`:
|
||||
Guarantees are covered by `test/align-grid.test.mjs` and the orchestration/
|
||||
idempotence case in `test/plan-optimizer.test.mjs`:
|
||||
|
||||
* every grid-bound element ends on a node; a rect's FAR corner too (a
|
||||
snapped *size* on an off-grid origin leaves the other side between
|
||||
@@ -442,7 +462,7 @@ Guarantees, all covered by `test/align-grid.test.mjs`:
|
||||
|
||||
### The report is a promise
|
||||
|
||||
The confirmation is the only gate in front of an action with no undo, so
|
||||
The confirmation is the decision gate in front of a geometry rewrite, so
|
||||
`maxShift`/`maxShiftCm` must never be smaller than what the run does:
|
||||
|
||||
* displacement is measured on the geometry **actually written back** —
|
||||
@@ -461,9 +481,9 @@ The confirmation is the only gate in front of an action with no undo, so
|
||||
says which space the maximum is in; openings corrected in angle alone
|
||||
are counted on a line of their own.
|
||||
|
||||
There is no undo, and the dialog says so: the card keeps no snapshot of
|
||||
the previous geometry. Re-running the action does not undo it either —
|
||||
it is a projection, and a projection is not invertible.
|
||||
One undo is available until the next config or layout edit. It restores
|
||||
the stored snapshot; re-running optimization itself is never treated as
|
||||
undo because a grid projection is not invertible.
|
||||
|
||||
## Every place that assumed the unit square
|
||||
|
||||
|
||||
@@ -2,6 +2,45 @@
|
||||
|
||||
## Unreleased (dev)
|
||||
|
||||
## v1.59.0-rc.1 — 2026-08-06
|
||||
|
||||
First release candidate of the 1.59 line: safe whole-plan optimization,
|
||||
clearer object editing and room feedback, plus the beta.10 audit follow-ups.
|
||||
|
||||
- **Whole-plan optimizer with safe undo.** The former Align-to-grid action is
|
||||
now Optimize plans. One preview runs every current lossless model migration,
|
||||
aligns grid- and wall-bound geometry, materialises legacy open boundaries,
|
||||
merges touching virtual pieces and compacts equal-thickness real-wall
|
||||
fragments. Config and layout are committed through a durable two-store
|
||||
intent; one undo remains available until the next plan edit. Backdrop
|
||||
calibration, saved views, unattached layout entries and user files are left
|
||||
alone. Interrupted optimization/undo completes on the next integration setup.
|
||||
- **Background-object properties on double click.** In the Background editor's
|
||||
Select mode, double-clicking any object now opens its properties: labels keep
|
||||
their text form, while lines, rectangles, ellipses and furniture get a compact
|
||||
colour / line-width dialog (plus fill for closed shapes). The new dialog is
|
||||
included in warm-remount draft recovery.
|
||||
- **Rooms identify themselves in View.** Hover now highlights the room under the
|
||||
pointer, including rooms with no configured fill or HA area. The room tooltip
|
||||
also shows its formatted clean-floor area; thick walls use the same inner
|
||||
contour as room labels and resize measurements.
|
||||
- **Yellow working state remains universal in source-light fill.** A device that
|
||||
is semantically on/working keeps its yellow marker plate when the space uses
|
||||
the per-source glow fill. The light pool supplements the marker state instead
|
||||
of replacing it.
|
||||
- **beta.10 audit fixes.** The first real device transition after load is no
|
||||
longer consumed while rebuilding the registry, and changing a marker's
|
||||
effective source synchronously clears an old source's short effect. Editing a
|
||||
legacy live label preserves an explicit unit or a non-representable attribute
|
||||
instead of silently dropping it; the old `attr: state` selector migrates to a
|
||||
bare state token instead of a nonexistent attribute. Wall thickness fragments
|
||||
now persist exact normalised endpoints (legacy midpoint keys still read),
|
||||
survive resize, and
|
||||
compact into maximal equal-thickness solid runs after virtual pieces are
|
||||
closed. Virtual-wall dashes remain visible in all editors even with borders
|
||||
hidden. Release CI now reports GitHub/HACS prerelease-order trouble instead of
|
||||
silently publishing a beta that HACS cannot discover.
|
||||
|
||||
## v1.59.0-beta.10 — 2026-08-05
|
||||
|
||||
Tenth pre-release of the 1.59 line: one coherent device-state language and a
|
||||
|
||||
@@ -8,6 +8,47 @@
|
||||
|
||||
## Не выпущено (dev)
|
||||
|
||||
## v1.59.0-rc.1 — 2026-08-06
|
||||
|
||||
Первый release candidate линейки 1.59: безопасная оптимизация всех планов,
|
||||
понятнее редактирование объектов и обратная связь по комнатам, а также правки
|
||||
по результатам аудита beta.10.
|
||||
|
||||
- **Оптимизация всех планов с безопасной отменой.** Бывшее действие
|
||||
«Выровнять по сетке» заменено на «Оптимизировать планы». В одном preview
|
||||
выполняются все актуальные без потерь миграции модели, привязка геометрии к
|
||||
сетке и стенам, материализация старых открытых границ, объединение соседних
|
||||
виртуальных частей и одинаковых по толщине реальных фрагментов. Config и
|
||||
layout записываются через устойчивое намерение для двух хранилищ; одна отмена
|
||||
доступна до следующей правки плана. Калибровка подложки, сохранённые виды,
|
||||
непривязанные позиции и пользовательские файлы не изменяются. Прерванная
|
||||
оптимизация или отмена завершается при следующем запуске интеграции.
|
||||
- **Свойства объекта подложки по двойному клику.** В режиме «Выбрать» редактора
|
||||
подложки двойной клик по любому объекту открывает его свойства: для надписи
|
||||
остаётся форма текста, а для линии, прямоугольника, овала и мебели появляется
|
||||
компактный диалог цвета и толщины линии (для замкнутых фигур — ещё и заливка).
|
||||
Черновик нового диалога переживает тёплый ре-маунт карточки.
|
||||
- **Комната обозначает себя в Просмотре.** При наведении подсвечивается именно
|
||||
текущая комната, в том числе без настроенной заливки или HA area. В тултипе
|
||||
также показывается форматированная чистая площадь пола; при толстых стенах
|
||||
используется тот же внутренний контур, что в подписях и измерениях Resize.
|
||||
- **Жёлтая подложка работающего устройства остаётся универсальной.** Устройство
|
||||
в семантическом состоянии «включено / работает» остаётся жёлтым и при заливке
|
||||
комнаты «Свет по источникам». Световое пятно дополняет состояние маркера, а
|
||||
не заменяет его.
|
||||
- **Исправления по аудиту beta.10.** Первое настоящее событие устройства после
|
||||
загрузки больше не поглощается перестроением реестра, а смена фактического
|
||||
источника маркера сразу сбрасывает короткий эффект старого источника. При
|
||||
редактировании старой живой надписи явная единица или непредставимый inline-
|
||||
грамматикой атрибут сохраняются без потерь; старый выбор `attr: state`
|
||||
превращается в токен состояния, а не в несуществующий атрибут. Фрагменты
|
||||
толщины стен теперь хранят точные нормализованные концы (старые midpoint-ключи
|
||||
продолжают читаться), переживают Resize и после закрытия виртуальных частей объединяются
|
||||
в максимальные сплошные отрезки одинаковой толщины. Пунктир виртуальных стен
|
||||
виден во всех редакторах даже при скрытых границах. Release CI теперь явно
|
||||
сообщает о проблеме порядка prerelease в GitHub/HACS, а не молча публикует
|
||||
beta, которую HACS не может обнаружить.
|
||||
|
||||
## v1.59.0-beta.10 — 2026-08-05
|
||||
|
||||
Десятый pre-release линейки 1.59: единая система состояния устройств и набор
|
||||
|
||||
+47
-2
@@ -19,6 +19,47 @@
|
||||
- GitHub pushes: classic PAT (repo+workflow scopes), created via the user's Chrome;
|
||||
stored in `~/.git-credentials` for the session.
|
||||
|
||||
## Local Windows workstation
|
||||
|
||||
The CI contract is **Node.js 22 + Python 3.13**. Do not use Codex's bundled
|
||||
Node 24 or the machine's default Python 3.14 as proof that a release will pass.
|
||||
|
||||
Minimal native setup (PowerShell):
|
||||
|
||||
```powershell
|
||||
winget install --id OpenJS.NodeJS.22 --source winget
|
||||
winget install --id GitHub.cli --source winget
|
||||
gh auth login
|
||||
Set-Location 'C:\Users\Sergey\Downloads\dev\houseplan-dev\houseplan-card-src'
|
||||
uv python install 3.13
|
||||
uv venv --python 3.13 .venv
|
||||
uv pip install --python '.venv\Scripts\python.exe' pytest voluptuous pytest-asyncio
|
||||
npm ci
|
||||
npx playwright install chromium
|
||||
```
|
||||
|
||||
Open a new Windows Terminal after installing Node/GitHub CLI so their PATH
|
||||
changes are visible. Keep the Playwright browser in its normal shared Windows
|
||||
cache; downloading it inside every repository wastes time and disk space.
|
||||
|
||||
WSL2 is optional for the ordinary frontend and pure-backend loop. It is required
|
||||
only when running the full HA harness locally: current Home Assistant imports
|
||||
the Unix-only `fcntl` module and cannot start its pytest plugin on native
|
||||
Windows. Keep a WSL clone inside the Linux ext4 filesystem rather than under
|
||||
`/mnt/c`, otherwise dependency installs become slower. The release CI always
|
||||
runs this harness on Ubuntu and gates the exact tagged commit. Docker Desktop is
|
||||
not currently required. Do not install the full Home Assistant pytest stack
|
||||
natively just for this repository: its pinned `lru-dict==1.3.0` first requires
|
||||
Visual Studio Build Tools to compile, but the resulting plugin still cannot run
|
||||
without `fcntl`.
|
||||
|
||||
Useful repo-local Git settings on NTFS (optional for this small repository):
|
||||
|
||||
```powershell
|
||||
git config core.fsmonitor true
|
||||
git config core.untrackedCache true
|
||||
```
|
||||
|
||||
### ⚠️ File-sync pitfalls (critical)
|
||||
1. The network mount sometimes serves files **truncated/scrambled** — edits via the Edit tool
|
||||
from the Windows side are unreliable. Rule: **apply python patches against a clean copy in /tmp,
|
||||
@@ -32,8 +73,12 @@
|
||||
|
||||
- Frontend: `npm test` — compiles src/logic.ts+rules.ts (tsconfig.test.json) and runs node:test
|
||||
(test/*.test.mjs). Strict typing: `npm run typecheck` (tsc --noEmit, part of `npm run build`).
|
||||
- Backend: `python -m pytest tests_backend/` — pure validation of custom_components/houseplan/validation.py
|
||||
(loaded by path, without importing the HA package).
|
||||
- Pure backend on native Windows (with no HA plugin autoload):
|
||||
`$env:PYTEST_DISABLE_PLUGIN_AUTOLOAD='1'; .\.venv\Scripts\python.exe -m pytest
|
||||
-p pytest_asyncio.plugin tests_backend/test_validation.py
|
||||
tests_backend/test_trails.py tests_backend/test_trail_recorder.py -q`.
|
||||
- Full backend (including `test_ha_*.py`): `python -m pytest tests_backend/ -q`
|
||||
in CI or WSL/Linux only.
|
||||
- IMPORTANT (audit lesson): the rollup typescript plugin reports a syntax error as a WARNING and still
|
||||
builds the bundle — a truncated file can "pass". That is why the build starts with `tsc --noEmit`,
|
||||
which fails on such errors. Always build with `npm run build`, never bare `rollup -c`.
|
||||
|
||||
+7
-3
@@ -1,6 +1,6 @@
|
||||
# The text block — a decor label that can show an entity's state
|
||||
|
||||
Status: **implemented (dev, unreleased).** Code: `src/logic.ts`
|
||||
Status: **released in v1.59.0-rc.1.** Code: `src/logic.ts`
|
||||
(`liveText`, `liveTextReference`, `liveTextToken`, `liveTextValue`,
|
||||
`decorTextScale`, `decorTextLines` — pure, unit-tested),
|
||||
`src/houseplan-card.ts` (`_renderDecorLayer`,
|
||||
@@ -12,7 +12,9 @@ Smokes: `demo/smoke_live_text.mjs`, `demo/smoke_decor_text.mjs`;
|
||||
The shape: `{kind:'text', x, y, text, color, scale?, angle?}` — plus legacy
|
||||
`size?`, `entity?`, `attr?`, and `unit?` fields that older plans may still
|
||||
carry. New live references are stored only inside `text`; existing linked
|
||||
labels render unchanged and migrate to inline references when edited.
|
||||
labels render unchanged and migrate to inline references when that conversion
|
||||
is lossless. A legacy explicit `unit`, or an attribute name that cannot be
|
||||
represented by the inline grammar, stays in the legacy fields when edited.
|
||||
|
||||
## 1. Why a live label
|
||||
|
||||
@@ -168,7 +170,9 @@ click opens its editor, and the corner/rotate handles appear.
|
||||
references included; `scale` optional, finite, `0.15…20`; `angle` optional,
|
||||
finite, `-360…360`. Legacy `entity`/`attr`/`unit` remain accepted and bounded
|
||||
so old saved plans continue to validate and render. The frontend writes none
|
||||
of them after the label has been edited. Tests: `tests_backend/test_validation.py`
|
||||
of them after an ordinary representable label has been edited. It deliberately
|
||||
retains them when dropping an explicit unit or non-representable attribute would
|
||||
change what the label says. Tests: `tests_backend/test_validation.py`
|
||||
(`test_decor_text_live_fields`, `test_decor_text_block_scale_and_angle`).
|
||||
|
||||
## 7. What this is not
|
||||
|
||||
+11
-4
@@ -11,15 +11,15 @@
|
||||
> (versions, publication, infrastructure), DEVELOPMENT.md for new gotchas,
|
||||
> ARCHITECTURE.md for design changes, ROADMAP.md when plans move.
|
||||
|
||||
## Snapshot (2026-08-05)
|
||||
## Snapshot (2026-08-06)
|
||||
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| Version | **v1.59.0-beta.10** everywhere (manifest, const.py, package.json, CARD_VERSION) — **pre-release**, tag `v1.59.0-beta.10` on **`dev`**, GitHub Release with `prerelease=true`; `main` is not touched and nothing is copied to the home instance by hand — HACS delivers it on the beta channel. On top of beta.9: unified device status/activity visuals; inline HA variables in decor text; doorway-light clipping by thick-wall reveals; view-only virtual-wall masking; a clearer wall toolbar; and canonical merging/compaction of wall fragments. Previous pre-release: v1.59.0-beta.9; previous stable: v1.58.0 |
|
||||
| Version | **v1.59.0-rc.1** everywhere (manifest, const.py, package.json, CARD_VERSION) — **pre-release**, tag `v1.59.0-rc.1` on **`dev`**, GitHub Release with `prerelease=true`; `main` is not touched. The rc line deliberately replaces the planned beta.11: GitHub lists beta.9 before beta.10/beta.11 lexicographically and HACS 2.0.x consumes that order without SemVer sorting, while rc.1 is both newer by SemVer and discoverable first. Previous pre-release: v1.59.0-beta.10; previous stable: v1.58.0 |
|
||||
|
||||
| Workflow | Owner's rule since 2026-08-05: ordinary fixes/features are made **locally, without tests and without commits**. Tests/build/smokes run only when the owner asks for a pre-release; then bump to `X.Y.Z-beta.N`, commit/tag the tested `dev` state and publish a GitHub Release with `prerelease=true`. `main` is not touched and nothing is copied to the home instance by hand; HACS delivers it on the beta channel |
|
||||
| Workflow | Owner's rule since 2026-08-05: ordinary fixes/features are made **locally, without tests and without commits**. Tests/build/smokes run only when the owner asks for a pre-release; then bump the version, commit/tag the tested `dev` state and publish a GitHub Release with `prerelease=true`. `main` is not touched and nothing is copied to the home instance by hand. The release workflow now reports a failed HACS-discovery check when GitHub does not return the new tag as its first prerelease |
|
||||
| GitHub | https://github.com/Matysh/houseplan-card — `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
|
||||
| CI | beta.10 passes the full local frontend, pure-backend and browser-smoke gate. Exact-SHA release gating remains active: `release.yml` withholds the asset until every matching Validate run finishes green; GitHub Validate and the release asset are verified after the tag is pushed |
|
||||
| CI | rc.1 passes the local Node 22 typecheck/build, all 386 frontend tests, all 93 pure-backend tests and all 113 browser smokes. Native Windows cannot start the current HA pytest plugin (`fcntl` is Unix-only), so the exact-SHA Ubuntu Validate remains mandatory: `release.yml` withholds the asset until every matching run finishes green; GitHub Validate and the release asset are verified after the tag is pushed |
|
||||
| HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled, mergeable clean, never drafted. Queue: 1212 open, 835 older than ours. Merge rate COLLAPSED: 75 in July but almost all in the first decade, 0 in the last week (checked 2026-07-29) — maintainers process in rare bursts; ETA unknowable, months at best. Nothing actionable on our side |
|
||||
| Home instance | ha.jbstudio.pro (SSH port **22222**, key `ha_jb`; HA config root is `/mnt/data/supervisor/homeassistant` — `/config` does NOT exist in this SSH environment), last direct copy was **v1.57.0**; from v1.58.0 on it updates itself through HACS by tag (no scp) |
|
||||
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
|
||||
@@ -74,6 +74,13 @@
|
||||
actual work, orange open/unlocked, unavailable and always-red alarms;
|
||||
activity distinguishes a short event, presence, mechanical travel and
|
||||
running. Legacy Ripple-only migrates to Icon + activity on the next save.
|
||||
- **v1.59.0-rc.1** (2026-08-06): whole-plan lossless optimization with an
|
||||
atomic config+layout commit and safe undo; the yellow actual-work plate is
|
||||
retained alongside source glow; all Background objects have Select-mode
|
||||
double-click properties; View hover highlights every room and reports its
|
||||
clean-floor area. Audit fixes add eager activity baselines/source resets,
|
||||
lossless legacy live-text editing, exact wall-fragment endpoints/compaction,
|
||||
editor-visible virtual walls and prerelease-discovery reporting in CI.
|
||||
|
||||
## Recent milestones (details in CHANGELOG.md)
|
||||
|
||||
|
||||
+41
-28
@@ -85,9 +85,11 @@
|
||||
placed on the plan (hidden by filtering or by the user) still feeds the
|
||||
room card, the tooltip and the temperature fill; fridges/TRVs still do
|
||||
not; an explicit per-room source still wins [auto: unit devices.test]
|
||||
- [ ] Room tooltip wording (v1.44.5): hovering a room shows its name (plus
|
||||
temperature/signal when available) and no longer claims "open the area" —
|
||||
room clicks were removed in v1.40.1 [manual]
|
||||
- [ ] Room hover + tooltip: in View, hovering any room visibly highlights it
|
||||
(filled, transparent and area-less alike) and shows its name plus clean-
|
||||
floor area; temperature/signal follow when available. Thick walls reduce
|
||||
the area to the inner contour. Editors do neither [auto: smoke_ux_fixes;
|
||||
manual visual]
|
||||
|
||||
## Environments matrix
|
||||
|
||||
@@ -283,9 +285,9 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
at 70% of a device icon and zooming WITH the plan; the small metric rows
|
||||
under the room name now show in the plan editor too
|
||||
[auto: smoke_room_cards gearDetached/plainInPlan]
|
||||
- [ ] One indicator always (v1.52.1, HP-1520-01): in a glow space the plan
|
||||
editor (no glow layer) shows the lit lamp yellow again; the devices
|
||||
editor draws the layer and keeps the badge standard
|
||||
- [ ] Working plate remains universal: in a source-glow space a lit lamp or
|
||||
other actually working device stays yellow in View and in every editor;
|
||||
the glow pool is an additional spatial indicator, not a replacement
|
||||
[auto: smoke_light_badges]
|
||||
- [ ] Size/angle parity (v1.52.1, HP-1513-01): a marker with size 3 / angle 37
|
||||
scales x3 and rotates on BOTH cards [auto: smoke_size_angle_parity]
|
||||
@@ -362,10 +364,10 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
the explicit action is yellow again, a wall switch still mirrors its
|
||||
controls, and an «Открыть/закрыть» marker whose device has no `cover.*`
|
||||
at all falls back to its primary [auto: smoke_cover_plate_precedence]
|
||||
- [ ] Light-source badges (v1.52.0): in glow fill a lit lamp's badge stays
|
||||
standard (the spot is the indicator) and a lit socket stays yellow; in
|
||||
other fills a lit lamp is plain yellow with no RGB tint; morphing and
|
||||
the activity colour fallback survive [auto: smoke_light_badges +
|
||||
- [ ] Light-source badges (current contract): in glow fill a lit lamp's badge
|
||||
stays yellow just like a lit socket; the pool keeps the source's RGB while
|
||||
the marker keeps semantic yellow. Other fills behave identically;
|
||||
morphing and the activity-colour fallback survive [auto: smoke_light_badges +
|
||||
smoke_rgb_alarm]
|
||||
- [ ] Icon size multiplier scales the glyph (dev): set a marker's size to 3 —
|
||||
the icon inside grows with the badge instead of staying default
|
||||
@@ -394,10 +396,13 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
- [ ] Yellow means working (dev): a TRV whose hvac_action is heating glows
|
||||
yellow; one that is merely enabled (idle) or has a service switch on
|
||||
(anti-scaling, child lock) stays dark; a lit light turns its state on by
|
||||
the same condition that lights the glow pool — and shows the yellow
|
||||
badge only where the glow spot is NOT drawn: with the glow layer
|
||||
visible the spot is the one indicator (v1.52.0)
|
||||
the same condition that lights the glow pool and shows the yellow badge
|
||||
even where that pool is drawn
|
||||
[auto: smoke_yellow_principle + smoke_light_badges]
|
||||
- [ ] Activity baseline (beta.10 audit): rebuilding the device registry seeds
|
||||
the current snapshot immediately, so the very first later motion/event
|
||||
transition is detected. Rebinding a marker's effective source clears the
|
||||
old source's finite flash in the same update [auto: smoke_motion_sense]
|
||||
- [ ] Editor gestures on touch (dev): in the plan editor on a phone, pinch
|
||||
zooms and a moving finger pans; releasing after a gesture does not draw
|
||||
a point, a clean tap still does [auto: smoke_editor_gestures]
|
||||
@@ -718,9 +723,11 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
while decor shapes stay fully opaque; no fade in the other editors [auto: smoke_decor / smoke_grid_fade]
|
||||
- [ ] Background editor (v1.33.0): third tab with its own toolbar (select /
|
||||
line / rect / oval / text / erase + color, width, fill, X); shapes drag-
|
||||
drawn with grid snap and live preview; degenerate shapes dropped; text
|
||||
via dialog (S/M/L, color; dblclick re-edits); Select moves (snap), Delete
|
||||
removes, Erase deletes on click; Esc: draft → selection → select tool →
|
||||
drawn with grid snap and live preview; degenerate shapes dropped. In
|
||||
Select, double click always opens properties: text/HA variables for a
|
||||
label, colour + line width for every other object, and Fill for rectangles
|
||||
or ovals. Select moves (snap), Delete removes, Erase deletes on click;
|
||||
Esc: draft → selection → select tool →
|
||||
View; decor renders under rooms, visible everywhere, inert outside the
|
||||
editor; stored in space.decor (rev/lock, backend schema) [auto: smoke_decor / smoke_grid_fade]
|
||||
- [ ] Opening hover preview (v1.32.1): with the Opening tool, hovering near a
|
||||
@@ -820,7 +827,8 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
|
||||
- [ ] HA instance with zero devices/areas → onboarding works, rooms can be drawn, no crashes [manual]
|
||||
- [ ] Space with zero rooms → renders; markup hint visible
|
||||
- [ ] Room without area + borders ON → drawn, click does nothing, no area tooltip signal
|
||||
- [ ] Room without HA area + borders OFF → still has a transparent View hit
|
||||
surface, highlights on hover, reports geometric floor area, click does nothing
|
||||
- [ ] No zigbee devices anywhere → no LQI badges, lqi fill leaves all rooms unfilled [manual]
|
||||
- [ ] 100+ devices in one space → build under ~50 ms [manual], drag stays smooth
|
||||
- [ ] Very long device/room names → ellipsis/wrap, no layout explosion
|
||||
@@ -1177,9 +1185,11 @@ require hands on real hardware — they remain for the human pass.
|
||||
- [ ] **Nothing is rounded**: a sensor reporting `23.94781` shows `23.94781`.
|
||||
Rounding is the sensor's `display_precision`, not ours [auto:
|
||||
smoke_live_text]
|
||||
- [ ] **Old links migrate on edit**: a stored beta.9 `text + entity/attr/unit`
|
||||
label still renders unchanged. Open it: the inline token is visible in
|
||||
the textarea; save it and only the new text template remains
|
||||
- [ ] **Old links migrate without loss**: a stored beta.9 `text + entity/attr`
|
||||
label still renders unchanged. A representable link moves into the
|
||||
textarea token and drops legacy fields on save; an explicit `unit` or an
|
||||
attribute name outside the inline grammar remains legacy and survives an
|
||||
otherwise unrelated text/colour edit
|
||||
[auto: smoke_live_text]
|
||||
- [ ] **The same everywhere**: the label reads identically in View, in the
|
||||
editors and on a kiosk screen [auto: smoke_live_text]
|
||||
@@ -1490,8 +1500,9 @@ require hands on real hardware — they remain for the human pass.
|
||||
the whole wall; click opens the cm/in field; empty/0 clears; Esc closes
|
||||
without applying; «Apply to all walls of this room» fills every allowed
|
||||
edge [auto: smoke_wall_thickness]
|
||||
- [ ] **Hatched body, area unchanged**: after setting thickness a `.wallbody`
|
||||
path appears; the room card's m² is identical before and after
|
||||
- [ ] **Hatched body, clean-floor area**: after setting thickness a `.wallbody`
|
||||
path appears; room-card and tooltip m² both decrease to the same inner-
|
||||
contour area
|
||||
[auto: smoke_wall_thickness]
|
||||
- [ ] **Openings cut the slab**: a door/window on a thick wall leaves a gap in
|
||||
the body; the door swing is offset toward the inner face; with
|
||||
@@ -1516,9 +1527,10 @@ require hands on real hardware — they remain for the human pass.
|
||||
[auto: test/wall-thickness.test.mjs + smoke_resize_virtual_thick]
|
||||
- [ ] **Fragment normalisation**: draw adjacent/overlapping virtual stretches
|
||||
along the same pair of rooms — they persist and select as one span. Close
|
||||
the last virtual stretch on a uniformly thick wall — its saved `walls`
|
||||
data returns to one whole-edge key. Repeat across a Split boundary: spans
|
||||
owned by different room pairs must stay separate
|
||||
the last virtual stretch — consecutive solid fragments merge into maximal
|
||||
runs of equal thickness; equal neighbours become one, a thickness change
|
||||
remains an exact endpoint. Resize transforms those endpoints. Repeat
|
||||
across a Split boundary: spans owned by different room pairs must stay separate
|
||||
[auto: test/open-spans.test.mjs + test/wall-thickness.test.mjs +
|
||||
smoke_resize_virtual_thick]
|
||||
- [ ] **Unit + backend**: inset/mitre/bevel, key from either end, degrade,
|
||||
@@ -1598,12 +1610,13 @@ require hands on real hardware — they remain for the human pass.
|
||||
still comes in at the window, a door with a contact sensor still reports
|
||||
open/closed in the room card, and the resize tool still refuses to
|
||||
shorten a wall past its opening [auto: smoke_hide_layers, smoke_glow]
|
||||
- [ ] **Virtual walls follow the borders switch** (owner 2026-08-05): make an
|
||||
- [ ] **Virtual walls follow the borders switch only in View** (owner 2026-08-05): make an
|
||||
open boundary between two rooms (Plan → «Открытая граница»), then turn
|
||||
**«Всегда отображать границы комнат» OFF**. The dashed stretch goes with
|
||||
the borders — no floating dashes on a plan that draws no walls. Turn the
|
||||
borders back on and it returns. In the Plan editor the dashes are always
|
||||
visible, whatever the switch says [auto: smoke_hide_layers, smoke_openwall]
|
||||
borders back on and it returns. In Plan, Devices and Background editors
|
||||
the dashes are always visible, whatever the switch says
|
||||
[auto: smoke_hide_layers, smoke_openwall]
|
||||
- [ ] **Nothing is stored when nothing is hidden**: with both boxes unticked,
|
||||
the space's config carries no `hide_decor` / `hide_openings` at all, and
|
||||
a plan saved by an older card still opens here unchanged
|
||||
|
||||
+11
-5
@@ -35,7 +35,8 @@ Allowed: pan/zoom (wheel, pinch, buttons), switching spaces, device tap
|
||||
(info / more-info / toggle per settings), long-press → info card, opening tap →
|
||||
door/lock info card (with an explicit Unlock/Lock button when a lock is bound —
|
||||
the only way to operate a lock from the card; plan-icon taps never toggle locks),
|
||||
room-card link icon → HA area (room taps do nothing since v1.40.1), hover tooltips (name, temperature, signal).
|
||||
room-card link icon → HA area (room taps do nothing since v1.40.1), room hover
|
||||
highlight, hover tooltips (name, clean-floor area, temperature, signal).
|
||||
|
||||
Removed from this mode (they move, not die):
|
||||
- icon dragging ("drag anywhere", v1.9 — consciously reversed),
|
||||
@@ -68,13 +69,15 @@ layer you cannot see is a layer you cannot edit.
|
||||
|
||||
| Setting | Off (default) | On | Always drawn in |
|
||||
|---|---|---|---|
|
||||
| `show_borders` — «Всегда отображать границы комнат» | borders (and the dashed **virtual walls**) are hidden | both are drawn | the Plan editor |
|
||||
| `show_borders` — «Всегда отображать границы комнат» | in View, borders and dashed **virtual walls** are hidden | both are drawn | room borders: Plan; virtual walls: all editors |
|
||||
| `hide_decor` — «Скрыть декоративный слой» | decor is drawn | lines, shapes, labels and furniture are hidden | the Background editor |
|
||||
| `hide_openings` — «Скрыть проёмы» | doors and windows are drawn | their symbols are hidden | the Plan editor |
|
||||
|
||||
- **Virtual walls follow `show_borders`** (owner, 2026-08-05). They are walls —
|
||||
dashed ones. Drawing them on a space with no borders left a plan whose only
|
||||
walls were a few floating dashed stretches.
|
||||
- **Virtual walls follow `show_borders` in View** (owner, 2026-08-05). They are
|
||||
walls — dashed ones. Drawing them on a View with no borders left a plan whose
|
||||
only walls were a few floating dashed stretches. Every editor deliberately
|
||||
shows them regardless of the switch: hiding geometry while editing a plan,
|
||||
device placement or its underlay makes those modes visually ambiguous.
|
||||
- **Their geometry and their presentation are separate.** Every virtual span
|
||||
still ends on the real wall centreline. In View the thick wall body is
|
||||
painted over the dash ends, so they visually stop at its faces; editors paint
|
||||
@@ -102,6 +105,9 @@ layer you cannot see is a layer you cannot edit.
|
||||
|
||||
- Toolbar tools: select / line / rect / oval / text / erase, plus colour, width
|
||||
and fill. Shapes are drag-drawn with grid snap and a live preview.
|
||||
- In **Select**, double click always means “edit this object”. Text opens its
|
||||
text/HA-variable form; a line, rectangle, oval or furniture symbol opens the
|
||||
common colour and line-width form, with Fill where the shape supports it.
|
||||
- **Live size badge** (owner 2026-08-04): while a LINE is being dragged out, the
|
||||
same `.measurelabel` the Plan editor puts on a wall shows «length · angle» —
|
||||
`segmentCm` over the space's `cell_cm`, metres or feet per the HA unit system,
|
||||
|
||||
+14
-8
@@ -15,17 +15,23 @@ Code: `src/wall-thickness.ts`, render in `src/houseplan-card.ts` /
|
||||
|
||||
## 1. Model
|
||||
|
||||
Per space: `walls: [{ key, cm }]`. Key = quantised midpoint + direction
|
||||
(modulo 180°). Config always stores centimetres. Open boundaries refuse
|
||||
thickness. One physical stretch has one thickness (atomic collinear spans when
|
||||
neighbours overlap only partially). Atomic keys are storage only while a real
|
||||
geometric break requires them: once an original edge is entirely solid with
|
||||
one thickness, normalisation writes its single whole-edge key again. Likewise,
|
||||
Per space: `walls: [{ key, cm, a?, b? }]`. `key` remains the quantised midpoint
|
||||
and direction (modulo 180°) compatibility lookup; new or rewritten entries also
|
||||
carry exact endpoints `a` / `b` in normalised plan coordinates. Config always
|
||||
stores centimetres. Old `{key, cm}` data remains readable and is upgraded when
|
||||
the affected boundary is edited. Open boundaries refuse thickness. One physical
|
||||
stretch has one thickness (atomic collinear spans when neighbours overlap only
|
||||
partially). Exact endpoints make a thickness boundary independent of whichever
|
||||
room topology later happens to split the same straight line. Normalisation
|
||||
merges consecutive solid pieces into each maximal run of equal thickness; a
|
||||
different thickness or a virtual gap remains a real break. Likewise,
|
||||
touching/overlapping `open_spans` of the same room pair are stored as one span;
|
||||
pair ownership remains a hard boundary so Split can derive exact `open_to` links.
|
||||
|
||||
Degrade unmatched keys silently on write. Resize / undo / scale re-key all
|
||||
touched spans in the same transaction and keep `walls` in the resize snapshot.
|
||||
Degrade unmatched keys silently on write. Resize / undo / scale transform exact
|
||||
endpoints and re-key all touched spans in the same transaction; legacy entries
|
||||
without endpoints keep the midpoint fallback. `walls` stays in the resize
|
||||
snapshot.
|
||||
|
||||
## 2. Growth (centreline ±½)
|
||||
|
||||
|
||||
@@ -169,9 +169,10 @@ Lovelace **пересоздаёт элемент карточки** — выбр
|
||||
|
||||
`_spaceDialog` (настройки/создание пространства), `_markerDialog` (устройство),
|
||||
`_settingsDialog` (общие настройки), `_rulesDialog` (правила иконок),
|
||||
`_openingDialog` (проём), `_decorTextDialog` (надпись), `_roomDialog` вместе со
|
||||
всей своей обвязкой (`_roomEditId`, `_roomFill`, источники температуры и
|
||||
влажности, масштабы подписей, `_areaSel`/`_nameSel`, `_pendingSplit`, `_path`).
|
||||
`_openingDialog` (проём), `_decorTextDialog` (надпись), `_decorShapeDialog`
|
||||
(цвет / толщина линии объекта подложки), `_roomDialog` вместе со всей своей
|
||||
обвязкой (`_roomEditId`, `_roomFill`, источники температуры и влажности,
|
||||
масштабы подписей, `_areaSel`/`_nameSel`, `_pendingSplit`, `_path`).
|
||||
|
||||
Информационные попапы — `_infoCard` (карточка устройства) и `_openingInfo` —
|
||||
восстанавливаются **по id**, а не по объекту: конфиг мог перезагрузиться под
|
||||
|
||||
Reference in New Issue
Block a user