Commit Graph
31 Commits
Author SHA1 Message Date
Matysh 5392dadeaa v1.50.2: the v1.50.1 review (HP-1501-01, HP-1501-02)
- HP-1501-01: v1.50.1 bounded layout positions and left room rectangles,
  polygon vertices, view_box and opening coordinates on bare _finite — the
  same absurd-magnitude failure, one schema over. _GEOM (±4) covers them all
  now, opening angles get ±360. And because a store may already hold such a
  vertex from before the door existed, contentBounds applies its canvas
  envelope to room geometry exactly as it does to device positions: the
  point renders where it is, the frame ignores it, a space of nothing but
  absurd points falls back to the whole canvas.
- HP-1501-02: a repair matching zero positions answered ok/moved:0 and
  replaced the one-deep backup with an empty one — a typo right after
  repairing the wrong space destroyed the promised way back. Empty match is
  nothing_to_repair now: no write, no revision bump, backup intact.

Old test fixtures carried view_box [0,0,100,100] from the render-unit days;
they now use the normalised box the product actually stores.
2026-07-29 07:30:22 +03:00
Matysh a8ce6020f4 v1.50.1: the v1.50.0 review (HP-1500-01..03)
- HP-1500-02: the stage budget was the absolute document coordinate, so any
  tall dashboard content before the card was billed as header and the stage
  collapsed to 0px. Measure our own chrome relative to the card plus a
  bounded (<=120px) allowance for what the viewport keeps above us; re-measure
  on window resize, remove the listener in disconnectedCallback.
- HP-1500-03, both layers: contentBounds opens a near-zero axis (< ~an icon)
  up to a 200-unit floor and ignores extra points outside a canvas envelope
  (-25%..125%) for FRAMING purposes only; the server bounds layout coordinates
  to +-4 — any finite float used to pass, and one 1e100 hid the plan from
  every viewer. A thin real room keeps its tight frame; the gate sensor past
  the edge still stretches it.
- HP-1500-01: no automatic double-transform — a correct layout and a stranded
  one are indistinguishable, and guessing wrong corrupts good data. Explicit
  admin command houseplan/geometry/repair: dry_run previews, the backup rides
  the same store write, undo restores, and routine layout writes now preserve
  unrelated store keys instead of eating the backup.

Tests: contentBounds guards (unit), layout coordinate bounds + repair
lifecycle (harness), card-below-content smoke. Inventory: 139 / 49 / 42 / 64.
2026-07-29 01:39:10 +03:00
Matysh 94b298962a v1.48.0: the canvas is always square, the plan is centred inside it
A space carried an aspect ratio, and coordinates were normalised against it: x
by the width, y by the height. Every geometric question therefore depended on a
per-space number, and picking a canvas orientation was a decision the user had
no reason to make. The render space is now NORM_W x NORM_W and a plan image is
fitted into it by its OWN ratio, centred — wide plans get margins above and
below, tall ones at the sides.

Migration (geometry_migration.py, pure and unit-tested) runs once at setup under
the write lock. Nothing about a drawing changes: the old box is padded out to a
square and every coordinate re-expressed against it — rooms as rects and
polygons, openings and their lengths, decor, view_box, and the marker positions
in the separate layout store. In render units it is a uniform scale plus an
offset, so angles and proportions are exact. cell_cm is scaled for tall plans,
because the grid pitch is a fraction of the width: without it a wall would
measure less than it does.

 is now dropped by the schema rather than accepted — a stale tab sending
it would be sending coordinates from the old normalisation too, and honouring
the field would not make them right.

The demo fixture was migrated with the same transform, so the smokes exercise
the new geometry rather than a square-native fake; six of them needed their
render-space helpers updated and one its click coordinates.

Not released — dev only, per the owner's instruction.
2026-07-28 22:20:59 +03:00
Matysh d3db9e30e6 v1.46.1: re-check of v1.46.0 — HP-1460-01, -02, -03
HP-1460-01: v1.46.0 stopped overwriting attachments, but picking a free name
and taking it were two steps. Two uploads racing between them agreed on the
same name, both answered 200, and one set of bytes replaced the other;
files/migrate had the same check-then-copy gap. reserve_filename now claims the
name with O_CREAT|O_EXCL as it picks it, and both paths use it. It also splits
the extension off the RAW name and budgets the stem against MAX_FILENAME
including the collision tag — a maximal name lost its '.pdf' and then grew past
the limit, so the view sanitised the request back to a different name and the
attachment 404'd for good.

