diff --git a/docs/specs/179-device-icons-redesign.md b/docs/specs/179-device-icons-redesign.md new file mode 100644 index 00000000..3cc4e0e7 --- /dev/null +++ b/docs/specs/179-device-icons-redesign.md @@ -0,0 +1,534 @@ +# Issue #179 — новый визуальный язык маркеров устройств + +- **Статус:** готово к ревью ТЗ +- **Issue:** https://github.com/Matysh/houseplan-card/issues/179 +- **Приоритет:** P1 +- **Тип:** feature / polish / accessibility +- **Область:** frontend, общий renderer маркера, preview, статическая карточка, + документация и visual QA +- **Нормативный дизайн-пакет:** `House Plan Icons — Developer Package`, + версия 1.1.1, экспорт 2026-08-18 +- **Архив:** https://github.com/user-attachments/files/31235094/House.plan.Icons.-.Developer.Package.2026-08-19.zip +- **SHA-256 архива:** + `63670C73E25D1E59DDAF1BE236F3D7F2FAC827B9B5D6DD4B77125EA9BC012025` +- **Figma frames:** Light `99:1290`, Dark `104:1539` + +## 1. Пользовательский сценарий и персона + +**Персона:** пользователь House Plan, который одновременно контролирует +устройства разных доменов, читает значения датчиков и оценивает состояние +связи на светлой либо тёмной теме Home Assistant. + +**Сценарий:** пользователь открывает план и за один взгляд различает обычное, +активное, тревожное, заблокированное, разблокированное, виртуальное и +недоступное устройство; видит полное значение и Zigbee LQI; получает тот же +результат в полном плане, preview редактора и статической карточке пространства. + +## 2. Проблема и результат + +### До реализации + +- plate маркера является одним заполненным rounded-square без общего внешнего + shell из дизайн-пакета; +- hover применяется и к unavailable-маркеру; +- selected обозначается amber-цветом и конфликтует с семантикой активности; +- value и legacy secondary value рисуются отдельными спутниками, длинные + значения обрезаются ellipsis; +- LQI использует непрерывный HSL-gradient, а не три читаемых диапазона; +- ordinary continuous pulse длится 2,4 с и по умолчанию разрастается до 3×; +- маркер не является клавиатурной целью и не активируется Enter/Space; +- light/dark, focus и semantic states не образуют единой дизайн-системы. + +### После реализации + +Все поверхности используют один runtime-renderer нового shell/core, +компоновок Text/Double, LQI и motion из пакета. Семантика устройств и действий +остаётся прежней; меняется её единое визуальное и доступное представление. + +## 3. Нормативный источник и порядок разрешения расхождений + +Единственный дизайн-источник этой задачи — архив #179. Нормативны его +`README.md`, `SPECIFICATION.md`, `ACTIVE_ANIMATION_SPEC.md`, +`DEVELOPER_HANDOFF.md`, `PACKAGE_ANNOTATION.txt`, `manifest.json` и SVG-примеры. + +Порядок приоритета: + +1. решения владельца в §4; +2. текстовые документы архива; +3. SVG-примеры архива; +4. текущая реализация House Plan — только для поведения, которое пакет не + переопределяет. + +Статические SVG являются эталонами геометрии и состояний, а не готовыми +production-иконками: runtime продолжает использовать динамические MDI glyphs, +HA-значения и локализованный текст. + +## 4. Принятые решения владельца + +1. **Unavailable.** Визуальный hover и motion отсутствуют. Click/tap остаётся + доступен и выполняет текущее действие поверхности (More Info либо открытие + настроек). +2. **Пользовательские pulse-настройки.** Если `ripple_color` или `ripple_size` + явно сохранены, они сохраняют приоритет. При отсутствии поля используются + геометрия и цвет пакета. +3. **Zigbee LQI.** `0…40` — red, `41…179` — amber, `180+` — green. +4. **Клавиатура.** Интерактивный маркер получает focus; Enter и Space выполняют + то же действие, что click на этой поверхности. +5. Специальная кнопка либо новый action для клавиатуры не вводятся. + +## 5. Цели + +1. Реализовать shell/core, цвета, состояния, focus и motion пакета #179. +2. Сохранить единую проекцию на полном плане, в editor preview и static card. +3. Показывать полное динамическое значение без ellipsis в Text/Double layout. +4. Сделать маркер читаемым в light/dark и на цветном фоне плана. +5. Обеспечить минимум 44×44 CSS px для интерактивной цели и keyboard parity. +6. Сохранить существующую HA-семантику, пользовательские настройки, координаты + и безопасный pipeline действий. + +## 6. Не входит в задачу + +- новые правила определения `on`, `working`, `open`, alarm и activity; +- новые HA service calls, actions или обход secure confirmation; +- новые display modes и новые пользовательские настройки дизайна; +- смена MDI-иконок либо поставка собственного icon font; +- настройка порогов LQI пользователем; +- автоматическое устранение пересечений соседних маркеров; +- изменение комнатной LQI-заливки, Glow, света, комнатных fills или isometry; +- миграция сохранённых координат и marker config; +- перенос каждого example SVG из архива в production bundle. + +## 7. Визуальная система + +### 7.1. Геометрия + +Маркер состоит из независимых слоёв: + +1. прозрачная hit area минимум 44×44 CSS px; +2. внешний shell: прозрачный fill, stroke и внешние shadows; +3. внутренний core с MDI glyph либо динамическим значением; +4. semantic/focus/selection decoration; +5. pulse layer с `pointer-events: none`; +6. LQI и дополнительные секции, если включены. + +Для Icon-эталона shell имеет диаметр `101.5/127` viewBox, core — `80/127`; +отношение core/shell равно `0.788` с допустимым расхождением не более 0,5 CSS +px на QA-размерах 32, 56 и 96 px. Координата сохранённого маркера остаётся +центром icon core, поэтому включение Text/Double и смена стороны не двигает +устройство на плане. + +Light shell: stroke `#BCBCBC`; core по умолчанию white. У light-варианта +запрещены backdrop blur и inner shadows. Внешние shadows масштабируются от +референса 56 px: `0 1px 2px rgb(0 0 0 / 12%)` и +`0 4px 8px -1.07px rgb(0 0 0 / 18%)`. + +Dark использует неизменённую dark-ревизию пакета, включая core `#252525` и её +эталонные эффекты. Тема берётся из актуальных HA theme tokens; переключение +темы не требует remount и не меняет semantic state. + +### 7.2. Цвета + +Нормативные semantic colors: + +| Смысл | Цвет | +|---|---| +| hover / focus / neutral activity | `#0C82F0` | +| active / working / unlocked | `#F0A00C` | +| alert / low LQI | `#F0410C` | +| unavailable core | `#B5BAC1` | +| locked glyph | black | +| high LQI | `#1DC21D` | + +Тема не подменяет semantic colors. Контраст glyph/core и читаемость текста +проверяются в light/dark и на светлом, тёмном и насыщенном фоне плана. + +### 7.3. Состояния и приоритет + +Нормативный визуальный приоритет: + +```text +Alert > Focus > Selected > Hover > semantic state > Default +``` + +- **Default:** theme-default shell/core и обычный glyph. +- **Hover:** blue decoration пакета; не меняет semantic state и не + перезапускает pulse. +- **Focus:** blue keyboard-focus decoration, видимая без hover. +- **Selected:** отдельная selection decoration пакета, не amber semantic fill. +- **Active/working:** amber. +- **Lock:** locked glyph black; **Unlock:** unlocked glyph amber. +- **Alert:** red и всегда выше остальных состояний. +- **Unavailable:** gray core/glyph по текущему правилу прозрачности; без + визуальной реакции на hover и без motion. +- **Virtual:** пунктирный внешний shell. У обычного virtual default/hover + меняется только цвет оформления. Реальная HA unavailable и active pulse для + HA-less virtual device не синтезируются. + +Текущая semantic-модель `neutral/open/working/alarm`, activity resolver и +domain-specific HA rules сохраняются. Для lock presentation дополнительно +различает locked/unlocked, не меняя secure action. Generic physical `open` +для cover/contact/valve не переименовывается в lock state и продолжает +существующую отдельную semantic-проекцию. + +`display: static_icon` получает новую theme-default shell/core геометрию, но +остаётся нереактивным к HA state, values и pulse. На интерактивной поверхности +он сохраняет разрешённые hover/focus/click; unavailable hover запрещён. + +## 8. Text, Double и дополнительные секции + +### 8.1. Text + +В `display: value` динамический текст находится внутри общего shell. Полная +строка всегда доступна визуально: CSS ellipsis, clipping и скрытие хвоста +запрещены. Сначала шрифт равномерно уменьшается до `0.25 × marker size` +(8 CSS px при размере 32); если этого недостаточно, text section расширяет +shell. Измерение выполняется детерминированно и кэшируемо, без layout-loop и +ResizeObserver на каждый маркер. + +### 8.2. Double + +Иконка и value badge входят в один общий shell. Поддерживаются сохранённые +позиции `right`, `bottom`, `left`, `top`; icon core остаётся anchor. Высота +внутренней value-секции равна `0.7875 × icon core`, как в референсе. + +Если у нетронутой legacy-конфигурации одновременно разрешены два значения, +второе становится третьей секцией того же shell, а не отдельным спутником. +Новая пользовательская модель второго бейджа не вводится. + +Длинное значение проходит тот же auto-fit, что Text: полная строка без +ellipsis, а shell при необходимости расширяется. Динамический текст остаётся +текстом DOM, а не SVG path; шрифт — HA/system Roboto с weight 600 и безопасным +system fallback. + +## 9. Zigbee LQI + +Числовое LQI располагается под shell по геометрии пакета и окрашивается +категориально: + +- `<= 40` — `#F0410C`; +- `41…179` — `#F0A00C`; +- `>= 180` — `#1DC21D`. + +Границы применяются к текущему resolved average LQI без изменения источника, +агрегации, форматирования и флагов `show_signal`. Значение `0` валидно. Для +missing/unavailable LQI сохраняется текущая политика отсутствия подписи. + +Категориальная функция применяется только к marker LQI. Непрерывный +HSL-gradient комнатной LQI-заливки остаётся неизменным. + +В `Light/Zigbee LQI Low.svg` архива low ошибочно окрашен amber. Текстовая +спецификация архива и решение владельца имеют приоритет: production low — red. + +## 10. Motion + +### 10.1. Continuous + +- одно кольцо; +- duration `3.6s`, infinite; +- easing `cubic-bezier(.45,.05,.55,.95)`; +- scale `1 → 1.5`, opacity `.55 → 0`; +- цвет соответствует resolved ordinary activity. + +### 10.2. Short + +- три кольца; +- каждое длится `1.1s`; +- delays `0`, `1.1s`, `2.2s`; +- общий цикл `3.3s`; +- новое событие перезапускает цикл через существующий generation/runtime. + +### 10.3. Alert + +- red независимо от custom pulse color; +- wave stroke 3 reference units; +- scale до `1.5`; +- два wave-starts с интервалом `1.2s`, цикл `2.4s`; +- alert имеет приоритет над ordinary short/continuous. + +### 10.4. Пользовательские настройки и fallback + +Явно сохранённые `ripple_color` и `ripple_size` сохраняются без миграции. +Отсутствующий `ripple_size` использует package scale `1.5`, отсутствующий +`ripple_color` разрешается в порядке: + +1. валидный live RGB контролируемого light, если он уже является текущим + источником pulse color; +2. semantic amber для working/open/unlocked; +3. package blue для neutral activity. + +UI-default размера меняется на `1.5`, но Open → Save не материализует поле и +не переписывает существующее явно сохранённое значение. Backend допустимый +диапазон и import/export round-trip не меняются. + +### 10.5. Reduced motion + +При `prefers-reduced-motion: reduce` animated rings отсутствуют. Ordinary +activity обозначается статической semantic point из существующего единого +pulse renderer; alert остаётся красным статическим состоянием. Marker не +создаёт собственную media-query subscription: используется одна подписка на +surface/card host. + +## 11. Интерактивность и доступность + +- Интерактивные маркеры в View/kiosk и Device editor получают `tabindex="0"` + и button semantics. +- Enter/Space вызывают тот же существующий click handler текущей поверхности: + action/More Info в View и открытие настроек в Device editor. +- Space предотвращает прокрутку только когда активирует marker. +- Pointer/touch поведение, long-press/context action и drag в редакторе не + меняются; keyboard path не создаёт прямой HA service call. +- Background/Plan, read-only static card и неинтерактивный preview не входят в + tab order и не получают ложный button role. +- Minimum hit area 44×44 CSS px центрирована по icon core и не влияет на + visual bounds, координату, pulse либо collision geometry. +- `aria-label` включает локализованное имя, semantic state, activity/alert и, + если LQI показан, число и понятный диапазон «низкий/средний/высокий». +- Цвет и motion не являются единственным носителем alert, availability, lock + или LQI band. +- Focus после закрытия открытого marker dialog возвращается на исходный + marker по существующему dialog contract. + +Secure lock/cover/valve actions продолжают проходить через текущий action +resolver и confirmation policy. Новый DOM-shell и keyboard handler не имеют +отдельного пути обхода подтверждения. + +## 12. Совместимость и данные + +- config schema version не меняется; +- `display`, `value_badge`, `value_badge_position`, `ripple_color`, + `ripple_size`, `show_signal` и legacy fields сохраняют формат и round-trip; +- существующие координаты относятся к icon core и не мигрируют; +- явно сохранённые pulse color/size воспроизводятся как раньше, кроме нового + shell и motion geometry; +- отсутствующие поля не материализуются при Open → Save; +- старые версии карточки читают тот же config; downgrade меняет только + внешний вид; +- runtime не зависит от наличия в bundle статических SVG-примеров; +- ru/en i18n parity обязательна. + +## 13. Архитектурный контракт + +```text +HA/device registry + marker config + → existing semantic/activity resolvers + → resolveDevicePresentation + → resolveDevicePulse + → renderDeviceFace (shell/core/text/LQI/motion) + → full plan | editor preview | static space card +``` + +Один resolved presentation и один `renderDeviceFace()` остаются источником +истины всех поверхностей. Surface передаёт только interaction/theme context и +не вычисляет semantic state повторно. + +Рекомендуемые зоны изменений: + +- `src/device-visual.ts` — lock presentation facet без смены action semantics; +- `src/device-presentation.ts` — theme/semantic projection, marker-only LQI + band, defaults pulse; +- `src/device-pulse.ts` — package timings/scale/reduced motion; +- `src/device-face.ts` — единый shell и Text/Double/third section DOM; +- `src/styles.ts` — tokens, geometry, states, focus, auto-fit и motion; +- `src/houseplan-card.ts` и общие surface adapters — role/tabindex/keydown; +- i18n, unit, smoke, golden fixtures и документация. + +Нельзя вводить surface-specific копию state mapping, formatter либо motion +runtime. Text auto-fit не должен читать layout на каждом render/animation +frame. Pulse использует transform/opacity и не меняет layout. + +## 14. Эдж-кейсы + +- light/dark hot switch во время continuous/short pulse; +- colored room background и минимальный marker size 32; +- marker sizes 32/56/96, user scale 0.5…3 и explicit ripple size 1…20; +- alarm во время hover/focus/selected и снятие alarm; +- unavailable во время active pulse, затем reconnect baseline без false short; +- locked, unlocked, locking и unlocking с secure action; +- virtual default/hover без HA entity и linked virtual with resolved controller; +- `static_icon` при реальном alarm/working/unavailable; +- `0`, `false`, very long localized string, unit symbol и third legacy value; +- Double на четырёх сторонах вместе с LQI; +- LQI exactly `0`, `40`, `41`, `179`, `180`, missing; +- rapid short retrigger, short поверх continuous, reduced-motion hot switch; +- keyboard activation после drag, click confirmation и dialog focus return; +- несколько cards одного плана с независимыми runtime и theme context; +- hidden/HA-disabled/orphaned marker и read-only static card. + +## 15. Acceptance criteria + +- [ ] **AC1 — shell/theme.** Icon, Text и Double используют package shell/core + geometry и Light/Dark tokens; light не содержит blur/inner shadow. + **Доказательство:** unit token assertions + reviewed golden 32/56/96. +- [ ] **AC2 — states.** Default, Hover, Focus, Selected, Active, Lock, Unlock, + Alert, Virtual и Unavailable соблюдают §7.3 и нормативный приоритет. + **Доказательство:** presentation unit matrix + light/dark state-table + golden. +- [ ] **AC3 — unavailable.** Unavailable не получает visual hover/motion, но + click/tap по-прежнему открывает текущее действие поверхности. + **Доказательство:** unit + browser smoke. +- [ ] **AC4 — secure locks.** Locked black, unlocked amber, locking/unlocking + continuous; pointer и keyboard проходят один action/confirmation path. + **Доказательство:** unit + secure-action smoke. +- [ ] **AC5 — virtual/static.** Virtual имеет dashed shell; HA-less virtual не + синтезирует unavailable/activity. `static_icon` использует theme-default + shell и не реагирует на live state/value/pulse. + **Доказательство:** unit + existing static-icon smoke. +- [ ] **AC6 — Text/Double.** Полные строки видимы без ellipsis; четыре стороны, + anchor, auto-fit и третья legacy section соответствуют §8. + **Доказательство:** unit layout facts + browser smoke + long-value golden. +- [ ] **AC7 — LQI.** Marker LQI использует red/amber/green thresholds на + границах, а room fill сохраняет continuous gradient. + **Доказательство:** boundary unit tests + room-fill regression test. +- [ ] **AC8 — motion.** Continuous/short/alert timings, ring counts, retrigger, + scale и colors соответствуют §10; explicit color/size сохраняются. + **Доказательство:** pure resolver tests + DOM/style smoke. +- [ ] **AC9 — reduced motion.** Rings отсутствуют, ordinary point и static red + alert остаются; одна subscription на host. + **Доказательство:** unit + reduced-motion browser smoke. +- [ ] **AC10 — keyboard/a11y.** Только интерактивные surfaces получают focus; + Enter/Space эквивалентны click, hit area >=44×44, aria-label содержит + state/LQI, focus возвращается после dialog. + **Доказательство:** keyboard/touch/browser smoke. +- [ ] **AC11 — parity.** Full plan, editor preview и static card используют + один presentation/face renderer и дают одинаковую read-only projection. + **Доказательство:** existing preview/static parity smokes. +- [ ] **AC12 — совместимость.** Schema и saved config не мигрируют; Open → Save + не материализует defaults; explicit pulse settings round-trip. + **Доказательство:** frontend/backend config tests. +- [ ] **AC13 — производительность.** Нет per-frame JS, per-marker media query, + ResizeObserver/layout loop или SVG example assets в production bundle; + transform/opacity motion не вызывает layout. + **Доказательство:** code assertion + `performance_smoke.mjs` перед beta. +- [ ] **AC14 — release artifacts.** ru/en copy, guides, architecture/testing + docs, changelogs, targeted smokes и golden review актуальны. + +## 16. Тестирование + +### 16.1. Цикл реализации + +После каждого логического блока: + +```bash +npm run typecheck +npm test +npm run build +``` + +Unit coverage: + +- полная state/priority/theme/display matrix; +- lock/unlock и generic open не смешиваются; +- LQI boundaries и неизменность room gradient; +- pulse kinds, timings, fallback/explicit settings и reduced motion; +- Text/Double/third section facts, long values и anchor; +- keyboard surface policy и accessible labels; +- config/default/round-trip compatibility. + +### 16.2. Targeted browser smoke перед S7 + +Добавить `demo/smoke_device_icon_design.mjs` и прогнать его вместе с: + +- `demo/smoke_device_preview_parity.mjs`; +- `demo/smoke_static_icon.mjs`; +- `demo/smoke_state_value.mjs`; +- `demo/smoke_disabled_device.mjs`; +- релевантным secure-action/keyboard smoke из существующего набора. + +Новый smoke проверяет light/dark, все состояния, Text/Double, четыре позиции, +long value, LQI boundaries, unavailable click, 44×44 hit area, keyboard и +reduced motion. + +### 16.3. Mutation gates + +Зарегистрировать targeted mutants, которые обязаны быть пойманы тестами: + +1. generic hover снова применяется к `.unavail`; +2. LQI `40/180` уходит в соседний диапазон либо меняет room gradient; +3. Text/Double снова получает ellipsis; +4. keyboard activation вызывает отдельный action path. + +### 16.4. Golden и pre-beta + +Golden matrix: desktop/mobile, light/dark, neutral/hover/focus/selected, +active/lock/unlock/alert/virtual/unavailable, Text, четыре Double, third section, +LQI low/mid/high, reduced motion, sizes 32/56/96 и цветной фон. + +Golden принимается только по Linux CI artifact согласно HP-QA-01. Перед beta +запускаются полный golden, smoke и performance наборы по release runbook; +Windows не является каноном полного HA harness из-за `fcntl`. + +## 17. Риски и откат + +### Риски + +- рост shell у длинных значений может чаще пересекаться с соседями; +- old explicit ripple size 3+ визуально заметно больше package default; +- dark package намеренно отличается от light по blur/shadow; +- новый focus/tab order увеличивает число клавиатурных остановок на больших + планах; +- ошибочная привязка общего `lqiColor()` изменит комнатную заливку; +- новый DOM может изменить snapshot/golden fingerprint всех поверхностей. + +### Митигации + +- центр icon core и saved coordinates неизменны; +- explicit settings не переписываются скрыто; +- marker-only LQI helper отделён и покрыт regression test; +- интерактивность добавляется только существующим actionable surfaces; +- один renderer и одна token table исключают drift; +- targeted golden содержит контрастные backgrounds и dense layout. + +### Откат + +Откат выполняется одним revert implementation commit. Миграции данных нет, +поэтому rollback возвращает старый renderer без преобразования config. ТЗ и +архив остаются диагностическим контрактом. + +## 18. Release-артефакты + +В том же user-visible implementation commit обновить: + +- `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md` со ссылкой на #179; +- `docs/USER-GUIDE.md` и `docs/USER-GUIDE.ru.md`: состояния, Text/Double, + LQI, keyboard и static mode; +- `docs/ARCHITECTURE.md`: общий presentation/face pipeline и theme context; +- `docs/TESTING.md`: новый smoke, mutation и golden matrix; +- ru/en i18n для доступных state/LQI descriptions и изменённого static hint; +- screenshot/golden fingerprints и review manifest по HP-QA-01. + +Release note: единая новая визуальная система устройств на светлой и тёмной +теме, полные значения, читаемый LQI и клавиатурное управление. Поставка сначала +в beta; stable — только после release gates. + +## 19. План реализации + +1. Зафиксировать package tokens и pure projection facts unit-тестами. +2. Расширить presentation lock/LQI/theme facts без изменения semantic/action + resolvers. +3. Перевести `renderDeviceFace()` на shell/core и общие Text/Double sections. +4. Реализовать package states, focus, unavailable и light/dark CSS. +5. Обновить pulse timings/defaults/reduced motion. +6. Подключить keyboard/hit area/accessibility к интерактивным surfaces. +7. Обновить preview/static parity, settings default и i18n/docs. +8. Добавить unit, mutation, targeted smoke и golden fixtures. +9. Прогнать typecheck, unit, build и named smokes; отправить в S7. +10. Перед beta выполнить полный golden/smoke/performance gate в Linux CI. + +## 20. Принятые технические предположения + +1. Package SVGs — visual reference; runtime DOM/SVG строится из тех же ratios и + tokens, потому что MDI glyph, HA text и theme динамические. +2. `Light/Zigbee LQI Low.svg` содержит ошибочный amber; red из textual spec и + решения владельца нормативен. +3. Package не задаёт minimum font size для бесконечно длинного значения: + используется floor `0.25 × marker size`, затем расширяется shell. +4. Generic physical `open` сохраняет текущую отдельную semantic-проекцию; + специальные black/amber Lock/Unlock применяются только к lock domain. +5. `static_icon` остаётся нереактивным, но default plate становится + theme-aware, потому что package задаёт разные Light/Dark defaults. +6. Отсутствующий pulse color сохраняет существующий validated live-light RGB + раньше semantic fallback; это единственный package-compatible способ не + потерять текущий пользовательский light-color effect. +7. Marker LQI меняет только цвет подписи; room LQI fill остаётся непрерывным. +8. Preview и static card показывают новый дизайн, но не получают ложную + интерактивность либо tab stop. diff --git a/docs/specs/README.md b/docs/specs/README.md index 1848ca3b..bff7dee9 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -1,6 +1,6 @@ # Спецификации задач -Актуально на 2026-08-17. +Актуально на 2026-08-19. GitHub Issues и GitHub Projects (v2) остаются единственным каноническим backlog проекта. Этот каталог содержит развёрнутые ТЗ: каждое ТЗ ссылается на issue, а issue — на соответствующий файл. Статус, приоритет и факт завершения меняются только в GitHub. @@ -53,6 +53,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным | [#166](https://github.com/Matysh/houseplan-card/issues/166) Солнечные лучи зеркально учитывают направление севера | [166-sun-north-rotation.md](166-sun-north-rotation.md) | | [#167](https://github.com/Matysh/houseplan-card/issues/167) Экспорт «только планировка» | [167-plan-only-export.md](167-plan-only-export.md) | | [#170](https://github.com/Matysh/houseplan-card/issues/170) HA-устройство не привязывается к комнате без HA-зоны | [170-room-without-area.md](170-room-without-area.md) | +| [#179](https://github.com/Matysh/houseplan-card/issues/179) Новый визуальный язык маркеров устройств | [179-device-icons-redesign.md](179-device-icons-redesign.md) | | [#205](https://github.com/Matysh/houseplan-card/issues/205) Продолжение следа после короткой остановки пылесоса | [205-vacuum-trail-resume-grace.md](205-vacuum-trail-resume-grace.md) | ## P2