Commit Graph
65 Commits
Author SHA1 Message Date
Matyshandclaude[bot] e2464cbb48 feat: add smooth viewport zoom transitions
Issue: #82
User-Visible: yes
2026-08-30 16:11:35 +00:00
Matyshandclaude[bot] bdf81fad58 feat: add device position undo history
Issue: #74
User-Visible: yes
2026-08-30 13:16:27 +00:00
Codexandclaude[bot] 187c90ba69 fix: give static Glow the full card's cache hierarchy (#375)
V6a: pass the stable devices array to resolvedLightSources — the WeakMap
cache is keyed by array identity, a spread guaranteed a miss on every
render in BOTH cards (full-card regression since beta.4).
V6b: the static wall geometry now carries the same non-enumerable
sourceFingerprint tag the full card attaches, so buildLightBarrierScene
takes the fast recutWallBodiesGeometry path on door state changes.
V6c: the static barrier-scene cache is an LRU of 8 per space (parity
with _lightBarrierPool) — a flipping door reuses both of its scenes.
V6d: enabledClip is cached by geometry fingerprint + disabled-room set,
with the full card's bbox prefilter for decor bodies.

User-Visible: yes
Issue: #375
2026-08-29 18:09:53 +00:00
Sergey Matyunin 0dfc74244b feat: add opt-in Glow to space card
Issue: #374
User-Visible: yes
2026-08-29 14:46:52 +03:00
Codex 7c31725ac9 feat: warn about huge backdrops and offer a safe reduced copy (#39)
A picked raster is now classified from its HEADER BYTES ONLY before anything
heavy happens: src/backdrop-probe.ts parses PNG IHDR (+colour type/tRNS for
alpha), JPEG SOF and WebP VP8/VP8L/VP8X at fixed offsets, never using a file
field as an allocation size; hostile or truncated headers collapse to
'unknown', which warns without numbers instead of passing silently. The
thresholds live in that module as the single calibration point
(WARN_DECODED_BYTES 128 MiB ≈ 32 MP, HARD_DIMENSION 16384 — the browser
canvas cap, DOWNSCALE_TARGET_PX 4096), derived from the desktop-Chromium
matrix now committed as demo/benchmark_backdrop_decode.mjs with a
conservative tablet margin documented in the spec.

The shared pick flow (src/backdrop-pick.ts) feeds BOTH lazy runtimes — the
editor space dialog and the onboarding first-space dialog — so the guard
cannot drift between them, and nothing of it enters the eager View graph.
Warn shows the real numbers and three actions; the reduced copy decodes
EXIF-aware, keeps aspect and alpha (PNG stays PNG, opaque becomes JPEG
q0.9) and flows through the ordinary planFile → upload path. Hard has two
phases with one outcome: beyond 16384 px only Cancel; a failed or timed-out
(10 s) reduce closes with a toast, clean staging and NO silent fallback to
the original the user just declined. SVG never reaches the probe. The safe
path swaps the manual byte-loop base64 for FileReader — half the JS-heap
peak on every upload, byte-identical output (parity asserted in the smoke).

Proofs: header-table units incl. a fuzz set of hostile headers and ±1
threshold bounds; smoke_backdrop_guard on the real bundle — zero decode
calls before the choice, byte parity of keep-original, a real 6200 px
reduce to 4096 for both alpha and opaque branches, cancel-only hard
dialog, both phase-2 failures (reject and hang under the test-only timeout
override), re-pick after refusal, SVG bypass; four registry mutants
(probe-always-safe, alpha-dropped, hard-demoted, phase-2 silent fallback).
Spec anchor corrected alongside: the server plan limit is 8 MB
(MAX_PLAN_BYTES), attachments are the 50 MB path — an 8 MB JPEG is easily
80-160 MP decoded, so the client-side guard stays the primary defence.

Issue: #39
User-Visible: yes
2026-08-29 10:13:09 +03:00
Matysh 12cd8a1af3 feat: add complete German localization
Add Deutsch across all card surfaces and backend flows, backed by the language registry introduced in #62. German loads as a fingerprint-checked page-shared locale chunk so EN/RU remain synchronous and the initial View budget stays intact. Root render gates prevent mixed-language flashes, retry once, and fail open to English. Extend parity, runtime, bundle, browser and visual coverage, plus contributor and user documentation.

Issue: #348
User-Visible: yes
2026-08-28 11:58:46 +03:00
Matyshandclaude[bot] 2c65fcec03 perf: lazy-load editor runtime
Issue: #337
User-Visible: yes
2026-08-28 05:40:28 +00:00
Matysh 6540474ff5 refactor: centralize i18n language registry
Issue: #62
User-Visible: no
2026-08-28 02:44:26 +03:00
Matyshandclaude[bot] cab8d128bf feat: add device lifecycle catalog
Issue: #29
User-Visible: yes
2026-08-27 23:17:52 +00:00
Codex 2c20f2dc35 perf: mutation-gate builds the bundle only for browser guards, compiles incrementally, shards and diffs (#332)
Four independent cuts into the 2-4 hour full run, none touching the contract
"a mutant must turn its guard red":

- guardNeedsBundle: rollup runs only for guards that open the built bundle
  (demo/ smokes, golden captures, bundle:sync) — 68 of 253 registry entries.
  Unit and backend guards never read dist/ as a build artifact (verified
  against every test that mentions dist/**: they read the git checkout or
  synthetic files), so 185 mutants skip the most expensive step entirely.
- seedTestBuild + incremental tsc: the mutant worktree starts from the main
  tree's warm test-build/ and .tsbuildinfo; tsc compares file hashes, not
  mtimes, so the fresh checkout stays warm and only the mutated delta is
  recompiled. This also speeds up the long guards that run tsc themselves.
- --changed[=range]: run only mutants whose patch files are touched by the
  diff (origin/dev..HEAD by default). An empty selection is an honest success
  with an explicit message — the full registry remains the pre-release
  contract, per the workflow comment.
- --shard=i/n: deterministic interleaved slices; the workflow runs a 4-way
  matrix, and a warm test-build step feeds every shard. Interleaving spreads
  the expensive browser mutants across shards instead of clumping them.

Measured per mutant on this machine: unit 12-13 s (was ~50-70 s), backend
6 s, browser 32 s (unchanged — the bundle is genuinely needed there). Full
run estimate drops to ~70 sequential minutes, ~20 on four shards.

Unit coverage: guard classification on real registry shapes, a floor on both
classes so the split cannot silently collapse, changed-selection semantics,
and shard completeness/disjointness with an anti-clumping bound.

Issue: #332
User-Visible: no
2026-08-28 01:33:36 +03:00
Codex 6fc57f2ae8 feat: pure wall-junction limit checks (#329 П1-П5)
The owner's five limits as pure functions: minimum 15 degrees between
neighbouring rays of a node (a straight wall through the node is a 180 pair,
not a violation), at most 6 walls per node, a segment at least
max(20 cm, its own thickness), 5 cm clearance between non-incident nodes and
between a node and a foreign wall (a T-joint sitting exactly on that wall is
incidence, not a near miss), and a room interior of at least 25 cm2 after the
masonry is subtracted. Thresholds are absolute and do not scale with cell_cm.
newViolations() implements the spec's inheritance boundary: only violations
introduced by the write are reported.

Issue: #329
User-Visible: no
2026-08-27 23:29:49 +03:00
Matysh ba66698f55 refactor: extract resize controller
Issue: #264
User-Visible: no
2026-08-27 20:30:07 +03:00
Matysh a66fb7ef53 feat: unify zero-thickness walls
Issue: #306
User-Visible: yes
2026-08-26 13:39:52 +03:00
Matysh b336eee996 feat: stabilize persisted wall segment identity
Issue: #282
User-Visible: yes
2026-08-26 02:20:57 +03:00
Codex 0d9320fe1d refactor: extract the base styles and finish the aggregator (#266 slice 5/5)
52 блока host/переменных/кросс-поверхностных групп → src/styles/base.styles.ts;
styles.ts — 19-строчный сборщик [base, plan, devices, chrome, dialogs] с
задокументированным контрактом порядка каскада. Два оставшихся мутантных
якоря (:host-гейт ховера устройств) переадресованы на base.styles.ts.

Юниты инвариантов (test/styles-split.test.mjs): состав и порядок сборщика;
непересечение (scope+селектор) между файлами — единственное именованное
исключение: кросс-поверхностная группа «:host(...) .dev:hover, .dev:focus-
visible» живёт в base по §1.1; выживание медиа-обёрток — 2 forced-colors и
10 prefers-reduced-motion (в ТЗ и ревью фигурировали 8 — фактический счёт по
исходнику 10, юнит держит точное число). fix-test-build научился точке в
имени модуля (styles/base.styles → .js). ARCHITECTURE.md — раздел Styles.

Refactor-proof diff (scope-ключ) пуст; golden 129/129 без переприёмки; смоки
plan_snap_overlay/preloader OK; npm test 1318/0; бандл 1 291 440 → 1 291 458
(+18 байт).

Issue: #266
User-Visible: no
2026-08-26 01:34:20 +03:00
Sergey Matyunin 390f77bd65 feat: улучшить измерения при Resize
Issue: #300
User-Visible: yes
2026-08-24 22:59:33 +03:00
Sergey Matyunin f6f877e393 fix(spaces): repair orphaned plan references
Issue: #244
User-Visible: yes
2026-08-23 00:34:55 +03:00
Sergey Matyunin 482afb73eb feat: block unsafe Optimize geometry
Issue: #199
User-Visible: yes
2026-08-22 16:29:33 +03:00
Sergey Matyunin 4dbdb446f8 fix: address coordinate review findings
Issue: #224
User-Visible: no
2026-08-22 15:32:55 +03:00
Sergey Matyunin 4a798e3e13 fix: canonicalize persisted geometry
Issue: #224
User-Visible: yes
2026-08-22 14:47:38 +03:00
Sergey Matyunin 0d91c1e18e fix: make plan visuals grid-scale invariant
Issue: #239
User-Visible: yes
2026-08-22 13:45:26 +03:00
Sergey Matyunin cb19b7d52e feat: measure opening preview to inner faces
Issue: #238
User-Visible: yes
2026-08-22 12:24:49 +03:00
Sergey Matyunin 2f968996b1 fix: make plan drawing fail closed
Issue: #228
User-Visible: yes
2026-08-22 10:56:22 +03:00
Codex e6be43b90f feat: a straight wall is one record, not a row of seams (#229)
Рисование прямой стены в несколько кликов оставляло по записи на каждый
отрезок. Швы невидимы, пока их не тронешь: выделение хватает кусок,
перетаскивание ломает стену пополам, толщина задаётся пофрагментно. У стен
комнат этого давно нет — `normalizeWallIntervals` схлопывает каждый сплошной
участок одной толщины. Независимые перегородки жили по другому правилу.

Новый чистый модуль `src/wall-merge.ts` даёт им то же правило:

- `mergeCollinearPartitions` сращивает соседей одинаковой толщины и
  направления до неподвижной точки, но только там, где узел никому не нужен.
  Узел остаётся, если в него приходит третья перегородка, стена комнаты
  (стороной, а не только вершиной), колонна или конец сохранённого черновика.
- Направление выжившей записи канонизируется лексикографически: иначе одна и
  та же физическая стена выходила то a→b, то b→a в зависимости от порядка
  входа, и каждый host.t вдоль неё переворачивался вместе с ней.
- `applyOpeningMoves` переносит проёмы на выжившую запись: и авторитетный
  `host`, и legacy-проекцию `x/y/angle`, которую рисует старый читатель
  конфига (docs/CONFIG-COMPATIBILITY.md, #132). Проекция здесь не кэш —
  канонизация направления разворачивает угол на 180°.

Рисование сращивает только свою цепочку и то, чего она коснулась (§8.6 ТЗ):
молча править чужие швы в стороне оно не вправе — для этого есть
«Оптимизировать планы» с предпросмотром, отчётом и отменой. Оптимизация
проходит по всему пространству без seed-ограничения и отдельной строкой
сообщает, сколько записей исчезло.

Issue: #229
User-Visible: yes
2026-08-21 12:40:34 +03:00
Codex 8369c0e839 feat: reorder space tabs by dragging one to a new position
The order of config.spaces used to be whatever order the spaces were created
in, and there was no way back other than deleting a space and drawing it
again.

The gesture is deliberately narrow — mouse, editors only. The same tabs are
the primary way to switch spaces in View, where touch is first class, so a
drag there would compete with the tap that switches. Recorded in the spec as
"Touch editor: not exposed".

The part that needed care is not the drag. Position in the array feeds three
things: the marker placement fallback, the swipe neighbour and a positional
`floor`. So the write that stores the new order also writes down the
placement that used to depend on it: a marker with neither an explicit space
nor an area that names one gets the space it has right now. Both changes go in
one save; splitting them would leave a window in which markers move on their
own. The positional `floor` cannot be fixed from here, so the card says so
once.

Issue: #220
User-Visible: yes
2026-08-21 00:27:35 +03:00
Sergey Matyunin 0e5ee030fe fix: polish device marker geometry and input
Issue: #213
User-Visible: yes
2026-08-20 11:00:22 +03:00
Sergey Matyunin 120d41317c fix: polish device icons and pointer feedback
Issue: #212
User-Visible: yes
2026-08-20 08:42:34 +03:00
Sergey Matyuninandclaude[bot] 6f89002e3a fix: show honest new-space display defaults
Issue: #204
User-Visible: yes
2026-08-19 17:44:48 +00:00
Sergey Matyunin 1e8503bd46 Make empty space model explicit
Issue: #113
User-Visible: no
2026-08-19 02:49:39 +03:00
Sergey Matyunin 9f77e3e932 feat: support openings in independent walls
Issue: #132
User-Visible: yes
2026-08-19 01:11:55 +03:00
Sergey Matyunin 8086399aa6 feat: select exact toggle entity
Issue: #178
User-Visible: yes
2026-08-18 21:31:04 +03:00
Sergey Matyunin fc1e7951e4 feat: unify Plan wall drawing
Issue: #173
User-Visible: yes
2026-08-18 20:33:44 +03:00
Sergey Matyuninandclaude[bot] 6c48c6d5c6 feat: add architectural snap overlay
Issue: #137
User-Visible: yes
2026-08-14 08:27:45 +00:00
Sergey Matyunin 1079cdfab2 feat: add persistent virtual light toggles
Issue: #107
User-Visible: yes
2026-08-14 03:32:53 +03:00
Sergey Matyuninandclaude[bot] 0af095a6c4 fix: complete read-only cold start
Issue: #131
User-Visible: yes
2026-08-13 23:28:19 +00:00
Sergey Matyunin 42b3f44c4a feat: add hidden isometric stage 2
Issue: #122
User-Visible: no
2026-08-14 00:43:13 +03:00
Sergey Matyunin 02502c990c feat: add labs and isometric geometry core
Issue: #89
User-Visible: no
2026-08-13 14:22:30 +03:00
Matysh 8e2973fa7a Release v1.62.0-beta.9 candidate
Issue: #108
User-Visible: yes
2026-08-12 21:49:29 +03:00
Matysh 9e74051652 Release v1.62.0-beta.8 candidate
Issue: #75
Issue: #76
Issue: #95
User-Visible: yes
2026-08-12 19:18:54 +03:00
Matysh 4b03b888ff Release v1.62.0-beta.5 candidate 2026-08-12 12:43:27 +03:00
Matysh c41231a7ba Release v1.62.0-beta.3 candidate 2026-08-12 02:33:54 +03:00
Matysh 446f33ed31 Release v1.62.0-beta.1 candidate 2026-08-11 19:15:21 +03:00
Matysh 48eebfd8a5 Release v1.61.0-beta.8 candidate 2026-08-11 05:40:32 +03:00
Matysh c237baaffd Release v1.61.0-beta.7 candidate 2026-08-11 02:49:42 +03:00
Matysh d2bc908280 Release v1.61.0-beta.6 candidate 2026-08-11 01:14:59 +03:00
Matysh fb382bfa11 Release v1.61.0-beta.2 candidate 2026-08-10 00:24:42 +03:00
Matysh 112c260314 Release v1.61.0-beta.1
Validate / hacs (push) Failing after 12s
Validate / hassfest (push) Failing after 13s
Validate / frontend (push) Successful in 3m2s
Validate / golden (push) Failing after 51s
Validate / backend (push) Failing after 6m50s
Validate / smoke (push) Failing after 13m44s
Validate / performance (push) Failing after 24m13s
2026-08-09 21:51:33 +03:00
Matysh 2a8302f4d6 v1.60.2-beta.3: unify boundaries and device presentation 2026-08-08 15:46:07 +03:00
Matysh d48d220a8c v1.60.2-beta.1: add persistent physical geometry 2026-08-07 22:02:41 +03:00
Matysh 29fb9deb43 v1.60.0-beta.1: unify background editing and device deletion 2026-08-07 11:14:20 +03:00