HP-1460-02: cleanup lived in an 'except Exception', which CancelledError walks
past, only one tmp_path was tracked, promotion had no finally, and the
collector only walks marker folders — an aborted transfer stranded a .upload-*
that nothing would ever remove. An outer finally owns every temporary, a second
'file' part is refused, promotion failure cleans up, and sweep_upload_temps
runs at setup, daily, and inside the commit-scoped collector. Chunks are
batched to 1 MB per disk task instead of one per 64 KB.

HP-1460-03: the layout event reached the static card and not the full one, so
two full cards diverged until a reload. The full card subscribes now and
re-reads ONLY the layout, keyed on its revision. Two hazards handled: it
records revisions it produced itself, and the reaction is deferred ~200 ms
because the event can beat the reply to our own write over the same socket;
positions dragged but not yet sent are flushed and merged on top, so a fix for
a stale UI cannot become a lost drag.

Tests: smoke_layout_sync (fails on a v1.46.0 build), four pure tests for atomic
reservation incl. 20-thread concurrency and the length boundary, a backend test
walking every failing exit path of an upload, and — as the report asked — an
HA-harness test that a repair issue disappears with its space.
Docs: CHANGELOG.md + CHANGELOG.ru.md + ARCHITECTURE.md + TESTING.md + STATUS.md.
2026-07-28 16:48:32 +03:00
Matysh 4418312b0b test: config cap under aiohttp's 4 MB frame; own marker id for the upload test
A 4 MB cap could never be reported: the frame limit rejects the message first
and the socket closes with 1009, so the user gets a dropped connection instead
of 'too_large'. 2 MB is ~30x a real three-floor configuration (70 KB measured).

The upload test listed a folder test_ha_upload.py also writes into.
2026-07-28 16:11:48 +03:00
Matysh 3f719cc32a test: match the new upload url shape; keep the config cap under the WS frame limit
test_upload_ok still asserted the old '<name>?v=<mtime>' url — uploads take a
free name now, so the name itself is the cache key and the query is gone.

MAX_CONFIG_BYTES was 12 MB, above the WebSocket frame limit: a payload that big
never reaches the handler, the socket just closes with 1009 and the user sees a
dropped connection instead of an actionable error. 4 MB is far above any real
configuration and comfortably inside the frame.

test_upload_never_overwrites listed the whole shared test config folder.
2026-07-28 16:09:00 +03:00
Matysh 260615a63f v1.46.0: full external audit of v1.45.4 — HP-1454-01 … -10
HP-1454-01 (high, release blocker): an uploaded SVG plan opened directly is a
top-level document of Home Assistant's own origin, so a <script> inside it
reaches the session's localStorage and API. Uploading needs write access, which
by default every authenticated user has. SVG responses now carry a sandbox CSP;
only SVG, because a CSP on a PDF can break the browser's viewer and a raster
image has nothing to disable. Verified in Chromium both ways: the script runs
without the header and does not with it.

HP-1454-02: attachment uploads wrote straight to <marker>/<filename>, outside
the config transaction — a cancelled dialog or a rejected save left the stored
url serving new bytes, and every new icon shared one 'new' folder, so two of
them attaching manual.pdf pointed at one file. Uploads take a free name, a new
icon gets a per-dialog staging folder promoted on an accepted save, and
config/set collects superseded and aged-orphan attachments like it does plans.

HP-1454-03: the debounce spaced out the starts of a write, not the writes. A
save slower than 500 ms let the next edit go out with the same expected_rev;
the server accepted the first, rejected the second, and the conflict handler
reloaded over the local copy. Writes are chained now — one in flight, each with
the revision the previous returned.

HP-1454-04: _openPairsCache keyed on room ids and links only, so an aspect
change or a dragged vertex left open boundaries and their glow cuts at old
coordinates. It keys on the rendered model object now — the same invalidation
the model cache already has, not a second strategy. The fingerprint also gained
an O(1) geometry roll-up per room.

HP-1454-05: outer collections were capped, inner ones were not. Limits for
poly points, open_to, controls, pdfs, text and url lengths, plus a total
serialized size cap; legacy  is dropped server-side.

HP-1454-06: upload streams to a temp file and downloads use FileResponse, so a
50 MB manual no longer costs ~100 MB of RSS per transfer.

HP-1454-07: spaceModels() dropped room.settings, so the static card ignored the
per-room fill override. HP-1454-08: layout had no revision on point-wise writes
and no event, leaving static cards stale forever; it now keeps a revision,
returns it and fires houseplan_layout_updated. HP-1454-09: repair cleanup only
walked existing spaces, so a deleted space kept its warning. HP-1454-10:
serialize-javascript pinned past two advisories.

Tests: smoke_svg_sandbox (proves both directions), smoke_config_writer and
smoke_render_parity (both verified failing against a v1.45.4 build), six pure
tests for attachment collection and inner limits, four HA-harness tests for the
CSP, non-overwriting uploads, the size cap and layout revisions.
Docs: CHANGELOG.md + CHANGELOG.ru.md + ARCHITECTURE.md + TESTING.md + STATUS.md.
2026-07-28 16:06:21 +03:00
Matysh 3d41fe16b8 v1.45.3: 'value instead of an icon' could never be saved (issue #3)
The device editor has offered display='value' since v1.26.0; MARKER_SCHEMA
accepted only badge/ripple/icon_ripple. Picking it produced

  not a valid value for dictionary value @ data['config']['markers'][n]['display']

and since one rejected marker fails the whole config write, the user could not
save the plan at all until the setting was undone. Reported by @RemyRoux with
the exact error text, 2026-07-27 — a year and a half after the feature shipped.

The schema now accepts it, and the class of bug is closed rather than the
instance: DISPLAY_MODES, TAP_ACTIONS, SPACE_FILL_MODES and ROOM_FILL_MODES are
exported from src/logic.ts, the editors render their options from them, and a
backend test parses those lists out of the TypeScript source and asserts the
schema accepts every one (and rejects a bogus value). Reverting the one-word
schema fix fails that test, which is the check that was missing.

Plus an HA-harness test saving a config that contains a value-display marker —
the exact call the user's card was making.
Docs: CHANGELOG.md + CHANGELOG.ru.md + TESTING.md + STATUS.md.
2026-07-28 00:23:37 +03:00
Matysh 09b0ba41a5 fix v1.44.4: audit follow-up B2, B5, L4 sub-item
B2: the HTTP upload view failed OPEN when the config entry was
unavailable while the WS path failed closed — both now share one
may_write() policy helper (new auth.py) that denies non-admins when the
policy cannot be read.

B5: _finite now guards room rects, polygon vertices, view_box and
opening coordinates, not just layout positions; the declared
MAX_OPENINGS cap is finally enforced.

L4 (sub-item): every drag pipeline captures the pointer through the
tolerant helper (an inactive pointerId used to kill device/label/resize
drags); decor shapes gained a bounds clamp so they cannot be dragged far
outside the plan and persisted there.

+2 backend tests (16); both changelogs updated in this commit
2026-07-27 14:14:25 +03:00
Matysh e04ef2f2e6 feat v1.44.0: control-first device card + light-source flag (user feedback)
- device card opens with controllable entities: toggles inline (finger
  targets), cover/lock/climate hand off to HA more-info; metadata and
  manuals moved below; config/diagnostic entities filtered; locks still
  never toggle from a card
- marker.is_light: a smart switch driving dumb fixtures glows in the
  light-sources fill (its own entity or the bound controls) — no
  light-group helper needed
- backend schema; smoke_card_controls.mjs, smoke_glow extended; docs
  same-commit
2026-07-27 12:41:26 +03:00
Matysh 49b0cb4e05 perf/fix v1.43.1: external audit P1 — render cost, drag threshold, geometry, backend
L1: memoized space model + open pairs (structural fingerprint key, epoch
bumped synchronously at mutation time, not inside the debounce); hoisted
per-room geometry out of the render loop; smoke asserts zero recomputation
across state pushes.

L4: openings get the 3 px drag threshold used by every other pipeline and
only write when the geometry actually changed — taps open the dialog again.

G2: interiorPoint() replaces the vertex mean, so island rooms inside
concave (U/L) parents are accepted and their evenodd holes render; traced
duplicates still are not containment.

G3: segKey rounds before ordering — one shared wall, one key.

B2: _check_write fails closed when the entry is unavailable.
B3: layout/set honours expected_rev and returns the new rev.
B4: config/set without expected_rev over a non-empty store logs a warning.
B5: coordinates reject NaN/Infinity; spaces/rooms/markers/decor/layout capped.

+2 unit tests (118), +2 backend tests (14), smoke_render_perf; docs
same-commit
2026-07-27 10:58:18 +03:00
Matysh 10d4084d17 feat v1.42.1: room-card font sizes — 3 sliders with a live preview
- space.settings.card_font_scale (tier 2 base) + room.settings
  name_scale/label_scale (tier 3), 50-300%, multiplied together and
  stacking with resize-k and kiosk per-screen multipliers
- live sample card in both the space and room dialogs
- backend schema; smoke_font_scales.mjs (9 checks); docs same-commit
2026-07-27 10:12:22 +03:00
Matysh 19e19b5c5f feat v1.42.0: room settings — the third settings tier
- four-tier principle fixed in ARCHITECTURE: global > space > room >
  device, specific overrides general, unset inherits
- room.settings { fill_mode, temp_source, hum_source } with backend
  schema; pure roomFillModeOf + sourceValue (+2 test suites, 114)
- gear on room cards in the Plan editor opens Room settings: rename,
  re-area (current area included in the list), fill override (may opt
  out of glow darkness), explicit temp/hum source with a searchable
  device+entity dropdown; the same section in the creation dialog
- source feeds the room card, tooltip and temp fill; works for rooms
  without an HA area (user-feedback case #1)
- smoke_room_settings.mjs (10 checks); TESTING/CHANGELOG/ARCHITECTURE
  same-commit
2026-07-27 09:58:21 +03:00
Matysh 20c7b55a87 feat v1.37.0: open boundaries (virtual walls)
- room.open_to symmetric links; 'Open boundary' plan tool toggles the
  shared wall pair under the click (sharedBoundary collinear-overlap
  math + distToSegment pull, same as Split); dashed rendering with an
  amber hot state while the tool is active; Esc -> Draw
- glow: the clip becomes the transitive open zone (openZoneOf BFS,
  either-direction links) + door sectors from the zone's outer walls
- model rooms carry open_to; backend ROOM_SCHEMA open_to: [str]
- +3 unit tests (104), smoke_openwall.mjs (8 checks); docs same-commit
2026-07-23 14:18:50 +03:00
Matysh 81ea5c0f3c feat v1.36.2: per-source glow radius
- marker.glow_radius_cm overrides the global default per device (field
  in the marker dialog, HA units, placeholder = global default); pools
  and door sectors use the per-source radius
- backend schema; smoke_glow extended (11 checks); docs same-commit
2026-07-23 13:53:19 +03:00
Matysh d7a1b344e4 feat v1.36.0: marker controls — wall switches that really switch
- marker.controls[]: bound light.*/switch.* entities; explicit per-marker
  tap_action=toggle flips them with HA-group semantics in one call
  (controlsAction + isControllable pure helpers, +2 unit tests, 100)
- icon state and RGB tint mirror the targets (stateless remotes and
  virtual dumb-switch markers finally show something); info card lists
  targets with states; locks filtered everywhere
- chips+search UI in the marker dialog; backend schema; smoke_controls
  (9 checks); TESTING/CHANGELOG same-commit
2026-07-23 13:10:58 +03:00
Matysh 7eaf513c9e feat v1.35.0: glow fill — dark house with glowing light sources
- fill_mode 'glow': uniform darkness over every room; lit lamps render
  radial gradient pools (rgb_color -> color temp via kelvinToRgb ->
  configurable default; brightness scales alpha)
- pools clipped by the source's room + doorway sectors (doorSector rays
  to door edges; hasRoomBehind blocks entrance doors); windows and
  islands don't participate (no shadow casting, documented)
- glow radius in HA units (m/ft) in general settings, stored in cm;
  palette group glow_base/glow_light; backend schema updated
- +4 unit tests (98 total); smoke_glow.mjs (10 checks); docs same-commit
2026-07-23 12:51:48 +03:00
Matysh dd930b64f7 feat v1.33.0: background editor — visual decor layer
- third mode tab: draw lines/rects/ovals/text with grid snap, drag
  preview; select+move (Delete key), erase, color/width/fill controls;
  Esc ladder; text dialog with S/M/L sizes and dblclick re-edit
- decor renders under rooms, visible in every mode, click-transparent
  outside its editor; stored in space.decor with backend schema
  (line/rect/ellipse/text variants), rev/optimistic locking as usual
- smoke_decor.mjs (19 checks); smoke_editor_tabs updated to 3 tabs;
  TESTING/CHANGELOG/UX-MODES same-commit
2026-07-22 14:45:25 +03:00
Matysh 821bdfbd9a feat v1.31.0: room cards — metrics line + proportional resize
- room label becomes a card: name on top, optional metrics below
  (temperature / humidity / avg zigbee / lights), four checkboxes in
  space settings, all off by default; lights render On/Off or '1 of 3'
- areaHum + areaLightStats pure helpers (+3 unit tests, 90 total)
- resize via corner handles in the Plan editor (hover), uniform 0.5-3x,
  scale stored as layout k next to the position; drag preserves it
- fix latent v1.25 regression: draggable HTML labels were never rendered
  in the Plan editor (static SVG only) — real name-only cards now render
  there, draggable and resizable
- repair merge/split smokes still calling removed _toggleMarkup
- validation.py: 4 label_* bools; smoke_room_cards.mjs; docs same-commit
2026-07-22 12:01:14 +03:00
Matysh 71f44fd528 feat v1.29.0: 'new device' flag with a red dot
- auto devices/light groups appearing after install get a server-side 'new'
  flag (settings.new_device_ids) and a red dot top-right of the icon; opening
  the device's editor clears it for every client
- known_devices baseline seeded silently on first run — upgrades never flood
  the plan with dots; hand-made markers never flagged
- pure diffNewDevices + unit tests; backend schema; smoke_new_device.mjs;
  TESTING.md row
2026-07-22 10:48:55 +03:00
Matysh 9fd36bd710 feat v1.28.0: sub-area rooms — manual device placement without an HA area (issue #3)
- area-less rooms appear in the marker room list ('no area, manual'); markers
  store room_id and land at the room centre; dialog reopen restores the choice
- pure parseRoomRef (space#area / space#@roomId) + unit tests; backend schema
- ROADMAP phase 11 closed; TESTING.md row; smoke_subarea.mjs
2026-07-22 10:25:20 +03:00
Matysh 90c558eee7 feat v1.24.0: general settings (global fill palette) + per-space LQI toggle
- General settings dialog: fill colors grouped by mode (light on/off, temp
  cold/ok/hot, lqi weak/strong), each with its own opacity; lqi fill lerps
  between the endpoints; stored in settings.fill_colors (defaults omitted);
  space-card uses the same palette
- per-space show_lqi toggle (badges + room tooltip line), inherits the card's
  show_signal when unset
- fillColorsOf/lerpColor/roomFillStyle helpers (+4 tests), backend schemas,
  smoke_general_settings; TESTING.md updated in the same commit
2026-07-22 09:28:38 +03:00
Matysh 1fa6c6cdee feat v1.23.2: raise the manual-upload limit to 50 MB
MAX_FILE_BYTES 25→50 MB (still enforced while the multipart body streams in);
frontend fallback in the error toast updated; TESTING.md row updated.
2026-07-21 08:02:53 +03:00
Matysh ff7c1b883c feat v1.23.0: doors & windows with live open/lock state
New markup tool 'Opening': click a wall — the opening snaps onto the nearest DERIVED
room wall (v1.19.0 model: openings keep absolute coords, so room edits never break
them) and takes its angle. Dialog: type, length in real cm (door 90 / window 120 —
per-space scale makes it honest), contact sensor (invertible), lock (doors).
Rendering after easy-floorplan (MIT): hinged leaf + swing arc drawing on via
stroke-dashoffset; window = two casements. No sensor → static plan defaults (door
open, window closed); unavailable freezes the default (pure openingAmount, tested).
Lock: padlock badge (green locked / orange unlocked / grey unknown); the lock is
never toggled from the plan — clicking shows an info card with both states.
snapToWall + openingAmount in logic.ts; space.openings validated server-side.
+2 tests (76). Verified live on the dacha (real TTLock + TRV window sensor).
2026-07-17 07:21:20 +03:00
Matysh 7c9731655e feat v1.22.0: presence ripples, per-device icon size/rotation, one-click HACS badge
Visuals borrowed from easy-floorplan (github.com/nicosandller/easy-floorplan), model kept ours.
isActiveState is independent of the live_states toggle; unavailable/unknown count as idle.
Honours prefers-reduced-motion. Badges scale with --dev-size. +1 test (74).
2026-07-17 07:02:22 +03:00
Matysh 65268a7985 feat v1.19.0: a line is never an entity of its own — walls are derived from rooms
A wall can only exist as an edge of a closed room:
- roomEdges(rooms) derives walls from room outlines, deduping shared ones, so deleting a
  room keeps the borders its neighbours still contribute and drops the rest — the rule
  falls out of the model instead of needing bookkeeping.
- Nothing is persisted while drawing: an outline you never close leaves no lines behind
  (previously every click pair was written to space.segments immediately).
- The 'Erase line' tool is gone; space.segments is stripped on every save (validation
  still tolerates it on read so a stale tab cannot fail a save).
- Dead code out: _addSegment/_removeSegmentByKey/_distToSeg/_pathSegs/_segKey.
Docs (ARCHITECTURE/TESTING/CHANGELOG) updated in the same commit. +2 tests (63).
2026-07-16 07:27:23 +03:00
Matysh 3eb5b4a14d feat v1.15.0: temperature room fill (blue/green/yellow) with editable comfort bounds
- fill_mode 'temp': below comfort → #4fc3f7, inside → #66d17a, above → #ffd45c;
  bounds default 20-25°C, editable in the space dialog (visible only for this
  mode), swapped bounds tolerated; rooms without a temperature reading unfilled
- areaTemp(): average of the area devices' temperatures
- tests: roomFillColor temp bands + bounds, spaceDisplayOf defaults, areaTemp,
  backend schema (fill_mode temp + temp_min/temp_max); smoke_temp_fill.mjs
- TESTING.md updated in the same commit (policy)
2026-07-07 18:23:22 +03:00
Matysh 4f8e98cdc7 feat v1.14.0: per-space display settings, hand-drawn spaces, manual-testing checklist
- space dialog 'Display' section: room borders/names toggles, color+opacity,
  fill by zigbee signal or lights (tri-state: on/off/no-lights)
- draggable room name labels persisted as layout rl_<roomId>
- 'no image, outline by hand' space source with canvas orientation; image
  optional now; switching an existing space to draw detaches its plan
- demo/ synthetic-home harness lives in the repo (serve.mjs + smokes + icons gen)
- docs/TESTING.md checklist (same-commit update policy); self-run found and
  fixed: plan_url not detached on image->draw, _stateClass crash on states
  without entity_id; perf measured (162 devices ~14ms build)
- backend: SPACE_DISPLAY_SCHEMA validation (+test)
2026-07-07 13:41:51 +03:00
Matysh afa8398602 feat v1.13.0: universality — floors-import wizard, editable icon rules, tap actions (phase 9)
- floors wizard: spaces from the HA floor registry, step-by-step with mandatory
  plan upload per floor, skip support, auto-markup after the last one
- icon rules as data (settings.icon_rules) + in-card editor with live test,
  invalid-regex guard, bilingual defaults, device_class fallback chain
- tap_action (card) + per-device override; security model: card-wide toggle only
  for light/switch/fan/humidifier, explicit override for covers, never lock/alarm;
  600ms long-press always opens the info card
- i18n dictionaries → src/i18n/*.json (+parity tests); light-theme badge fixes
- frontend tests 15→28
2026-07-06 00:54:13 +03:00
Matysh 94fb4af14c feat v1.11.0: full English translation + en/ru UI localization
- All card UI strings moved to src/i18n.ts (en/ru); language follows the HA
  profile automatically, new 'language: en|ru' card option forces it; GUI
  editor localized and got the language dropdown; generated device names
  localized via BuildCtx.loc.
- English-only codebase: comments, docstrings, test names, backend error
  messages and logs. Russian remains only in the ru dictionary, ru.json,
  iconFor regexes matching Russian device names (+their fixtures) and README.ru.md.
- Docs English-first: README (EN) + README.ru.md, ARCHITECTURE/DEVELOPMENT/
  ROADMAP/CHANGELOG fully translated; translations/en.json had Russian - fixed.
- Removed obsolete RELEASE_NOTES_v1.9.3.md and scripts_publish.sh.
2026-07-05 21:43:58 +03:00
JB e5e02faa6c audit+refactor v1.7.0: drop hardcoded dacha bundle (293KB->83KB) + onboarding empty-state; remove migration & dead device_overrides/virtual_devices; extract src/logic.ts + validation.py (pure, unit-tested: 9 front + 10 back); fix latent bugs unmasked by truncated rules.ts (DOMAIN_PRIORITY, poly _defaultPositions); harden path sanitizers (leading-dot traversal); tsc strict in build+CI; sync versions 2026-07-04 13:42:03 +03:00