diff --git a/assets/furniture/houseplan-0.3.0/LICENSE.md b/assets/furniture/houseplan-0.3.0/LICENSE.md new file mode 100644 index 00000000..c02b0212 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Sergey Matyushin (Matysh) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/furniture/houseplan-0.3.0/README.md b/assets/furniture/houseplan-0.3.0/README.md new file mode 100644 index 00000000..cc0d025d --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/README.md @@ -0,0 +1,18 @@ +# House Plan furniture pack 0.3.0 + +Canonical source artwork for the built-in House Plan furniture library. + +- `svg/menu/`: 33 front-view category illustrations used only by the lazy + editor bundle. +- `svg/plan/`: 44 top-view drawings used on the plan. +- `pack.json`: stable ids, category links, default dimensions and names. The + filename deliberately avoids the `*manifest.json` suffix reserved by HACS. + +The original author, Sergey Matyushin (`Matysh`), granted House Plan permission +to use, modify and distribute all 77 SVG files under the repository MIT +License without separate UI attribution in +[issue #159](https://github.com/Matysh/houseplan-card/issues/159#issuecomment-5454085168). + +The editable source is linked from `pack.json`. Generated TypeScript must +not be edited by hand; run `npm run furniture:generate` after changing this +directory. diff --git a/assets/furniture/houseplan-0.3.0/pack.json b/assets/furniture/houseplan-0.3.0/pack.json new file mode 100644 index 00000000..2ac55071 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/pack.json @@ -0,0 +1,818 @@ +{ + "schema_version": 1, + "view_schema": 2, + "pack_id": "houseplan", + "pack_version": "0.3.0", + "title_ru": "Набор мебели: меню и план", + "title_en": "Furniture pack: menu and plan", + "author": "Sergey Matyushin (Matysh)", + "license": "MIT", + "source_url": "https://www.figma.com/design/cpGN8MhJGydwVOUzZv8dia/House-plan?node-id=271-95", + "menu_icons": [ + { + "id": "air_conditioner", + "name_ru": "Кондиционер", + "name_en": "Air conditioner", + "group": "appliance", + "file": "svg/menu/air_conditioner.svg" + }, + { + "id": "armchair", + "name_ru": "Кресла", + "name_en": "Armchairs", + "group": "furniture", + "file": "svg/menu/armchair.svg" + }, + { + "id": "bathtub", + "name_ru": "Ванны", + "name_en": "Bathtubs", + "group": "sanitary", + "file": "svg/menu/bathtub.svg" + }, + { + "id": "bed", + "name_ru": "Кровати", + "name_en": "Beds", + "group": "furniture", + "file": "svg/menu/bed.svg" + }, + { + "id": "bidet", + "name_ru": "Биде", + "name_en": "Bidets", + "group": "sanitary", + "file": "svg/menu/bidet.svg" + }, + { + "id": "boiler", + "name_ru": "Бойлер", + "name_en": "Water heater", + "group": "appliance", + "file": "svg/menu/boiler.svg" + }, + { + "id": "chair", + "name_ru": "Стулья", + "name_en": "Chairs", + "group": "furniture", + "file": "svg/menu/chair.svg" + }, + { + "id": "coffee_table", + "name_ru": "Журнальные столы", + "name_en": "Coffee tables", + "group": "furniture", + "file": "svg/menu/coffee_table.svg" + }, + { + "id": "computer", + "name_ru": "Компьютер", + "name_en": "Computer", + "group": "appliance", + "file": "svg/menu/computer.svg" + }, + { + "id": "cooktop", + "name_ru": "Варочные панели", + "name_en": "Cooktops", + "group": "appliance", + "file": "svg/menu/cooktop.svg" + }, + { + "id": "dining_table", + "name_ru": "Обеденные столы", + "name_en": "Dining tables", + "group": "furniture", + "file": "svg/menu/dining_table.svg" + }, + { + "id": "dishwasher", + "name_ru": "Посудомоечная машина", + "name_en": "Dishwasher", + "group": "appliance", + "file": "svg/menu/dishwasher.svg" + }, + { + "id": "dryer", + "name_ru": "Сушильная машина", + "name_en": "Dryer", + "group": "appliance", + "file": "svg/menu/dryer.svg" + }, + { + "id": "exercise", + "name_ru": "Тренажёр", + "name_en": "Exercise equipment", + "group": "other", + "file": "svg/menu/exercise.svg" + }, + { + "id": "fireplace", + "name_ru": "Камин", + "name_en": "Fireplace", + "group": "other", + "file": "svg/menu/fireplace.svg" + }, + { + "id": "fridge", + "name_ru": "Холодильник", + "name_en": "Refrigerator", + "group": "appliance", + "file": "svg/menu/fridge.svg" + }, + { + "id": "hood", + "name_ru": "Вытяжка", + "name_en": "Range hood", + "group": "appliance", + "file": "svg/menu/hood.svg" + }, + { + "id": "kitchen_cabinet", + "name_ru": "Кухонные шкафы", + "name_en": "Kitchen cabinets", + "group": "furniture", + "file": "svg/menu/kitchen_cabinet.svg" + }, + { + "id": "kitchen_sink", + "name_ru": "Кухонные мойки", + "name_en": "Kitchen sinks", + "group": "sanitary", + "file": "svg/menu/kitchen_sink.svg" + }, + { + "id": "nightstand", + "name_ru": "Тумбы", + "name_en": "Cabinets", + "group": "furniture", + "file": "svg/menu/nightstand.svg" + }, + { + "id": "oven", + "name_ru": "Духовка", + "name_en": "Oven", + "group": "appliance", + "file": "svg/menu/oven.svg" + }, + { + "id": "plant", + "name_ru": "Растение", + "name_en": "Plant", + "group": "other", + "file": "svg/menu/plant.svg" + }, + { + "id": "rug", + "name_ru": "Ковер", + "name_en": "Rug", + "group": "other", + "file": "svg/menu/rug.svg" + }, + { + "id": "shelving", + "name_ru": "Стеллажи", + "name_en": "Shelving", + "group": "furniture", + "file": "svg/menu/shelving.svg" + }, + { + "id": "shower", + "name_ru": "Душ", + "name_en": "Shower", + "group": "sanitary", + "file": "svg/menu/shower.svg" + }, + { + "id": "sink", + "name_ru": "Раковина", + "name_en": "Sink", + "group": "sanitary", + "file": "svg/menu/sink.svg" + }, + { + "id": "sofa", + "name_ru": "Диваны", + "name_en": "Sofas", + "group": "furniture", + "file": "svg/menu/sofa.svg" + }, + { + "id": "stairs", + "name_ru": "Лестница", + "name_en": "Stairs", + "group": "other", + "file": "svg/menu/stairs.svg" + }, + { + "id": "toilet", + "name_ru": "Унитазы", + "name_en": "Toilets", + "group": "sanitary", + "file": "svg/menu/toilet.svg" + }, + { + "id": "tv", + "name_ru": "Телевизоры", + "name_en": "Televisions", + "group": "appliance", + "file": "svg/menu/tv.svg" + }, + { + "id": "wardrobe", + "name_ru": "Шкафы", + "name_en": "Wardrobes", + "group": "furniture", + "file": "svg/menu/wardrobe.svg" + }, + { + "id": "washer", + "name_ru": "Стиральная машина", + "name_en": "Washing machine", + "group": "appliance", + "file": "svg/menu/washer.svg" + }, + { + "id": "work_table", + "name_ru": "Рабочие столы", + "name_en": "Desks", + "group": "furniture", + "file": "svg/menu/work_table.svg" + } + ], + "symbols": [ + { + "id": "coffee_table", + "operation": "replace", + "name_ru": "Журнальный стол, прямоугольный", + "name_en": "Rectangular coffee table", + "group": "furniture", + "width_cm": 120, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/coffee_table.svg", + "menu_icon": "coffee_table", + "notes": "Типовой размер; свободно стоящий предмет." + }, + { + "id": "coffee_table_round", + "operation": "add", + "name_ru": "Журнальный стол, круглый", + "name_en": "Round coffee table", + "group": "furniture", + "width_cm": 80, + "depth_cm": 80, + "back": "top", + "file": "svg/plan/coffee_table_round.svg", + "menu_icon": "coffee_table", + "notes": "Круглый свободно стоящий предмет." + }, + { + "id": "coffee_table_oval", + "operation": "add", + "name_ru": "Журнальный стол, овальный", + "name_en": "Oval coffee table", + "group": "furniture", + "width_cm": 120, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/coffee_table_oval.svg", + "menu_icon": "coffee_table", + "notes": "Свободно стоящий предмет." + }, + { + "id": "coffee_table_rounded", + "operation": "add", + "name_ru": "Журнальный стол, скруглённый", + "name_en": "Rounded coffee table", + "group": "furniture", + "width_cm": 120, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/coffee_table_rounded.svg", + "menu_icon": "coffee_table", + "notes": "Прямоугольная форма со скруглёнными углами." + }, + { + "id": "table_dining", + "operation": "replace", + "name_ru": "Обеденный стол, прямоугольный", + "name_en": "Rectangular dining table", + "group": "furniture", + "width_cm": 160, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/table_dining.svg", + "menu_icon": "dining_table", + "notes": "Типовой стол на 4–6 мест." + }, + { + "id": "table_round", + "operation": "replace", + "name_ru": "Обеденный стол, круглый", + "name_en": "Round dining table", + "group": "furniture", + "width_cm": 110, + "depth_cm": 110, + "back": "top", + "file": "svg/plan/table_round.svg", + "menu_icon": "dining_table", + "notes": "Круглый стол на 4 места." + }, + { + "id": "table_dining_oval", + "operation": "add", + "name_ru": "Обеденный стол, овальный", + "name_en": "Oval dining table", + "group": "furniture", + "width_cm": 180, + "depth_cm": 100, + "back": "top", + "file": "svg/plan/table_dining_oval.svg", + "menu_icon": "dining_table", + "notes": "Овальный стол на 6 мест." + }, + { + "id": "table_dining_rounded", + "operation": "add", + "name_ru": "Обеденный стол, скруглённый", + "name_en": "Rounded dining table", + "group": "furniture", + "width_cm": 160, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/table_dining_rounded.svg", + "menu_icon": "dining_table", + "notes": "Прямоугольная форма со скруглёнными углами." + }, + { + "id": "desk", + "operation": "replace", + "name_ru": "Рабочий стол, прямоугольный", + "name_en": "Rectangular desk", + "group": "furniture", + "width_cm": 140, + "depth_cm": 70, + "back": "top", + "file": "svg/plan/desk.svg", + "menu_icon": "work_table", + "notes": "BACK — длинная сторона у стены." + }, + { + "id": "desk_corner", + "operation": "add", + "name_ru": "Рабочий стол, угловой", + "name_en": "Corner desk", + "group": "furniture", + "width_cm": 160, + "depth_cm": 160, + "back": "top", + "file": "svg/plan/desk_corner.svg", + "menu_icon": "work_table", + "notes": "Угловой стол; основная задняя сторона направлена вверх." + }, + { + "id": "chair", + "operation": "replace", + "name_ru": "Стул", + "name_en": "Chair", + "group": "furniture", + "width_cm": 50, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/chair.svg", + "menu_icon": "chair", + "notes": "Спинка находится сверху." + }, + { + "id": "chair_bar", + "operation": "add", + "name_ru": "Барный стул", + "name_en": "Bar stool", + "group": "furniture", + "width_cm": 45, + "depth_cm": 48, + "back": "top", + "file": "svg/plan/chair_bar.svg", + "menu_icon": "chair", + "notes": "Спинка находится сверху." + }, + { + "id": "armchair", + "operation": "replace", + "name_ru": "Кресло мягкое", + "name_en": "Soft armchair", + "group": "furniture", + "width_cm": 90, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/armchair.svg", + "menu_icon": "armchair", + "notes": "Спинка находится сверху." + }, + { + "id": "armchair_office", + "operation": "add", + "name_ru": "Кресло офисное", + "name_en": "Office chair", + "group": "furniture", + "width_cm": 65, + "depth_cm": 65, + "back": "top", + "file": "svg/plan/armchair_office.svg", + "menu_icon": "armchair", + "notes": "Спинка находится сверху." + }, + { + "id": "sofa", + "operation": "replace", + "name_ru": "Диван двухместный", + "name_en": "Two-seat sofa", + "group": "furniture", + "width_cm": 180, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/sofa.svg", + "menu_icon": "sofa", + "notes": "Спинка находится сверху." + }, + { + "id": "sofa_three_seat", + "operation": "add", + "name_ru": "Диван трёхместный", + "name_en": "Three-seat sofa", + "group": "furniture", + "width_cm": 240, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/sofa_three_seat.svg", + "menu_icon": "sofa", + "notes": "Спинка находится сверху." + }, + { + "id": "sofa_corner_right", + "operation": "add", + "name_ru": "Угловой диван, правый", + "name_en": "Right sectional sofa", + "group": "furniture", + "width_cm": 260, + "depth_cm": 170, + "back": "top", + "file": "svg/plan/sofa_corner_right.svg", + "menu_icon": "sofa", + "notes": "Шезлонг справа при взгляде сверху; спинка сверху." + }, + { + "id": "bed_single", + "operation": "replace", + "name_ru": "Кровать односпальная", + "name_en": "Single bed", + "group": "furniture", + "width_cm": 90, + "depth_cm": 200, + "back": "top", + "file": "svg/plan/bed_single.svg", + "menu_icon": "bed", + "notes": "Узкая кровать с одной подушкой; изголовье сверху." + }, + { + "id": "bed_double", + "operation": "replace", + "name_ru": "Кровать двуспальная", + "name_en": "Double bed", + "group": "furniture", + "width_cm": 160, + "depth_cm": 200, + "back": "top", + "file": "svg/plan/bed_double.svg", + "menu_icon": "bed", + "notes": "Широкая кровать с двумя подушками; изголовье сверху." + }, + { + "id": "nightstand", + "operation": "replace", + "name_ru": "Тумба прикроватная", + "name_en": "Bedside table", + "group": "furniture", + "width_cm": 50, + "depth_cm": 40, + "back": "top", + "file": "svg/plan/nightstand.svg", + "menu_icon": "nightstand", + "notes": "Задняя сторона сверху." + }, + { + "id": "cabinet_tv", + "operation": "add", + "name_ru": "Тумба под телевизор", + "name_en": "TV cabinet", + "group": "furniture", + "width_cm": 140, + "depth_cm": 45, + "back": "top", + "file": "svg/plan/cabinet_tv.svg", + "menu_icon": "nightstand", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "cabinet_shoe", + "operation": "add", + "name_ru": "Тумба для обуви", + "name_en": "Shoe cabinet", + "group": "furniture", + "width_cm": 80, + "depth_cm": 35, + "back": "top", + "file": "svg/plan/cabinet_shoe.svg", + "menu_icon": "nightstand", + "notes": "Задняя сторона сверху." + }, + { + "id": "cabinet_sink", + "operation": "add", + "name_ru": "Тумба под раковину", + "name_en": "Sink cabinet", + "group": "furniture", + "width_cm": 80, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/cabinet_sink.svg", + "menu_icon": "nightstand", + "notes": "Задняя сторона сверху." + }, + { + "id": "bookshelf", + "operation": "replace", + "name_ru": "Шкаф книжный", + "name_en": "Bookcase", + "group": "furniture", + "width_cm": 100, + "depth_cm": 35, + "back": "top", + "file": "svg/plan/bookshelf.svg", + "menu_icon": "wardrobe", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "wall_unit", + "operation": "add", + "name_ru": "Шкаф-стенка", + "name_en": "Wall unit", + "group": "furniture", + "width_cm": 240, + "depth_cm": 45, + "back": "top", + "file": "svg/plan/wall_unit.svg", + "menu_icon": "wardrobe", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "wardrobe", + "operation": "replace", + "name_ru": "Шкаф гардеробный", + "name_en": "Wardrobe", + "group": "furniture", + "width_cm": 180, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/wardrobe.svg", + "menu_icon": "wardrobe", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "kitchen_floor", + "operation": "add", + "name_ru": "Кухонный напольный модуль", + "name_en": "Kitchen floor module", + "group": "furniture", + "width_cm": 60, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/kitchen_floor.svg", + "menu_icon": "kitchen_cabinet", + "notes": "Задняя сторона сверху." + }, + { + "id": "kitchen_floor_corner", + "operation": "add", + "name_ru": "Кухонный напольный угловой модуль", + "name_en": "Kitchen floor corner module", + "group": "furniture", + "width_cm": 90, + "depth_cm": 90, + "back": "top", + "file": "svg/plan/kitchen_floor_corner.svg", + "menu_icon": "kitchen_cabinet", + "notes": "Угловой модуль; основной BACK сверху." + }, + { + "id": "kitchen_wall", + "operation": "add", + "name_ru": "Кухонный навесной модуль", + "name_en": "Kitchen wall module", + "group": "furniture", + "width_cm": 60, + "depth_cm": 35, + "back": "top", + "file": "svg/plan/kitchen_wall.svg", + "menu_icon": "kitchen_cabinet", + "notes": "Задняя сторона сверху." + }, + { + "id": "kitchen_wall_corner", + "operation": "add", + "name_ru": "Кухонный навесной угловой модуль", + "name_en": "Kitchen wall corner module", + "group": "furniture", + "width_cm": 60, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/kitchen_wall_corner.svg", + "menu_icon": "kitchen_cabinet", + "notes": "Угловой модуль; основной BACK сверху." + }, + { + "id": "shelf_floor", + "operation": "add", + "name_ru": "Стеллаж напольный", + "name_en": "Floor shelving unit", + "group": "furniture", + "width_cm": 100, + "depth_cm": 35, + "back": "top", + "file": "svg/plan/shelf_floor.svg", + "menu_icon": "shelving", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "shelf_wall", + "operation": "add", + "name_ru": "Полка настенная", + "name_en": "Wall shelf", + "group": "furniture", + "width_cm": 100, + "depth_cm": 25, + "back": "top", + "file": "svg/plan/shelf_wall.svg", + "menu_icon": "shelving", + "notes": "Задняя длинная сторона сверху." + }, + { + "id": "cooktop_two", + "operation": "add", + "name_ru": "Варочная панель, 2 конфорки", + "name_en": "Two-burner cooktop", + "group": "appliance", + "width_cm": 30, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/cooktop_two.svg", + "menu_icon": "cooktop", + "notes": "Панель ориентирована управляющей стороной вниз." + }, + { + "id": "stove", + "operation": "replace", + "name_ru": "Варочная панель, 4 конфорки", + "name_en": "Four-burner cooktop", + "group": "appliance", + "width_cm": 60, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/stove.svg", + "menu_icon": "cooktop", + "notes": "Панель ориентирована управляющей стороной вниз." + }, + { + "id": "tv", + "operation": "replace", + "name_ru": "Телевизор на подставке", + "name_en": "TV on stand", + "group": "appliance", + "width_cm": 120, + "depth_cm": 28, + "back": "top", + "file": "svg/plan/tv.svg", + "menu_icon": "tv", + "notes": "Задняя сторона экрана сверху." + }, + { + "id": "tv_wall", + "operation": "add", + "name_ru": "Телевизор на кронштейне", + "name_en": "Wall-mounted TV", + "group": "appliance", + "width_cm": 120, + "depth_cm": 15, + "back": "top", + "file": "svg/plan/tv_wall.svg", + "menu_icon": "tv", + "notes": "Сторона крепления к стене сверху." + }, + { + "id": "toilet", + "operation": "replace", + "name_ru": "Унитаз напольный", + "name_en": "Floor-standing toilet", + "group": "sanitary", + "width_cm": 40, + "depth_cm": 70, + "back": "top", + "file": "svg/plan/toilet.svg", + "menu_icon": "toilet", + "notes": "Сторона подключения к стене сверху." + }, + { + "id": "toilet_built_in", + "operation": "add", + "name_ru": "Унитаз встроенный", + "name_en": "Built-in toilet", + "group": "sanitary", + "width_cm": 40, + "depth_cm": 55, + "back": "top", + "file": "svg/plan/toilet_built_in.svg", + "menu_icon": "toilet", + "notes": "Инсталляция находится сверху." + }, + { + "id": "bathtub", + "operation": "replace", + "name_ru": "Ванна прямоугольная", + "name_en": "Rectangular bathtub", + "group": "sanitary", + "width_cm": 170, + "depth_cm": 75, + "back": "top", + "file": "svg/plan/bathtub.svg", + "menu_icon": "bathtub", + "notes": "Длинная задняя сторона сверху." + }, + { + "id": "bathtub_corner", + "operation": "add", + "name_ru": "Ванна угловая", + "name_en": "Corner bathtub", + "group": "sanitary", + "width_cm": 140, + "depth_cm": 140, + "back": "top", + "file": "svg/plan/bathtub_corner.svg", + "menu_icon": "bathtub", + "notes": "Угловая ванна; основной BACK сверху." + }, + { + "id": "bidet", + "operation": "replace", + "name_ru": "Биде напольное", + "name_en": "Floor-standing bidet", + "group": "sanitary", + "width_cm": 40, + "depth_cm": 60, + "back": "top", + "file": "svg/plan/bidet.svg", + "menu_icon": "bidet", + "notes": "Сторона подключения к стене сверху." + }, + { + "id": "bidet_built_in", + "operation": "add", + "name_ru": "Биде встроенное", + "name_en": "Built-in bidet", + "group": "sanitary", + "width_cm": 40, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/bidet_built_in.svg", + "menu_icon": "bidet", + "notes": "Инсталляция находится сверху." + }, + { + "id": "kitchen_sink", + "operation": "replace", + "name_ru": "Кухонная мойка одинарная", + "name_en": "Single kitchen sink", + "group": "sanitary", + "width_cm": 60, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/kitchen_sink.svg", + "menu_icon": "kitchen_sink", + "notes": "Задняя сторона столешницы сверху." + }, + { + "id": "kitchen_sink_double", + "operation": "add", + "name_ru": "Кухонная мойка двойная", + "name_en": "Double kitchen sink", + "group": "sanitary", + "width_cm": 90, + "depth_cm": 50, + "back": "top", + "file": "svg/plan/kitchen_sink_double.svg", + "menu_icon": "kitchen_sink", + "notes": "Задняя сторона столешницы сверху." + } + ] +} diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/air_conditioner.svg b/assets/furniture/houseplan-0.3.0/svg/menu/air_conditioner.svg new file mode 100644 index 00000000..3b03c1e9 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/air_conditioner.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/armchair.svg b/assets/furniture/houseplan-0.3.0/svg/menu/armchair.svg new file mode 100644 index 00000000..4326c7c7 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/armchair.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/bathtub.svg b/assets/furniture/houseplan-0.3.0/svg/menu/bathtub.svg new file mode 100644 index 00000000..89707558 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/bathtub.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/bed.svg b/assets/furniture/houseplan-0.3.0/svg/menu/bed.svg new file mode 100644 index 00000000..3dfcdeb2 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/bed.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/bidet.svg b/assets/furniture/houseplan-0.3.0/svg/menu/bidet.svg new file mode 100644 index 00000000..96b19560 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/bidet.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/boiler.svg b/assets/furniture/houseplan-0.3.0/svg/menu/boiler.svg new file mode 100644 index 00000000..867c62bd --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/boiler.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/chair.svg b/assets/furniture/houseplan-0.3.0/svg/menu/chair.svg new file mode 100644 index 00000000..d0fe866b --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/chair.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/coffee_table.svg b/assets/furniture/houseplan-0.3.0/svg/menu/coffee_table.svg new file mode 100644 index 00000000..61712004 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/coffee_table.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/computer.svg b/assets/furniture/houseplan-0.3.0/svg/menu/computer.svg new file mode 100644 index 00000000..3a87cf53 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/computer.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/cooktop.svg b/assets/furniture/houseplan-0.3.0/svg/menu/cooktop.svg new file mode 100644 index 00000000..3396dcb3 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/cooktop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/dining_table.svg b/assets/furniture/houseplan-0.3.0/svg/menu/dining_table.svg new file mode 100644 index 00000000..18c7044c --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/dining_table.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/dishwasher.svg b/assets/furniture/houseplan-0.3.0/svg/menu/dishwasher.svg new file mode 100644 index 00000000..001a2f4c --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/dishwasher.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/dryer.svg b/assets/furniture/houseplan-0.3.0/svg/menu/dryer.svg new file mode 100644 index 00000000..1c6035be --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/dryer.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/exercise.svg b/assets/furniture/houseplan-0.3.0/svg/menu/exercise.svg new file mode 100644 index 00000000..4f251765 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/exercise.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/fireplace.svg b/assets/furniture/houseplan-0.3.0/svg/menu/fireplace.svg new file mode 100644 index 00000000..ef19e073 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/fireplace.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/fridge.svg b/assets/furniture/houseplan-0.3.0/svg/menu/fridge.svg new file mode 100644 index 00000000..fd77f06a --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/fridge.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/hood.svg b/assets/furniture/houseplan-0.3.0/svg/menu/hood.svg new file mode 100644 index 00000000..7608b32f --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/hood.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_cabinet.svg b/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_cabinet.svg new file mode 100644 index 00000000..086a37a4 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_cabinet.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_sink.svg b/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_sink.svg new file mode 100644 index 00000000..209369a3 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/kitchen_sink.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/nightstand.svg b/assets/furniture/houseplan-0.3.0/svg/menu/nightstand.svg new file mode 100644 index 00000000..5735a814 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/nightstand.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/oven.svg b/assets/furniture/houseplan-0.3.0/svg/menu/oven.svg new file mode 100644 index 00000000..0440a008 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/oven.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/plant.svg b/assets/furniture/houseplan-0.3.0/svg/menu/plant.svg new file mode 100644 index 00000000..5b118934 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/plant.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/rug.svg b/assets/furniture/houseplan-0.3.0/svg/menu/rug.svg new file mode 100644 index 00000000..7f90443e --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/rug.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/shelving.svg b/assets/furniture/houseplan-0.3.0/svg/menu/shelving.svg new file mode 100644 index 00000000..540c7163 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/shelving.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/shower.svg b/assets/furniture/houseplan-0.3.0/svg/menu/shower.svg new file mode 100644 index 00000000..343fa7e2 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/shower.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/sink.svg b/assets/furniture/houseplan-0.3.0/svg/menu/sink.svg new file mode 100644 index 00000000..b7f286f3 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/sink.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/sofa.svg b/assets/furniture/houseplan-0.3.0/svg/menu/sofa.svg new file mode 100644 index 00000000..7309f4fe --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/sofa.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/stairs.svg b/assets/furniture/houseplan-0.3.0/svg/menu/stairs.svg new file mode 100644 index 00000000..e18cf556 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/stairs.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/toilet.svg b/assets/furniture/houseplan-0.3.0/svg/menu/toilet.svg new file mode 100644 index 00000000..33016688 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/toilet.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/tv.svg b/assets/furniture/houseplan-0.3.0/svg/menu/tv.svg new file mode 100644 index 00000000..04036687 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/tv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/wardrobe.svg b/assets/furniture/houseplan-0.3.0/svg/menu/wardrobe.svg new file mode 100644 index 00000000..fb0e4d2f --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/wardrobe.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/washer.svg b/assets/furniture/houseplan-0.3.0/svg/menu/washer.svg new file mode 100644 index 00000000..0d8f96a0 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/washer.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/menu/work_table.svg b/assets/furniture/houseplan-0.3.0/svg/menu/work_table.svg new file mode 100644 index 00000000..2ca3d9f5 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/menu/work_table.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/armchair.svg b/assets/furniture/houseplan-0.3.0/svg/plan/armchair.svg new file mode 100644 index 00000000..f306a64f --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/armchair.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/armchair_office.svg b/assets/furniture/houseplan-0.3.0/svg/plan/armchair_office.svg new file mode 100644 index 00000000..031204af --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/armchair_office.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bathtub.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bathtub.svg new file mode 100644 index 00000000..75a04cdf --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bathtub.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bathtub_corner.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bathtub_corner.svg new file mode 100644 index 00000000..a7c16e9d --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bathtub_corner.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bed_double.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bed_double.svg new file mode 100644 index 00000000..7983d180 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bed_double.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bed_single.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bed_single.svg new file mode 100644 index 00000000..9ebb9855 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bed_single.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bidet.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bidet.svg new file mode 100644 index 00000000..5ff45881 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bidet.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bidet_built_in.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bidet_built_in.svg new file mode 100644 index 00000000..cea7b718 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bidet_built_in.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/bookshelf.svg b/assets/furniture/houseplan-0.3.0/svg/plan/bookshelf.svg new file mode 100644 index 00000000..f91ee9b8 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/bookshelf.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_shoe.svg b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_shoe.svg new file mode 100644 index 00000000..98c273f9 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_shoe.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_sink.svg b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_sink.svg new file mode 100644 index 00000000..65576c87 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_sink.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_tv.svg b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_tv.svg new file mode 100644 index 00000000..dab8890c --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/cabinet_tv.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/chair.svg b/assets/furniture/houseplan-0.3.0/svg/plan/chair.svg new file mode 100644 index 00000000..54ab25f3 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/chair.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/chair_bar.svg b/assets/furniture/houseplan-0.3.0/svg/plan/chair_bar.svg new file mode 100644 index 00000000..e6542a5e --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/chair_bar.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table.svg b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table.svg new file mode 100644 index 00000000..031c386b --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_oval.svg b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_oval.svg new file mode 100644 index 00000000..871f5ef9 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_oval.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_round.svg b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_round.svg new file mode 100644 index 00000000..0da72878 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_round.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_rounded.svg b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_rounded.svg new file mode 100644 index 00000000..d3cace6e --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/coffee_table_rounded.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/cooktop_two.svg b/assets/furniture/houseplan-0.3.0/svg/plan/cooktop_two.svg new file mode 100644 index 00000000..a735cd07 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/cooktop_two.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/desk.svg b/assets/furniture/houseplan-0.3.0/svg/plan/desk.svg new file mode 100644 index 00000000..f98ed85b --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/desk.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/desk_corner.svg b/assets/furniture/houseplan-0.3.0/svg/plan/desk_corner.svg new file mode 100644 index 00000000..d073b8b7 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/desk_corner.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor.svg new file mode 100644 index 00000000..13cd102b --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor_corner.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor_corner.svg new file mode 100644 index 00000000..c02f8546 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_floor_corner.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink.svg new file mode 100644 index 00000000..c5b9d12a --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink_double.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink_double.svg new file mode 100644 index 00000000..abd7968f --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_sink_double.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall.svg new file mode 100644 index 00000000..1b842e33 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall_corner.svg b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall_corner.svg new file mode 100644 index 00000000..b12c6cc2 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/kitchen_wall_corner.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/nightstand.svg b/assets/furniture/houseplan-0.3.0/svg/plan/nightstand.svg new file mode 100644 index 00000000..1e8e4a28 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/nightstand.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/shelf_floor.svg b/assets/furniture/houseplan-0.3.0/svg/plan/shelf_floor.svg new file mode 100644 index 00000000..ce494fb8 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/shelf_floor.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/shelf_wall.svg b/assets/furniture/houseplan-0.3.0/svg/plan/shelf_wall.svg new file mode 100644 index 00000000..fd7ddb4a --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/shelf_wall.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/sofa.svg b/assets/furniture/houseplan-0.3.0/svg/plan/sofa.svg new file mode 100644 index 00000000..a15e91d6 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/sofa.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/sofa_corner_right.svg b/assets/furniture/houseplan-0.3.0/svg/plan/sofa_corner_right.svg new file mode 100644 index 00000000..504f317d --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/sofa_corner_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/sofa_three_seat.svg b/assets/furniture/houseplan-0.3.0/svg/plan/sofa_three_seat.svg new file mode 100644 index 00000000..6e38540c --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/sofa_three_seat.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/stove.svg b/assets/furniture/houseplan-0.3.0/svg/plan/stove.svg new file mode 100644 index 00000000..2ac27094 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/stove.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/table_dining.svg b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining.svg new file mode 100644 index 00000000..712f27f7 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_oval.svg b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_oval.svg new file mode 100644 index 00000000..7b7a4501 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_oval.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_rounded.svg b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_rounded.svg new file mode 100644 index 00000000..7a71c7c6 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/table_dining_rounded.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/table_round.svg b/assets/furniture/houseplan-0.3.0/svg/plan/table_round.svg new file mode 100644 index 00000000..e8abb146 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/table_round.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/toilet.svg b/assets/furniture/houseplan-0.3.0/svg/plan/toilet.svg new file mode 100644 index 00000000..2482cf4e --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/toilet.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/toilet_built_in.svg b/assets/furniture/houseplan-0.3.0/svg/plan/toilet_built_in.svg new file mode 100644 index 00000000..8cc3887e --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/toilet_built_in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/tv.svg b/assets/furniture/houseplan-0.3.0/svg/plan/tv.svg new file mode 100644 index 00000000..7a9559d9 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/tv.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/tv_wall.svg b/assets/furniture/houseplan-0.3.0/svg/plan/tv_wall.svg new file mode 100644 index 00000000..eb461071 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/tv_wall.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/wall_unit.svg b/assets/furniture/houseplan-0.3.0/svg/plan/wall_unit.svg new file mode 100644 index 00000000..cf460403 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/wall_unit.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/furniture/houseplan-0.3.0/svg/plan/wardrobe.svg b/assets/furniture/houseplan-0.3.0/svg/plan/wardrobe.svg new file mode 100644 index 00000000..eb172f32 --- /dev/null +++ b/assets/furniture/houseplan-0.3.0/svg/plan/wardrobe.svg @@ -0,0 +1,3 @@ + + + diff --git a/custom_components/houseplan/frontend/houseplan-assets.json b/custom_components/houseplan/frontend/houseplan-assets.json index 3a619579..f0fabfc7 100644 --- a/custom_components/houseplan/frontend/houseplan-assets.json +++ b/custom_components/houseplan/frontend/houseplan-assets.json @@ -1,111 +1,111 @@ { "schema": 1, - "fingerprint": "967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76", + "fingerprint": "9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", + "houseplan-assets/houseplan-card-VbP9hCtk.js", "houseplan-card.js" ], - "initialViewGzipBytes": 257212, + "initialViewGzipBytes": 270802, "lazyFiles": [ - "houseplan-assets/de-xcuT8cEm.js", - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/de-XuJl1rK9.js", + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyGzipBytes": 160415, + "lazyGzipBytes": 166322, "lazyEditorFiles": [ - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyEditorGzipBytes": 131835, + "lazyEditorGzipBytes": 137208, "lazyOnboardingFiles": [ - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyOnboardingGzipBytes": 7197, + "lazyOnboardingGzipBytes": 7201, "lazyLocaleFiles": [ - "houseplan-assets/de-xcuT8cEm.js" + "houseplan-assets/de-XuJl1rK9.js" ], - "lazyLocaleGzipBytes": 21934, + "lazyLocaleGzipBytes": 22467, "files": [ { - "path": "houseplan-assets/de-xcuT8cEm.js", - "sha256": "59061d1e31cc89b374431c04110733c3543141b08bd06fac01f5e4d4e2ce6942", - "rawBytes": 71667, - "gzipBytes": 21934, + "path": "houseplan-assets/de-XuJl1rK9.js", + "sha256": "4759ac8d21f6807c7c9de205475195ea5d762a2bbbc091cfb6b6edbd0f53892b", + "rawBytes": 73919, + "gzipBytes": 22467, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-BBdPV7Z-.js", - "sha256": "935764bc9cdfafa0a1a4004aa88cdc423db7d6d73707b52c92b0b67987cc1959", + "path": "houseplan-assets/editor-Bet9LJop.js", + "sha256": "4d2dca304dbccae9d843ae77db565ba5ffddb44dd7ab35a76e7ccb1252e859d7", "rawBytes": 3831, - "gzipBytes": 1582, + "gzipBytes": 1585, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-DJctcs0x.js", - "sha256": "485bee0aa697af7c9dac12278c5253f0a309677443730cd1cb4c08bd5eb4e633", - "rawBytes": 912403, - "gzipBytes": 256459, + "path": "houseplan-assets/houseplan-card-VbP9hCtk.js", + "sha256": "8ae379231c6c4ef97e1fcca55aa4a0a5e520d6dd5845dce741f98a13a0247bfc", + "rawBytes": 955358, + "gzipBytes": 270046, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-xcuT8cEm.js", - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js" + "houseplan-assets/de-XuJl1rK9.js", + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "sha256": "cb0b5e161592d1149866e12e6d5aa56e9731665d546404f5b65457e9d40b5129", - "rawBytes": 504959, - "gzipBytes": 129702, + "path": "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "sha256": "05dcd1aa05b8821af3dc98b31db7ebe4d3cfd8a8890f5af3ffe64c6fef650e14", + "rawBytes": 521651, + "gzipBytes": 135069, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "sha256": "ba6bd6549d7222fad7f4a82aeca50d875c5dd126e0d5fc863e15d43eb9953483", + "path": "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "sha256": "c28e4a2ceb830523010d588babc01188d6885083c4ba9f4fc824aef1f72a1cbd", "rawBytes": 26356, - "gzipBytes": 6646, + "gzipBytes": 6647, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/space-deletion-BIiYcPJz.js", - "sha256": "f6bc8f6227d8e9b3cf52cd88449a29557d68b43649a7768d7deca9fad5bfb525", - "rawBytes": 906, - "gzipBytes": 551, + "path": "houseplan-assets/space-deletion-Dh9IGz13.js", + "sha256": "f95f8f94d4f034fa79551e7754dd48a3ae5f7002a7a7818fcb2fe76abeb8564c", + "rawBytes": 911, + "gzipBytes": 554, "isEntry": false, "imports": [], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "21ba57c3ff76114b14dd9c1510559511521ad707a736014fe6d2ebf47bd46765", + "sha256": "b4bd5117eb453a81f681b85f69c4125e2227b9077c262eb605c32b22b235b91b", "rawBytes": 1089, - "gzipBytes": 753, + "gzipBytes": 756, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js" ], "dynamicImports": [] } diff --git a/custom_components/houseplan/frontend/houseplan-assets/de-xcuT8cEm.js b/custom_components/houseplan/frontend/houseplan-assets/de-XuJl1rK9.js similarity index 81% rename from custom_components/houseplan/frontend/houseplan-assets/de-xcuT8cEm.js rename to custom_components/houseplan/frontend/houseplan-assets/de-XuJl1rK9.js index 49ae9b85..dca9cb5c 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/de-xcuT8cEm.js +++ b/custom_components/houseplan/frontend/houseplan-assets/de-XuJl1rK9.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.pick_hint":"Wählen Sie ein Symbol aus und klicken Sie dann auf den Plan.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Sessel","furn.sym_coffee_table":"Couchtisch","furn.sym_table_dining":"Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_chair":"Stuhl","furn.sym_desk":"Schreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Herd","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Toilette","furn.sym_bathtub":"Badewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Küchenspüle","furn.sym_bidet":"Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht"};const n="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";export{e as dictionary,n as fingerprint}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht"};const n="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";export{e as dictionary,n as fingerprint}; diff --git a/dist/houseplan-assets/editor-BBdPV7Z-.js b/custom_components/houseplan/frontend/houseplan-assets/editor-Bet9LJop.js similarity index 93% rename from dist/houseplan-assets/editor-BBdPV7Z-.js rename to custom_components/houseplan/frontend/houseplan-assets/editor-Bet9LJop.js index cd509ec3..94ddc9ab 100644 --- a/dist/houseplan-assets/editor-BBdPV7Z-.js +++ b/custom_components/houseplan/frontend/houseplan-assets/editor-Bet9LJop.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-DJctcs0x.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,A=()=>D.createComment(""),O=e=>null===e||"object"!=typeof e&&"function"!=typeof e,z=Array.isArray,P="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${P}(?:([^\\s"'>=/]+)(${P}*=${P}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,j=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=j(1),W=j(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!z(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const Z=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=F:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class X{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=Z(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iz(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==G&&O(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){z(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(A()),this.O(A()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!O(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(A(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=2,pe=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:me}=se,ge=e=>e,fe=()=>document.createComment(""),ve=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(fe(),r),o=n.insertBefore(fe(),r);i=new me(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=ge(e).nextSibling;ge(n).insertBefore(e,r),e=t}}}return i},ye=(e,t,i=e)=>(e._$AI(t,i),e),be={},we=(e,t=be)=>e._$AH=t,ke=e=>{e._$AR(),e._$AA.remove()},xe=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},$e=pe(class extends _e{constructor(e){if(super(e),e.type!==ue)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=ye(r[d],o[p]),d++,p++;else if(a[u]===s[_])l[_]=ye(r[u],o[_]),u--,_--;else if(a[d]===s[_])l[_]=ye(r[d],o[_]),ve(e,l[_+1],r[d]),d++,_--;else if(a[u]===s[p])l[p]=ye(r[u],o[p]),ve(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=xe(s,p,_),h=xe(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=ve(e,r[d]);ye(t,o[p]),l[p]=t}else l[p]=ye(i,o[p]),ve(e,r[d],i),r[t]=null;p++}else ke(r[u]),u--;else ke(r[d]),d++;for(;p<=_;){const t=ve(e,l[_+1]);ye(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&ke(e)}return this.ut=s,we(e,l),U}}),Se=new WeakMap;let Me=0;class Re extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Me,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Se.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Se.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?Se.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=Se.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Se.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` - ${this.icon?B``:G} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,O=Array.isArray,P="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${P}(?:([^\\s"'>=/]+)(${P}*=${P}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,W=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),j=W(1),B=W(2),V=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!O(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const Z=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class X{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=Z(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iO(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==U&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new X(e)),t}k(e){O(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=U}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==V,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return V}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=2,pe=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:me}=se,fe=e=>e,ge=()=>document.createComment(""),ve=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(ge(),r),o=n.insertBefore(ge(),r);i=new me(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=fe(e).nextSibling;fe(n).insertBefore(e,r),e=t}}}return i},ye=(e,t,i=e)=>(e._$AI(t,i),e),be={},we=(e,t=be)=>e._$AH=t,ke=e=>{e._$AR(),e._$AA.remove()},xe=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},$e=pe(class extends _e{constructor(e){if(super(e),e.type!==ue)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=ye(r[d],o[p]),d++,p++;else if(a[u]===s[_])l[_]=ye(r[u],o[_]),u--,_--;else if(a[d]===s[_])l[_]=ye(r[d],o[_]),ve(e,l[_+1],r[d]),d++,_--;else if(a[u]===s[p])l[p]=ye(r[u],o[p]),ve(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=xe(s,p,_),h=xe(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=ve(e,r[d]);ye(t,o[p]),l[p]=t}else l[p]=ye(i,o[p]),ve(e,r[d],i),r[t]=null;p++}else ke(r[u]),u--;else ke(r[d]),d++;for(;p<=_;){const t=ve(e,l[_+1]);ye(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&ke(e)}return this.ut=s,we(e,l),V}}),Se=new WeakMap;let Me=0;class Re extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Me,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Se.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Se.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?Se.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=Se.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Se.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=j` + ${this.icon?j``:U} ${this.title} - `;return this._useHaDialog?B``;return this._useHaDialog?j`${e} -
`:B`
`:j`0||e===t?t:t-1}function Ye(e){for(var t,i,n=1,r=e.length,o=e[0]+"";nc^i?1:-1;for(a=(l=r.length)<(c=o.length)?l:c,s=0;so[s]^i?1:-1;return l==c?0:l>c^i?1:-1}function Xe(e,t,i,n){if(ei||e!==He(e))throw Error(Le+(n||"Argument")+("number"==typeof e?ei?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function Je(e){var t=e.c.length-1;return Ke(e.e/Be)==t&&e.c[t]%2!=0}function Qe(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function et(e,t,i){var n,r;if(t<0){for(r=i+".";++t;r+=i);e=r+e}else if(++t>(n=e.length)){for(r=i,t-=n;--t;r+=i);e+=r}else ty?u.c=u.e=null:e.e=10;l/=10,a++);return void(a>y?u.c=u.e=null:(u.e=a,u.c=[e]))}d=String(e)}else{if(!Ee.test(d=String(e)))return r(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Xe(t,2,$.length,"Base"),10==t&&S)return D(u=new M(e),_+u.e+1,m);if(d=String(e),c="number"==typeof e){if(0*e!=0)return r(u,d,c,t);if(u.s=1/e<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(qe+e)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,t),a=l=0,h=d.length;la){a=h;continue}}else if(!s&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){s=!0,l=-1,a=0;continue}return r(u,String(e),c,t)}c=!1,(a=(d=n(d,t,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(e>We||e!==He(e)))throw Error(qe+u.s*e);if((a=a-l-1)>y)u.c=u.e=null;else if(a=f)?Qe(l,s):et(l,s,"0");else if(o=(e=D(new M(e),t,i)).e,a=(l=Ye(e.c)).length,1==n||2==n&&(t<=o||o<=g)){for(;as),l=et(l,o,"0"),o+1>a){if(--t>0)for(l+=".";t--;l+="0");}else if((t+=o-a)>0)for(o+1==a&&(l+=".");t--;l+="0");return e.s<0&&r?"-"+l:l}function T(e,t){for(var i,n,r=1,o=new M(e[0]);r=10;r/=10,n++);return(i=n+i*Be-1)>y?e.c=e.e=null:i=10;a/=10,r++);if((o=t-r)<0)o+=Be,s=t,l=d[c=0],h=He(l/u[r-s-1]%10);else if((c=Ne((o+1)/Be))>=d.length){if(!n)break e;for(;d.length<=c;d.push(0));l=h=0,r=1,s=(o%=Be)-Be+1}else{for(l=a=d[c],r=1;a>=10;a/=10,r++);h=(s=(o%=Be)-Be+r)<0?0:He(l/u[r-s-1]%10)}if(n=n||t<0||null!=d[c+1]||(s<0?l:l%u[r-s-1]),n=i<4?(h||n)&&(0==i||i==(e.s<0?3:2)):h>5||5==h&&(4==i||n||6==i&&(o>0?s>0?l/u[r-s]:0:d[c-1])%10&1||i==(e.s<0?8:7)),t<1||!d[0])return d.length=0,n?(t-=e.e+1,d[0]=u[(Be-t%Be)%Be],e.e=-t||0):d[0]=e.e=0,e;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=u[Be-o],d[c]=s>0?He(l/u[r-s]%u[s])*a:0),n)for(;;){if(0==c){for(o=1,s=d[0];s>=10;s/=10,o++);for(s=d[0]+=a,a=1;s>=10;s/=10,a++);o!=a&&(e.e++,d[0]==je&&(d[0]=1));break}if(d[c]+=a,d[c]!=je)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}e.e>y?e.c=e.e=null:e.e=f?Qe(t,i):et(t,i,"0"),e.s<0?"-"+t:t)}return M.clone=e,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(e){var t,i;if(null!=e){if("object"!=typeof e)throw Error(Le+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(Xe(i=e[t],0,Ve,t),_=i),e.hasOwnProperty(t="ROUNDING_MODE")&&(Xe(i=e[t],0,8,t),m=i),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((i=e[t])&&i.pop?(Xe(i[0],-Ve,0,t),Xe(i[1],0,Ve,t),g=i[0],f=i[1]):(Xe(i,-Ve,Ve,t),g=-(f=i<0?-i:i))),e.hasOwnProperty(t="RANGE"))if((i=e[t])&&i.pop)Xe(i[0],-Ve,-1,t),Xe(i[1],1,Ve,t),v=i[0],y=i[1];else{if(Xe(i,-Ve,Ve,t),!i)throw Error(Le+t+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(e.hasOwnProperty(t="CRYPTO")){if((i=e[t])!==!!i)throw Error(Le+t+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Le+"crypto unavailable");b=i}else b=i}if(e.hasOwnProperty(t="MODULO_MODE")&&(Xe(i=e[t],0,9,t),w=i),e.hasOwnProperty(t="POW_PRECISION")&&(Xe(i=e[t],0,Ve,t),k=i),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(i=e[t]))throw Error(Le+t+" not an object: "+i);x=i}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(i=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Le+t+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!M.DEBUG)return!0;var t,i,n=e.c,r=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(n)){if((1===o||-1===o)&&r>=-Ve&&r<=Ve&&r===He(r)){if(0===n[0]){if(0===r&&1===n.length)return!0;break e}if((t=(r+1)%Be)<1&&(t+=Be),String(n[0]).length==t){for(t=0;t=je||i!==He(i))break e;if(0!==i)return!0}}}else if(null===n&&null===r&&(null===o||1===o||-1===o))return!0;throw Error(Le+"Invalid BigNumber: "+e)},M.maximum=M.max=function(){return T(arguments,-1)},M.minimum=M.min=function(){return T(arguments,1)},M.random=(o=9007199254740992,s=Math.random()*o&2097151?function(){return He(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,i,n,r,o,a=0,l=[],c=new M(p);if(null==e?e=_:Xe(e,0,Ve),r=Ne(e/Be),b)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(r*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),t[a]=i[0],t[a+1]=i[1]):(l.push(o%1e14),a+=2);a=r/2}else{if(!crypto.randomBytes)throw b=!1,Error(Le+"crypto unavailable");for(t=crypto.randomBytes(r*=7);a=9e15?crypto.randomBytes(7).copy(t,a):(l.push(o%1e14),a+=7);a=r/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==s[r+1]&&(s[r+1]=0),s[r+1]+=s[r]/i|0,s[r]%=i)}return s.reverse()}return function(n,r,o,s,a){var l,c,h,d,u,p,g,f,v=n.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,n=n.replace(".",""),p=(f=new M(r)).pow(n.length-v),k=d,f.c=t(et(Ye(p.c),p.e,"0"),10,o,e),f.e=f.c.length),h=d=(g=t(n,r,o,a?(l=$,e):(l=e,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=s,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])n=u?et(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,n="";v<=d;n+=l.charAt(g[v++]));n=et(n,h,l.charAt(0))}return n}}(),i=function(){function e(e,t,i){var n,r,o,s,a=0,l=e.length,c=t%Ge,h=t/Ge|0;for(e=e.slice();l--;)a=((r=c*(o=e[l]%Ge)+(n=h*o+(s=e[l]/Ge|0)*c)%Ge*Ge+a)/i|0)+(n/Ge|0)+h*s,e[l]=r%i;return a&&(e=[a].concat(e)),e}function t(e,t,i,n){var r,o;if(i!=n)o=i>n?1:-1;else for(r=o=0;rt[r]?1:-1;break}return o}function i(e,t,i,n){for(var r=0;i--;)e[i]-=r,r=e[i]1;e.splice(0,1));}return function(n,r,o,s,a){var l,c,h,d,u,p,_,m,g,f,v,y,b,w,k,x,$,S=n.s==r.s?1:-1,R=n.c,T=r.c;if(!(R&&R[0]&&T&&T[0]))return new M(n.s&&r.s&&(R?!T||R[0]!=T[0]:T)?R&&0==R[0]||!T?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=o+(c=n.e-r.e)+1,a||(a=je,c=Ke(n.e/Be)-Ke(r.e/Be),S=S/Be|0),h=0;T[h]==(R[h]||0);h++);if(T[h]>(R[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=R.length,x=T.length,h=0,S+=2,(u=He(a/(T[0]+1)))>1&&(T=e(T,u,a),R=e(R,u,a),x=T.length,w=R.length),b=x,v=(f=R.slice(0,x)).length;v=a/2&&k++;do{if(u=0,(l=t(T,f,x,v))<0){if(y=f[0],x!=v&&(y=y*a+(f[1]||0)),(u=He(y/k))>1)for(u>=a&&(u=a-1),_=(p=e(T,u,a)).length,v=f.length;1==t(p,f,_,v);)u--,i(p,x<_?$:T,_,a),_=p.length,l=1;else 0==u&&(l=u=1),_=(p=T.slice()).length;if(_=10;S/=10,h++);D(m,o+(m.e=h+c*Be-1)+1,s,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,r=function(e,t,i,n){var r,o=i?t:t.replace(d,"");if(h.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(e,t,i){return r="x"==(i=i.toLowerCase())?16:"b"==i?2:8,n&&n!=r?e:t}),n&&(r=n,o=o.replace(l,"$1").replace(c,"0.$1")),t!=o))return new M(o,r);if(M.DEBUG)throw Error(Le+"Not a"+(n?" base "+n:"")+" number: "+t);e.s=null}e.c=e.e=null},u.absoluteValue=u.abs=function(){var e=new M(this);return e.s<0&&(e.s=1),e},u.comparedTo=function(e,t){return Ze(this,new M(e,t))},u.decimalPlaces=u.dp=function(e,t){var i,n,r,o=this;if(null!=e)return Xe(e,0,Ve),null==t?t=m:Xe(t,0,8),D(new M(o),e+o.e+1,t);if(!(i=o.c))return null;if(n=((r=i.length-1)-Ke(this.e/Be))*Be,r=i[r])for(;r%10==0;r/=10,n--);return n<0&&(n=0),n},u.dividedBy=u.div=function(e,t){return i(this,new M(e,t),_,m)},u.dividedToIntegerBy=u.idiv=function(e,t){return i(this,new M(e,t),0,1)},u.exponentiatedBy=u.pow=function(e,t){var i,n,r,o,s,a,l,c,h=this;if((e=new M(e)).c&&!e.isInteger())throw Error(Le+"Exponent not an integer: "+A(e));if(null!=t&&(t=new M(t)),s=e.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!e.c||!e.c[0])return c=new M(Math.pow(+A(h),s?e.s*(2-Je(e)):+A(e))),t?c.mod(t):c;if(a=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new M(NaN);(n=!a&&h.isInteger()&&t.isInteger())&&(h=h.mod(t))}else{if(e.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||s&&h.c[1]>=24e7:h.c[0]<8e13||s&&h.c[0]<=9999975e7)))return o=h.s<0&&Je(e)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Ne(k/Be+2))}for(s?(i=new M(.5),a&&(e.s=1),l=Je(e)):l=(r=Math.abs(+A(e)))%2,c=new M(p);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):n&&(c=c.mod(t))}if(r){if(0===(r=He(r/2)))break;l=r%2}else if(D(e=e.times(i),e.e+1,1),e.e>14)l=Je(e);else{if(0===(r=+A(e)))break;l=r%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):n&&(h=h.mod(t))}return n?c:(a&&(c=p.div(c)),t?c.mod(t):o?D(c,k,m,void 0):c)},u.integerValue=function(e){var t=new M(this);return null==e?e=m:Xe(e,0,8),D(t,t.e+1,e)},u.isEqualTo=u.eq=function(e,t){return 0===Ze(this,new M(e,t))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(e,t){return Ze(this,new M(e,t))>0},u.isGreaterThanOrEqualTo=u.gte=function(e,t){return 1===(t=Ze(this,new M(e,t)))||0===t},u.isInteger=function(){return!!this.c&&Ke(this.e/Be)>this.c.length-2},u.isLessThan=u.lt=function(e,t){return Ze(this,new M(e,t))<0},u.isLessThanOrEqualTo=u.lte=function(e,t){return-1===(t=Ze(this,new M(e,t)))||0===t},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(e,t){var i,n,r,o,s=this,a=s.s;if(t=(e=new M(e,t)).s,!a||!t)return new M(NaN);if(a!=t)return e.s=-t,s.plus(e);var l=s.e/Be,c=e.e/Be,h=s.c,d=e.c;if(!l||!c){if(!h||!d)return h?(e.s=-t,e):new M(d?s:NaN);if(!h[0]||!d[0])return d[0]?(e.s=-t,e):new M(h[0]?s:3==m?-0:0)}if(l=Ke(l),c=Ke(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,r=h):(c=l,r=d),r.reverse(),t=a;t--;r.push(0));r.reverse()}else for(n=(o=(a=h.length)<(t=d.length))?a:t,a=t=0;t0)for(;t--;h[i++]=0);for(t=je-1;n>a;){if(h[--n]=0;){for(i=0,u=y[r]%g,p=y[r]/g|0,o=r+(s=l);o>r;)i=((c=u*(c=v[--s]%g)+(a=p*c+(h=v[s]/g|0)*u)%g*g+_[o]+i)/m|0)+(a/g|0)+p*h,_[o--]=c%m;_[o]=i}return i?++n:_.splice(0,1),C(e,_,n)},u.negated=function(){var e=new M(this);return e.s=-e.s||null,e},u.plus=function(e,t){var i,n=this,r=n.s;if(t=(e=new M(e,t)).s,!r||!t)return new M(NaN);if(r!=t)return e.s=-t,n.minus(e);var o=n.e/Be,s=e.e/Be,a=n.c,l=e.c;if(!o||!s){if(!a||!l)return new M(r/0);if(!a[0]||!l[0])return l[0]?e:new M(a[0]?n:0*r)}if(o=Ke(o),s=Ke(s),a=a.slice(),r=o-s){for(r>0?(s=o,i=l):(r=-r,i=a),i.reverse();r--;i.push(0));i.reverse()}for((r=a.length)-(t=l.length)<0&&(i=l,l=a,a=i,t=r),r=0;t;)r=(a[--t]=a[t]+l[t]+r)/je|0,a[t]=je===a[t]?0:a[t]%je;return r&&(a=[r].concat(a),++s),C(e,a,s)},u.precision=u.sd=function(e,t){var i,n,r,o=this;if(null!=e&&e!==!!e)return Xe(e,1,Ve),null==t?t=m:Xe(t,0,8),D(new M(o),e,t);if(!(i=o.c))return null;if(n=(r=i.length-1)*Be+1,r=i[r]){for(;r%10==0;r/=10,n--);for(r=i[0];r>=10;r/=10,n++);}return e&&o.e+1>n&&(n=o.e+1),n},u.shiftedBy=function(e){return Xe(e,-9007199254740991,We),this.times("1e"+e)},u.squareRoot=u.sqrt=function(){var e,t,n,r,o,s=this,a=s.c,l=s.s,c=s.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?s:1/0);if(0==(l=Math.sqrt(+A(s)))||l==1/0?(((t=Ye(a)).length+c)%2==0&&(t+="0"),l=Math.sqrt(+t),c=Ke((c+1)/2)-(c<0||c%2),n=new M(t=l==1/0?"5e"+c:(t=l.toExponential()).slice(0,t.indexOf("e")+1)+c)):n=new M(l+""),n.c[0])for((l=(c=n.e)+h)<3&&(l=0);;)if(o=n,n=d.times(o.plus(i(s,o,h,1))),Ye(o.c).slice(0,l)===(t=Ye(n.c)).slice(0,l)){if(n.e0&&_>0){for(o=_%a||a,h=p.substr(0,o);o<_;o+=a)h+=c+p.substr(o,a);l>0&&(h+=c+p.slice(o)),u&&(h="-"+h)}n=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+n+(i.suffix||"")},u.toFraction=function(e){var t,n,r,o,s,a,l,c,h,d,u,_,g=this,f=g.c;if(null!=e&&(!(l=new M(e)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Le+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+A(l));if(!f)return new M(g);for(t=new M(p),h=n=new M(p),r=c=new M(p),_=Ye(f),s=t.e=_.length-g.e-1,t.c[0]=Ue[(a=s%Be)<0?Be+a:a],e=!e||l.comparedTo(t)>0?s>0?t:h:l,a=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,t,0,1),1!=(o=n.plus(d.times(r))).comparedTo(e);)n=r,r=o,h=c.plus(d.times(o=h)),c=o,t=l.minus(d.times(o=t)),l=o;return o=i(e.minus(n),r,0,1),c=c.plus(o.times(h)),n=n.plus(o.times(r)),c.s=h.s=g.s,u=i(h,r,s*=2,m).minus(g).abs().comparedTo(i(c,n,s,m).minus(g).abs())<1?[h,r]:[c,n],y=a,u},u.toNumber=function(){return+A(this)},u.toPrecision=function(e,t){return null!=e&&Xe(e,1,Ve),R(this,e,t,2)},u.toString=function(e){var t,i=this,r=i.s,o=i.e;return null===o?r?(t="Infinity",r<0&&(t="-"+t)):t="NaN":(null==e?t=o<=g||o>=f?Qe(Ye(i.c),o):et(Ye(i.c),o,"0"):10===e&&S?t=et(Ye((i=D(new M(i),_+o+1,m)).c),i.e,"0"):(Xe(e,2,$.length,"Base"),t=n(et(Ye(i.c),o,"0"),10,e,r,!0)),r<0&&i.c[0]&&(t="-"+t)),t},u.valueOf=u.toJSON=function(){return A(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=t&&M.set(t),M}(),it=class{key;left=null;right=null;constructor(e){this.key=e}},nt=class extends it{constructor(e){super(e)}},rt=class{size=0;modificationCount=0;splayCount=0;splay(e){const t=this.root;if(null==t)return this.compare(e,e),-1;let i=null,n=null,r=null,o=null,s=t;const a=this.compare;let l;for(;;)if(l=a(s.key,e),l>0){let t=s.left;if(null==t)break;if(l=a(t.key,e),l>0&&(s.left=t.right,t.right=s,s=t,t=s.left,null==t))break;null==i?n=s:i.left=s,i=s,s=t}else{if(!(l<0))break;{let t=s.right;if(null==t)break;if(l=a(t.key,e),l<0&&(s.right=t.left,t.left=s,s=t,t=s.right,null==t))break;null==r?o=s:r.right=s,r=s,s=t}}return null!=r&&(r.right=s.left,s.left=o),null!=i&&(i.left=s.right,s.right=n),this.root!==s&&(this.root=s,this.splayCount++),l}splayMin(e){let t=e,i=t.left;for(;null!=i;){const e=i;t.left=e.right,e.right=t,t=e,i=t.left}return t}splayMax(e){let t=e,i=t.right;for(;null!=i;){const e=i;t.right=e.left,e.left=t,t=e,i=t.right}return t}_delete(e){if(null==this.root)return null;if(0!=this.splay(e))return null;let t=this.root;const i=t,n=t.left;if(this.size--,null==n)this.root=t.right;else{const e=t.right;t=this.splayMax(n),t.right=e,this.root=t}return this.modificationCount++,i}addNewRoot(e,t){this.size++,this.modificationCount++;const i=this.root;null!=i?(t<0?(e.left=i,e.right=i.right,i.right=null):(e.right=i,e.left=i.left,i.left=null),this.root=e):this.root=e}_first(){const e=this.root;return null==e?null:(this.root=this.splayMin(e),this.root)}_last(){const e=this.root;return null==e?null:(this.root=this.splayMax(e),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(e){return this.validKey(e)&&0==this.splay(e)}defaultCompare(){return(e,t)=>et?1:0}wrap(){return{getRoot:()=>this.root,setRoot:e=>{this.root=e},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:e=>{this.splayCount=e},splay:e=>this.splay(e),has:e=>this.has(e)}}},ot=class e extends rt{root=null;compare;validKey;constructor(e,t){super(),this.compare=e??this.defaultCompare(),this.validKey=t??(e=>null!=e&&null!=e)}delete(e){return!!this.validKey(e)&&null!=this._delete(e)}deleteAll(e){for(const t of e)this.delete(t)}forEach(e){const t=this[Symbol.iterator]();let i;for(;i=t.next(),!i.done;)e(i.value,i.value,this)}add(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new nt(e),t),this}addAndReturn(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new nt(e),t),this.root.key}addAll(e){for(const t of e)this.add(t)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)<0)return this.root.key;let t=this.root.left;if(null==t)return null;let i=t.right;for(;null!=i;)t=i,i=t.right;return t.key}firstAfter(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)>0)return this.root.key;let t=this.root.right;if(null==t)return null;let i=t.left;for(;null!=i;)t=i,i=t.left;return t.key}retainAll(t){const i=new e(this.compare,this.validKey),n=this.modificationCount;for(const e of t){if(n!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(e)&&0==this.splay(e)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(e){if(!this.validKey(e))return null;return 0!=this.splay(e)?null:this.root.key}intersection(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)&&i.add(e);return i}difference(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)||i.add(e);return i}union(e){const t=this.clone();return t.addAll(e),t}clone(){const t=new e(this.compare,this.validKey);return t.size=this.size,t.root=this.copyNode(this.root),t}copyNode(e){if(null==e)return null;const t=new nt(e.key);return function e(t,i){let n,r;do{if(n=t.left,r=t.right,null!=n){const t=new nt(n.key);i.left=t,e(n,t)}if(null!=r){const e=new nt(r.key);i.right=e,t=r,i=e}}while(null!=r)}(e,t),t}toSet(){return this.clone()}entries(){return new lt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new at(this.wrap())}[Symbol.toStringTag]="[object Set]"},st=class{tree;path=new Array;modificationCount=null;splayCount;constructor(e){this.tree=e,this.splayCount=e.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const e=this.path[this.path.length-1];return this.getValue(e)}rebuildPath(e){this.path.splice(0,this.path.length),this.tree.splay(e),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(e){for(;null!=e;)this.path.push(e),e=e.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let e=this.tree.getRoot();for(;null!=e;)this.path.push(e),e=e.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let e=this.path[this.path.length-1],t=e.right;if(null!=t){for(;null!=t;)this.path.push(t),t=t.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===e;)e=this.path.pop();return this.path.length>0}},at=class extends st{getValue(e){return e.key}},lt=class extends st{getValue(e){return[e.key,e.key]}},ct=e=>()=>e,ht=e=>{const t=e?(t,i)=>i.minus(t).abs().isLessThanOrEqualTo(e):ct(!1);return(e,i)=>t(e,i)?0:e.comparedTo(i)};function dt(e){const t=e?(t,i,n,r,o)=>t.exponentiatedBy(2).isLessThanOrEqualTo(r.minus(i).exponentiatedBy(2).plus(o.minus(n).exponentiatedBy(2)).times(e)):ct(!1);return(e,i,n)=>{const r=e.x,o=e.y,s=n.x,a=n.y,l=o.minus(a).times(i.x.minus(s)).minus(r.minus(s).times(i.y.minus(a)));return t(l,r,o,s,a)?0:l.comparedTo(0)}}var ut=e=>e,pt=e=>{if(e){const t=new ot(ht(e)),i=new ot(ht(e)),n=(e,t)=>t.addAndReturn(e),r=e=>({x:n(e.x,t),y:n(e.y,i)});return r({x:new tt(0),y:new tt(0)}),r}return ut},_t=e=>({set:e=>{mt=_t(e)},reset:()=>_t(e),compare:ht(e),snap:pt(e),orient:dt(e)}),mt=_t(),gt=(e,t)=>e.ll.x.isLessThanOrEqualTo(t.x)&&t.x.isLessThanOrEqualTo(e.ur.x)&&e.ll.y.isLessThanOrEqualTo(t.y)&&t.y.isLessThanOrEqualTo(e.ur.y),ft=(e,t)=>{if(t.ur.x.isLessThan(e.ll.x)||e.ur.x.isLessThan(t.ll.x)||t.ur.y.isLessThan(e.ll.y)||e.ur.y.isLessThan(t.ll.y))return null;const i=e.ll.x.isLessThan(t.ll.x)?t.ll.x:e.ll.x,n=e.ur.x.isLessThan(t.ur.x)?e.ur.x:t.ur.x;return{ll:{x:i,y:e.ll.y.isLessThan(t.ll.y)?t.ll.y:e.ll.y},ur:{x:n,y:e.ur.y.isLessThan(t.ur.y)?e.ur.y:t.ur.y}}},vt=(e,t)=>e.x.times(t.y).minus(e.y.times(t.x)),yt=(e,t)=>e.x.times(t.x).plus(e.y.times(t.y)),bt=e=>yt(e,e).sqrt(),wt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return vt(r,n).div(bt(r)).div(bt(n))},kt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return yt(r,n).div(bt(r)).div(bt(n))},xt=(e,t,i)=>t.y.isZero()?null:{x:e.x.plus(t.x.div(t.y).times(i.minus(e.y))),y:i},$t=(e,t,i)=>t.x.isZero()?null:{x:i,y:e.y.plus(t.y.div(t.x).times(i.minus(e.x)))},St=class e{point;isLeft;segment;otherSE;consumedBy;static compare(t,i){const n=e.comparePoints(t.point,i.point);return 0!==n?n:(t.point!==i.point&&t.link(i),t.isLeft!==i.isLeft?t.isLeft?1:-1:zt.compare(t.segment,i.segment))}static comparePoints(e,t){return e.x.isLessThan(t.x)?-1:e.x.isGreaterThan(t.x)?1:e.y.isLessThan(t.y)?-1:e.y.isGreaterThan(t.y)?1:0}constructor(e,t){void 0===e.events?e.events=[this]:e.events.push(this),this.point=e,this.isLeft=t}link(e){if(e.point===this.point)throw new Error("Tried to link already linked events");const t=e.point.events;for(let e=0,i=t.length;e{const n=i.otherSE;t.set(i,{sine:wt(this.point,e.point,n.point),cosine:kt(this.point,e.point,n.point)})};return(e,n)=>{t.has(e)||i(e),t.has(n)||i(n);const{sine:r,cosine:o}=t.get(e),{sine:s,cosine:a}=t.get(n);return r.isGreaterThanOrEqualTo(0)&&s.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:r.isLessThan(0)&&s.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:s.isLessThan(r)?-1:s.isGreaterThan(r)?1:0}}},Mt=class e{events;poly;_isExteriorRing;_enclosingRing;static factory(t){const i=[];for(let n=0,r=t.length;n0&&(e=i)}let t=e.segment.prevInResult(),i=t?t.prevInResult():null;for(;;){if(!t)return null;if(!i)return t.ringOut;if(i.ringOut!==t.ringOut)return i.ringOut?.enclosingRing()!==t.ringOut?t.ringOut:t.ringOut?.enclosingRing();t=i.prevInResult(),i=t?t.prevInResult():null}}},Rt=class{exteriorRing;interiorRings;constructor(e){this.exteriorRing=e,e.poly=this,this.interiorRings=[]}addInterior(e){this.interiorRings.push(e),e.poly=this}getGeom(){const e=this.exteriorRing.getGeom();if(null===e)return null;const t=[e];for(let e=0,i=this.interiorRings.length;e0?(this.tree.delete(t),i.push(e)):(this.segments.push(t),t.prev=n)}else{if(n&&r){const e=n.getIntersection(r);if(null!==e){if(!n.isAnEndpoint(e)){const t=this._splitSafely(n,e);for(let e=0,n=t.length;e0)return-1;const n=t.comparePoint(e.rightSE.point);return 0!==n?n:-1}if(i.isGreaterThan(n)){if(s.isLessThan(a)&&s.isLessThan(c))return-1;if(s.isGreaterThan(a)&&s.isGreaterThan(c))return 1;const i=t.comparePoint(e.leftSE.point);if(0!==i)return i;const n=e.comparePoint(t.rightSE.point);return n<0?1:n>0?-1:1}if(s.isLessThan(a))return-1;if(s.isGreaterThan(a))return 1;if(r.isLessThan(o)){const i=t.comparePoint(e.rightSE.point);if(0!==i)return i}if(r.isGreaterThan(o)){const i=e.comparePoint(t.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!r.eq(o)){const e=l.minus(s),t=r.minus(i),h=c.minus(a),d=o.minus(n);if(e.isGreaterThan(t)&&h.isLessThan(d))return 1;if(e.isLessThan(t)&&h.isGreaterThan(d))return-1}return r.isGreaterThan(o)?1:r.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:e.idt.id?1:0}constructor(e,t,i,n){this.id=++Ot,this.leftSE=e,e.segment=this,e.otherSE=t,this.rightSE=t,t.segment=this,t.otherSE=e,this.rings=i,this.windings=n}static fromRing(t,i,n){let r,o,s;const a=St.comparePoints(t,i);if(a<0)r=t,o=i,s=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${t.x}, ${t.y}]`);r=i,o=t,s=-1}const l=new St(r,!0),c=new St(o,!1);return new e(l,c,[n],[s])}replaceRightSE(e){this.rightSE=e,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const e=this.leftSE.point.y,t=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:e.isLessThan(t)?e:t},ur:{x:this.rightSE.point.x,y:e.isGreaterThan(t)?e:t}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(e){return e.x.eq(this.leftSE.point.x)&&e.y.eq(this.leftSE.point.y)||e.x.eq(this.rightSE.point.x)&&e.y.eq(this.rightSE.point.y)}comparePoint(e){return mt.orient(this.leftSE.point,e,this.rightSE.point)}getIntersection(e){const t=this.bbox(),i=e.bbox(),n=ft(t,i);if(null===n)return null;const r=this.leftSE.point,o=this.rightSE.point,s=e.leftSE.point,a=e.rightSE.point,l=gt(t,s)&&0===this.comparePoint(s),c=gt(i,r)&&0===e.comparePoint(r),h=gt(t,a)&&0===this.comparePoint(a),d=gt(i,o)&&0===e.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&r.x.eq(a.x)&&r.y.eq(a.y)?null:r;if(l)return d&&o.x.eq(s.x)&&o.y.eq(s.y)?null:s;if(d&&h)return null;if(d)return o;if(h)return a;const u=((e,t,i,n)=>{if(t.x.isZero())return $t(i,n,e.x);if(n.x.isZero())return $t(e,t,i.x);if(t.y.isZero())return xt(i,n,e.y);if(n.y.isZero())return xt(e,t,i.y);const r=vt(t,n);if(r.isZero())return null;const o={x:i.x.minus(e.x),y:i.y.minus(e.y)},s=vt(o,t).div(r),a=vt(o,n).div(r),l=e.x.plus(a.times(t.x)),c=i.x.plus(s.times(n.x)),h=e.y.plus(a.times(t.y)),d=i.y.plus(s.times(n.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(r,this.vector(),s,e.vector());return null===u?null:gt(n,u)?mt.snap(u):null}split(t){const i=[],n=void 0!==t.events,r=new St(t,!0),o=new St(t,!1),s=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(r);const a=new e(r,s,this.rings.slice(),this.windings.slice());return St.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),St.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),n&&(r.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const e=this.rightSE;this.rightSE=this.leftSE,this.leftSE=e,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let e=0,t=this.windings.length;e0){const e=i;i=n,n=e}if(i.prev===n){const e=i;i=n,n=e}for(let e=0,t=n.rings.length;e1===e.length&&e[0].isSubject;this._isInResult=i(e)!==i(t);break}}return this._isInResult}},Pt=class{poly;isExterior;segments;bbox;constructor(e,t,i){if(!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=t,this.isExterior=i,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const n=mt.snap({x:new tt(e[0][0]),y:new tt(e[0][1])});this.bbox={ll:{x:n.x,y:n.y},ur:{x:n.x,y:n.y}};let r=n;for(let t=1,i=e.length;tAt.run("union",e,t),Nt=(e,...t)=>At.run("intersection",e,t),Ht=(e,...t)=>At.run("difference",e,t);mt.set;const Lt=/^#[0-9a-fA-F]{6}$/;function qt(e,t){return"string"==typeof e&&Lt.test(e)?e:t}const jt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",Bt=new RegExp(`^rgb\\(${jt}, ${jt}, ${jt}\\)$`);function Wt(e){if(!Array.isArray(e)||e.length<3)return null;const t=e.slice(0,3);if(!t.every(e=>"number"==typeof e&&Number.isFinite(e)))return null;const[i,n,r]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${n}, ${r})`}function Ut(e){const t=Math.max(0,Math.min(120,(e-40)/140*120));return`hsl(${Math.round(t)}, 85%, 55%)`}function Gt(e,t){if(!(Number.isFinite(e)&&t>0))return e;const i=Math.round(e/t)*t;return Math.abs(i-e)<=1e-9*t?e:i}function Vt(e,t,i,n=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const r=t[0]-e[0],o=t[1]-e[1];if(Math.abs(r)+Math.abs(o)<=1e-12)return[e[0],e[1]];const s=Math.PI/4;let a=Math.atan2(o,r);a<0&&(a+=2*Math.PI);const l=Math.floor(a/s+.5)%8,[c,h]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=c*c+h*h;let u=Math.max(0,Math.round((r*c+o*h)/(i*d)));if(Number.isFinite(n)&&n>=0){const t=(e,t)=>{if(!t)return 1/0;const r=t>0?n-e:e+n;return Math.max(0,Math.floor((r+1e-9*i)/i))};u=Math.min(u,t(e[0],c),t(e[1],h))}return[e[0]+c*u*i,e[1]+h*u*i]}function Kt(e,t){if(t){const t=e/2.54;let i=Math.floor(t/12),n=Math.round(t-12*i);return 12===n&&(i+=1,n=0),`${i}′ ${n}″`}return`${(e/100).toFixed(2)} m`}function Yt(e,t,i=1){const n=e[0].toFixed(i),r=e[1].toFixed(i),o=t[0].toFixed(i),s=t[1].toFixed(i),a=n=3?e.poly:e&&null!=e.x&&null!=e.y&&null!=e.w&&null!=e.h?[[e.x,e.y],[e.x+e.w,e.y],[e.x+e.w,e.y+e.h],[e.x,e.y+e.h]]:null}function Xt(e){const t=[];for(const i of e||[])i?.poly?.length>=3?t.push({poly:i.poly.map(e=>e.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&t.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return t}function Jt(e){const t=[],i=new Set;for(const n of e||[]){const e=Zt(n);if(e)for(let n=0;n=90?e-=180:e<-90&&(e+=180),n.step&&n.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(n.length||0,0)/2,i/2);let a=Math.round(u*i/n.step)*n.step;Math.abs(u*i-i/2)<=n.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;r={x:t+l*c,y:s+l*h,angle:e}}else r={x:p[0],y:p[1],angle:e}}}return r}function ei(e,t,i){let n=null,r=1/0;for(let o=0;o=r)continue;r=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;n=[s+g*h,a+g*d]}return n}function ti(e,t,i,n,r,o=1){const s=t*Math.PI/180,a=[Math.cos(s),Math.sin(s)];let l=null,c=o;for(const t of Jt(n)){const i=[[t[0],t[1]],[t[2],t[3]]],n=t=>Math.abs(a[0]*(t[1]-e[1])-a[1]*(t[0]-e[0]));if(n(i[0])>o||n(i[1])>o)continue;const r=(i[0][0]-e[0])*a[0]+(i[0][1]-e[1])*a[1],s=(i[1][0]-e[0])*a[0]+(i[1][1]-e[1])*a[1],h=Math.min(r,s),d=Math.max(r,s),u=h>0?h:d<0?-d:0;u[e[0]+a[0]*t,e[1]+a[1]*t],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:p,sideB:_,midA:m((h-u)/2),midB:m((u+d)/2),wallCenter:m(g),centered:Math.abs(g)<=r}}function ii(e,t,i=!1){if("passage"===e)return 1;if(null==t||"unavailable"===t||"unknown"===t)return"window"===e?0:1;const n=function(e){return["on","open","home","detected","playing","cleaning"].includes(String(e))}(t)!==!!i;return n?1:0}function ni(e){return"passage"===e.type?[]:[e.contact,e.lock].filter(e=>"string"==typeof e&&e.length>0)}function ri(e,t,i=200){const n=t.trim().toLowerCase();return(n?e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)):e).slice(0,Math.max(0,i))}function oi(e,t,i=.001){return Math.abs(e[0]-t[0])e[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s)+o&&(i=!i)}return i}function ai(e,t,i){const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;let s=o?((e[0]-t[0])*n+(e[1]-t[1])*r)/o:0;return s=Math.max(0,Math.min(1,s)),Math.hypot(e[0]-(t[0]+s*n),e[1]-(t[1]+s*r))}function li(e,t){if(!t||t.length<2)return null;let i=null,n=1/0;for(let r=0;rr&&s<-r||o<-r&&s>r)&&(a>r&&l<-r||a<-r&&l>r)}function pi(e,t=24){const i=e.map(e=>e[0]),n=e.map(e=>e[1]),r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1;let c=0,h=0,d=0;for(let t=0;t1e-9?[h/(3*c),d/(3*c)]:[(r+o)/2,(s+a)/2],p=(t,i)=>{const n=((t,i)=>{if(!si([t,i],e))return-1/0;let n=1/0;for(let r=0;rm&&(m=c,_=[n,l])}if(_){const[e,i]=_,n=(o-r)/t,l=(a-s)/t;for(let t=-4;t<=4;t++)for(let r=-4;r<=4;r++){const o=e+n*t/4,s=i+l*r/4,a=p(o,s);a>m&&(m=a,_=[o,s])}}return _||_i(e)||e[0]}function _i(e,t=1e-6){if(!e||e.length<3)return null;const i=e.length,n=[e.reduce((e,t)=>e+t[0],0)/i,e.reduce((e,t)=>e+t[1],0)/i];if(hi(n,e,t))return n;for(let n=0;ngi(e,t,i));return n.filter(e=>!n.some(t=>t!==e&&gi(t,e,i)))}function yi(e){if(!e||e.length<3)return 0;let t=0;for(let i=0;i[e[0],e[1]]),[e[0][0],e[0][1]]]]}function wi(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=Et(bi(e),bi(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const n=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return n.length>=3?n:null}function ki(e,t,i){for(let n=0;n1&&oi(i[0],i[i.length-1],t)&&i.pop(),i}function $i(e,t,i=1e-6){if(!e||e.length<3||!t||t.length<2)return null;const n=t[0],r=t[t.length-1];if(oi(n,r,i))return null;const o=ki(e,n,i),s=ki(e,r,i);if(o<0||s<0)return null;const a=t.slice(1,-1);for(const t of a)if(!hi(t,e,i))return null;for(let i=0;i{const s=[t];let a=(n+1)%e.length;for(let t=0;t<=e.length&&(s.push(e[a]),a!==o);t++)a=(a+1)%e.length;return s.push(r),xi(s,i)};let c,h;if(o===s){const s=xi([...t],i);if(s.length<3||yi(s)<=i)return null;const a=[];for(let i=0;i=0?t:[...t].reverse();for(const e of i)a.push(e)}c=xi(a,i),h=s}else c=xi([...l(n,o,r,s),...[...a].reverse()],i),h=xi([...l(r,s,n,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(e))>Math.max(i,1e-6*yi(e))?null:[c,h]}function Si(e,t,i){const[n,r]=e.split(":");return"device"===n?r:"entity"===n?"lg_"+r:t&&t.startsWith("v_")?t:i()}function Mi(e){return e.length?Math.round(e.reduce((e,t)=>e+t,0)/e.length):null}function Ri(e,t){if(t>e[2]/e[3]){const i=e[3],n=e[3]*t;return{x:e[0]-(n-e[2])/2,y:e[1],w:n,h:i}}const i=e[2],n=e[2]/t;return{x:e[0],y:e[1]-(n-e[3])/2,w:i,h:n}}function Ti(e,t,i,n){if(e.length<2)return;const r=t.x+n,o=t.x+t.w-n,s=t.y+n,a=t.y+t.h-n;for(let t=0;t<60;t++){let t=!1;for(let n=0;n=0)i=t.slice(0,r).trim(),n=t.slice(r+1).trim();else{const e=t.split(".");e.length>2&&(i=e.slice(0,2).join("."),n=e.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?r>=0&&!n||n&&!/^[a-zA-Z0-9_.-]+$/.test(n)?null:n?{entity:i,attr:n}:{entity:i}:null}function Li(e,t){const i=String(e??"").trim(),n=String(t??"").trim(),r=Hi(n?`${i}:${n}`:i);return r?`{${r.entity}${r.attr?`:${r.attr}`:""}}`:""}function qi(e,t,i){const n=String(t??"").trim();if(!n)return null;const r=e?.states?.[n];if(!r)return null;const o=String(i??"").trim(),s=e=>e.slice(0,60);if(o){const t=function(e){if(null==e)return null;if(Array.isArray(e)){const t=e.map(e=>null==e?"":String(e)).join(", ");return t?t.slice(0,60):null}if("object"==typeof e)return null;const t=String(e);return""===t?null:t.slice(0,60)}(r.attributes?.[o]);if(null===t)return null;const i=e?.formatEntityAttributeValue;if("function"==typeof i)try{const t=i.call(e,r,o);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:t,formatted:!1}}const a=r.state;if(null==a||""===a)return null;const l=e?.formatEntityState;if("function"==typeof l)try{const t=l.call(e,r);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:s(String(a)),formatted:!1}}function ji(e,t,i){const n=String(t??"").trim(),r=String(i??"").trim()||n;if(!r)return e.text;const o=e.formatted&&n?function(e,t){if(!t)return e;const i=e.replace(/\s+$/,"");return i.endsWith(t)?i.slice(0,i.length-t.length).replace(/\s+$/,""):e}(e.text,n):e.text;return`${o} ${r}`}function Bi(e,t){const i=(t?.entity||"").trim();if(!i)return"";const n=e?.states?.[i],r=n?.state;if(!n||null==r||""===r||"unavailable"===r||"unknown"===r)return Ei;const o=(t?.attr||"").trim(),s=qi(e,i,o||null);if(null===s)return Ei;return ji(s,o?"":String(n.attributes?.unit_of_measurement??"").trim(),t?.unit)}function Wi(e,t,i,n=()=>!0){const r=e??"";let o=!1;const s=r.replace(/\{([^{}\r\n]+)\}/g,(e,t)=>{const r=Hi(t);return r?(o=!0,n(r.entity||"")?Bi(i,r):Ei):e});if(o)return s;const a=(t?.entity||"").trim();if(!a)return r;if(!n(a)){const e=r.indexOf(Ni);return e>=0?r.slice(0,e)+Ei+r.slice(e+2):r?`${r} ${Ei}`:Ei}const l=Bi(i,t),c=r.indexOf(Ni);return c>=0?r.slice(0,c)+l+r.slice(c+2):r?`${r} ${l}`:l}const Ui=20;function Gi(e){const t=Number(e?.scale);if(Number.isFinite(t)&&t>0)return Math.min(20,Math.max(.15,t));return{s:.7,m:1,l:1.5}[String(e?.size??"")]??1}function Vi(e,t){if(!t)return e;let i=e;for(const[e,n]of Object.entries(t))i=i.split("{"+e+"}").join(String(n));return i}const Ki="#55606c",Yi=.55,Zi=20,Xi=25,Ji={c:"#607d8b",a:.18};function Qi(e,t=Ji){const i=e&&"object"==typeof e?e:null,n=i?.a;return{c:qt(i?.c,t.c),a:"number"==typeof n&&Number.isFinite(n)?Math.min(1,Math.max(0,n)):t.a}}function en(e,t){const i=Qi(e),n=t?.settings?.custom_fill;return n&&"object"==typeof n?Qi(n,i):i}function tn(e){const t=e?.settings||{},i=!e?.plan_url,n="glow"===t.fill_mode;return{showBorders:t.show_borders??i,showNames:t.show_names??i,color:qt(t.room_color,Ki),opacity:"number"==typeof t.room_opacity?Math.min(1,Math.max(0,t.room_opacity)):.55,fill:["lqi","light","temp","custom"].includes(t.fill_mode)?t.fill_mode:"none",customFill:Qi(t.custom_fill),glow:"boolean"==typeof t.glow_enabled?t.glow_enabled:n,tempMin:"number"==typeof t.temp_min?t.temp_min:20,tempMax:"number"==typeof t.temp_max?t.temp_max:25,showLqi:"boolean"==typeof t.show_lqi?t.show_lqi:null,cardFontScale:"number"==typeof t.card_font_scale&&t.card_font_scale>0?Math.min(3,Math.max(.5,t.card_font_scale)):1,labelTemp:!0===t.label_temp,labelHum:!0===t.label_hum,labelLqi:!0===t.label_lqi,labelLight:!0===t.label_light,bgColor:qt(t.bg_color,null),hideDecor:!0===t.hide_decor,hideOpenings:!0===t.hide_openings}}function nn(e,t){if(t.bgColor)return t.bgColor;const i=e?.bg_color;return qt(i,"")}const rn={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function on(e){const t={},i=e?.fill_colors||{};for(const e of Object.keys(rn)){const n=rn[e],r=i[e];t[e]={c:qt(r?.c,n.c),a:r&&"number"==typeof r.a?Math.min(1,Math.max(0,r.a)):n.a}}return t}function sn(e,t,i){const n=Math.min(1,Math.max(0,i)),r=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),s=r.map((e,t)=>Math.round(e+(o[t]-e)*n));return"#"+s.map(e=>e.toString(16).padStart(2,"0")).join("")}function an(e,t,i,n,r,o,s,a=Ji){const l=function(e,t,i,n,r,o,s,a=Ji){if("custom"===e)return Qi(a);if("lqi"===e){if(null==t)return null;const e=(t-40)/140;return{c:sn(s.lqi_low.c,s.lqi_high.c,e),a:s.lqi_low.a+(s.lqi_high.a-s.lqi_low.a)*Math.min(1,Math.max(0,e))}}if("light"===e)return"none"===i?s.light_none.a>0?s.light_none:null:"on"===i?s.light_on:s.light_off;if("temp"===e){if(null==n)return null;const e=Math.min(r,o),t=Math.max(r,o);return nt?s.temp_hot:s.temp_ok}return null}(e,t,i,n,r,o,s,a);return l?{color:l.c,opacity:l.a,mode:e}:null}const ln={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},cn=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function hn(e){for(const t of[...Object.values(ln),...cn])if(e===t[0]||e===t[1])return t;return null}function dn(e,t,i,n,r){if(!n||"unavailable"===n||"unknown"===n)return e;if(r){const i="cover"===t?hn(e):null;return i?"closed"===n?i[0]:i[1]:e}if("binary_sensor"===t){if("door"===i)return"on"===n?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===n?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===n?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===t){const t=ln[String(i||"")];if(t)return"closed"===n?t[0]:t[1];const r=hn(e);return r?"closed"===n?r[0]:r[1]:e}return"lock"===t?"locked"===n?"mdi:lock":"mdi:lock-open-variant":"light"===t&&"mdi:lightbulb"===e&&"on"===n?"mdi:lightbulb-on":e}function un(e){const t=Math.min(4e4,Math.max(1e3,e))/100,i=t<=66?255:329.698727446*Math.pow(t-60,-.1332047592),n=t<=66?99.4708025861*Math.log(t)-161.1195681661:288.1221695283*Math.pow(t-60,-.0755148492),r=t>=66?255:t<=19?0:138.5177312231*Math.log(t-10)-305.0447927307,o=e=>Math.round(Math.min(255,Math.max(0,e)));return[o(i),o(n),o(r)]}const pn=1/2.2;function _n(e){const t=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(e);return t?"#"+t.slice(1).map(e=>Math.min(255,Number(e)).toString(16).padStart(2,"0")).join(""):null}function mn(e){if(!e||"object"!=typeof e||Array.isArray(e))return null;const t=e;if(Object.keys(t).some(e=>"c"!==e&&"bri"!==e))return null;const i=qt(t.c,null);return i?void 0===t.bri||null===t.bri?{c:i}:"number"!=typeof t.bri||!Number.isFinite(t.bri)||t.bri<.01||t.bri>1?null:{c:i,bri:t.bri}:null}function gn(e,t,i){const n=mn(t),r=e?.attributes||{},o=n?.bri??function(e){const t=e?.attributes?.brightness,i="number"==typeof t?t:"string"==typeof t&&""!==t.trim()?Number(t):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(e);if(n)return{c:n.c,bri:o};const s=Wt(r.rgb_color);if(s)return{c:_n(s)||qt(i,"#ffd9a0"),bri:o};const a=Number(r.color_temp_kelvin)||(Number(r.color_temp)>0?1e6/Number(r.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=un(a),"#"+l.slice(0,3).map(e=>Math.min(255,Math.max(0,Math.round(Number(e)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:qt(i,"#ffd9a0"),bri:o};var l}function fn(e,t,i){return"on"===e?.state?gn(e,t,i):null}function vn(e,t=1){const i=Math.max(0,Math.min(1,Number.isFinite(e)?e:1)),n=.7*Math.max(0,Math.min(1,Number.isFinite(t)?t:1))*(.4+.6*Math.pow(i,pn));return Math.max(0,Math.min(1,n))}function yn(e){return e.startsWith("light.")||e.startsWith("switch.")}function bn(e,t,i=1e-6){const n=[];if(!e||!t||e.length<3||t.length<3)return n;for(let r=0;ru||l>u)continue;const p=(r[0]-o[0])*h+(r[1]-o[1])*d,_=(s[0]-o[0])*h+(s[1]-o[1])*d,m=Math.max(0,Math.min(p,_)),g=Math.min(c,Math.max(p,_));g-m>i&&n.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return n}function wn(e,t,i=1e-6){const n=[];for(const r of e){const e=[r[0],r[1]],o=[r[2],r[3]],s=o[0]-e[0],a=o[1]-e[1],l=Math.hypot(s,a);if(lo||r>o)continue;const s=(n[0]-e[0])*c+(n[1]-e[1])*h,a=(n[2]-e[0])*c+(n[3]-e[1])*h,u=Math.max(0,Math.min(s,a)),p=Math.min(l,Math.max(s,a));p-u>i&&d.push([u,p])}if(!d.length){n.push([e[0],e[1],o[0],o[1]]);continue}d.sort((e,t)=>e[0]-t[0]);let u=0;for(const[t,r]of d)t-u>i&&n.push([e[0]+c*u,e[1]+h*u,e[0]+c*t,e[1]+h*t]),u=Math.max(u,r);l-u>i&&n.push([e[0]+c*u,e[1]+h*u,o[0],o[1]])}return n}function kn(e,t,i=1e-6){const n=[];for(let t=0;t{if("string"!=typeof e||!e)return;const i=Mn(e);i.startsWith("/api/houseplan/content/")&&t.add(i)};for(const t of e?.spaces||[]){i(t?.plan_url);for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url)}for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url);return t}function Mn(e){return e?e.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+e.slice(23):e.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+e.slice(23):e:""}function Rn(e,t){const i=t?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:e}function Tn(e,t){const i=t?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||e}function Cn(e,t,i,n){if(!t||!i||t===i)return e;const r="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(r))return e;const t=e.url.split(r)[1]||"",[i,s]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(n){const t=n[decodeURIComponent(i)]??n[i];return t?{...e,url:e.url.split(r+i)[0]+o+encodeURIComponent(t)+s}:e}return{...e,url:e.url.split(r).join(o)}})}function Dn(e,t=1){const i=Number(e);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):t}function An(e,t,i){let n=null,r=null;for(const o of t){if(!(Math.abs(o[0]-e[0])<1e-6&&Math.abs(o[1]-e[1])<1e-6)){if(Math.abs(o[0]-e[0])<=i){const t=Math.abs(o[1]-e[1]);t>1e-6&&(!n||t1e-6&&(!r||t"number"==typeof e&&Number.isFinite(e),Wn=(e,t,i)=>Math.min(i,Math.max(t,e));function Un(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function Gn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;return t>=300&&t<480?"dawn":t>=480&&t<1080?"day":t>=1080&&t<1260?"dusk":"night"}function Vn(e){const t=Un(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-Wn(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:Wn((e.elevation+6)/12,0,1)}}function Kn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;if(t<300||t>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(t-300)/960,n=(t-300)/120,r=(1260-t)/120;return{sunX:8+84*i,sunY:78-64*Math.sin(i*Math.PI),sunOpacity:Math.max(.18,Math.min(n,r,1))}}function Yn(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&Bn(t.azimuth)&&Bn(t.elevation)&&"boolean"==typeof t.rising?{azimuth:Ln(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:Un(i),source:"sun",...Vn(i)};const n="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:Gn(n),source:"clock",...Kn(n)}}function Zn(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const Xn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],Jn=e=>Math.min(1,Math.max(0,e));function Qn(e,t,i=6){const n=e.angle*Math.PI/180,r=[Math.sin(n),-Math.cos(n)],o=n=>{const o=[e.x+r[0]*i*n,e.y+r[1]*i*n];return t.find(e=>e.poly.length>=3&&si(o,e.poly))||null},s=o(1),a=o(-1);return s&&a?null:s||a?s?{normal:[-r[0],-r[1]],roomId:s.id}:{normal:r,roomId:a.id}:null}function er(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function tr(e,t,i,n){return[[e[0],e[1]],[t[0],t[1]],[t[0]+i[0]*n,t[1]+i[1]*n],[e[0]+i[0]*n,e[1]+i[1]*n]]}function ir(e,t){try{const i=Nt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]),n=[];for(const e of i){const t=e?.[0];!Array.isArray(t)||t.length<4||n.push(t.slice(0,t.length-1).map(e=>[e[0],e[1]]))}return n}catch{return[]}}function nr(e,t,i,n,r,o,s){if(!(n>0))return[];const a=qn(i,r),l=[-a[0],-a[1]],c=function(e){const t=Math.min(90,Math.max(0,e));return.7*(.8+1.7*Math.pow(1-t/90,1.6))}(n),h=[];for(const i of t){if(!(i.length>0))continue;const t=Qn(i,e);if(!t||!er(t.normal,a,n))continue;const r=e.find(e=>e.id===t.roomId);if(!r)continue;const d=o&&o[t.roomId]||r.poly,u=i.angle*Math.PI/180,p=i.length/2,_=[-t.normal[0],-t.normal[1]],m=Math.max(0,s?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,x=ir(tr(b,w,l,k),d);if(!x.length)continue;const $=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:t.roomId,polys:x,a:b,b:w,dir:l,len:k,normal:_,depth:k*$})}return h}const rr=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function or(e,t){const i=rr(t?.north_deg);return null!==i?i:rr(e?.north_deg)}function sr(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function ar(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function lr(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),n=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(n)?{azimuth:i,elevation:n}:null}const cr=["dawn","day","dusk","night"];function hr(e){if(!e)return"";const t=jn[e.phase];return[`--hp-day-cycle-outline-near:${t.outlineNear}`,`--hp-day-cycle-outline-mid:${t.outlineMid}`,`--hp-day-cycle-outline-far:${t.outlineFar}`].join(";")}function dr(e,t=1){if(!e)return G;const i=[`--hp-day-cycle-sun-x:${e.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${e.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${e.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,t)).toFixed(4)}`].join(";");return B``:U}
this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -4875,163 +4884,163 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> ${dr(F,I)} - ${this._editorRuntime?this._renderEditorSecondary():G} + ${this._editorRuntime?this._renderEditorSecondary():U}
- ${h&&m?.structural?W``:G} - `:U} + - + ${""} - ${this._wallHatchDefs(_.color)}${W`${this._paperShapes(s.rooms).map(e=>"path"in e?W``:"poly"in e?W``:W``)}`} - ${this._editing?this._renderMarkupDefs(l):G} + ${this._wallHatchDefs(_.color)}${B`${this._paperShapes(s.rooms).map(e=>"path"in e?B``:"poly"in e?B``:B``)}`} + ${this._editing?this._renderMarkupDefs(l):U} ${""} - ${this._editing&&!this._markup&&this._gridLevels()?W``:G} - ${s.bg&&O?W``:U} + ${s.bg&&A?B`this._onBackdropLoaded(s.bg.href,O)} - transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:G} + @load=${()=>this._onBackdropLoaded(s.bg.href,A)} + transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:U} @dblclick=${e=>this._openBackdropDialog(e)} - preserveAspectRatio="none" />`:G} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Zt(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Rn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=f.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},d=r(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),p=d&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return d.some((e,i)=>Pn(t,[e[0],e[1],...d[(i+1)%d.length]])<=.08*this._gridPitch)}):[],m=u?[]:t,g=p.concat(m);g.length&&(o+=" noedge");const v=this._spaceWalls.length&&n.id&&d&&this._innerRoomContour(s,n.id,e,i)||d,b=v?vi(v,(w=n,s.rooms.filter(e=>e!==w).map(r).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=v?this._cleanFloor(n,v,s).path:"",$=n.id||G,S=n.area||G,M=x&&v?W``:U} + ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Zt(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Rn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=g.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},d=r(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),p=d&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return d.some((e,i)=>Pn(t,[e[0],e[1],...d[(i+1)%d.length]])<=.08*this._gridPitch)}):[],m=u?[]:t,f=p.concat(m);f.length&&(o+=" noedge");const v=this._spaceWalls.length&&n.id&&d&&this._innerRoomContour(s,n.id,e,i)||d,b=v?vi(v,(w=n,s.rooms.filter(e=>e!==w).map(r).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=v?this._cleanFloor(n,v,s).path:"",$=n.id||U,S=n.area||U,M=x&&v?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:b.length&&v?W`this._clearTransientHover()}>`:b.length&&v?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:v&&v!==d?W`this._clearTransientHover()}>`:v&&v!==d?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:n.poly?W`this._clearTransientHover()}>`:n.poly?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:W`this._clearTransientHover()}>`:B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`,R=g.length&&d?kn(d,g,.02*this._gridPitch):null,T=R?W`this._clearTransientHover()}>`,R=f.length&&d?kn(d,f,.02*this._gridPitch):null,T=R?B``:G;return W`${M}${T}`})})()} - ${this._renderRoomHoverFill(A)} - ${this._renderOpeningTunnelFills(s,f)} + style=${this._markup?U:`stroke:${_.color};stroke-opacity:${_.showBorders?_.opacity:0}`}>`:U;return B`${M}${T}`})})()} + ${this._renderRoomHoverFill(z)} + ${this._renderOpeningTunnelFills(s,g)} ${this._renderGlowBaseRooms(s,v)} ${this._renderOpeningTunnelFills(s,v,"glow-base")} ${""} - ${_.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()} - ${N?this._renderGlowLayer(s,_):G} + ${_.hideDecor&&"decor"!==this._mode?U:this._renderDecorLayer()} + ${H?this._renderGlowLayer(s,_):U} ${this._renderSunRays(s)} - ${this._editing?W`${this._renderAlignGuides()}`:G} - ${this._markup?W`${this._renderMarkupLayer(l)}`:G} + ${this._editing?B`${this._renderAlignGuides()}`:U} + ${this._markup?B`${this._renderMarkupLayer(l)}`:U} ${""} ${""} - ${this._editing?G:this._renderZeroWalls(_)} + ${this._editing?U:this._renderZeroWalls(_)} ${this._renderWallBodies(_)} - ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():G} - ${this._renderRoomHoverOutline(A)} + ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():U} + ${this._renderRoomHoverOutline(z)} ${""} - ${this._editing?this._renderZeroWalls(_):G} - ${this._markup?W`${this._renderHiddenWallDiagnosticOverlay()}`:G} - ${this._markup?W`${this._renderOpeningPlacementPreview()}`:G} - ${M?this._renderOpeningDimensionGuides(M):G} - ${M?.guide?this._renderOpeningCenterTick(M.guide):G} + ${this._editing?this._renderZeroWalls(_):U} + ${this._markup?B`${this._renderHiddenWallDiagnosticOverlay()}`:U} + ${this._markup?B`${this._renderOpeningPlacementPreview()}`:U} + ${M?this._renderOpeningDimensionGuides(M):U} + ${M?.guide?this._renderOpeningCenterTick(M.guide):U} ${""} ${""} - ${this._markup?W`${this._renderActiveChainInk()}`:G} - ${this._markup?W`${this._renderPlanSnapOverlay()}`:G} - ${_.hideOpenings&&!this._markup||m&&!m.floorSymbols?G:this._renderOpenings(_)} + ${this._markup?B`${this._renderActiveChainInk()}`:U} + ${this._markup?B`${this._renderPlanSnapOverlay()}`:U} + ${_.hideOpenings&&!this._markup||m&&!m.floorSymbols?U:this._renderOpenings(_)} ${this._renderWallThickUi()} - ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):G} + ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):U} ${""} ${this._renderBackdropFrame(x)} ${this._renderTextFrame(x)} - ${h&&m?.structural?W` `:G} + ${this._renderIsoWalls(m,f)} + `:U} ${""}
${u.map(e=>this._renderDevice(e,x,y))} ${this._renderVacuums(u,x)} ${this._renderVacFit(x)} ${this._renderOpeningLocks(x)} - ${_.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,_)):G} - ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):G} + ${_.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,_)):U} + ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):U}
- ${this._measureAnchor?B`
${this._renderMeasureLabel(x)}
`:G} - ${this._resize?.liveLabels?B`
${this._resize?.liveLabels?.map(e=>B`
${this._renderMeasureLabel(x)}
`:U} + ${this._resize?.liveLabels?j`
${this._resize?.liveLabels?.map(e=>j`
${e.text}
`)}
`:G} - ${M?B`
${M.labels.map(e=>B`
${e.text}
`)}
`:U} + ${M?j`
${M.labels.map(e=>j`
${e.text}
`)}
`:G} - ${this._wallDialog?B`
${this._renderWallThickDialog()}
`:G} - ${R?B`
${e.text}
`)}
`:U} + ${this._wallDialog?j`
${this._renderWallThickDialog()}
`:U} + ${R?j`
${R.text}
`:G} - ${C?B`
${C.map(e=>B`
${R.text}
`:U} + ${C?j`
${C.map(e=>j`
${e.text}
`)}
`:G} - ${T?B`
${e.text}
`)}
`:U} + ${T?j`
${T.text}
`:G} + style="left:${((T.x-x.x)/x.w*100).toFixed(2)}%;top:${((T.y-x.y)/x.h*100).toFixed(2)}%">${T.text}
`:U}
- ${this._zoom>1?B`
${Math.round(100*this._zoom)}%
`:G} + ${this._zoom>1?j`
${Math.round(100*this._zoom)}%
`:U} ${this._renderFarHint()} ${this._renderHomeArrow()} ${this._renderEditorRuntimeLoading()} ${this._renderRecoveryOverlay()} - ${this._booting||this._bootFading?B``:U} - ${this._roomDialog&&this._editorRuntime?this._renderRoomDialog():G} - ${this._mergeDialog&&this._editorRuntime?this._renderMergeDialog():G} - ${this._openingDialog&&this._editorRuntime?this._renderOpeningDialog():G} - ${this._physicalDialog&&this._editorRuntime?this._renderPhysicalDialog():G} - ${this._partitionDeleteDialog&&this._editorRuntime?this._renderPartitionDeleteDialog():G} - ${this._roomDeleteDialog&&this._editorRuntime?this._renderRoomDeleteDialog():G} - ${this._openingInfo?this._renderOpeningInfoCard():G} - ${this._decorTextDialog&&this._editorRuntime?this._renderDecorTextDialog():G} - ${this._decorShapeDialog&&this._editorRuntime?this._renderDecorShapeDialog():G} - ${this._backdropDialog&&this._editorRuntime?this._renderBackdropDialog():G} - ${this._decorEraseConfirm&&this._editorRuntime?this._renderDecorEraseConfirm():G} - ${this._spaceDialog&&(this._onboardingRuntime||this._editorRuntime)?this._renderSpaceDialog():G} - ${this._deviceInbox&&this._editorRuntime?this._renderDeviceInbox():G} - ${this._markerDialog&&this._editorRuntime?this._renderMarkerDialog():G} - ${this._vacCalConfirm?B`this._vacCalConfirm=null}>
@@ -5045,38 +5054,38 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this._t("vac.apply_proposal")}
-
`:G} - ${this._infoCard?this._renderInfoCard():G} - ${this._rulesDialog&&this._editorRuntime?this._renderRulesDialog():G} - ${this._settingsDialog&&this._editorRuntime?this._renderSettingsDialog():G} - ${this._alignDialog&&this._editorRuntime?this._renderAlignDialog():G} - ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():G} - ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():G} - ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():G} - ${this._tip?B`
- ${this._tip.title}${this._tip.meta?B`${this._tip.meta}`:G} - ${null!=this._tip.temp?B`${this._t("tip.temp_avg")} ${this._tip.temp}°`:G} - ${null!=this._tip.hum?B`${this._t("tip.hum_avg")} ${this._tip.hum}%`:G} - ${null!=this._tip.lqi?B`${this._t("tip.lqi")} - ${this._tip.lqi}`:G} -
`:G} - ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?B`
- ${this._model.map(e=>B``)} -
`:G} - ${this._kioskDialog?this._renderKioskDialog():G} - ${this._vacFit?B`
+ `:U} + ${this._infoCard?this._renderInfoCard():U} + ${this._rulesDialog&&this._editorRuntime?this._renderRulesDialog():U} + ${this._settingsDialog&&this._editorRuntime?this._renderSettingsDialog():U} + ${this._alignDialog&&this._editorRuntime?this._renderAlignDialog():U} + ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():U} + ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():U} + ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():U} + ${this._tip?j`
+ ${this._tip.title}${this._tip.meta?j`${this._tip.meta}`:U} + ${null!=this._tip.temp?j`${this._t("tip.temp_avg")} ${this._tip.temp}°`:U} + ${null!=this._tip.hum?j`${this._t("tip.hum_avg")} ${this._tip.hum}%`:U} + ${null!=this._tip.lqi?j`${this._t("tip.lqi")} + ${this._tip.lqi}`:U} +
`:U} + ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?j`
+ ${this._model.map(e=>j``)} +
`:U} + ${this._kioskDialog?this._renderKioskDialog():U} + ${this._vacFit?j`
${this._t("vac.fit_hint")} -
`:G} - ${this._tapConfirm?B``:U} + ${this._tapConfirm?j`this._tapConfirm=null}>
- ${"run"===this._tapConfirm.kind?B`

${this._tapConfirm.text}

`:this._tapConfirm.lines.map((e,t)=>B` + ${"run"===this._tapConfirm.kind?j`

${this._tapConfirm.text}

`:this._tapConfirm.lines.map((e,t)=>j`

${e}

`)}
@@ -5086,32 +5095,32 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this._t("btn.run")}
-
`:G} - ${this._toast?B``:G} +
`:U} + ${this._toast?j``:U} - `}_vacCandidateStatus(e,t,i=this._planHass){const n=this._bindingStatus("entity:"+e);if("ha_disabled"===n.kind)return"disabled";if("orphaned"===n.kind)return"missing";if("unverified"===n.kind)return"unverified";const r=i?.states?.[e];return"unavailable"===r?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,n]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const r=Gl(e,n,t[e]);r&&i.push(r)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const n=e.marker?.vacuum,r="string"==typeof n?.source&&!!n.source,o=new Set(e.entities||[]),s=new Set(o);r&&s.add(n.source);const a=i?.entities||{},l=[];for(const e of s){const t=i?.states?.[e],o=Gl(e,t,a[e]);o?l.push(o):r&&e===n.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:a[e]?.platform?String(a[e].platform):null,category:e.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(t&&this._vacAllCameraCache?.devId===e.id){const e=new Set(l.map(e=>e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Mh(this._planHass,this._devices,null,this._virtualLights)){const i=Nu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:qu(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=Ap(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),zp(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Mh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==Ai(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Op(a,s,o,window.clearTimeout.bind(window)):(a=Ap(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==Ai(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Pp(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Ai(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Xl(this.hass.states[i||""]?.state),r=Bl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==nc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){return this._editorRuntimeOrThrow()._vacMapId(e,t,i)}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):G}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Ai(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??Bl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=nc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===u&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,n]=zl(c,e,t),r=this._scenePoint([i,n]);return r[0].toFixed(1)+","+r[1].toFixed(1)}).join(" ");o.push(W``)}if(p){const e=Ll(a,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:ql(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[n,r]=zl(c,e,t),o=this._scenePoint([n,r]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=zl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(W``)}}}if(!d||!a.pos)continue;const[f,v]=zl(c,a.pos.x,a.pos.y),y=this._scenePoint([f,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(B`
e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Th(this._planHass,this._devices,null,this._virtualLights)){const i=Lu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:ju(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=Op(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),Fp(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Th(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==zi(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Pp(a,s,o,window.clearTimeout.bind(window)):(a=Op(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==zi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Ip(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===zi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Ql(this.hass.states[i||""]?.state),r=Vl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==oc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Jl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Jl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){return this._editorRuntimeOrThrow()._vacMapId(e,t,i)}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):U}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return U;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===zi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??Vl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=oc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,f=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===u&&f&&f.points.length>1){const e=f.points.map(([e,t])=>{const[i,n]=Fl(c,e,t),r=this._scenePoint([i,n]);return r[0].toFixed(1)+","+r[1].toFixed(1)}).join(" ");o.push(B``)}if(p){const e=Wl(a,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:jl(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[n,r]=Fl(c,e,t),o=this._scenePoint([n,r]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(B``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=Fl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(B``)}}}if(!d||!a.pos)continue;const[g,v]=Fl(c,a.pos.x,a.pos.y),y=this._scenePoint([g,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(j`
{e.stopPropagation();const t=this._vacEntity(i);t&&this._openMoreInfo(t)}}> -
`)}return this._vacLastView=t,r.length&&!this._vacRaf&&this._vacRafLoop(),r.length||o.length?B` - ${o.length?W`${o}`:G} - ${r}`:G}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...Yu(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Ou(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",uu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),_=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return B`
`)}return this._vacLastView=t,r.length&&!this._vacRaf&&this._vacRafLoop(),r.length||o.length?j` + ${o.length?B`${o}`:U} + ${r}`:U}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...Xu(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Pu(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",_u(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),_=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return j`
this._clickDevice(t,e)} @keydown=${t=>this._keyDevice(t,e)} @@ -5123,35 +5132,35 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointerup=${t=>this._pointerUp(t,e)} @pointercancel=${t=>this._pointerUp(t,e)} > - ${Xu(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} -
`}_roomArea(e){const t=Zt(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Hn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Gh(this._renderPlanHass,t,"temp",this._markers);const i=Kh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Gh(this._renderPlanHass,t,"hum",this._markers);const i=Kh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Zh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*km,y:i.y*km};const n=this._roomCenter(e),r=this._gridPitch;return{x:qa(Gt(n[0],r)),y:qa(Gt(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return G;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(B`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(B`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(B`${t}`)}if(n.labelLight){const t=(d=Mh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(B`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return B`
`}_roomArea(e){const t=Zt(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Nn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Kh(this._renderPlanHass,t,"temp",this._markers);const i=Zh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Kh(this._renderPlanHass,t,"hum",this._markers);const i=Zh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Jh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*$m,y:i.y*$m};const n=this._roomCenter(e),r=this._gridPitch;return{x:qa(Ut(n[0],r)),y:qa(Ut(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return U;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(j`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(j`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(j`${t}`)}if(n.labelLight){const t=(d=Th(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(j`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return j`
this._labelDown(i,e,t.id)} @pointermove=${i=>this._labelMove(i,e,t.id)} @pointerup=${()=>this._labelUp(e)} @pointercancel=${()=>this._labelUp(e)} - >${e.name||(this._markup?this._t("room.unnamed"):"")}${u?B`{t.stopPropagation(),this._clickRoom(e)}:G} - @pointerdown=${p?e=>e.stopPropagation():G}>`:G} - ${h.length?B`${h}`:G} - ${"plan"===this._mode?["tl","tr","bl","br"].map(i=>B`${e.name||(this._markup?this._t("room.unnamed"):"")}${u?j`{t.stopPropagation(),this._clickRoom(e)}:U} + @pointerdown=${p?e=>e.stopPropagation():U}>`:U} + ${h.length?j`${h}`:U} + ${"plan"===this._mode?["tl","tr","bl","br"].map(i=>j`this._rlResizeDown(i,e,t.id)} @pointermove=${e=>this._rlResizeMove(e)} @pointerup=${()=>this._rlResizeUp()} - @pointercancel=${()=>this._rlResizeUp()}>`):G} -
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=On(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return B`
- ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=On(e.a,e.b);return{x:o,y:s,on45:zn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Hn(vr(Math.abs(n-t),this._cellCm,this._gridPitch)*vr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=km,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return ii(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!qh(this._fullRegistryHass,e,Lh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!qh(this._renderPlanHass,e,Lh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Vc(e)){return!!t&&"active"===th(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W`this._rlResizeUp()}>`):U} +
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=An(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return j`
+ ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=An(e.a,e.b);return{x:o,y:s,on45:On(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Nn(vr(Math.abs(n-t),this._cellCm,this._gridPitch)*vr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=$m,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return ii(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!jh(this._fullRegistryHass,e,Wh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!jh(this._renderPlanHass,e,Wh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Yc(e)){return!!t&&"active"===nh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return B``;const i=this._spaceModel();if(!i)return B``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return B`${t.map(e=>{if(e.orphanReason)return B`{t.stopPropagation(),this._editOpening(e)}}> ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=cu(l),u=xa(10,this._cellCm),p=xa(12,this._cellCm);return W``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=du(l),u=xa(10,this._cellCm),p=xa(12,this._cellCm);return B` - ${hu(l)} + ${uu(l)} @@ -5162,7 +5171,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return B``;const i=this._spaceModel();if(!i)return B``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return B`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=xa(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,_=(u[1]-e.y)/e.h*100;return B`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return j`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=xa(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,_=(u[1]-e.y)/e.h*100;return j`
{e.stopPropagation(),"view"===this._mode&&(this._openingInfo=t)}}> -
`})}`}_lockAction(e,t){if(this._openingEntityAvailable(e)){if("unlock"===t){const t=this.hass?.states?.[e]?.attributes?.friendly_name||e;if(!confirm(this._t("confirm.unlock",{name:t})))return}this.hass?.callService?.("lock",t,{entity_id:e})}}_renderOpeningInfoCard(){const e=this._openingInfo,t="passage"!==e.type&&e.contact&&this._openingEntityAvailable(e.contact)?e.contact:null,i=("door"===e.type||"gate"===e.type)&&e.lock&&this._openingEntityAvailable(e.lock)?e.lock:null,n=t?this.hass.states[t]?.state:null,r=this._openingAmt(e),o=i?this.hass.states[i]?.state:null,s="door"===e.type?"opening.door":"gate"===e.type?"opening.gate":"passage"===e.type?"opening.passage":"opening.window",a="door"===e.type?"mdi:door":"gate"===e.type?"mdi:gate":"passage"===e.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===e.type?r>0?"mdi:gate-open":"mdi:gate":r>0?"mdi:door-open":"mdi:door-closed",c=(e,t,i,n="")=>B`
${t}${i}
`;return B``})}`}_lockAction(e,t){if(this._openingEntityAvailable(e)){if("unlock"===t){const t=this.hass?.states?.[e]?.attributes?.friendly_name||e;if(!confirm(this._t("confirm.unlock",{name:t})))return}this.hass?.callService?.("lock",t,{entity_id:e})}}_renderOpeningInfoCard(){const e=this._openingInfo,t="passage"!==e.type&&e.contact&&this._openingEntityAvailable(e.contact)?e.contact:null,i=("door"===e.type||"gate"===e.type)&&e.lock&&this._openingEntityAvailable(e.lock)?e.lock:null,n=t?this.hass.states[t]?.state:null,r=this._openingAmt(e),o=i?this.hass.states[i]?.state:null,s="door"===e.type?"opening.door":"gate"===e.type?"opening.gate":"passage"===e.type?"opening.passage":"opening.window",a="door"===e.type?"mdi:door":"gate"===e.type?"mdi:gate":"passage"===e.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===e.type?r>0?"mdi:gate-open":"mdi:gate":r>0?"mdi:door-open":"mdi:door-closed",c=(e,t,i,n="")=>j`
${t}${i}
`;return j`this._openingInfo=null}>
- ${t?c(l,this._t("opening.contact_label"),"unavailable"===n||null==n?this._t("opening.state_unknown"):this._t(r>0?"opening.open":"opening.closed"),r>0?"warn":"ok"):G} - ${i?c("locked"===o?"mdi:lock":"mdi:lock-open-variant",this._t("opening.lock_label"),"locked"===o?this._t("opening.locked"):["unlocked","open"].includes(String(o))?this._t("opening.unlocked"):this._t("opening.state_unknown"),"locked"===o?"ok":"warn"):G} - ${i&&("locked"===o||["unlocked","open"].includes(String(o)))?B``:i&&["locking","unlocking"].includes(String(o))?B``:i&&["locking","unlocking"].includes(String(o))?j``:G} - ${t||i?G:B`

${this._t("opening.no_entities")}

`} + `:U} + ${t||i?U:j`

${this._t("opening.no_entities")}

`}
-
`}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Mh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?qi(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(yn).filter(e=>this._planEntityAvailable(e));return B``}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Th(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?qi(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(yn).filter(e=>this._planEntityAvailable(e));return j`
- ${(()=>{const t=this._cardEntities(e);return t.length?B`
- ${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?qi(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return B`
+ ${(()=>{const t=this._cardEntities(e);return t.length?j`
+ ${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?qi(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return j`
${n} - ${"toggle"===t?B``:"open"===t?B``:B`${r}`} + ${"toggle"===t?j``:"open"===t?j``:j`${r}`}
`})} -
`:G})()} - ${e.model?B`
${this._t("info.model")}${e.model}
`:G} - ${i&&!this._cardEntities(e).length?B`
${this._t("info.state")}${i}
`:G} - ${Ci(e.link)?B`
${this._t("info.link")} - ${e.link}
`:G} - ${e.description?B`
${e.description}
`:G} - ${e.pdfs&&e.pdfs.length?B`
${this._t("info.manuals")} - ${e.pdfs.map(e=>B` - ${e.name}`)}
`:G} - ${n.length?B`
${this._t("info.controls")} +
`:U})()} + ${e.model?j`
${this._t("info.model")}${e.model}
`:U} + ${i&&!this._cardEntities(e).length?j`
${this._t("info.state")}${i}
`:U} + ${Ci(e.link)?j`
${this._t("info.link")} + ${e.link}
`:U} + ${e.description?j`
${e.description}
`:U} + ${e.pdfs&&e.pdfs.length?j`
${this._t("info.manuals")} + ${e.pdfs.map(e=>j` + ${e.name}`)}
`:U} + ${n.length?j`
${this._t("info.controls")} - ${n.map(e=>{const t=this.hass.states[e],i="on"===t?.state;return B` + ${n.map(e=>{const t=this.hass.states[e],i="on"===t?.state;return j` ${t?.attributes?.friendly_name||e}`})} -
`:G} - ${e.model||i||e.link||e.description||e.pdfs&&e.pdfs.length||n.length?G:B`
${this._t("info.none")}
`} +
`:U} + ${e.model||i||e.link||e.description||e.pdfs&&e.pdfs.length||n.length?U:j`
${this._t("info.none")}
`}
- ${e.primary?B``:G} + `:U}
- `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return this._editorRuntimeOrThrow()._toggleIntent(e,t)}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){return this._editorRuntimeOrThrow()._toggleStateText(e,t)}_toggleConfirmationStateText(e){return this._editorRuntimeOrThrow()._toggleConfirmationStateText(e)}_toggleConfirmationLines(e){return this._editorRuntimeOrThrow()._toggleConfirmationLines(e)}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return B`
+ `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return this._editorRuntimeOrThrow()._toggleIntent(e,t)}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){return this._editorRuntimeOrThrow()._toggleStateText(e,t)}_toggleConfirmationStateText(e){return this._editorRuntimeOrThrow()._toggleConfirmationStateText(e)}_toggleConfirmationLines(e){return this._editorRuntimeOrThrow()._toggleConfirmationLines(e)}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return j`
${this._t("preview.room_name")} @@ -5235,4 +5244,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d 45% · ${this._t("roomcard.light_partial",{on:1,total:3})} -
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Dm.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Dm.ZOOM_MAX=8,Dm.ZOOM_MIN=1/3,Dm.styles=[Ud,Hp],customElements.get("houseplan-card")||customElements.define("houseplan-card",Dm),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${sm} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{Ao as $,G as A,Qt as B,Ia as C,le as D,U as E,Zt as F,Na as G,Dd as H,Nd as I,gs as J,lo as K,Id as L,Td as M,No as N,vs as O,Ad as P,Bs as Q,Gs as R,vo as S,oi as T,ao as U,xo as V,np as W,rp as X,ss as Y,Wr as Z,zo as _,o as a,ts as a$,no as a0,oo as a1,qr as a2,pp as a3,Ra as a4,Fo as a5,Ea as a6,Oo as a7,Ca as a8,Da as a9,ea as aA,Al as aB,_p as aC,Rl as aD,Yp as aE,_s as aF,Ns as aG,ks as aH,qp as aI,hi as aJ,si as aK,$i as aL,va as aM,sa as aN,oa as aO,Pa as aP,Io as aQ,Eo as aR,Ls as aS,Kt as aT,tl as aU,ma as aV,_a as aW,Nn as aX,Hn as aY,pi as aZ,is as a_,Ui as aa,Gi as ab,Hi as ac,Li as ad,za as ae,ms as af,ns as ag,qs as ah,qo as ai,Xs as aj,ba as ak,mp as al,Qs as am,fs as an,ar as ao,sr as ap,or as aq,nn as ar,Ce as as,lu as at,z_ as au,fo as av,qa as aw,Gt as ax,Vt as ay,Cr as az,B as b,Yi as b$,W as b0,Bp as b1,Wp as b2,kr as b3,Mr as b4,Sr as b5,vr as b6,ja as b7,_r as b8,gr as b9,Ed as bA,wi as bB,li as bC,ei as bD,ci as bE,Lo as bF,Si as bG,mn as bH,$u as bI,Su as bJ,uh as bK,gd as bL,Ai as bM,Ii as bN,En as bO,Cn as bP,Eh as bQ,ro as bR,Hh as bS,Ih as bT,tn as bU,Qi as bV,Ji as bW,Sa as bX,hp as bY,Xi as bZ,Zi as b_,br as ba,On as bb,pr as bc,fr as bd,mr as be,xr as bf,gl as bg,dl as bh,ml as bi,ul as bj,bl as bk,sl as bl,hl as bm,Ho as bn,_o as bo,xa as bp,cu as bq,Fd as br,im as bs,Pd as bt,Vs as bu,X_ as bv,nm as bw,op as bx,ti as by,Od as bz,he as c,co as c$,Ki as c0,$a as c1,Kp as c2,jp as c3,rn as c4,Kr as c5,lr as c6,De as c7,Fe as c8,Ae as c9,bd as cA,$d as cB,Md as cC,Rh as cD,Mh as cE,kh as cF,_h as cG,yn as cH,dh as cI,Nh as cJ,gn as cK,Bu as cL,_u as cM,xh as cN,$h as cO,mh as cP,dd as cQ,Oi as cR,Di as cS,mu as cT,Ci as cU,Ma as cV,zi as cW,Pi as cX,so as cY,cl as cZ,vl as c_,Bl as ca,Kl as cb,Vi as cc,nc as cd,jl as ce,El as cf,Yl as cg,Wl as ch,Ul as ci,ec as cj,tc as ck,Ql as cl,ic as cm,zl as cn,Dn as co,An as cp,ii as cq,J_ as cr,hu as cs,Z_ as ct,nl as cu,ys as cv,Vo as cw,Ko as cx,Mu as cy,Uh as cz,Ud as d,ri as d0,Fa as d1,ou as d2,Yd as d3,eu as d4,Zd as d5,Dm as d6,pe as e,Ku as f,Yu as g,ui as h,_e as i,Tr as j,fi as k,su as l,Ol as m,Nt as n,yi as o,gi as p,Lh as q,Xu as r,qt as s,au as t,qh as u,uu as v,Tu as w,Jt as x,wn as y,Pn as z}; +
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Am.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_furnCategory:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Am.ZOOM_MAX=8,Am.ZOOM_MIN=1/3,Am.styles=[Gd,qp],customElements.get("houseplan-card")||customElements.define("houseplan-card",Am),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${lm} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{zo as $,U as A,Qt as B,Ia as C,le as D,V as E,Zt as F,Ha as G,Ad as H,Ld as I,fs as J,lo as K,Hd as L,Dd as M,Ho as N,vs as O,Od as P,js as Q,Us as R,vo as S,oi as T,ao as U,xo as V,op as W,sp as X,ss as Y,Br as Z,Oo as _,o as a,ts as a$,no as a0,oo as a1,qr as a2,mp as a3,Ra as a4,Fo as a5,Ea as a6,Ao as a7,Ca as a8,Da as a9,ea as aA,Ol as aB,fp as aC,Cl as aD,Xp as aE,_s as aF,Hs as aG,ks as aH,jp as aI,hi as aJ,si as aK,$i as aL,va as aM,sa as aN,oa as aO,Pa as aP,Io as aQ,Eo as aR,Ls as aS,Kt as aT,tl as aU,ma as aV,_a as aW,Hn as aX,Nn as aY,pi as aZ,is as a_,Vi as aa,Ui as ab,Ni as ac,Li as ad,Oa as ae,ms as af,ns as ag,qs as ah,qo as ai,Xs as aj,ba as ak,gp as al,Qs as am,gs as an,ar as ao,sr as ap,or as aq,nn as ar,Ce as as,hu as at,F_ as au,go as av,qa as aw,Ut as ax,Gt as ay,Cr as az,j as b,Yi as b$,B as b0,Vp as b1,Up as b2,kr as b3,Mr as b4,Sr as b5,vr as b6,Wa as b7,_r as b8,fr as b9,Nd as bA,wi as bB,li as bC,ei as bD,ci as bE,Lo as bF,Si as bG,mn as bH,Mu as bI,Ru as bJ,_h as bK,vd as bL,zi as bM,Ii as bN,En as bO,Cn as bP,Nh as bQ,ro as bR,qh as bS,Hh as bT,tn as bU,Qi as bV,Ji as bW,Sa as bX,up as bY,Xi as bZ,Zi as b_,br as ba,An as bb,pr as bc,gr as bd,mr as be,xr as bf,vl as bg,pl as bh,gl as bi,_l as bj,kl as bk,sl as bl,ul as bm,No as bn,_o as bo,xa as bp,du as bq,Ed as br,rm as bs,Id as bt,Gs as bu,Q_ as bv,om as bw,ap as bx,ti as by,Pd as bz,he as c,ri as c$,Ki as c0,$a as c1,Zp as c2,Bp as c3,rn as c4,Kr as c5,lr as c6,De as c7,Fe as c8,ze as c9,kd as cA,Md as cB,Td as cC,Ch as cD,Th as cE,$h as cF,fh as cG,yn as cH,ph as cI,Lh as cJ,fn as cK,Vu as cL,fu as cM,Sh as cN,Mh as cO,gh as cP,pd as cQ,Ai as cR,Di as cS,gu as cT,Ci as cU,Ma as cV,Oi as cW,Pi as cX,so as cY,bl as cZ,co as c_,Vl as ca,Zl as cb,Gi as cc,oc as cd,Bl as ce,Nl as cf,Xl as cg,Ul as ch,Gl as ci,ic as cj,nc as ck,tc as cl,rc as cm,Fl as cn,Dn as co,zn as cp,ii as cq,em as cr,uu as cs,J_ as ct,nl as cu,ys as cv,Go as cw,Ko as cx,Tu as cy,Gh as cz,Gd as d,Fa as d0,au as d1,Xd as d2,iu as d3,Jd as d4,Am as d5,pe as e,Zu as f,Xu as g,ui as h,_e as i,Tr as j,gi as k,lu as l,Pl as m,Ht as n,yi as o,fi as p,Wh as q,Qu as r,qt as s,cu as t,jh as u,_u as v,Du as w,Jt as x,wn as y,Pn as z}; diff --git a/dist/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js similarity index 74% rename from dist/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js index 2615950d..8fad2f54 100644 --- a/dist/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as l,m as h,d as c,l as d,t as p,w as _,f as u,g as m,v as g,r as f,h as b,j as y,p as v,k as $,n as w,o as k,q as x,u as S,x as M,y as D,z as C,B as P,G as I,C as T,F as R,H as F,I as N,J as z,K as A,L as O,M as L,N as E,O as B,P as H,Q as G,R as W,S as j,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as _t,ae as ut,af as mt,ag as gt,ah as ft,ai as bt,aj as yt,ak as vt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as Ft,ay as Nt,az as zt,aA as At,aB as Ot,aC as Lt,aD as Et,aE as Bt,aF as Ht,aG as Gt,aH as Wt,aI as jt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as _e,b4 as ue,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ye,ba as ve,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as Fe,bo as Ne,bp as ze,bq as Ae,br as Oe,bs as Le,bt as Ee,bu as Be,bv as He,bw as Ge,bx as We,by as je,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as _s,bW as us,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as ys,c0 as vs,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as Fs,ce as Ns,cf as zs,cg as As,ch as Os,ci as Ls,cj as Es,ck as Bs,cl as Hs,cm as Gs,cn as Ws,co as js,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as _i,cM as ui,cN as mi,cO as gi,cP as fi,cQ as bi,cR as yi,cS as vi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti}from"./houseplan-card-DJctcs0x.js";import{i as Ri,c as Fi,a as Ni,s as zi,t as Ai}from"./space-deletion-BIiYcPJz.js";const Oi={},Li=t(class extends e{constructor(){super(...arguments),this.ot=Oi}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Ei=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Bi(t){return Number.isFinite(t)?(t%360+360)%360:0}function Hi(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Gi(t){const e=Hi(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function Wi(t){const e=Ei(t.r,0,255)/255,s=Ei(t.g,0,255)/255,i=Ei(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Bi(n),s:0===o?0:a/o*100,v:100*o}}function ji(t){return function(t){const e=t=>Math.round(Ei(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Bi(t.h),s=Ei(t.s,0,100)/100,i=Ei(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function qi(t){const e=t.visualViewport;return e&&Number.isFinite(e.width)&&Number.isFinite(e.height)?{left:Number(e.offsetLeft)||0,top:Number(e.offsetTop)||0,width:Math.max(0,Number(e.width)||0),height:Math.max(0,Number(e.height)||0)}:{left:0,top:0,width:t.innerWidth,height:t.innerHeight}}function Ui(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),l=Math.max(n,s.top+s.height-o),h=Math.max(0,r-a),c=Math.max(0,l-n),d=Math.min(Math.max(0,e.width),h),p=Math.min(Math.max(0,e.height),c);let _=t.left;_+d>r&&(_=t.right-d),_=Math.min(Math.max(a,_),Math.max(a,r-d));const u=t.bottom+i,m=t.top-i-p;let g="bottom",f=u;return u+p>l&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,l-p))):f=Math.min(Math.max(n,u),Math.max(n,l-p)),{left:Math.round(_),top:Math.round(f),side:g,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class Ki{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(t=!1){const e=this.window();return!t&&!!e&&function(t){const e=t.HTMLElement?.prototype;return"function"==typeof e?.showPopover&&"function"==typeof e?.hidePopover}(e)}get hasFallback(){return!!this.host}containsPath(t){return t.includes(this.owner)||!!this.host&&t.includes(this.host)}ownsActiveElement(){let t=this.owner.ownerDocument.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return!(!t||t!==this.owner&&!this.owner.shadowRoot?.contains(t)&&!this.root?.contains(t))}renderFallback(t,e){const s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const t=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!t)return null;const e=this.owner.ownerDocument.createElement("div");return e.dataset.hpOverlay=this.name,e.style.display="contents",this.keydown&&e.addEventListener("keydown",this.keydown,!0),t.append(e),this.host=e,this.root=e.attachShadow({mode:"open"}),this.root}}const Vi={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ji extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Vi,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Ki(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Hi(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Ji.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=qi(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=Ui(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Vi;return{title:t.title||Vi.title,hue:t.hue||Vi.hue,saturation:t.saturation||Vi.saturation,value:t.value||Vi.value,hex:t.hex||Vi.hex,invalidHex:t.invalidHex||Vi.invalidHex}}_syncFromColor(t,e){const s=Hi(l(t,"#607d8b"))||"#607d8b",i=Gi(s);if(!i)return;const o=Wi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(ji({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Hi(e),i=s?Gi(s):null;if(!s||!i)return;const o=Wi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Hi(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Hi(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=ji({h:this._hue,s:this._saturation,v:this._value}),i=ji({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),l=Math.round(this._saturation),h=Math.round(this._value);return o` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as h,m as l,d as c,l as d,t as p,w as _,f as u,g as m,v as g,r as f,h as b,j as v,p as y,k as $,n as w,o as k,q as x,u as M,x as S,y as C,z as D,B as P,G as I,C as T,F as R,H as z,I as F,J as A,K as N,L as H,M as O,N as L,O as E,P as B,Q as W,R as G,S as j,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as ht,a9 as lt,aa as ct,ab as dt,ac as pt,ad as _t,ae as ut,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as Mt,aq as St,ar as Ct,as as Dt,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as Ft,az as At,aA as Nt,aB as Ht,aC as Ot,aD as Lt,aE as Et,aF as Bt,aG as Wt,aH as Gt,aI as jt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as he,a$ as le,b0 as ce,b1 as de,b2 as pe,b3 as _e,b4 as ue,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Me,bg as Se,bh as Ce,bi as De,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Fe,bp as Ae,bq as Ne,br as He,bs as Oe,bt as Le,bu as Ee,bv as Be,bw as We,bx as Ge,by as je,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as hs,bR as ls,bS as cs,bT as ds,bU as ps,bV as _s,bW as us,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ms,c6 as Ss,c7 as Cs,c8 as Ds,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as Fs,cf as As,cg as Ns,ch as Hs,ci as Os,cj as Ls,ck as Es,cl as Bs,cm as Ws,cn as Gs,co as js,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as hi,cH as li,cI as ci,cJ as di,cK as pi,cL as _i,cM as ui,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Mi,cY as Si,cZ as Ci,c_ as Di,c$ as Pi,d0 as Ii}from"./houseplan-card-VbP9hCtk.js";import{i as Ti,c as Ri,a as zi,s as Fi,t as Ai}from"./space-deletion-Dh9IGz13.js";const Ni={},Hi=t(class extends e{constructor(){super(...arguments),this.ot=Ni}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Oi=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Li(t){return Number.isFinite(t)?(t%360+360)%360:0}function Ei(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Bi(t){const e=Ei(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function Wi(t){const e=Oi(t.r,0,255)/255,s=Oi(t.g,0,255)/255,i=Oi(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Li(n),s:0===o?0:a/o*100,v:100*o}}function Gi(t){return function(t){const e=t=>Math.round(Oi(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Li(t.h),s=Oi(t.s,0,100)/100,i=Oi(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function ji(t){const e=t.visualViewport;return e&&Number.isFinite(e.width)&&Number.isFinite(e.height)?{left:Number(e.offsetLeft)||0,top:Number(e.offsetTop)||0,width:Math.max(0,Number(e.width)||0),height:Math.max(0,Number(e.height)||0)}:{left:0,top:0,width:t.innerWidth,height:t.innerHeight}}function qi(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),h=Math.max(n,s.top+s.height-o),l=Math.max(0,r-a),c=Math.max(0,h-n),d=Math.min(Math.max(0,e.width),l),p=Math.min(Math.max(0,e.height),c);let _=t.left;_+d>r&&(_=t.right-d),_=Math.min(Math.max(a,_),Math.max(a,r-d));const u=t.bottom+i,m=t.top-i-p;let g="bottom",f=u;return u+p>h&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,h-p))):f=Math.min(Math.max(n,u),Math.max(n,h-p)),{left:Math.round(_),top:Math.round(f),side:g,maxWidth:Math.round(l),maxHeight:Math.round(c)}}class Ui{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(t=!1){const e=this.window();return!t&&!!e&&function(t){const e=t.HTMLElement?.prototype;return"function"==typeof e?.showPopover&&"function"==typeof e?.hidePopover}(e)}get hasFallback(){return!!this.host}containsPath(t){return t.includes(this.owner)||!!this.host&&t.includes(this.host)}ownsActiveElement(){let t=this.owner.ownerDocument.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return!(!t||t!==this.owner&&!this.owner.shadowRoot?.contains(t)&&!this.root?.contains(t))}renderFallback(t,e){const s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const t=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!t)return null;const e=this.owner.ownerDocument.createElement("div");return e.dataset.hpOverlay=this.name,e.style.display="contents",this.keydown&&e.addEventListener("keydown",this.keydown,!0),t.append(e),this.host=e,this.root=e.attachShadow({mode:"open"}),this.root}}const Ki={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Vi extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Ki,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Ui(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Ei(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Vi.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=ji(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=qi(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Ki;return{title:t.title||Ki.title,hue:t.hue||Ki.hue,saturation:t.saturation||Ki.saturation,value:t.value||Ki.value,hex:t.hex||Ki.hex,invalidHex:t.invalidHex||Ki.invalidHex}}_syncFromColor(t,e){const s=Ei(h(t,"#607d8b"))||"#607d8b",i=Bi(s);if(!i)return;const o=Wi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Gi({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Ei(e),i=s?Bi(s):null;if(!s||!i)return;const o=Wi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Ei(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Ei(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=Gi({h:this._hue,s:this._saturation,v:this._value}),i=Gi({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),h=Math.round(this._saturation),l=Math.round(this._value);return o` `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=l(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return o` +
`}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=h(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return o` ${this.label?o`${this.label}`:a} ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):a} - `}}Ji.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Ji.styles=n` + `}}Vi.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Vi.styles=n` :host { position: relative; display: inline-flex; @@ -322,9 +322,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d box-shadow: none; } } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",Ji);let Yi=0;class Xi extends r{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++Yi,this._surfacePointerEnter=()=>{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=this._floating.containsPath(t.composedPath()),s=t.target,i=this._dialog(),o=s instanceof Node&&(s===i||s instanceof Element&&s.contains(this));var a;this._open&&(a=o,!e&&a)&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{if(!this._open||t.defaultPrevented)return;if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const e=this.renderRoot.querySelector(".trigger");if(!e||!t.composedPath().includes(e))return;const s=this._surface();if(!s||s.scrollHeight<=s.clientHeight)return;const i=Math.max(40,.8*s.clientHeight),o={ArrowDown:40,ArrowUp:-40,PageDown:i,PageUp:-i};if("Home"===t.key)s.scrollTop=0;else if("End"===t.key)s.scrollTop=s.scrollHeight;else{if(!(t.key in o))return;s.scrollBy({top:o[t.key],behavior:"auto"})}t.preventDefault(),t.stopImmediatePropagation()},this._floating=new Ki(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const t=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("pointerHover")&&!this.pointerHover){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return t=this.text,e=this.ariaLabel,"string"==typeof t&&t.trim().length>0&&"string"==typeof e&&e.trim().length>0;var t,e}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();this.pointerHover&&t&&!this._open&&!this._openTimer&&(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){this.pointerHover&&"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const t=this._window();t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const t=this._window();t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".tooltip");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return o`
{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=this._floating.containsPath(t.composedPath()),s=t.target,i=this._dialog(),o=s instanceof Node&&(s===i||s instanceof Element&&s.contains(this));var a;this._open&&(a=o,!e&&a)&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{if(!this._open||t.defaultPrevented)return;if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const e=this.renderRoot.querySelector(".trigger");if(!e||!t.composedPath().includes(e))return;const s=this._surface();if(!s||s.scrollHeight<=s.clientHeight)return;const i=Math.max(40,.8*s.clientHeight),o={ArrowDown:40,ArrowUp:-40,PageDown:i,PageUp:-i};if("Home"===t.key)s.scrollTop=0;else if("End"===t.key)s.scrollTop=s.scrollHeight;else{if(!(t.key in o))return;s.scrollBy({top:o[t.key],behavior:"auto"})}t.preventDefault(),t.stopImmediatePropagation()},this._floating=new Ui(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const t=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("pointerHover")&&!this.pointerHover){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return t=this.text,e=this.ariaLabel,"string"==typeof t&&t.trim().length>0&&"string"==typeof e&&e.trim().length>0;var t,e}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();this.pointerHover&&t&&!this._open&&!this._openTimer&&(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){this.pointerHover&&"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const t=this._window();t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const t=this._window();t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".tooltip");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return o``}_renderFallback(){const t=Xi.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=qi(t);if(s.style.maxWidth=`${Math.max(0,Math.min(320,i.width-16))}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._usesPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=Ui(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}render(){if(!this._hasContent())return a;const t=this.ariaLabel.trim();return o` + @pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}
`}_renderFallback(){const t=Yi.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=ji(t);if(s.style.maxWidth=`${Math.max(0,Math.min(320,i.width-16))}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._usesPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=qi(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}render(){if(!this._hasContent())return a;const t=this.ariaLabel.trim();return o` ${this.text} ${this._usesPopover()?this._tooltipTemplate(!0):a} - `}}Xi.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Xi.styles=n` + `}}Yi.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Yi.styles=n` :host { display: none; flex: none; @@ -461,7 +461,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d transition: none; } } - `,customElements.get("hp-help")||customElements.define("hp-help",Xi);const Zi=new WeakMap;function Qi(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Zi.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Zi.set(e,s)),s}function to(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function eo(t,e){const s=Qi(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,to(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,to(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,to(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function so(t){const e=Qi(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function io(t,e,s=so(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function oo(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:io(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function ao(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function no(t,e,s,i=so(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return oo(t,o.platform||n,"entity-platform",i,a)}class ro extends r{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=eo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return d(this.hass)}_t(t,e){return p(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=so(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=no(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return oo(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return ao(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(no(t,e,s,i));return h.some(Boolean)?ao(h):ao((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return oo(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,so(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=no(this.hass,t,this.registry,so(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return a;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=_(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),n="none"!==t.pulse.kind,r=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let l=-r/2,h=r/2,c=-r/2,d=r/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(h=Math.max(h,i+t)),"left"===s.valueBadge.position&&(l=Math.min(l,-i-t)),"top"===s.valueBadge.position&&(c=Math.min(c,-i-e)),"bottom"===s.valueBadge.position&&(d=Math.max(d,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);d=Math.max(d,t)}const p=Math.min(1,2.35/Math.max(1,h-l),2.35/Math.max(1,d-c)),b=Math.round(100*p),y=[`left:calc(50% - ${54*((l+h)/2)*p}px)`,`top:calc(50% - ${54*((c+d)/2)*p}px)`,p<1?`transform:scale(${p})`:""].filter(Boolean).join(";"),v=["dev",u(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),$=m(s).join(";"),w=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),k=[this.deviceName,this._providerSummary(i),this._stateSummary(t),w,t.valueFullText||"",g(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return o`
+ `,customElements.get("hp-help")||customElements.define("hp-help",Yi);const Xi=new WeakMap;function Zi(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Xi.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Xi.set(e,s)),s}function Qi(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function to(t,e){const s=Zi(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Qi(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Qi(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,Qi(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function eo(t){const e=Zi(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function so(t,e,s=eo(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function io(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:so(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function oo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function ao(t,e,s,i=eo(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return io(t,o.platform||n,"entity-platform",i,a)}class no extends r{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=to(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return d(this.hass)}_t(t,e){return p(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=eo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=ao(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const h=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return io(t,s?.domain,"registry-owner",i,e)});if(h.some(Boolean))return oo(h);const l=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&l.push(ao(t,e,s,i));return l.some(Boolean)?oo(l):oo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return io(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,eo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=ao(this.hass,t,this.registry,eo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return a;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=_(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),n="none"!==t.pulse.kind,r=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let h=-r/2,l=r/2,c=-r/2,d=r/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(l=Math.max(l,i+t)),"left"===s.valueBadge.position&&(h=Math.min(h,-i-t)),"top"===s.valueBadge.position&&(c=Math.min(c,-i-e)),"bottom"===s.valueBadge.position&&(d=Math.max(d,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);d=Math.max(d,t)}const p=Math.min(1,2.35/Math.max(1,l-h),2.35/Math.max(1,d-c)),b=Math.round(100*p),v=[`left:calc(50% - ${54*((h+l)/2)*p}px)`,`top:calc(50% - ${54*((c+d)/2)*p}px)`,p<1?`transform:scale(${p})`:""].filter(Boolean).join(";"),y=["dev",u(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),$=m(s).join(";"),w=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),k=[this.deviceName,this._providerSummary(i),this._stateSummary(t),w,t.valueFullText||"",g(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return o`
${this._t("marker.preview.title")} @@ -470,8 +470,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d
`}}ro.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ro.styles=[c,n` +
`}}no.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},no.styles=[c,n` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -581,7 +581,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",ro);const lo=(t,e)=>[t[0]-e[0],t[1]-e[1]],ho=(t,e)=>[t[0]+e[0],t[1]+e[1]],co=(t,e)=>t[0]*e[0]+t[1]*e[1],po=t=>Math.hypot(t[0],t[1]);function _o(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function go(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(u),f(m))}}return i}function bo(t,e,s){if($(t,e,s))return!0;if(v(t,e,s)||v(e,t,s))return!1;let i=0;try{const s=w([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=k(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function yo(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function vo(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function $o(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function wo(t,e,s,i,o){return $o(t,s,o)&&$o(e,i,o)||$o(t,i,o)&&$o(e,s,o)}function ko(t,e,s,i,o){const a=vo(t,e,o);return a&&vo(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function xo(t,e,s,i){const o=t.length,a=vo(t[(e-1+o)%o],t[e],i),n=vo(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function So(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function Mo(t,e,s){const i=So(t),o=So(e);return i[2]o}const Co=(t,e)=>"h"===e?t[0]:t[1],Po=(t,e)=>"h"===e?t[1]:t[0];function Io(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=vo(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=Co(h,l),p=Co(c,l),_=Po(h,l),u=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(Po(r,e)-s)>i)continue;const l=Co(n,e),h=Co(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,_,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(u,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:_,fixed:d,moving:p,intervals:u,baseline:m}}function To(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(vo(n,r,s)!==t.axis||Math.abs(Po(n,t.axis)-t.line)>s||Math.abs(Po(r,t.axis)-t.line)>s)continue;const l=Co(n,t.axis),h=Co(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function Ro(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function Fo(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(vo(o,a,s)!==i.axis)return!1;const n=Co(0===i.movedEndpoint?o:a,i.axis),r=To(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),_=Math.min(i.fixed,i.moving),u=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1_-s&&am-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function No(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?ho(t,o):[...t]),a.movedSpans[e]=[[ho(i.poly[n],o),ho(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function zo(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(uo([t.x,t.y],e,s)>2*i)return!1;const o=po(lo(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(uo([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return po(lo([t.x,t.y],l))>=t.length/2+n-r}function Ao(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?uo(t.center,e,s)2)return!1;const r=No(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!Fo(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!go(h))return!1;const d=l.poly.length,p=(c-1+d)%d,_=[p,c,(c+1)%d];for(const t of _)if(y(h[t],h[(t+1)%d]))return!1;const u=_o(l.poly),m=_o(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!Mo(s.poly,o.poly,a)||!Mo(i,t,a))if(v(s.poly,o.poly,a)){if(!v(i,t,a))return!1}else if(v(o.poly,s.poly,a)){if(!v(t,i,a))return!1}else if(v(i,t,a)||v(t,i,a)||bo(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],_=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(Ao(t,p,_,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!yo({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const Lo=new WeakMap,Eo=t=>"number"==typeof t&&Number.isFinite(t),Bo=(t,e)=>{const s=new Map;for(const i of t||[])!Eo(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Ho=t=>[String(t),"см"].join(" ");function Go(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=Bo(t,i),a=Bo(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Ho(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Ho(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Ho(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Wo{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=Lo.get(s);h&&h.opts===a||(h={opts:a,values:new Map},Lo.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=Oo(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=No(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Go(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if(Oo(e.rooms,e.openings,e.plan,e.delta,e.options))if(Go(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;return!s||void 0!==e&&s.pointerId!==e?{kind:"no-op"}:(this._session=null,this._eligibility=null,{kind:"cancelled",restoreWallUnion:t===s.snapshotIdentity?s.wallUnionBefore:null})}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const jo=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function qo(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=jo(r,o),p=jo(t.gearCenter,o),_=Math.max(34,7.2*i.length+12),u=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,_/2+4):Math.max(28,13),m=d[0]+l[0]*u,g=d[1]+l[1]*u,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=_,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const y=l[0]*u+c[0]*b,v=l[1]*u+c[1]*b,$=Math.hypot(y,v)||1,w=v/$*12,k=[r[0]+y/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:y,offsetYPx:v,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const Uo=1e-9,Ko=1e7,Vo=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*Ko+.5)/Ko;return Object.is(e,-0)?0:e},Jo=t=>`${Vo(t[0])},${Vo(t[1])}`,Yo=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Xo=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),Zo=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),Qo=t=>(t||[]).filter(t=>Zo(t?.a)&&Zo(t?.b)&&Yo(t.a,t.b)>Uo);function ta(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of Qo(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(Jo(t))||[];e.push(Xo(t,s)),i.set(Jo(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tUo&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,sa=(t,e,s=1)=>{const i=Math.abs(ea(t)-ea(e));return Math.min(i,180-i)<=s},ia=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(Jo(t));i?i.push(s):e.set(Jo(t),[s])}return e};function oa(t,e,s){const i=Qo(e),o=s??ia(i),a=new Set([t]);let n=Yo(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(Jo(e))||[])a.has(s)||sa(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=Yo(s.a,s.b),r.push(s.a,s.b))}return n}function aa(t,e,s,{minLengthCm:i=20}={}){const o=[],a=Qo(t),n=ia(a);for(const t of a){const r=oa(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=Uo?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function ra(t,e,s,{minDistanceCm:i=5}={}){const o=Qo(t),a=new Map;for(const t of o)a.set(Jo(t.a),t.a),a.set(Jo(t.b),t.b);const n=((t,e,s)=>t/(e||1)*s)(i,e,s),r=n>Uo?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,_]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=Yo(o,_);a<=2e-7||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return lt.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(S(e,t,l)&&!h.has(i))continue;const o=ha.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(S(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,_=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${_?` · ${_}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const da={kind:"active",enabledEntityIds:[],allEntityIds:[]};function pa(t){return t.startsWith("entity:")?"entity":"device"}function _a(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const ua=.001;function ma(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function ga(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function fa(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=ba(t.a,t.b);return{a:e,b:s,key:`hidden|${va(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${ya(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function xa(t){const e=t.epsilon??ua,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!ma(e.a)||!ma(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of M(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1wa(s,t,e)))continue;const t=ga(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function Sa(t,e,s){return e.some(e=>ma(e)&&fa(t,e,s))}function Ma(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function Da(t,e,s){const i=s.epsilon??ua,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>ma(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(Sa(t.point,o,i))continue;const e=ga(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function Ca(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function Pa(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function Ia(t,e,s){if(!(ma(e)&&ma(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=Da(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=Ca(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||Sa(d,s.excludePoints||[],i)||!Ma(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const Ta=.001;function Ra(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function za(t,e,s,i){return t*i-e*s}function Aa(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function Ha(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function Ga(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function Wa(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function ja(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=Wa(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function qa(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=za(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=za(h,c,r,l)/d,e=za(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(za(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=ja(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=ja(s,e,o);null!=t&&i.push(t)}}}function Ua(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function Ka(t,e){return t.minY-e.minY||t.index-e.index}function Va(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Ja(t){const e=t.right;return t.right=e.left,e.left=Va(t),Va(e)}function Ya(t){const e=t.left;return t.left=e.right,e.right=Va(t),Va(e)}function Xa(t,e){return t?(Ka(e,t.item)<0?(t.left=Xa(t.left,e),t.left.priority0)t.right=Za(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&Qa(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&Qa(t.right,e,s,i))}function tn(t,e=.001){const s=Number.isFinite(e)&&e>0?e:Ta,i=t.filter(t=>Na(t.a)&&Na(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)qa(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=Xa(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function en(t,e=.001){const s=Number.isFinite(e)&&e>0?e:Ta,i=tn(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=La(t.a,s),i=La(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[La(t.a,s),La(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,_=a,u=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,_);if(l.has(s)){u=p===t&&_===a;break}l.add(s),e.push(p),d.push(Ea(p,_));const i=r.get(_)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=_,_=i[(o-1+i.length)%i.length],p===t&&_===a){u=!0;break}}if(!u||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=Aa(m);if(!(g>s*s))continue;const f=Ba(Ha(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const y={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,y)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function sn(t,e,s=.001){if(!Na(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function on(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function an(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function nn(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(an(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&on(e.a,t.a,t.b)<=s&&on(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:an(e.angle,s)?on([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function rn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const ln=t=>!t.key.startsWith("static:room|");function hn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function cn(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function dn(t,e,s){if(ln(t.source)!==ln(e.source))return ln(t.source)?t:e;const i=cn(t.point,s),o=cn(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function pn(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function _n(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function un(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=dn(n,r,e.gridStep||0),c=h===n?r:n;if(!ln(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(pn(d),d)}for(const a of o)if(ln(a.source))for(const o of t){if(o===a.source)continue;const t=hn(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(pn(n),n)}const a=[];for(const o of i.values()){const i=en(_n(t,o),s),n=e.point?sn(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||pn(t.proposal).localeCompare(pn(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${pn(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const mn=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class gn{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,a=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return o`
-
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(mn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=mn(t,t=>t.classList.contains("editor-secondary")),i=mn(t,t=>!!t.dataset.editorGroup),o=mn(t,t=>!!t.dataset.editorPalette),a=mn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const fn=t=>t.stopPropagation(),bn=1e-6*I,yn=6*I;function vn(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const $n=(t,e,s,i)=>Math.hypot(s-t,i-e),wn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),kn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,_=Math.max($n(t+h,e+c,o+d,a+p),$n(t-h,e-c,o-d,a-p)),u=Math.max($n(t+h,e+c,o-d,a-p),$n(t-h,e-c,o+d,a+p));return Math.min(_,u)},xn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Sn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=bn&&c++},_=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},u={};let m=5;for(const t of s){const e=xn(t);null!=t?.id&&(u[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>bn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,l=s)};for(const t of s){const e=xn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[vn(e[0]),vn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),_(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=vn(e+o),r=vn(s+a),l=vn(e),h=vn(s),c=Math.max(I,n-l),d=Math.max(I,r-h);t=wn(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[vn(e[0]),vn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)_(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[vn(o[0]),vn(o[1])],l=[vn(n[0]),vn(n[1])];let h=Math.max($n(o[0],o[1],r[0],r[1]),$n(n[0],n[1],l[0],l[1]));const c=$n(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-bn&&i+e/2<=c+bn});c>bn&&d?(i.a=r,i.b=l,_(o,r),_(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[vn(t[0]),vn(t[1])],n=$n(t[0],t[1],o[0],o[1]);i.center=o,_(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[vn(e[0]),vn(e[1])],a=[vn(s[0]),vn(s[1])];t=Math.max($n(e[0],e[1],o[0],o[1]),$n(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],_(e,o),_(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=vn(e),r=vn(s);if(null!=i.w&&null!=i.h){const l=vn(e+o),h=vn(s+a),c=Math.max(I,l-n),d=Math.max(I,h-r);t=wn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=$n(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=bn)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),_=!(Number.isFinite(p)&&p===d),u=kn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,_&&h++,g(u,e,s,_);continue}const o=P([i.x,i.y],t.rooms||[],yn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=kn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=vn(s.x),n=vn(s.y),r=$n(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,u[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function Mn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Dn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Cn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function Pn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Cn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function In(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Dn(i.a)&&Dn(i.b)&&(Cn(t,i.a)<=o||Cn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Cn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function Rn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Dn(t.a)&&Dn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tCn(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/_:0,m=_>0?(d[1]-c[1])/_:0,g=t=>_>0?((t[0]-c[0])*u+(t[1]-c[1])*m)/_:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function Fn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Mn(t.t,n)},a++;const r=F(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,N(s,r,i.coordScale))}return a}function Nn(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const zn=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),An=(t,e)=>t[0]-e[0]||t[1]-e[1],On=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],Ln=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,En=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=Ln(s,t,l,h),p=Ln(i,t,l,h),_=Math.max(0,Math.min(d,p)),u=Math.min(r,Math.max(d,p));return u-_>o?{lo:_,hi:u}:null},Bn=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Hn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Gn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!zn(o))return!0;const a=Ln(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},Wn=(t,e,s)=>{if(t.host){const i=F(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},jn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Un=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Vn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,_=r;const u=z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:A;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const y=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Hn(t))continue;const s=y.get(t.id);if(!(s&&zn(s.a)&&zn(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=An(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const u=r/h,g=l/h,v=z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=v.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=En(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=_.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:F(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=Ln(e.center,a,u,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>qn(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=On(a,u,g,i),l=On(a,u,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],_=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:A)),f=p[0]?.kind,b=[...d.keys()].sort(),y=1===_.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),v={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!En(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:A:0,I=y?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tBn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&O(e,v,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=L(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)Bn(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>Bn(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=L(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)Bn(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=An(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=E(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=B(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const K=new Set(p.map(t=>t.id));K.delete(s.id);const V=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Un(s.id,i,a,K);return K.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...V);const X=Y.map(t=>qn(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=N(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=S.get(t.id),a=V[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=Ln(e.center,n.a,r/h,l/h),p={...N(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!H(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=_.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>Wn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=Wn(t,X,o);e&&st.some(t=>jn(t,e,c))&&(it=!0)}if(it)continue;const at=G(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=W(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,_=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:_,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Jn=t=>JSON.parse(JSON.stringify(t)),Yn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Xn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Zn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Qn=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const tr=1e-9;class er extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const sr=t=>JSON.parse(JSON.stringify(t)),ir=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),or=t=>[Number(t[0]),Number(t[1])],ar=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,nr=(t,e)=>{const s=ar(t),i=ar(e);return sar(t)<=ar(e)?[or(t),or(e)]:[or(e),or(t)],lr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),hr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},dr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},pr=(t,e,s)=>{const i=Math.max(0,Math.min(1,dr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=tr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=dr(s,t,e),c=dr(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},ur=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,_]=r;for(let t=0;t<64;t++){const a=_+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;_=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+_>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},gr=(t,e,s,i,o="")=>{const[a,n]=rr(e,s),r=`${t}|${ar(a)}|${ar(n)}|${[...i].sort().join(",")}${o}`;return`wall-${mr(ur(r)).slice(0,20)}`};let fr=0;const br=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||mr(ur(`${Date.now()}|${fr++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new er("duplicate-id","id factory exhausted")},yr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&ir(s.a)&&ir(s.b)){if(e.has(s.id))throw new er("duplicate-id",s.id);e.set(s.id,s)}return e},vr=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=tr&&Math.abs(e[1]-i[1]-a)<=tr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},$r=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>pr([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(pr(i,t,e)>.02*I)return!1;const o=lr(t,e),a=dr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-tr&&a-n<=o+tr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new er("invalid-room",r);const _=Y(s,r,n,I,1,[...t.walls||[],...l]);if(!_||_.poly.length<3)throw new er("invalid-room",r);const u=Array.isArray(i.wall_ids)?i.wall_ids:[],m=u.length===p.length;let g=null,f=m;if(f)for(let t=0;ttr||Math.abs(i[1]-g[1])>tr)){f=!1;break}g=i}const b=[];for(let t=0;t<_.poly.length;t++){const s=_.poly[t],i=_.poly[(t+1)%_.poly.length];if(lr(s,i)<=tr)throw new er("zero-length",r);const n=nr(s,i);let l=c.get(n);if(!l){const[t,e]=rr(s,i);l={key:n,a:t,b:e,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},c.set(n,l)}if(l.owners.add(r),l.owners.size>2)throw new er("third-owner",n);const d=m?u[_.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>tr?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:or(p[_.parent[t]]),b:or(p[(_.parent[t]+1)%p.length])})}const g=_.parent[t];l.parentKeys.add(K(p[g],p[(g+1)%p.length],I));const y=[(s[0]+i[0])/2,(s[1]+i[1])/2],v=t=>t.some(t=>pr(y,[t[0],t[1]],[t[2],t[3]])<=.04*I);l.zeroWall||=v(a)||v(o)&&!h(s,i),b.push(n)}const y={...i};delete y.wall_ids,d.push({...y,poly:_.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},wr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[nr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new er("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new er("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>tr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||lr(e.a,e.b)-lr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new er("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=pr(o,t.a,t.b)<=tr?0:1,a=pr(o,e.a,e.b)<=tr?0:1;if(s!==a)return s-a;return(pr(i.a,t.a,t.b)<=tr?0:1)-(pr(i.a,e.a,e.b)<=tr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new er("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=rr(e.a,e.b),o=`${String(t.id||"")}|${ar(s)}|${ar(i)}|${[...e.owners].sort().join(",")}`,a=mr(ur(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new er("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=br(h),h.add(t.id)},kr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=dr(s,e.a,e.b),o=lr(e.a,e.b),a=Number(t.length)/2;return i>=-tr&&i<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-tr&&i*o+a<=o+tr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,dr(s,n.a,n.b)))}},xr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=dr(s,e.a,e.b),a=lr(e.a,e.b);return o>=-tr&&o<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-tr&&o*a+i<=a+tr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>pr(s,t.a,t.b)-pr(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,dr(s,a.a,a.b)))}:null},Sr=(t,e,s)=>{const i=yr(t),{atoms:o,rooms:a}=$r(t,i);wr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=K(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!ir(t.a)||!ir(t.b))&&_r(e.a,e.b,t.a,t.b)>=lr(e.a,e.b)-tr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new er("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:K(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=kr(t,e);s&&(t.host=s);continue}const i=kr(t,e)??(s?xr(t,e):null);if(i)t.host=i;else{if(!s)throw new er("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Mr(t,e={}){const s=JSON.stringify(t),i=sr(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Sr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Dr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Cr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Dr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Dr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):sr(e)}return o[e,e in t?s(t[e],i):sr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return sr(e)};return s(t,e)}const Pr=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Ir=(t,e,s)=>Math.min(s,Math.max(e,t)),Tr=t=>ut({spaces:[t]})[0],Rr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of R(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const _=mt(t,n.id,e,s,i,o,a,r);if(_)for(let t=0;t<_.orig.length;t++){const e=[];for(let s=0;s<_.parent.length;s++)_.parent[s]===t&&e.push(s);for(let t=1;t+10&&u>0&&n===m&&u!==n))continue;const g=_.poly[i],f=_.poly[(i+1)%_.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const y=Rr(g,f,r),v=p.get(y);v?v.targets.add(n):p.set(y,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let _=e.slice();const u=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(u(s,e.a)&&u(i,e.b)||u(s,e.b)&&u(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Rr(t.a,t.b,r).localeCompare(Rr(e.a,e.b,r)))){const e=[...t.targets][0],s=_.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;_=_.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(_=E(_,t.a,t.b,e,i,r))}return _}const Nr=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=Zn("space",t);e.layers>0&&Xn("space",e.root)&&Qn(d,e.root,t)}const p=new Map,_=new Map,u=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Zn("room",t);o.layers>0&&Xn("room",o.root)&&Qn(s,o.root,t),_.has(t)||_.set(t,e),u.has(t)||u.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(_.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const y={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},v=new Set,$=t=>c.has(t)?null:(()=>{const e=Zn("space",t).root;if(!Xn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Zn("room",t).root;if(!Xn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,y.roomRefsRemapped++):s?(t.room_id=s.roomId,y.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,y.roomRefsRemapped++):y.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,y.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,y.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,y.spaceRefsRemapped++):(delete t.space,h=null,y.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},y.positionsRemapped++,v.add(e))}for(const t of Object.keys(o)){if(v.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Yn(o,s)||(o[s]={...e,s:i}),delete o[t],y.roomRefsRemapped++,y.positionsRemapped++;continue}}o[t]={...e,s:i},y.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?y.orphanRoomLabelsRemoved++:"group"===t?y.orphanGroupPositionsRemoved++:y.orphanDevicePositionsRemoved++},F=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",u.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(y.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],y.liveMissingPositionsRemoved++,R(a.kind),y.removedPositions.push(a);continue}}else y.unverifiedPositions.push(a);y.positionsUnresolved++,F.add(i)}else delete o[t],R(a.kind),y.removedPositions.push(a)}return y.deadSpaceIds=[...F].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:y,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Pr(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Pr(o,"segments")&&(delete o.segments,e++),Pr(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=lt&&t<=ht&&(Pr(o,"plan_scale_x")||(o.plan_scale_x=t),Pr(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Pr(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Ir(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Pr(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Pr(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Pr(t,"width")){const s=Number(t.width);(Pr(t,"width_cm")||Number.isFinite(s))&&(Pr(t,"width_cm")||(t.width_cm=Number(Ir(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Pr(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Pr(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Ir(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Pr(t,"scale")||Pr(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Pr(t,"entity")||Pr(t,"attr")||Pr(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":_t(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const _=(u=n.spaces||[],JSON.parse(JSON.stringify(u)));var u;const m=Sn(n.spaces||[],a.layout);let g=0,f=0,b=0,y="";const v=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,y=String(t?.id||"")),e.space}),$=g?Sn(v,m.layout):{...m,spaces:v};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Tr(e),r=Tr(s);if(!a||!r)continue;const l=Nr(a.rooms),h=Nr(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:K(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),u=it(e,a.rooms,ot,c);x+=u.length;for(const t of u){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=K(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,l,h,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const y=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...y,...b.map(({key:t,...e})=>e)];const v=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,l,h,I,ot);w=Fr(r.rooms,w,v,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=B(r.rooms,w,v,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,v.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=Rn(s.partitions||[],{pitch:I,geometry:Nn(s)});R.merged&&(M+=R.merged,s.partitions=R.partitions,Fn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const F=Tr(s);if(F){const t=o(s,F,s.walls||[],v,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Mr(t);return Cr(t,e.config),{...e,config:t}}(n).migratedSegments);const F=U(n),N=et($.layout),z=JSON.stringify(F)!==l||JSON.stringify(N)!==h;c<9&&z&&(F.model_version=9);const A=JSON.stringify(F)!==l||JSON.stringify(N)!==h,O=Number.isInteger(Number(F.model_version))?Number(F.model_version):c,L=A?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},E=A?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:F,layout:N,report:{...L,modelFrom:c,modelTo:O,migrated:A?p:0,glowSpacesMigrated:A?d.glowSpaces:0,glowRoomsMigrated:A?d.glowRooms:0,canonicalized:A?T:0,wallSegmentsMigrated:A?R:0,legacyZeroWallsMigrated:A?x:0,wallsMerged:A?k:0,spansMerged:A?S:0,partitionsMerged:A?M:0,partitionsReconciled:A?D:0,openingsRehosted:A?C:0,redundantDraftsRemoved:A?P:0,wallsStraightened:A?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:A?b:0,maxStraightenSpace:A?y:"",latticeCoordinatesCanonicalized:A?r.canonicalized:0,latticeCoordinatesFar:A?r.far:0,latticeMaxShift:A?r.maxShift:0,latticeMaxShiftCm:A?r.maxShiftCm:0,latticeSpaces:A?r.spaces:[],...E},changed:A}}const Ar=(t,e)=>t[0]*e[1]-t[1]*e[0],Or=(t,e)=>t[0]*e[0]+t[1]*e[1],Lr=(t,e)=>[t[0]-e[0],t[1]-e[1]],Er=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Br(t,e,s){return Math.abs(Ar(t,e))<=s}function Hr(t,e,s){return Math.abs(Ar(Lr(t,e),s))}function Gr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Br(l,[i/n,a/n],1e-6)))return[];if(Hr(e.a,t,l)>1.1*Math.max(s,o)||Hr(e.b,t,l)>1.1*Math.max(s,o))return[];return[[Or(Lr(e.a,t),l),Or(Lr(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=bt({a:Er(t,l,e),b:Er(t,l,i),halfDepth:s});return o?[o]:[]})}function Wr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Er([t.x,t.y],s,-i),b:Er([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Gr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Gr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function jr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Er(s.center,a,o),r=[];for(let e=0;ei&&Br(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(Hr(o,n,s.axis)>i||Hr(a,n,s.axis)>i)continue;const c=Or(Lr(o,n),s.axis),d=Or(Lr(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=Er(n,s.axis,-s.half),p=Er(n,s.axis,s.half),_=Math.max(0,-s.half-h),u=Math.max(0,c-s.half),m=_>0?Er(n,s.axis,h):d,g=u>0?Er(n,s.axis,c):p,f=Or(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,_),right:b(p,g,u)}}function qr(t){const e=Er(t.center,t.axis,-t.half),s=Er(t.center,t.axis,t.half),i=Er(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Er(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ur(t,e,s,i){const o=Lr(s,e),a=Or(o,o);if(!(a>i*i))return Math.hypot(...Lr(t,e))<=i;const n=Or(Lr(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Er(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Lr(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function Vr(t,e,s,i,o,a){const n=Lr(i,s),r=Ar(e,n);if(Math.abs(r)<=a)return null;const l=Lr(s,t),h=Ar(l,n)/r,c=Ar(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Jr(t,e,s){const i=qr(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iOr(Lr(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Lr(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&Br(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Hr(a,e.center,e.axis)>s||Hr(n,e.center,e.axis)>s)continue;const h=Or(Lr(a,e.center),e.axis),c=Or(Lr(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||_2)return qr(s);if(i.length){const t=i.map(({room:t,edge:i})=>jr(t,i,s,e.epsilon));if(t.some(t=>!t))return qr(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Jr(t,e,s)}const Xr="1.69.0-beta.1",Zr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Qr={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},tl=2e3,el=1e3,sl=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},il=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},ol=1e3,al=2e3,nl=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},rl="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";class ll{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=sl(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?yt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?yt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=sl(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&vt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=sl(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),l)},this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=nn(z(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>al)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*ol,Number(e.a?.[1])*ol],i=[Number(e.b?.[0])*ol,Number(e.b?.[1])*ol];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/ol,a.interval.a[1]/ol],b:[a.interval.b[0]/ol,a.interval.b[1]/ol],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*ol,i.a[1]*ol],b:[i.b[0]*ol,i.b[1]*ol]};e.host={kind:"partition",id:i.id,t:rn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>al)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=Ra(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,A);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Dt(this.host._settings,{bgColor:null})||null,northDeg:Mt(this.host._settings,{}),bgMode:St(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Ct).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new gn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Wo}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const _=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(_,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&_===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(A,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=Rn(s,{pitch:I,seedIds:e,geometry:Nn(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,Fn(t.openings,t.partitions,i.openingMoves,{coordScale:ol,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=Fa(this.host._path,Ra(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,A)),s=e.length;if((t.partitions||[]).length+s>al)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=al:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(Ft(t[0],e)),Rt(Ft(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Nt(s,t,this.host._gridPitch,Ti):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=W(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=xa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=ka({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ia(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ma(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=s.excludePoints||[],a=Ca(Da(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=C([e[0],e[1]],t);if(r>s.tolerance)continue;const l=Pa(a,e,s.gridStep);!Sa(l,o,i)&&Ma(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?zt(i,l):l;return{point:h,candidate:r&&q(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:At(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=hr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ot(ut({spaces:[t]}),t.id),s=e?Lt(t,e,ol,.02*nt).contour.map(t=>t.map(t=>t/ol)):V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Et(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Bt(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=Mi(this.host._serverCfg),e=Et(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Et(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof er?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ta(a),...aa(a,o,I),...ra(a,o,I)];let r=null;try{r=Ht(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let l="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!l&&r&&r.nodes.length)try{const t=Gt(i.rooms||[],i.walls||[],[],[],I,o,I,1);l="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{l=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Wt(i.rooms||[],e,i.walls||[],[],I,o,I,1,l??void 0,r)}catch{s=null}n.push(...la(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Mr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let l=[];try{l=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(l,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new er("invalid-room",e.spaceId);const s=Mr(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new er("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=$r(s,yr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=R(l),c=R(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;atr||pr(r,s,i)>tr)continue;const l=dr(n,s,i),h=dr(r,s,i);l<-tr||h>1+tr||h<=l+tr||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/ol,t[1]/ol]):[];i.push(...s.map(t=>[t[0],t[1]])),h=cr(t)<=cr(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Cr(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),_=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:_?.before||l,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Mr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Cr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Cr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!go(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>al)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=zt(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),_=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],u=[...p,...null==d?[]:[{cm:d}],...l],m=_.length>=4&&this._samePt(_[0],_[_.length-1]),g=_,f=u;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const y=this._geometrySnapshot(),v=this.host._activeDraftId||t.draft.id,$={...a||n,id:v,points:g.map(t=>[t[0]/ol,t[1]/ol]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=v,this.host._resumeDraftBySpace[this.host._space]=v,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),v),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),y),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=Ra(i,e,this.host._drawWallCm,A);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*ol,Number(t?.[1])*ol]):[],o=Array.isArray(e?.segments)?e.segments:[];return Ra(Math.max(0,t.length-1),s,this.host._drawWallCm,A).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=en(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=un(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Kt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=sn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=un(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=un(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(yt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>vt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/ol,e[1]/ol]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?At(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),nl(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Vt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Vt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*ol-Math.min(...o),Math.min(Xt*ol-Math.max(...o),s[0])),Math.max(-Xt*ol-Math.min(...a),Math.min(Xt*ol-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/ol,o.a[1]/ol],t.b=[o.b[0]/ol,o.b[1]/ol];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=F(t,[o],ol,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,N(t,s,ol))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&vt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/ol,(o.center[1]+e.delta[1])/ol])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),nl(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:At(e.angle),angle:At(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=At(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&vt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*ol,t.a[1]*this.host._spaceH],b:[t.b[0]*ol,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!go(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=vo(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!xo(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Do(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=po(lo(l,r)),d=[],p=new Set;let _=!1,u=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!go(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!xo(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(uo([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),y=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Io(t,e,o,1,a),l=Io(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};y.push(r,l)}const v={roomId:s,edge:i,a:[...r],b:[...l],n:mo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:y};if(!Oo(t,e,v,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>Oo(t,e,v,s,o))){if(!s.some(s=>Fo(No(t,e,v,s),v,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>Oo(t,e,v,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>Oo(t,[],v,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:v}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/ol,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/ol,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*ol,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/ol,i[t][1]/ol],o.b=[i[(t+1)%i.length][0]/ol,i[(t+1)%i.length][1]/ol])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,h,c,this.host._wallKeyPitch,ol,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=p(t);_.set(e,(_.get(e)||0)+1)}const u=[];for(const t of r.walls||[]){const e=p(t),s=_.get(e)||0;s?_.set(e,s-1):u.push(t)}if(Qt(u,d,this.host._wallKeyPitch,ol,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Et([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};il(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));nl(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};il(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),_=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[u,m]=this.host._lastValidStageSize||[_.w,_.h],g={..._,stageWidth:Math.max(1,u),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,y=f?se(b>8?2.5:b,f,_.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,v=Math.max(10,.77*y),$=this.host._t("room.settings_short"),w=v*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),h=ne(r,l),p=qo({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:v});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=le(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>he(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(mn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=mn(t,t=>t.classList.contains("editor-secondary")),i=mn(t,t=>!!t.dataset.editorGroup),o=mn(t,t=>!!t.dataset.editorPalette),a=mn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const fn=t=>t.stopPropagation(),bn=1e-6*I,vn=6*I;function yn(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const $n=(t,e,s,i)=>Math.hypot(s-t,i-e),wn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),kn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,h=Math.PI/180,l=Math.cos(s*h)*r,c=Math.sin(s*h)*r,d=Math.cos(n*h)*r,p=Math.sin(n*h)*r,_=Math.max($n(t+l,e+c,o+d,a+p),$n(t-l,e-c,o-d,a-p)),u=Math.max($n(t+l,e+c,o-d,a-p),$n(t-l,e-c,o+d,a+p));return Math.min(_,u)},xn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Mn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,h="",l=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=bn&&c++},_=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},u={};let m=5;for(const t of s){const e=xn(t);null!=t?.id&&(u[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>bn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,h=s)};for(const t of s){const e=xn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),_(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=yn(e+o),r=yn(s+a),h=yn(e),l=yn(s),c=Math.max(I,n-h),d=Math.max(I,r-l);t=wn(e,s,o,a,h,l,c,d),i.x=h,i.y=l,i.w=c,i.h=d,p(e,h),p(s,l),p(e+o,h+c),p(s+a,l+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],h=n.length?[[o[0],n[0]]]:[],l=[];for(let t=0;t+1=2)for(const[t,e]of h)_(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[yn(o[0]),yn(o[1])],h=[yn(n[0]),yn(n[1])];let l=Math.max($n(o[0],o[1],r[0],r[1]),$n(n[0],n[1],h[0],h[1]));const c=$n(r[0],r[1],h[0],h[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-bn&&i+e/2<=c+bn});c>bn&&d?(i.a=r,i.b=h,_(o,r),_(n,h)):l=0,g(l,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[yn(t[0]),yn(t[1])],n=$n(t[0],t[1],o[0],o[1]);i.center=o,_(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[yn(e[0]),yn(e[1])],a=[yn(s[0]),yn(s[1])];t=Math.max($n(e[0],e[1],o[0],o[1]),$n(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],_(e,o),_(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=yn(e),r=yn(s);if(null!=i.w&&null!=i.h){const h=yn(e+o),l=yn(s+a),c=Math.max(I,h-n),d=Math.max(I,l-r);t=wn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=$n(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=bn)continue;const h=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),_=!(Number.isFinite(p)&&p===d),u=kn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,h,c,d);i.x=h,i.y=c,i.angle=d,_&&l++,g(u,e,s,_);continue}const o=P([i.x,i.y],t.rooms||[],vn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),h=kn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&l++,g(h,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=yn(s.x),n=yn(s.y),r=$n(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const h="string"==typeof s.s?s.s:"";g(r,u[h]??m,h)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:h,rotated:l,removedDrafts:d},changed:o>0||c>0}}function Sn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Cn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Dn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function Pn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Dn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function In(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Cn(i.a)&&Cn(i.b)&&(Dn(t,i.a)<=o||Dn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Dn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function Rn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Cn(t.a)&&Cn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tDn(e,t.at)>s);let c=l[0]??r.a,d=l[l.length-1]??h.b;if(l.length<2&&(c=r.a,d=h.b),d[0]0?(d[0]-c[0])/_:0,m=_>0?(d[1]-c[1])/_:0,g=t=>_>0?((t[0]-c[0])*u+(t[1]-c[1])*m)/_:0;for(const t of[r,h]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function zn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Sn(t.t,n)},a++;const r=z(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,F(s,r,i.coordScale))}return a}function Fn(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const An=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Nn=(t,e)=>t[0]-e[0]||t[1]-e[1],Hn=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],On=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,Ln=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const h=a/r,l=n/r,c=e=>Math.abs((e[0]-t[0])*l-(e[1]-t[1])*h);if(c(s)>o||c(i)>o)return null;const d=On(s,t,h,l),p=On(i,t,h,l),_=Math.max(0,Math.min(d,p)),u=Math.min(r,Math.max(d,p));return u-_>o?{lo:_,hi:u}:null},En=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Bn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Wn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,h=a/n,l=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!An(o))return!0;const a=On(o,t.a,r,h),c=Math.abs((o[0]-t.a[0])*h-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=l+d+i})},Gn=(t,e,s)=>{if(t.host){const i=z(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},jn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),h=Math.sin(n);if(Math.abs(o*h-a*r)>1e-6)return!1;const l=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(l*a-c*o)>s)return!1;const d=l*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Un=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Vn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],h=Array.isArray(t?.room_drafts)?t.room_drafts:[],l={walls:a,partitions:n,openings:r,roomDrafts:h,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!h.length)return l;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,_=r;const u=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const h=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(h>i)){e=!1;break}const l=s.flatMap(t=>{const e=t.cm>0?t.cm:N;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of l){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=h-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Bn(t))continue;const s=v.get(t.id);if(!(s&&An(s.a)&&An(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Nn(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],h=n[1]-a[1],l=Math.hypot(r,h);if(!(l>c))continue;const u=r/l,g=h/l,y=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=Ln(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=_.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:z(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,l];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),M=new Map;for(const{opening:t,resolved:e}of w){const s=On(e.center,a,u,g),i={lo:s-e.length/2,hi:s+e.length/2};M.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(l,i.hi))}const S=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=l+c).map(t=>Math.max(0,Math.min(l,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),C=p.map(t=>qn(t,o.coordScale)).filter(t=>t.id!==s.id),D=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Hn(a,u,g,i),h=Hn(a,u,g,n),l=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of l)d.set(t.roomId,t);const p=[...d.values()],_=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:N)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===_.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:h,cm:s.cm},w=C.some(t=>!!Ln(r,h,t.a,t.b,c)),k=D.some(t=>t.points.some((e,s)=>s+10?p[0].cm:N:0,I=v?Math.max(M,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:h,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:M,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=M.get(t.id),a=k.some(t=>t>i.lo+c&&tEn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&H(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const h=O(s,o.cellCm,o.gridPitch),l={lo:i.lo-h,hi:i.hi+h};for(const t of P)En(t,l,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=M.get(t),a=P.filter(t=>En(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=O(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)En(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=Nn(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=L(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=E(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const K=new Set(p.map(t=>t.id));K.delete(s.id);const V=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Un(s.id,i,a,K);return K.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...V);const X=Y.map(t=>qn(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=F(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=M.get(t.id),a=V[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],h=n.b[1]-n.a[1],l=Math.hypot(r,h),d=On(e.center,n.a,r/l,h/l),p={...F(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/l}};if(!B(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=_.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>Gn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=Gn(t,X,o);e&&st.some(t=>jn(t,e,c))&&(it=!0)}if(it)continue;const at=W(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=G(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,_=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:_,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Jn=t=>JSON.parse(JSON.stringify(t)),Yn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Xn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Zn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Qn=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const tr=1e-9;class er extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const sr=t=>JSON.parse(JSON.stringify(t)),ir=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),or=t=>[Number(t[0]),Number(t[1])],ar=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,nr=(t,e)=>{const s=ar(t),i=ar(e);return sar(t)<=ar(e)?[or(t),or(e)]:[or(e),or(t)],hr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),lr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},dr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},pr=(t,e,s)=>{const i=Math.max(0,Math.min(1,dr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=tr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const h=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(h(s)>o||h(i)>o)return 0;const l=dr(s,t,e),c=dr(i,t,e);return Math.max(0,Math.min(1,Math.max(l,c))-Math.max(0,Math.min(l,c)))*r},ur=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),h=new Uint32Array(64),l=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=l(s,17)^l(s,19)^s>>>10;h[t]=h[t-16]+i+h[t-7]+o>>>0}let[e,s,i,o,c,d,p,_]=r;for(let t=0;t<64;t++){const a=_+(l(c,6)^l(c,11)^l(c,25))+(c&d^~c&p)+n[t]+h[t]>>>0,r=e&s^e&i^s&i;_=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((l(e,2)^l(e,13)^l(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+_>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},gr=(t,e,s,i,o="")=>{const[a,n]=rr(e,s),r=`${t}|${ar(a)}|${ar(n)}|${[...i].sort().join(",")}${o}`;return`wall-${mr(ur(r)).slice(0,20)}`};let fr=0;const br=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||mr(ur(`${Date.now()}|${fr++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new er("duplicate-id","id factory exhausted")},vr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&ir(s.a)&&ir(s.b)){if(e.has(s.id))throw new er("duplicate-id",s.id);e.set(s.id,s)}return e},yr=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=tr&&Math.abs(e[1]-i[1]-a)<=tr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},$r=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>pr([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],h=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),l=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(pr(i,t,e)>.02*I)return!1;const o=hr(t,e),a=dr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-tr&&a-n<=o+tr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new er("invalid-room",r);const _=Y(s,r,n,I,1,[...t.walls||[],...h]);if(!_||_.poly.length<3)throw new er("invalid-room",r);const u=Array.isArray(i.wall_ids)?i.wall_ids:[],m=u.length===p.length;let g=null,f=m;if(f)for(let t=0;ttr||Math.abs(i[1]-g[1])>tr)){f=!1;break}g=i}const b=[];for(let t=0;t<_.poly.length;t++){const s=_.poly[t],i=_.poly[(t+1)%_.poly.length];if(hr(s,i)<=tr)throw new er("zero-length",r);const n=nr(s,i);let h=c.get(n);if(!h){const[t,e]=rr(s,i);h={key:n,a:t,b:e,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},c.set(n,h)}if(h.owners.add(r),h.owners.size>2)throw new er("third-owner",n);const d=m?u[_.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>tr?h.preferredIds.add(d):h.positionalIds.add(d),h.preferredCarriers.set(d,{a:or(p[_.parent[t]]),b:or(p[(_.parent[t]+1)%p.length])})}const g=_.parent[t];h.parentKeys.add(K(p[g],p[(g+1)%p.length],I));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>pr(v,[t[0],t[1]],[t[2],t[3]])<=.04*I);h.zeroWall||=y(a)||y(o)&&!l(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:_.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},wr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[nr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new er("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new er("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],h=i?s.get(i):void 0;if(h){r.set(t,h);continue}const l=i?t.preferredCarriers.get(i):void 0;if(i&&l){r.set(t,{id:i,a:l.a,b:l.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>tr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||hr(e.a,e.b)-hr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const h=new Map;for(const[t,e]of r){const s=h.get(e.id)||[];s.push(t),h.set(e.id,s)}for(const[t,e]of h){const i=s.get(t)||r.get(e[0]);if(!i)throw new er("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=pr(o,t.a,t.b)<=tr?0:1,a=pr(o,e.a,e.b)<=tr?0:1;if(s!==a)return s-a;return(pr(i.a,t.a,t.b)<=tr?0:1)-(pr(i.a,e.a,e.b)<=tr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const l=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(l.has(t.id))throw new er("duplicate-id",t.id);l.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=rr(e.a,e.b),o=`${String(t.id||"")}|${ar(s)}|${ar(i)}|${[...e.owners].sort().join(",")}`,a=mr(ur(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new er("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;l.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,l.add(o)}}else for(const t of c)t.id=br(l),l.add(t.id)},kr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=dr(s,e.a,e.b),o=hr(e.a,e.b),a=Number(t.length)/2;return i>=-tr&&i<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-tr&&i*o+a<=o+tr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,dr(s,n.a,n.b)))}},xr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=dr(s,e.a,e.b),a=hr(e.a,e.b);return o>=-tr&&o<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-tr&&o*a+i<=a+tr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>pr(s,t.a,t.b)-pr(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,dr(s,a.a,a.b)))}:null},Mr=(t,e,s)=>{const i=vr(t),{atoms:o,rooms:a}=$r(t,i);wr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=K(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!ir(t.a)||!ir(t.b))&&_r(e.a,e.b,t.a,t.b)>=hr(e.a,e.b)-tr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new er("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:K(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=kr(t,e);s&&(t.host=s);continue}const i=kr(t,e)??(s?xr(t,e):null);if(i)t.host=i;else{if(!s)throw new er("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Sr(t,e={}){const s=JSON.stringify(t),i=sr(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Mr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Cr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Dr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Cr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Cr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):sr(e)}return o[e,e in t?s(t[e],i):sr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return sr(e)};return s(t,e)}const Pr=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Ir=(t,e,s)=>Math.min(s,Math.max(e,t)),Tr=t=>ut({spaces:[t]})[0],Rr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,h=Math.max(i*r*.02,1e-9),l=[];for(const e of t||[])for(const t of R(e)||[])l.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h),p=new Map;for(const n of t||[]){if(!n?.id)continue;const _=mt(t,n.id,e,s,i,o,a,r);if(_)for(let t=0;t<_.orig.length;t++){const e=[];for(let s=0;s<_.parent.length;s++)_.parent[s]===t&&e.push(s);for(let t=1;t+10&&u>0&&n===m&&u!==n))continue;const g=_.poly[i],f=_.poly[(i+1)%_.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>h&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,l)&&d(f,l))continue;const v=Rr(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let _=e.slice();const u=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(u(s,e.a)&&u(i,e.b)||u(s,e.b)&&u(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Rr(t.a,t.b,r).localeCompare(Rr(e.a,e.b,r)))){const e=[...t.targets][0],s=_.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;_=_.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(_=L(_,t.a,t.b,e,i,r))}return _}const Fr=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(l),d=new Map;for(const t of l){const e=Zn("space",t);e.layers>0&&Xn("space",e.root)&&Qn(d,e.root,t)}const p=new Map,_=new Map,u=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Zn("room",t);o.layers>0&&Xn("room",o.root)&&Qn(s,o.root,t),_.has(t)||_.set(t,e),u.has(t)||u.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(_.keys()),f=new Map,b=new Map;for(const t of h)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=Zn("space",t).root;if(!Xn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Zn("room",t).root;if(!Xn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of h){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),h=!0===t.removed;let l=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,l=i,p=!0,v.spaceRefsRemapped++):h||"virtual"===t.binding?h||(delete t.space,l=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,l=d.spaceId,v.spaceRefsRemapped++):(delete t.space,l=null,v.markersDetached++))}h&&!p||x(t,l,d),r&&p&&n===s&&l&&(o[e]={...a,s:l},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Yn(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const M=s.ownerRoster,S=!0===M?.authoritative,C=new Set(M?.deviceIds||[]),D=new Set(M?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(M?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),h=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(h)a=T(t,i,"device",String(h.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",u.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=D.has(e)?"live":S?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else C.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=S?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const h=JSON.stringify(t||{}),l=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Pr(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Pr(o,"segments")&&(delete o.segments,e++),Pr(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=ht&&t<=lt&&(Pr(o,"plan_scale_x")||(o.plan_scale_x=t),Pr(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Pr(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Ir(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Pr(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Pr(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Pr(t,"width")){const s=Number(t.width);(Pr(t,"width_cm")||Number.isFinite(s))&&(Pr(t,"width_cm")||(t.width_cm=Number(Ir(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Pr(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Pr(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Ir(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Pr(t,"scale")||Pr(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Pr(t,"entity")||Pr(t,"attr")||Pr(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":_t(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const _=(u=n.spaces||[],JSON.parse(JSON.stringify(u)));var u;const m=Mn(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?Mn(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,M=0,S=0,C=0,D=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Tr(e),r=Tr(s);if(!a||!r)continue;const h=Fr(a.rooms),l=Fr(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:K(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),u=it(e,a.rooms,ot,c);x+=u.length;for(const t of u){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=K(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,h,l,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return l.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});M+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,h,l,I,ot);w=zr(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=E(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,y.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=Rn(s.partitions||[],{pitch:I,geometry:Fn(s)});R.merged&&(S+=R.merged,s.partitions=R.partitions,zn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const z=Tr(s);if(z){const t=o(s,z,s.walls||[],y,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(C+=t.partitionsReconciled,D+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Sr(t);return Dr(t,e.config),{...e,config:t}}(n).migratedSegments);const z=U(n),F=et($.layout),A=JSON.stringify(z)!==h||JSON.stringify(F)!==l;c<9&&A&&(z.model_version=9);const N=JSON.stringify(z)!==h||JSON.stringify(F)!==l,H=Number.isInteger(Number(z.model_version))?Number(z.model_version):c,O=N?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},L=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:z,layout:F,report:{...O,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?R:0,legacyZeroWallsMigrated:N?x:0,wallsMerged:N?k:0,spansMerged:N?M:0,partitionsMerged:N?S:0,partitionsReconciled:N?C:0,openingsRehosted:N?D:0,redundantDraftsRemoved:N?P:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?b:0,maxStraightenSpace:N?v:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...L},changed:N}}const Nr=(t,e)=>t[0]*e[1]-t[1]*e[0],Hr=(t,e)=>t[0]*e[0]+t[1]*e[1],Or=(t,e)=>[t[0]-e[0],t[1]-e[1]],Lr=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Er(t,e,s){return Math.abs(Nr(t,e))<=s}function Br(t,e,s){return Math.abs(Nr(Or(t,e),s))}function Wr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const h=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Er(h,[i/n,a/n],1e-6)))return[];if(Br(e.a,t,h)>1.1*Math.max(s,o)||Br(e.b,t,h)>1.1*Math.max(s,o))return[];return[[Hr(Or(e.a,t),h),Hr(Or(e.b,t),h)]]}),o).flatMap(([e,i])=>{const o=bt({a:Lr(t,h,e),b:Lr(t,h,i),halfDepth:s});return o?[o]:[]})}function Gr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Lr([t.x,t.y],s,-i),b:Lr([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Wr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Wr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function jr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Lr(s.center,a,o),r=[];for(let e=0;ei&&Er(s.axis,[h[0]/l,h[1]/l],1e-6)))continue;if(Br(o,n,s.axis)>i||Br(a,n,s.axis)>i)continue;const c=Hr(Or(o,n),s.axis),d=Hr(Or(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const h=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!h)return null;const[l,c]=h,d=Lr(n,s.axis,-s.half),p=Lr(n,s.axis,s.half),_=Math.max(0,-s.half-l),u=Math.max(0,c-s.half),m=_>0?Lr(n,s.axis,l):d,g=u>0?Lr(n,s.axis,c):p,f=Hr(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,_),right:b(p,g,u)}}function qr(t){const e=Lr(t.center,t.axis,-t.half),s=Lr(t.center,t.axis,t.half),i=Lr(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Lr(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ur(t,e,s,i){const o=Or(s,e),a=Hr(o,o);if(!(a>i*i))return Math.hypot(...Or(t,e))<=i;const n=Hr(Or(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Lr(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Or(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=l>t[1]&&t[0]<(h-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Vr(t,e,s,i,o,a){const n=Or(i,s),r=Nr(e,n);if(Math.abs(r)<=a)return null;const h=Or(s,t),l=Nr(h,n)/r,c=Nr(h,e)/r;return l<-a||l>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,l))}function Jr(t,e,s){const i=qr(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iHr(Or(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Or(n,a),h=Math.hypot(r[0],r[1]);if(!(h>s&&Er(e.axis,[r[0]/h,r[1]/h],1e-6)))continue;if(Br(a,e.center,e.axis)>s||Br(n,e.center,e.axis)>s)continue;const l=Hr(Or(a,e.center),e.axis),c=Hr(Or(n,e.center),e.axis),d=Math.min(l,c),p=Math.max(l,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||_2)return qr(s);if(i.length){const t=i.map(({room:t,edge:i})=>jr(t,i,s,e.epsilon));if(t.some(t=>!t))return qr(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Jr(t,e,s)}const Xr="1.69.0-beta.1",Zr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Qr={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},th=2e3,eh=1e3,sh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},ih=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},oh=1e3,ah=2e3,nh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},rh="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";class hh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=sh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?vt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?vt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=sh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&yt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=sh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const h=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,h||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),h)},this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=nn(A(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot(),l=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*oh,Number(e.a?.[1])*oh],i=[Number(e.b?.[0])*oh,Number(e.b?.[1])*oh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/oh,a.interval.a[1]/oh],b:[a.interval.b[0]/oh,a.interval.b[1]/oh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*oh,i.a[1]*oh],b:[i.b[0]*oh,i.b[1]*oh]};e.host={kind:"partition",id:i.id,t:rn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(l,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),h),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=Ra(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,N);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Ct(this.host._settings,{bgColor:null})||null,northDeg:St(this.host._settings,{}),bgMode:Mt(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Dt).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new gn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Wo}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const h=!s.bg&&"view"===t!=("view"===i),l=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=l?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=l?l.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=l?l.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}h&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const _=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(_,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&_===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(N,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=Rn(s,{pitch:I,seedIds:e,geometry:Fn(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,zn(t.openings,t.partitions,i.openingMoves,{coordScale:oh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=za(this.host._path,Ra(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,N)),s=e.length;if((t.partitions||[]).length+s>ah)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=ah:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(zt(t[0],e)),Rt(zt(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Ft(s,t,this.host._gridPitch,Ii):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=G(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=xa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=ka({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ia(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ma(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=s.excludePoints||[],a=Da(Ca(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=D([e[0],e[1]],t);if(r>s.tolerance)continue;const h=Pa(a,e,s.gridStep);!Ma(h,o,i)&&Sa(r,a.key,n)&&(n={kind:"line",point:h,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,h=r?[...r.point]:this._snapDrawPoint(t,e),l=i?At(i,h):h;return{point:l,candidate:r&&q(l,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Nt(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=lr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ht(ut({spaces:[t]}),t.id),s=e?Ot(t,e,oh,.02*nt).contour.map(t=>t.map(t=>t/oh)):V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Lt(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Et(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=Si(this.host._serverCfg),e=Lt(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Lt(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),h=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=h.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof er?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ta(a),...aa(a,o,I),...ra(a,o,I)];let r=null;try{r=Bt(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let h="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!h&&r&&r.nodes.length)try{const t=Wt(i.rooms||[],i.walls||[],[],[],I,o,I,1);h="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{h=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Gt(i.rooms||[],e,i.walls||[],[],I,o,I,1,h??void 0,r)}catch{s=null}n.push(...ha(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Sr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let h=[];try{h=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(h,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let h=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new er("invalid-room",e.spaceId);const s=Sr(t).config;h=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new er("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),l=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of l?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=$r(s,vr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),h=(s.rooms||[]).find(e=>e.id===t.id),l=R(h),c=R(n);if(r&&l&&c&&r.length===l.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;atr||pr(r,s,i)>tr)continue;const h=dr(n,s,i),l=dr(r,s,i);h<-tr||l>1+tr||l<=h+tr||o.push({start:h,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,l[e],l[(e+1)%l.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/oh,t[1]/oh]):[];i.push(...s.map(t=>[t[0],t[1]])),l=cr(t)<=cr(h,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{l=!1}if(!l)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Dr(i,n),this._recordGeometry(t,h);const p=i.spaces.find(t=>t.id===e.spaceId),_=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:_?.before||h,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Sr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Dr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Dr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!mo(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>ah)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=At(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const h=this.host._drawWallCm;if(null==h)return void this._showPhysicalRange(100);const l=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,h],this._persistActiveDraftSegment(),this._offerWallFaces(l)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),h=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),l=this.host._path[this.host._path.length-1],c=this._samePt(l,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),_=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],u=[...p,...null==d?[]:[{cm:d}],...h],m=_.length>=4&&this._samePt(_[0],_[_.length-1]),g=_,f=u;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/oh,t[1]/oh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=Ra(i,e,this.host._drawWallCm,N);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*oh,Number(t?.[1])*oh]):[],o=Array.isArray(e?.segments)?e.segments:[];return Ra(Math.max(0,t.length-1),s,this.host._drawWallCm,N).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/oh,t[1]/oh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=en(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=un(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Kt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,h=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!h)return;return void this._beginWallFaceBatch([{...h,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const h=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));h.length&&this._beginWallFaceBatch(h)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=sn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=un(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=un(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(vt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>yt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/oh,e[1]/oh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?Nt(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),nh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Vt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Vt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),h=this.host._physicalPickCycle,l=a.length>1&&h?.signature===n&&r-h.at>380&&r-h.at<=1200&&Math.hypot(t.clientX-h.x,t.clientY-h.y)<=10?(h.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:l,x:t.clientX,y:t.clientY,at:r},e=a[l].kind,s=a[l].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*oh-Math.min(...o),Math.min(Xt*oh-Math.max(...o),s[0])),Math.max(-Xt*oh-Math.min(...a),Math.min(Xt*oh-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/oh,o.a[1]/oh],t.b=[o.b[0]/oh,o.b[1]/oh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=z(t,[o],oh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,F(t,s,oh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/oh,(o.center[1]+e.delta[1])/oh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),nh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:Nt(e.angle),angle:Nt(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=Nt(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*oh,t.a[1]*this.host._spaceH],b:[t.b[0]*oh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!mo(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],h=n.poly[(i+1)%n.poly.length],l=vo(r,h,a);if(!l)return{enabled:!1,reason:"diagonal"};if(!ko(n.poly,i,l,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(So(t,r,h,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=co(ro(h,r)),d=[],p=new Set;let _=!1,u=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!mo(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!ko(t.room.poly,t.edge,l,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(_o([t.x,t.y],r,h)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Po(t,e,o,1,a),h=Po(t,e,n,0,a);if(!r||!h)return{enabled:!1,reason:"partial-shared"};v.push(r,h)}const y={roomId:s,edge:i,a:[...r],b:[...h],n:uo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!No(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>No(t,e,y,s,o))){if(!s.some(s=>Ro(zo(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>No(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>No(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const h=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/oh,t[1]/h]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/oh,s.y=e[1]/h)}const l=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*oh,t[1]*h]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/oh,i[t][1]/oh],o.b=[i[(t+1)%i.length][0]/oh,i[(t+1)%i.length][1]/oh])}}if(l.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,l,c,this.host._wallKeyPitch,oh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=p(t);_.set(e,(_.get(e)||0)+1)}const u=[];for(const t of r.walls||[]){const e=p(t),s=_.get(e)||0;s?_.set(e,s-1):u.push(t)}if(Qt(u,d,this.host._wallKeyPitch,oh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Lt([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};ih(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));nh(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,h=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,l=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:l,before:JSON.parse(l),wallUnionBefore:h})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};ih(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const h="mi"===this.host.hass?.config?.unit_system?.length,l=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),_=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[u,m]=this.host._lastValidStageSize||[_.w,_.h],g={..._,stageWidth:Math.max(1,u),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?se(b>8?2.5:b,f,_.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of l){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),l=ne(r,h),p=jo({poly:a,edge:e.edgeByRoom[s],text:l,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:l,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=he(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>le(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
this.host._markerDialog={...t,glowRadius:e.target.value}} /> ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")}
- ${M||C?o`

+ ${S||D?o`

${I}

`:a} @@ -2139,7 +2158,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @@ -2166,20 +2185,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${$?o`

${$.technical}

`:a} - ${v?o`

+ ${y?o`

${this.host._t("marker.value_badge_missing_hint")}

`:a} - ${"value"===t.display&&y===w?o`

+ ${"value"===t.display&&v===w?o`

${this.host._t("marker.value_badge_duplicate")}

`:a}
@@ -2199,7 +2218,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d .hass=${this.host.hass} .presentation=${_} .registry=${this.host._haRegistry} - .deviceName=${t.name.trim()||r?.name||L||""}> + .deviceName=${t.name.trim()||r?.name||O||""}> `:o`
${this.host._t("marker.preview.select_source")} @@ -2292,7 +2311,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${"file"===t.source?o`
@@ -2312,7 +2331,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${t.pickSaved?this._renderServerPlans(t):a}`:a} @@ -2320,9 +2339,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d
{const s=e.target.value,i=sl(s),o=null==i?null:ki(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(el,o)):t.cellCm}}} /> + @input=${e=>{const s=e.target.value,i=sh(s),o=null==i?null:ki(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(eh,o)):t.cellCm}}} /> ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")}
@@ -2386,10 +2405,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d - ${"static"===(t.bgMode??St(this.host._settings,{}))?o`
+ ${"static"===(t.bgMode??Mt(this.host._settings,{}))?o`
{this.host._spaceDialog={...t,bgColor:e.detail.color}}}> ${t.bgColor?o`
{const s=sl(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMin:s})}} /> + @input=${e=>{const s=sh(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMin:s})}} /> – {const s=sl(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMax:s})}} /> + @input=${e=>{const s=sh(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMax:s})}} /> °C `:a} @@ -2505,7 +2524,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${t.label}${t.sub}
`)}
-
`:a}`:a}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),n=ps(this.host._curSpaceCfg),r=this.host._roomFill||n.fill,l=this.host._roomCustomFill||n.customFill,h=[...this.host._freeAreas];if(t&&this.host._areaSel&&!h.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&h.unshift(t)}return o``:a}`:a}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),n=ps(this.host._curSpaceCfg),r=this.host._roomFill||n.fill,h=this.host._roomCustomFill||n.customFill,l=[...this.host._freeAreas];if(t&&this.host._areaSel&&!l.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&l.unshift(t)}return o`this._roomDialogCancel()}>
@@ -2520,12 +2539,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d - ${[["","fill.inherit"],...Si.map(t=>[t,"fill."+t])].map(([t,e])=>o`
`:a} @@ -2569,4 +2588,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this.host._t("btn.save")} `}
- `}}export{rl as EDITOR_RUNTIME_FINGERPRINT,ll as HouseplanEditorRuntime}; + `}}export{rh as EDITOR_RUNTIME_FINGERPRINT,hh as HouseplanEditorRuntime}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js similarity index 98% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js index f11f6dc2..ff1054a5 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as n,b$ as c,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-DJctcs0x.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-BIiYcPJz.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";class C{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:c,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return p`
${this.host._t("space.no_saved")}
`;return p`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as n,b$ as c,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-VbP9hCtk.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-Dh9IGz13.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";class C{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:c,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return p`
${this.host._t("space.no_saved")}
`;return p`
${s.map(s=>{return p`
diff --git a/custom_components/houseplan/frontend/houseplan-assets/space-deletion-BIiYcPJz.js b/custom_components/houseplan/frontend/houseplan-assets/space-deletion-Dh9IGz13.js similarity index 56% rename from custom_components/houseplan/frontend/houseplan-assets/space-deletion-BIiYcPJz.js rename to custom_components/houseplan/frontend/houseplan-assets/space-deletion-Dh9IGz13.js index 7b87fb44..02209851 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/space-deletion-BIiYcPJz.js +++ b/custom_components/houseplan/frontend/houseplan-assets/space-deletion-Dh9IGz13.js @@ -1 +1 @@ -function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function a(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),a=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),t=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&a.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:t,count:t.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";export{a,e as c,o as i,r as s,s as t}; +function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";export{t as a,e as c,o as i,r as s,s as t}; diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index be3bdd10..f3376912 100644 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";try{await import("./houseplan-assets/houseplan-card-DJctcs0x.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";try{await import("./houseplan-assets/houseplan-card-VbP9hCtk.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/demo/golden/harness.mjs b/demo/golden/harness.mjs index a54aa07c..727b970d 100644 --- a/demo/golden/harness.mjs +++ b/demo/golden/harness.mjs @@ -1206,6 +1206,28 @@ export async function prepareGoldenScenario(page, scenario) { ); if (!tray) throw new Error(`golden editor tray did not open: ${scenario.editorTray}`); } + if (scenario.furniturePalette) { + if (scenario.editorTray !== 'furniture-palette') { + card._decorSel = null; + card._furnPalette = null; + card._furnCategory = null; + card._editorSecondary.openPalette(); + card._decorTool = 'furniture'; + } + if (scenario.furniturePalette === 'variants') { + card._furnCategory = scenario.furnitureCategory || 'sofa'; + } + card.requestUpdate(); + await card.updateComplete; + await frame(); + const palette = card.renderRoot.querySelector('.furnpalette'); + if (!palette) throw new Error(`golden furniture palette did not open: ${scenario.id}`); + const selector = scenario.furniturePalette === 'variants' + ? '.furnitem[data-symbol]' + : '.furnitem[data-category]'; + if (!palette.querySelector(selector)) + throw new Error(`golden furniture palette level is empty: ${scenario.id}`); + } if (scenario.hoverRoom) { const room = card._spaceModel().rooms.find((item) => item.id === scenario.hoverRoom); if (!room) throw new Error(`golden hover room missing: ${scenario.hoverRoom}`); diff --git a/demo/golden/matrix.mjs b/demo/golden/matrix.mjs index 085a97da..d7d2d448 100644 --- a/demo/golden/matrix.mjs +++ b/demo/golden/matrix.mjs @@ -397,8 +397,28 @@ export const GOLDEN_SCENARIOS = Object.freeze([ editorTray: 'decor-selection', language: 'ru', theme: 'dark', viewport: { width: 760, height: 820 }, ...page }, { id: 'tray-narrow-palette-en', fixture: 'visual', space: 'golden-geometry', mode: 'decor', - editorTray: 'furniture-palette', language: 'en', theme: 'dark', + editorTray: 'furniture-palette', furniturePalette: 'categories', language: 'en', theme: 'dark', viewport: { width: 390, height: 760 }, ...page }, + { id: 'furniture-categories-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor', + furniturePalette: 'categories', language: 'ru', theme: 'light', + viewport: { width: 900, height: 820 }, ...page }, + { id: 'furniture-variants-dark', fixture: 'visual', space: 'golden-geometry', mode: 'decor', + furniturePalette: 'variants', furnitureCategory: 'sofa', language: 'en', theme: 'dark', + viewport: { width: 900, height: 820 }, ...page }, + { id: 'furniture-variants-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor', + furniturePalette: 'variants', furnitureCategory: 'coffee_table', language: 'ru', theme: 'light', + viewport: { width: 900, height: 820 }, ...page }, + { id: 'furniture-plan-art-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view', + decorOverride: [ + ...decorLayerFixture.filter((shape) => shape.kind !== 'furniture'), + { id: 'furn-replaced', kind: 'furniture', symbol: 'sofa', x: 0.16, y: 0.18, + w: 0.18, h: 0.09, color: '#141832', opacity: 1, width_cm: 1.2 }, + { id: 'furn-retained', kind: 'furniture', symbol: 'fridge', x: 0.43, y: 0.18, + w: 0.06, h: 0.065, color: '#141832', opacity: 1, width_cm: 1.2 }, + { id: 'furn-new', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.58, y: 0.18, + w: 0.26, h: 0.17, color: '#141832', opacity: 1, width_cm: 1.2 }, + ], + language: 'en', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage }, { id: 'tray-narrow-tool-ru', fixture: 'visual', space: 'golden-geometry', mode: 'decor', editorTray: 'decor-tool', language: 'ru', theme: 'dark', viewport: { width: 390, height: 760 }, ...page }, diff --git a/demo/smoke_furniture.mjs b/demo/smoke_furniture.mjs index 443da8ca..54491a36 100644 --- a/demo/smoke_furniture.mjs +++ b/demo/smoke_furniture.mjs @@ -46,7 +46,7 @@ const res = await page.evaluate(async () => { sr().querySelectorAll('.modetab')[2].click(); await c.updateComplete; c._curSpaceCfg.decor = []; - c._decorTool = 'select'; c._decorSel = null; c._furnPalette = null; + c._decorTool = 'select'; c._decorSel = null; c._furnPalette = null; c._furnCategory = null; c._cfgEpoch++; c.requestUpdate(); await c.updateComplete; out.cellCmIsFive = c._cellCm === CELL; @@ -65,11 +65,22 @@ const res = await page.evaluate(async () => { out.groupsArePresent = ['furniture', 'appliance', 'sanitary'].every((g) => groups.includes(g)); out.everyGroupHasSymbols = groups.length > 0 && [...pal().querySelectorAll('.furnrow')] .every((r) => r.querySelectorAll('.furnitem').length > 0); + const category = (id) => pal()?.querySelector(`.furnitem[data-category="${id}"]`); + out.firstLevelHasNoPlanVariants = !pal()?.querySelector('.furnitem[data-symbol]'); + out.requiredCategoriesArePresent = ['sofa', 'toilet', 'washer'] + .every((id) => !!category(id)); + out.menuOnlyCategoriesStayHidden = ['computer', 'oven', 'hood', 'exercise'] + .every((id) => !category(id)); + const categoryD = category('sofa')?.querySelector('svg.furncatprev path')?.getAttribute('d') || ''; + out.categoryUsesFrontArtwork = categoryD.length > 10; + category('sofa')?.click(); await c.updateComplete; + out.categoryDrillsToVariants = c._furnCategory === 'sofa' + && !!pal()?.querySelector('.furnback'); const item = (sym) => pal()?.querySelector(`.furnitem[data-symbol="${sym}"]`); const pick = (sym) => { const b = item(sym); if (b) b.click(); }; out.sofaIsInThePalette = !!item('sofa'); - out.toiletIsInThePalette = !!item('toilet'); - out.washerIsInThePalette = !!item('washer'); + out.sofaVariantsAreInThePalette = ['sofa', 'sofa_three_seat', 'sofa_corner_right'] + .every((id) => !!item(id)); const prevD = item('sofa')?.querySelector('svg.furnprev path')?.getAttribute('d') || ''; out.previewIsDrawn = prevD.length > 10; const previewEl = item('sofa')?.querySelector('svg.furnprev'); @@ -103,15 +114,16 @@ const res = await page.evaluate(async () => { .find((b) => /мебел|furnitur/i.test(b.textContent || ''))?.click(); await c.updateComplete; out.paletteReopensAfterDismiss = c._decorTool === 'furniture' && !!pal(); + category('sofa')?.click(); await c.updateComplete; // ================= 2. размер по умолчанию и поля ========================= pick('sofa'); await c.updateComplete; out.pickArmsTheSymbol = c._furnPalette?.symbol === 'sofa'; - out.defaultSizeIsTheSymbols = c._furnPalette?.w === 220 && c._furnPalette?.h === 90; + out.defaultSizeIsTheSymbols = c._furnPalette?.w === 180 && c._furnPalette?.h === 90; const wIn = () => pal()?.querySelector('input.furnw'); const hIn = () => pal()?.querySelector('input.furnh'); out.sizeFieldsAppear = !!wIn() && !!hIn(); - out.fieldsShowMetres = +(wIn()?.value ?? NaN) === 2.2 && +(hIn()?.value ?? NaN) === 0.9; + out.fieldsShowMetres = +(wIn()?.value ?? NaN) === 1.8 && +(hIn()?.value ?? NaN) === 0.9; out.selectedItemIsMarked = !!item('sofa')?.classList.contains('on'); // ================= 3. размещение в реальном размере ====================== @@ -122,7 +134,7 @@ const res = await page.evaluate(async () => { const sofaId = sofa?.id ?? null; const sofaNow = () => c._decorList.find((s) => s.id === sofaId) || {}; out.placedOnClick = sofa?.symbol === 'sofa'; - out.realWidthThroughCellCm = near((sofa?.w ?? NaN) * 1000, cmToUnits(220), 1e-6); + out.realWidthThroughCellCm = near((sofa?.w ?? NaN) * 1000, cmToUnits(180), 1e-6); out.realDepthThroughCellCm = near((sofa?.h ?? NaN) * 1000, cmToUnits(90), 1e-6); out.centredOnTheClick = !!sofa && near((sofa.x + sofa.w / 2) * 1000, 300, 1e-6) @@ -138,10 +150,13 @@ const res = await page.evaluate(async () => { out.hookSymbol = attr(sofaId, 'data-symbol') === 'sofa'; out.strokedNotFilled = attr(sofaId, 'fill') === 'none' && attr(sofaId, 'stroke') === c._decorStyle.color; - out.pathIsAtRealSize = /^M0 0H183\.333V75H0Z/.test(attr(sofaId, 'd') || ''); + out.pathIsAtRealSize = (attr(sofaId, 'd') || '').length > 20 + && /scale\(/.test(attr(sofaId, 'transform') || '') + && attr(sofaId, 'vector-effect') === 'non-scaling-stroke'; // ================= 4. свой размер до размещения ========================== c._decorTool = 'furniture'; await c.updateComplete; + category('toilet')?.click(); await c.updateComplete; pick('toilet'); await c.updateComplete; if (wIn()) { wIn().value = '0.5'; @@ -157,6 +172,7 @@ const res = await page.evaluate(async () => { // ================= 5. магнит к стене ===================================== c._decorTool = 'furniture'; await c.updateComplete; + category('bed')?.click(); await c.updateComplete; pick('bed_double'); await c.updateComplete; ev('pointerdown', stageEl(), 300, 150); // 10 единиц от стены y=140 await c.updateComplete; @@ -170,6 +186,7 @@ const res = await page.evaluate(async () => { // …Shift больше не отключает магнит c._decorTool = 'furniture'; await c.updateComplete; + category('bed')?.click(); await c.updateComplete; pick('bed_single'); await c.updateComplete; ev('pointerdown', stageEl(), 301.7, 151.3, { shiftKey: true }); await c.updateComplete; diff --git a/dist/houseplan-assets.json b/dist/houseplan-assets.json index 3a619579..f0fabfc7 100644 --- a/dist/houseplan-assets.json +++ b/dist/houseplan-assets.json @@ -1,111 +1,111 @@ { "schema": 1, - "fingerprint": "967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76", + "fingerprint": "9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", + "houseplan-assets/houseplan-card-VbP9hCtk.js", "houseplan-card.js" ], - "initialViewGzipBytes": 257212, + "initialViewGzipBytes": 270802, "lazyFiles": [ - "houseplan-assets/de-xcuT8cEm.js", - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/de-XuJl1rK9.js", + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyGzipBytes": 160415, + "lazyGzipBytes": 166322, "lazyEditorFiles": [ - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyEditorGzipBytes": 131835, + "lazyEditorGzipBytes": 137208, "lazyOnboardingFiles": [ - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], - "lazyOnboardingGzipBytes": 7197, + "lazyOnboardingGzipBytes": 7201, "lazyLocaleFiles": [ - "houseplan-assets/de-xcuT8cEm.js" + "houseplan-assets/de-XuJl1rK9.js" ], - "lazyLocaleGzipBytes": 21934, + "lazyLocaleGzipBytes": 22467, "files": [ { - "path": "houseplan-assets/de-xcuT8cEm.js", - "sha256": "59061d1e31cc89b374431c04110733c3543141b08bd06fac01f5e4d4e2ce6942", - "rawBytes": 71667, - "gzipBytes": 21934, + "path": "houseplan-assets/de-XuJl1rK9.js", + "sha256": "4759ac8d21f6807c7c9de205475195ea5d762a2bbbc091cfb6b6edbd0f53892b", + "rawBytes": 73919, + "gzipBytes": 22467, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-BBdPV7Z-.js", - "sha256": "935764bc9cdfafa0a1a4004aa88cdc423db7d6d73707b52c92b0b67987cc1959", + "path": "houseplan-assets/editor-Bet9LJop.js", + "sha256": "4d2dca304dbccae9d843ae77db565ba5ffddb44dd7ab35a76e7ccb1252e859d7", "rawBytes": 3831, - "gzipBytes": 1582, + "gzipBytes": 1585, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-DJctcs0x.js", - "sha256": "485bee0aa697af7c9dac12278c5253f0a309677443730cd1cb4c08bd5eb4e633", - "rawBytes": 912403, - "gzipBytes": 256459, + "path": "houseplan-assets/houseplan-card-VbP9hCtk.js", + "sha256": "8ae379231c6c4ef97e1fcca55aa4a0a5e520d6dd5845dce741f98a13a0247bfc", + "rawBytes": 955358, + "gzipBytes": 270046, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-xcuT8cEm.js", - "houseplan-assets/editor-BBdPV7Z-.js", - "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js" + "houseplan-assets/de-XuJl1rK9.js", + "houseplan-assets/editor-Bet9LJop.js", + "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js", - "sha256": "cb0b5e161592d1149866e12e6d5aa56e9731665d546404f5b65457e9d40b5129", - "rawBytes": 504959, - "gzipBytes": 129702, + "path": "houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js", + "sha256": "05dcd1aa05b8821af3dc98b31db7ebe4d3cfd8a8890f5af3ffe64c6fef650e14", + "rawBytes": 521651, + "gzipBytes": 135069, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js", - "sha256": "ba6bd6549d7222fad7f4a82aeca50d875c5dd126e0d5fc863e15d43eb9953483", + "path": "houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js", + "sha256": "c28e4a2ceb830523010d588babc01188d6885083c4ba9f4fc824aef1f72a1cbd", "rawBytes": 26356, - "gzipBytes": 6646, + "gzipBytes": 6647, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js", - "houseplan-assets/space-deletion-BIiYcPJz.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js", + "houseplan-assets/space-deletion-Dh9IGz13.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/space-deletion-BIiYcPJz.js", - "sha256": "f6bc8f6227d8e9b3cf52cd88449a29557d68b43649a7768d7deca9fad5bfb525", - "rawBytes": 906, - "gzipBytes": 551, + "path": "houseplan-assets/space-deletion-Dh9IGz13.js", + "sha256": "f95f8f94d4f034fa79551e7754dd48a3ae5f7002a7a7818fcb2fe76abeb8564c", + "rawBytes": 911, + "gzipBytes": 554, "isEntry": false, "imports": [], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "21ba57c3ff76114b14dd9c1510559511521ad707a736014fe6d2ebf47bd46765", + "sha256": "b4bd5117eb453a81f681b85f69c4125e2227b9077c262eb605c32b22b235b91b", "rawBytes": 1089, - "gzipBytes": 753, + "gzipBytes": 756, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-DJctcs0x.js" + "houseplan-assets/houseplan-card-VbP9hCtk.js" ], "dynamicImports": [] } diff --git a/dist/houseplan-assets/de-xcuT8cEm.js b/dist/houseplan-assets/de-XuJl1rK9.js similarity index 81% rename from dist/houseplan-assets/de-xcuT8cEm.js rename to dist/houseplan-assets/de-XuJl1rK9.js index 49ae9b85..dca9cb5c 100644 --- a/dist/houseplan-assets/de-xcuT8cEm.js +++ b/dist/houseplan-assets/de-XuJl1rK9.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.pick_hint":"Wählen Sie ein Symbol aus und klicken Sie dann auf den Plan.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Sessel","furn.sym_coffee_table":"Couchtisch","furn.sym_table_dining":"Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_chair":"Stuhl","furn.sym_desk":"Schreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Herd","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Toilette","furn.sym_bathtub":"Badewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Küchenspüle","furn.sym_bidet":"Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht"};const n="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";export{e as dictionary,n as fingerprint}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";var e={"editor.context_actions":"Aktionen: {object}","editor.tool_options":"Werkzeugoptionen: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Werkzeuggruppe: {group}","editor.group_active":"{group} — aktiv: {item}","editor.disabled_action":"{action} ist nicht verfügbar: {reason}","editor.loading":"Editor wird geladen…","editor.loading_aria":"Der Plan-Editor wird geladen","editor.load_failed":"Der Editor konnte nicht geladen werden.","editor.refresh_advice":"Seite aktualisieren und erneut versuchen.","editor.retry_advice":"Verbindung prüfen und erneut drücken.","color_picker.title":"Farbwähler","color_picker.hue":"Farbton","color_picker.saturation":"Sättigung","color_picker.value":"Helligkeit","color_picker.hex":"Hex-Farbe","color_picker.invalid_hex":"Geben Sie eine 3- oder 6-stellige Hex-Farbe ein","btn.properties":"Eigenschaften","btn.keep_as_walls":"Als Wände behalten","btn.delete_room_keep_walls":"Raum löschen, Wände behalten","btn.delete_room_with_walls":"Raum und Wände löschen","title.markup_select":"Wände, Säulen und gespeicherte Umrisse auswählen und bearbeiten","title.markup_column":"Säule: Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","markup.partition":"Trennwand","markup.column":"Säule","markup.hint_column":"Klicken Sie auf einen Rasterpunkt, um eine quadratische Säule zu platzieren","history.draft_segment":"Raum-Entwurfsegment hinzufügen","history.draft_merge":"Unvollständige Raumumrisse verbinden","history.draft_segment_delete":"Raum-Entwurfssegment entfernen","history.partition_add":"Trennwand hinzufügen","history.column_add":"Säule hinzufügen","history.physical_edit":"Physisches Objekt ändern","history.physical_delete":"Physisches Objekt löschen","history.physical_move":"Physisches Objekt verschieben","history.contour_to_partitions":"Umriss in geschlossene Wände umwandeln","history.wall_chain_finish":"Wandkette fertigstellen","history.wall_face_batch":"Räume aus Wänden erstellen","toast.column_duplicate":"Eine Säule mit demselben Zentrum und äußerer Größe existiert bereits","confirm.delete_draft":"Sofort den gesamten unvollständigen Raumumriss löschen?","confirm.delete_draft_segment":"Dieses Entwurfssegment löschen? Der verbleibende Umriss könnte sich in zwei teilen.","physical.partition_properties":"Eigenschaften der Trennwand","physical.column_properties":"Eigenschaften der Säule","physical.draft_properties":"Eigenschaften des Entwurfssegments","physical.shape":"Form","physical.square":"Quadrat","physical.circle":"Kreis","physical.diameter":"Durchmesser","physical.side":"Seite","physical.rotation":"Drehwinkel","physical.length":"Länge","physical.allowed_range":"Erlaubt: {min}–{max} {unit}","physical.delete_segment":"Segment löschen","physical.delete_draft":"Gesamten Umriss löschen","physical.column_size_title":"Außenmaß der per Klick platzierten quadratischen Säule (1–150 cm).","card.title":"Hausplan","count.devices":"{n} Entwicklung","empty.no_spaces":"Noch keine Bereiche.","empty.add_first":"Ersten Bereich hinzufügen und einen Grundriss hochladen.","empty.install":"Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.","btn.add_space":"Bereich hinzufügen","btn.cancel":"Abbrechen","btn.save":"Speichern","btn.close":"Schließen","btn.delete":"Löschen","btn.edit":"Bearbeiten","btn.open_in_ha":"In HA öffnen","btn.reset":"Zurücksetzen","btn.attach":"Anhängen…","btn.upload":"Hochladen…","btn.replace":"Ersetzen…","title.zoom_in":"Zoomen","title.zoom_out":"Herauszoomen","title.zoom_fit":"Alles anpassen","title.markup":"Raum-Markierung: Gitter, Linien, Konturen","title.configure_space":"Bereich konfigurieren","title.add_space":"Bereich hinzufügen","title.markup_add":"Wände: eine durchgehende Kette zeichnen; Shift sperrt auf 45°. Werkzeugwechsel oder Verlassen des Editors beendet die Kette und wandelt sie in unabhängige Wände","title.markup_merge":"Zusammenführen: Klicken Sie auf einen Raum und dann auf den Nachbarraum, mit dem er eine Wand teilt","title.markup_split":"Raum teilen: Klicken Sie auf den Raum und dann auf zwei Punkte an seinen Wänden","title.markup_delroom":"Raum löschen: Klicken Sie in einen Raum und bestätigen Sie","title.need_plan":"Einen Grundriss hochladen","markup.add":"Wände","markup.merge":"Zusammenführen","markup.split":"Teilen","markup.resize":"Größe ändern","title.markup_resize":"Räume vergrößern/verkleinern: Ziehen Sie einen verfügbaren horizontalen oder vertikalen Wandgriff","markup.hint_resize":"Ziehen Sie einen verfügbaren Wandgriff · die Wand stoppt an der ersten unsicheren Position · Esc bricht ab · Strg+Z — rückgängig","resize.disabled.diagonal":"Diese Wand ist geneigt; Größe ändern unterstützt nur horizontale und vertikale Wände","resize.disabled.side-angle":"Eine angrenzende Wand ist geneigt; Größe ändern erfordert, dass beide angrenzenden Wände diese Wand im rechten Winkel treffen","resize.disabled.duplicate-physical-wall":"Eine separate Trennwand, unfertige Kontur oder ein Pfeiler überlappt diese Wand; entfernen oder verschieben Sie sie, bevor Sie die Größe ändern","resize.disabled.partial-shared":"Ein angrenzender Raum nutzt nur einen Teil dieser Wand, daher kann sie nicht sicher als gemeinsame Wand verschoben werden","resize.disabled.unequal-shared":"Der angrenzende Raum nutzt andere Endpunkte oder Wandlängen, daher können die Wände nicht gemeinsam sicher bewegt werden","resize.disabled.multiple-rooms":"Das Verschieben dieser Wand würde mehr als zwei Räume betreffen","resize.disabled.thickness-conflict":"Die Wandstärke kann nicht sicher beibehalten werden","resize.disabled.opening-conflict":"Eine Öffnung an dieser Wand kann nicht sicher verschoben werden","resize.disabled.invalid-geometry":"Diese Wand kann nicht ohne Änderung der Planstruktur geändert werden","resize.limit_stopped":"Wand gestoppt: der nächste Schritt würde ein Anschlusslimit verletzen","resize.commit_failed":"Größenänderung wurde abgebrochen, da der endgültige Plan die Geometrieprüfung nicht bestanden hat","resize.preview_failed":"Größenänderung am letzten sicheren Punkt gestoppt, da die Plan-Geometrie nicht beibehalten werden konnte","markup.opening":"Öffnung","markup.delete_room":"Raum löschen","history.undo":"Rückgängig","history.redo":"Wiederholen","history.undo_named":"Rückgängig: {name} (Strg+Z)","history.redo_named":"Wiederholen: {name} (Strg+Shift+Z / Strg+Y)","history.undo_empty":"Nichts zu widerrufen","history.redo_empty":"Nichts zu wiederholen","history.undone":"Rückgängig gemacht: {name}","history.redone":"Wiederholt: {name}","history.add_room":"Raum erstellen","history.split_room":"Raum teilen","history.merge_rooms":"Räume zusammenführen","history.resize_room":"Raumgröße ändern","history.wall_thickness":"Wandstärke ändern","history.add_opening":"Tür- oder Fensteröffnung hinzufügen","history.edit_opening":"Öffnung bearbeiten","history.move_opening":"Öffnung verschieben","history.delete_opening":"Öffnung löschen","history.delete_room":"Raum löschen","history.delete_room_keep_walls":"Raum löschen, Wände behalten","history.delete_room_with_walls":"Raum und Wände löschen","history.decor_add":"Dekorobjekt hinzufügen","history.decor_edit":"Dekorobjekt bearbeiten","history.decor_move":"Dekorobjekt verschieben","history.decor_transform":"Dekorobjekt transformieren","history.decor_delete":"Dekorobjekt löschen","history.backdrop_transform":"Plan-Hintergrund transformieren","opening.new":"Neue Öffnung","opening.edit":"Öffnung","opening.door":"Tür","opening.window":"Fenster","opening.gate":"Tor","opening.passage":"Offener Durchgang","opening.passage_binding_warning":"Das Speichern entfernt den Öffnungs-/Schließsensor und das Schloss.","opening.invalid_passage_fields":"Der offene Durchgang auf dem Plan „{room}“ hat nicht unterstützte Einstellungen: {fields}.","opening.type_label":"Typ","opening.length_label":"Länge, cm","opening.contact_label":"Öffnungs-/Schließsensor","opening.lock_label":"Schloss","opening.none":"— keine —","opening.search_ph":"Suche: Name oder Entitäts-ID…","opening.invert":"Geöffnet/geschlossen invertieren","opening.flip_h":"Scharnier an der anderen Zarge","opening.flip_v":"Öffnet zur anderen Seite","opening.open":"Offen","opening.closed":"Geschlossen","opening.locked":"Verriegelt","opening.unlocked":"Entriegelt","opening.state_unknown":"nicht verfügbar","opening.no_entities":"Keine Sensoren gebunden — ein statisches Symbol auf dem Plan.","toast.opening_no_wall":"Neben eine Raumwand oder eine unabhängige Wand klicken","opening.host_partition":"Unabhängige Wand","opening.partition_orphan":"Die unabhängige Wand für diese Öffnung existiert nicht mehr","opening.partition_jamb_margin":"Mindestens {distance} zwischen der Öffnung und dem Wandende lassen","opening.rebind_partition":"An eine andere unabhängige Wand anfügen","confirm.delete_partition_openings_title":"Wand und Öffnungen löschen?","confirm.delete_partition_openings_body":"Diese Wand enthält {count} Öffnung(en). Sie werden zusammen gelöscht.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.delete":"Löschen","markup.hint_points":"Punkte: {n} · Shift — 45° Schritte · Esc — Kette beenden · Ctrl+Z — Punkt rückgängig machen · Schließen eines Bereichs erstellt einen Raum","markup.hint_start":"Klicken Sie auf einen Rasterpunkt, um eine Wandkette zu starten","tip.lqi":"durchschnittliches Zigbee-Signal:","tip.area":"Bereich: {value}","info.device_header":"Gerät im Plan","info.model":"Modell","info.state":"Status","info.link":"Verbindung","info.manuals":"Handbücher","info.none":"Keine zusätzlichen Informationen","marker.new_device":"Neues Gerät","marker.name_label":"Name (im Plan angezeigt)","marker.name_ph":"Name","marker.binding_label":"An ein HA-Gerät binden","marker.binding_disabled":"in Home Assistant deaktiviert","marker.virtual_option":"Virtuelles Gerät (keine Bindung)","marker.search_ph":"Gerät/Gruppe suchen…","marker.nothing_found":"Nichts gefunden","marker.room_label":"Raum","marker.room_override":" (Platzierung überschreiben)","marker.room_choose":"— Raum auswählen —","marker.room_auto":"— nach Gerätebereich (auto) —","marker.icon_label":"Symbol","marker.icon_ph":"mdi:… (leer = automatisch)","marker.display_label":"Anzeige","display.badge":"Symbol + Zustand","display.icon_ripple":"Symbol + Zustand und Aktivität","display.static_icon":"Immer statisches Symbol","marker.display_hint":"Symbol + Zustand ändert die Platte für die Zustände Arbeit, geöffnet, Alarm und nicht verfügbar. Symbol + Zustand und Aktivität zeigt zusätzlich einen kurzen Impuls für Ereignisse und einen kontinuierlichen Impuls für laufende Arbeit, Bewegung oder Präsenz. Wert + Zustand ersetzt das Symbol durch einen eindeutigen HA-Wert. Ein statisches Symbol reagiert niemals auf Gerätezustände.","marker.display_hint_badge":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon":"Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.","marker.display_hint_icon_ripple":"Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.","marker.display_hint_value":"Ein eindeutiger Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.","marker.display_hint_static_icon":"Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.","marker.static_alarm_warning":"Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.","marker.preview.title":"Anzeigevorschau","marker.preview.actual":"Jetzt","marker.preview.example":"Beispiel","marker.preview.integration":"Bereitgestellt von","marker.preview.source":"Anzeigquelle","marker.preview.current_state":"Aktueller Zustand","marker.preview.result":"Auf dem Plan","marker.preview.details":"Quelldetails","marker.preview.select_source":"Ein Home-Assistant-Gerät oder eine Entität auswählen, um die tatsächliche Anzeige zu sehen.","marker.preview.unknown_provider":"Unbekannte Integration","marker.preview.virtual_provider":"House Plan · virtuelles Gerät","marker.preview.no_source":"Keine aktive Quelle","marker.preview.no_state":"Kein aktueller Zustand","marker.preview.mixed_states":"Mehrere verschiedene Zustände","marker.preview.multiple_sources":"{n}-Quellen","marker.preview.more_sources":"+{n} mehr","marker.preview.scaled":"Die Vorschau wird auf {n}% skaliert, um zu passen. Die gespeicherte Größe bleibt unverändert.","marker.preview.demo_short":"Kurze Aktivität anzeigen","marker.preview.demo_continuous":"Kontinuierliche Aktivität anzeigen","marker.preview.stop_continuous":"Kontinuierliche Aktivität stoppen","marker.preview.demo_short_notice":"Kurzes Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_continuous_notice":"Kontinuierliches Aktivitätsbeispiel; der tatsächliche Gerätezustand bleibt unverändert.","marker.preview.demo_already_visible":"Der aktuelle Zustand zeigt bereits eine echte Aktivitäts- oder Alarmeffekt.","marker.preview.reduced_motion":"Systemweit reduzierte Bewegungen sind aktiviert, daher wird gewöhnliche Aktivität als Punkt angezeigt.","marker.preview.reason.neutral":"Neutrale dunkle Platte","marker.preview.reason.working":"Gelbe Platte: Das Gerät arbeitet jetzt","marker.preview.reason.working_activity":"Gelbe Platte und Aktivitätseffekt: das Gerät funktioniert jetzt","marker.preview.reason.open":"Orange Platte: physisch geöffnet oder entriegelt","marker.preview.reason.cover_icon_state":"Neutrale Platte; die Abdeckungseinheit steuert den angezeigten Zustand und das Symbol","marker.preview.reason.presence":"Aktivitätseffekt, während Präsenz erkannt wird","marker.preview.reason.event":"Kurzer Aktivitätseffekt nach einem erkannten Ereignis","marker.preview.reason.transition":"Aktivitätseffekt, während das Gerät sich bewegt oder den Zustand ändert","marker.preview.reason.media_neutral":"Mediengeräte bleiben im verfügbaren Zustand auf einer neutralen dunklen Platte","marker.preview.reason.unavailable":"Gedämpfte neutrale Platte: nicht verfügbar oder ausgeschaltet","marker.preview.reason.alarm":"Rote Alarmplatte; Alarmanzeige wird immer angezeigt","marker.preview.reason.live_states_disabled":"Live-Zustandsstil ist für diese Karte deaktiviert","marker.preview.reason.value_no_state":"Kein nutzbarer Zustandswert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_ambiguous_sources":"Mehrere mögliche Wertequellen; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_non_scalar":"Die Quelle lieferte keinen einfachen Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.value_virtual":"Ein virtuelles Gerät hat keinen Home Assistant-Wert; stattdessen wird das Symbol angezeigt","marker.preview.reason.vacuum_live_plan_only":"Echtzeitposition und -spur des Staubsaugers sind nur im vollständigen Plan verfügbar","marker.preview.reason.hidden_design_preview":"Das Gerät ist im Plan ausgeblendet; diese Designvorschau bleibt sichtbar","marker.preview.reason.composite_power_source":"Der Status stammt vom Power-Entity des Geräts; Hilfsschalter werden ignoriert","marker.preview.reason.activity_display_disabled":"Der gewählte Anzeigemodus zeigt gewöhnliche Aktivitätseffekte nicht an","marker.preview.reason.ha_disabled":"Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet","marker.preview.reason.orphaned":"Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden","marker.preview.reason.static_icon":"Statischer Modus: Der Gerätestatus ändert das Symbol nicht","marker.activity_color":"Aktivitätspulsfarbe","marker.ripple_size":"Aktivitätspulsgröße","marker.activity_alarm_note":"Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.","marker.pulse_a11y_alarm":"Alarm","marker.pulse_a11y_event":"Kürzliches Ereignis","marker.pulse_a11y_presence":"Anwesenheit erkannt","marker.pulse_a11y_transition":"Statusänderung","marker.pulse_a11y_running":"Arbeitet","marker.state_a11y_neutral":"Standardzustand","marker.state_a11y_working":"Funktionierend","marker.state_a11y_open":"Offen","marker.state_a11y_locked":"Verschlossen","marker.state_a11y_unlocked":"Entriegelt","marker.state_a11y_alarm":"Alarm","marker.state_a11y_unavailable":"Nicht verfügbar","marker.lqi_a11y_low":"LQI {value}, schwaches Signal","marker.lqi_a11y_mid":"LQI {value}, mittleres Signal","marker.lqi_a11y_high":"LQI {value}, starkes Signal","marker.size_label":"Symbolgröße / Drehung","marker.angle_label":"Drehen","marker.model_label":"Modell","marker.model_ph":"z.B. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Beschreibung","marker.desc_ph":"Notizen, Spezifikationen…","marker.manuals_label":"Handbücher (PDF usw.)","marker.sub_device":"Gerät","marker.sub_z2m_group":"· Z2M Gruppe","marker.sub_group":"Gruppe","marker.sub_helper":"Helfer","space.new":"Neuer Bereich","space.header":"Bereich","space.title_label":"Titel","space.title_ph":"z.B. Garage","space.plan_label":"Grundriss (Hintergrund)","space.no_plan":"Kein Planbild","space.plan_alt":"Grundriss","room.new":"Neuer Raum","room.name_label":"Anzeigename","room.name_ph":"z.B. Terrasse","room.area_label":"Home Assistant Bereich (nicht zugewiesen)","room.no_area_option":"— kein Bereich —","room.default_name":"Raum","device.unnamed":"unbenannt","device.light_group":"Lichtgruppe","device.fallback":"Gerät","device.virtual":"Virtuelles Gerät","confirm.delete_room":'Raum "{name}" löschen?',"confirm.delete_room_title":'Raum "{name}" löschen?',"confirm.delete_room_body":"Festlegen, ob die ausschließlich zu diesem Raum gehörenden physischen Wände erhalten bleiben. Mit einem anderen Raum geteilte Wände bleiben immer erhalten.","confirm.remove_marker":"„{name}“ aus dem Plan löschen? Das Gerät verschwindet vollständig und trägt nicht mehr zu den Plandaten bei. Es kann später erneut hinzugefügt werden.","confirm.erase_decor":"Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.","confirm.delete_space":"Bereich „{title}“ mit allen Räumen und Markierungen löschen?","space.delete_blocked":"Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.","toast.pos_save_failed":"Position nicht gespeichert: {err}","toast.no_entity":"Das Gerät hat keine geeignete Entität","toast.ha_disabled_action":"Ein deaktiviertes Home Assistant-Objekt kann im Plan nicht verwendet werden.","toast.ha_disabled_show_device":"Ein in Home Assistant deaktiviertes Gerät kann im Plan nicht angezeigt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_disabled_show_entity":"Eine in Home Assistant deaktivierte Entität kann im Plan nicht angezeigt werden. Aktivieren Sie sie zuerst in Home Assistant.","toast.ha_disabled_add":"Ein deaktiviertes Home Assistant-Objekt kann nicht dem Plan hinzugefügt werden. Aktivieren Sie es zuerst in Home Assistant.","toast.ha_binding_unverified":"Der Status des Objekts konnte über das Home Assistant-Register nicht überprüft werden. Anzeige und Aktionen sind vorübergehend nicht verfügbar.","toast.markup_needs_server":"Markup ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde.","toast.space_order_changed":"Reihenfolge geändert. Wenn eine Karte ihr Stockwerk nach Nummer festlegt, überprüfen Sie diese Paneele.","toast.conflict":"Die Konfiguration wurde in einem anderen Fenster geändert — Daten aktualisiert, wiederholen Sie Ihre letzte Aktion.","toast.cfg_save_failed":"Speichern der Konfiguration fehlgeschlagen: {err}","toast.room_overlap":"Die Umrisse überlappen den Raum „{name}“ — Räume dürfen sich nicht überlappen","toast.contour_cannot_close":"Die Kontur kann nicht geschlossen werden, da sie entartet ist oder sich selbst schneidet","toast.merge_not_adjacent":"Es können nur Räume zusammengeführt werden, die eine Wand teilen","toast.rooms_merged":"Räume zu „{name}“ zusammengeführt","toast.split_pick_wall":"Beginnen Sie den Schnitt an der Wand des Raumes","toast.split_bad_cut":"Der Schnitt muss wand-zu-wand innerhalb des Raumes verlaufen, ohne Wände oder sich selbst zu kreuzen","merge.header":"Räume zusammenführen","merge.hint":"Der zusammengeführte Raum behält einen Namen und eine Fläche. Die andere Fläche wird freigegeben — ihre Geräte verlassen den Plan, bis ein anderer Raum sie beansprucht.","merge.keep":"Behalten","merge.no_area":"keine Fläche","toast.room_saved":"Raum gespeichert ({n}). Hinzugefügte Geräte: {added}. Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.room_saved_no_area":"Raum gespeichert ({n}, keine Fläche). Zeichnen Sie die nächste Kontur oder verlassen Sie die Markierung.","toast.marker_needs_server":"Die Gerätebearbeitung ist verfügbar, nachdem die Konfiguration auf den Server verschoben wurde","toast.virtual_name_required":"Einen Namen für das virtuelle Gerät eingeben","toast.marker_saved":"Gerät gespeichert","toast.marker_removed":"Gerät aus dem Plan gelöscht","toast.integration_missing":"Die House Plan-Integration ist nicht installiert — Verwaltung nicht verfügbar","toast.plan_formats":"Unterstützte Formate: SVG, PNG, JPG, WebP","toast.plan_required":"Grundriss hochladen — erforderlich","toast.space_added_onboard":"Bereich hinzugefügt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","toast.space_added":"Bereich hinzugefügt","toast.space_saved":"Bereich gespeichert","toast.space_deleted":"Bereich gelöscht","toast.delete_failed":"Löschen fehlgeschlagen: {err}","toast.error":"Fehler: {err}","toast.toggle_target_unavailable":"Ziel „{name}“ ist nicht verfügbar — keine Aktion durchgeführt","toast.toggle_targets_unavailable":"Ziele nicht verfügbar: {names}. Keine Aktion durchgeführt","toast.file_failed":'Datei "{name}" wurde nicht hochgeladen: {err}',"toast.files_attached":"Dateien angehängt: {n}","err.unknown":"unbekannter Fehler","err.code":"Code {code}","err.too_large":"Datei größer als {mb} MB","err.bad_ext":"nicht unterstützter Typ (PDF/Bild erwartet)","err.unauthorized":"Administratorrechte erforderlich","editor.title":"Titel","editor.default_floor":"Anfangsbereich","editor.default_floor_missing":"Anfangsbereich „{id}“ existiert nicht mehr. Wählen Sie einen anderen Bereich.","editor.floor":"Fester Bereich","editor.floor_none":"— nicht fest —","editor.floor_index":"YAML-Index {index} (beibehalten)","fixed_floor.loading":"Lade den festen Bereich…","fixed_floor.invalid_title":"Der feste Bereich ist nicht verfügbar","fixed_floor.invalid_body":"Überprüfen Sie die Etage in der Kartenkonfiguration. Der konfigurierte Wert ist: {value}","editor.icon_size":"Symbolgröße, % der Planbreite","editor.show_temperature":"Temperatur anzeigen","editor.live_states":"Live-Zustände (ein/aus, offen…)","editor.show_signal":"Zigbee-Signal anzeigen (LQI)","editor.language":"Schnittstellensprache","editor.lang_auto":"Automatisch (HA-Profil)","title.icon_rules":"Symbolregeln: welche MDI-Symbole Geräte nach Namen erhalten","rules.title":"Symbolregeln","rules.hint":'Regeln werden von oben anhand von "Gerätename + Modell" (Großschreibungs-unempfindliches Regex) überprüft; das erste Match gewinnt. Wenn nichts übereinstimmt, entscheidet die Entität-Geräteklasse, dann das generische Chip-Symbol.',"rules.pattern_ph":"regex, z. B. Stecker|Steckdose","rules.icon_ph":"mdi:power-socket-de","rules.add":"Regel hinzufügen","rules.reset":"Zurücksetzen auf Standardeinstellungen","rules.test_ph":"Versuch mal einen Gerätenamen...","rules.invalid":"ungültiges regex","rules.saved":"Icon-Regeln gespeichert","btn.up":"Nach oben","btn.down":"Nach unten","tap.info":"Gerätekarte","tap.more_info":"HA Mehr-Info-Dialog","tap.toggle":"Zustand umschalten","marker.tap_label":"Tap-Aktion für dieses Gerät","tap.toggle_note":"Der aufgelöste Zustand ist unten dargestellt. Sichere Geräte werden niemals aus dem Plan umgeschaltet.","import.title":"Bereiche aus HA-Etagen erstellen","import.hint":"Home Assistant kennt diese Etagen bereits. Die Etagen auswählen, die zu Planbereichen werden sollen; anschließend für jeden Bereich einen Grundriss hochladen. Die Räume werden danach von Hand eingezeichnet.","import.start":"{n} Bereich(e) erstellen","import.manual":"Von Grund auf neu starten","import.progress":"Etage {i} von {n}","import.done":"Bereiche erstellt. Räume einzeichnen: Rasterpunkte anklicken und die Kontur schließen.","btn.skip":"Überspringen","space.scale_label":"Maßstab (Rasterzellen-Größe)","space.scale_unit":"cm pro Zelle","space.scale_unit_imperial":"Zoll pro Zelle","space.display_section":"Anzeige","space.show_borders":"Raumgrenzen immer anzeigen","space.zero_wall_style":"Wände ohne Dicke","space.zero_wall_dashed":"Gestrichelt","space.zero_wall_solid":"Durchgezogen","space.zero_wall_help":"Gestrichelte Wände lassen Licht durch. Durchgezogene Wände blockieren Licht auch bei einer Dicke von 0.","space.show_names":"Raumnamen anzeigen (zum Verschieben ziehen)","space.room_color":"Farbe der Begrenzung & des Namens","space.opacity":"Deckkraft","space.fill_label":"Raumausfüllung","fill.none":"Keine","fill.lqi":"Zigbee-Signal","fill.light":"Lichter","fill.custom":"Benutzerdefinierte Farbe","space.custom_fill":"Füllfarbe","space.source_file":"Ich habe einen Grundriss als Bild","space.source_draw":"Kein Bild – ich werde die Räume von Hand umreißen","fill.temp":"Temperatur","tip.temp_avg":"Durchschnittstemperatur:","tip.hum_avg":"Durchschnittliche Luftfeuchtigkeit:","space_card.button":"Grundriss öffnen","space_card.not_found":"Bereich „{id}“ nicht gefunden","space_card.loading":"Laden…","continuity.restore_plan":"Grundriss wiederherstellen…","continuity.restore_connection":"Geräteverbindung wiederherstellen…","continuity.retry":"Erneut versuchen","editor.space":"Bereich","editor.show_button":"Schaltfläche anzeigen","editor.button_label":"Beschriftung der Schaltfläche","editor.button_target":"Ziel-Dashboard-Pfad","marker.sub_entity":"Entität","title.general_settings":"Allgemeine Einstellungen","gs.title":"Allgemeine Einstellungen","gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.light_group":"Füllung: Licht","gs.light_on":"Lichter an","gs.light_off":"Alle Lichter aus","gs.temp_group":"Füllung: Temperatur","gs.temp_cold":"Kalt","gs.temp_ok":"Angenehm","gs.temp_hot":"Heiß","gs.lqi_group":"Füllung: Zigbee-Signal","gs.lqi_low":"Schwaches Signal","gs.lqi_high":"Starkes Signal","gs.reset":"Auf Standard zurücksetzen","gs.saved":"Allgemeine Einstellungen gespeichert","space.show_lqi":"Zigbee-Signal (LQI) neben Geräten anzeigen","space.hide_decor":"Dekorationsschicht ausblenden","space.hide_decor_tip":"Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.","space.hide_openings":"Öffnungen ausblenden","space.hide_openings_tip":"Tür-, Fenster- und Tor-Symbole werden nicht gezeichnet, aber die Öffnungen funktionieren weiterhin: Licht fällt hindurch, die Sonne kommt durch ein Fenster herein, Kontaktsensoren öffnen sie weiterhin. Der Planeditor zeigt sie immer an.","gs.light_none":"Keine Lichtquellen","mode.plan":"Planeditor","mode.devices":"Geräteeditor","display.value":"Wert + Zustand","marker.subarea":"kein Bereich, manuell","device.new":"Neues Gerät — Editor öffnen, um zu schließen","opening.unlock_action":"Entsperren","opening.lock_action":"Sperren","opening.lock_pending":"Wird ausgeführt…","title.close_editor":"Editor schließen (zur Ansicht zurück)","devbar.rules":"Symbolregeln","device_inbox.button":"Geräte","device_inbox.title":"Geräte auf dem Plan","device_inbox.search":"Geräte, Entitäten und Integrationen suchen…","device_inbox.add_virtual":"Virtuelles Gerät hinzufügen","device_inbox.tab_on_plan":"Auf Plan","device_inbox.tab_available":"Verfügbar","device_inbox.tab_hidden":"Versteckt","device_inbox.tab_readd":"Wieder verfügbar","device_inbox.only_new":"Nur neu","device_inbox.show_entities":"Entitäten anzeigen","device_inbox.show_hidden":"Versteckte auf Plan anzeigen","device_inbox.new":"Neu","device_inbox.find":"Auf dem Plan finden","device_inbox.edit":"Bearbeiten","device_inbox.hide":"Ausblenden","device_inbox.show":"Anzeigen","device_inbox.add":"Hinzufügen","device_inbox.readd":"Wieder hinzufügen","device_inbox.hide_available":"Aus der Liste ausblenden","device_inbox.show_more":"Mehr anzeigen","device_inbox.more_actions":"Weitere Aktionen","device_inbox.find_hidden_hint":"Zuerst „Versteckte auf Plan anzeigen“ aktivieren","device_inbox.show_disabled":"Diese Bindung in Home Assistant aktivieren, bevor sie angezeigt wird","device_inbox.empty_on_plan":"Es befinden sich noch keine passenden Geräte auf dem Plan.","device_inbox.empty_available":"Es sind keine Geräte zum Hinzufügen verfügbar.","device_inbox.empty_hidden":"Es gibt keine versteckten Geräte.","device_inbox.empty_readd":"Es sind keine entfernten Geräte verfügbar, um sie erneut hinzuzufügen.","device_inbox.reason_visible_auto":"Automatisch erkannt","device_inbox.reason_visible_explicit":"Explizit hinzugefügt","device_inbox.reason_manual_hidden":"Vom Benutzer versteckt","device_inbox.reason_automatic_hidden":"Automatisch versteckt","device_inbox.reason_service_entry":"Nur Service-Registry-Eintrag","device_inbox.reason_excluded_integration":"Integration durch Gerätefilter ausgeschlossen","device_inbox.reason_excluded_domain":"Nicht-räumlicher Entitätstyp","device_inbox.reason_grouped_light":"Dargestellt durch eine Raumlichtgruppe","device_inbox.reason_represented_by_parent":"Dargestellt durch sein übergeordnetes Gerät","device_inbox.reason_removed":"Zuvor aus dem Plan entfernt","device_inbox.reason_available":"Zum Hinzufügen zum Plan verfügbar","device_inbox.reason_no_bound_room":"HA-Bereich ist nicht mit einem Planraum verknüpft","device_inbox.status_ha_disabled":"In Home Assistant deaktiviert","device_inbox.status_orphaned":"Bindung ist in Home Assistant nicht mehr vorhanden","device_inbox.status_unverified":"Home Assistant-Register vorübergehend nicht verfügbar","device_inbox.saved":"Geräteliste aktualisiert","space.roomcard_section":"Raumkarte zeigt:","space.label_temp":"Temperatur","space.label_hum":"Luftfeuchtigkeit","space.label_lqi":"Durchschnittliches Zigbee-Signal","space.label_light":"Lichter ein/aus","roomcard.light_on":"Ein","roomcard.light_off":"Aus","roomcard.light_partial":"{on} von {total}","toast.split_pick_inside":"Zwischenliegende Schnittpunkte müssen innerhalb des Raumes liegen","mode.decor":"Hintergrundeditor","decor.select":"Auswählen","decor.line":"Linie","decor.rect":"Rechteck","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Löschen","decor.erase_confirm_title":"Objekt löschen?","decor.color":"Farbe","decor.width":"Linienbreite","decor.line_style":"Linienstil","decor.line_style_solid":"Durchgehend","decor.line_style_dashed":"Gepunktet","decor.fill":"Füllen","decor.fill_color":"Füllfarbe","decor.length":"Länge","decor.size":"Größe","decor.angle":"Rotationswinkel, °","decor.text_size":"Textgröße","decor.backdrop_properties":"Eigenschaften des Planhintergrunds","decor.text_title":"Textbeschriftung","decor.object_title":"{kind} bearbeiten","decor.text_label":"Text","decor.live_group":"HA-Variable einfügen","decor.live_entity":"Entität","decor.live_entity_ph":"Wählen Sie eine Entität","decor.live_attr":"Wert","decor.live_attr_ph":"Wählen Sie Status oder Attribut","decor.live_state":"Status","decor.backdrop":"Hintergrundbild","decor.backdrop_hint":"Ziehen zum Verschieben; eine Ecke ziehen zum Größenändern; den oberen Griff zum Drehen verwenden. Shift ändert das Verhältnis oder befreit den Winkel.","decor.backdrop_reset":"Bild zurücksetzen","decor.backdrop_reset_done":"Das Bild ist wieder an seinem ursprünglichen Platz und in seiner ursprünglichen Größe","marker.icon_auto":"Auto: {icon} (nach Symbolregeln; wählen Sie eines zur Überschreibung)","marker.icon_pin_auto":"Anheften","mode.plan_tip":"Planungseditor — die Geometrie des Hauses: Räume zeichnen und teilen/zusammenführen, sie mit HA-Bereichen verbinden, Türen, Fenster und Tore platzieren, Raumkarten verschieben, Maßstab festlegen","mode.devices_tip":"Geräteeditor — alles über Symbole: ziehen zum Positionieren, klicken zum Bearbeiten von Bindung/Symbol/Anzeige, virtuelle Geräte hinzufügen, Symbolregeln","mode.decor_tip":"Hintergrundeditor — rein visuelle Dekoration unter dem Plan: Linien, Rechtecke, Ellipsen und Textbeschriftungen, die niemals auf Klicks reagieren","space.glow_enabled":"Lichtquellen-Glühen","gs.glow_group":"Glow von Lichtquellen","gs.glow_base":"Grunddunkelheit des Hauses","gs.glow_light":"Standardlichtfarbe / Intensität","gs.wall_group":"Wände","gs.wall_fill":"Wandfüllung","gs.glow_radius":"Glow-Radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Steuert andere Lichtquellen","marker.controls_hint":"Mit der Tippen-Aktion „Umschalten“ wechselt ein Klick jede hier hinzugefügte Quelle. Die eigene zugeordnete Entität des Markers wird direkt gesteuert und kann nicht hinzugefügt werden; verwenden Sie den untenstehenden Schalter, um ihn als Licht zu klassifizieren.","marker.controls_filter":"Suchlichter und Schalter…","info.controls":"Steuerungen","marker.glow_radius_label":"Leuchtradius","marker.glow_radius.help":"Legt den Leuchtradius in Metern oder Fuß fest; ein leerer Wert verwendet den Radius aus den allgemeinen Einstellungen.","marker.glow_radius.help.aria":"Hilfe: Leuchtradius","markup.wallthick":"Dicke","markup.select":"Auswählen","title.markup_wallthick":"Dicke — klicken Sie auf eine Wand, um ihre Dicke von 0 bis 100 cm festzulegen.","markup.hint_wallthick":"klicken Sie auf eine Wand · Esc schließt ohne Anwendung","wallthick.field":"Dicke","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Auf alle Wände dieses Raums anwenden","markup.draw_wall_title":"Dicke jedes neuen Wandsegments (0–100 cm). Gemeinsame Wände behalten den bestehenden Wert.","room.queue_progress":"Raum {current} von {total}","toast.wall_rooms_saved":"Erstellte Räume: {n}","toast.wall_chain_saved":"Wandkette gespeichert","toast.wallthick_pick":"Eine Wand anklicken","toast.wallthick_set":"Wandstärke eingestellt","toast.wallthick_cleared":"Wandstärke entfernt","toast.physical_range":"Einen Wert von {min} bis {max} {unit} eingeben","toast.zero_wall_opening_conflict":"Entfernen Sie zuerst die Öffnung an diesem Wandsegment.","toast.zero_wall_ambiguous":"Das Wandsegment ist mehrdeutig. Vereinfachen oder passen Sie den Anschluss an.","toast.zero_wall_migration_blocked":"Der Bereich wurde nicht konvertiert: {reason}. Es wurden keine Daten geändert.","toast.physical_angle":"Einen Drehwinkel von 0° bis unter 90° eingeben","toast.physical_limit":"Der Bereich hat das Limit für diesen Geometrietyp erreicht","toast.geometry_unsafe":"Änderung abgebrochen: Wandgeometrie konnte nicht sicher erstellt werden.","junction.limit_angle":"Der Winkel zwischen den Wänden ist zu spitz: {actual}°, mindestens {limit}°.","junction.limit_valence":"Zu viele Wände treffen in einem Knoten aufeinander: {actual}, maximal {limit}.","junction.limit_length":"Das Wandsegment ist zu kurz: {actual} cm, mindestens {limit} cm.","junction.limit_distance":"Wände und Knoten sind zu dicht beieinander: {actual} cm, Minimum {limit} cm.","junction.limit_clearance":"Kein Wohnbereich ist übrig: {actual} cm², Minimum {limit} cm².","junction.limit_check_failed":"Die Überprüfung der Verbindungen konnte nicht durchgeführt werden — die Änderung wurde nicht gespeichert.","toast.wall_model_migration_blocked":"Das Wandmodell konnte nicht aktualisiert werden: {reason}. Der Plan wurde nicht geändert. Führen Sie „Pläne optimieren“ aus; wenn der Fehler erneut auftritt, beheben Sie die widersprüchliche Wandgeometrie.","toast.wall_model_client_outdated":"Aktualisieren Sie die Karte und laden Sie die Seite neu, bevor Sie den Plan bearbeiten.","wall_model.reason.invalid-room":"ungültiger Raumkontur","wall_model.reason.zero-length":"Wandsegment mit null Länge","wall_model.reason.third-owner":"Wand, die von mehr als zwei Räumen geteilt wird","wall_model.reason.duplicate-id":"widersprüchliche Wandkennungen","wall_model.reason.thickness-conflict":"widersprüchliche Wandstärkenwerte","wall_model.reason.opening-host":"Öffnung kann nicht einer Wand zugewiesen werden","toast.delete_room_pick":"Klicken Sie in den Raum, den Sie löschen möchten","toast.plan_snap_ambiguous":"Zoomen Sie hinein, um den Wandknoten auszuwählen","toast.wall_repair_ambiguous":"Der Raumumriss hat mehr als eine mögliche Verbindung. Zoomen Sie hinein und verbinden Sie ihn explizit.","toast.wall_repair_too_large":"Die Lücke ist größer als 2 cm. Verbinden Sie die Wände, bevor Sie den Raum erstellen.","toast.wall_repair_changed":"Die Wandgeometrie hat sich geändert. Versuchen Sie erneut, den Raum zu erstellen.","toast.opening_on_zero_wall":"Öffnungen können nicht auf einer Wand mit null Dicke sitzen.","marker.from_ha_option":"Wählen Sie aus der HA-Liste.","marker.show_entities":"Entitäten anzeigen.","marker.show_entities_tip":"Fügt nicht nur Geräte zur Liste hinzu, sondern auch alle ihre Entitäten.","marker.pick_ph":"Wählen Sie ein Gerät…","room.open_area":"Öffnen Sie den HA-Bereich.","view.volumetric":"Volumetrische Ansicht.","view.flat":"Flache Ansicht.","kiosk.title":"Die Größen dieses Bildschirms.","kiosk.hint":"Nur auf diesem Gerät gespeichert – jedes Wandtablet oder TV kann seine eigenen komfortablen Größen haben.","kiosk.icon_scale":"Gerätesymbolgröße.","kiosk.font_scale":"Textgröße der Raumkarte.","editor.kiosk":"Wandgeräte- (Kiosk-)Modus.","editor.cycle":"Bereiche automatisch alle N Sekunden wechseln (Kiosk, 0 = aus)","room.settings_title":"Raumeinstellungen.","room.settings_section":"Raumeinstellungen (überschreibt den Bereich)","room.fill_label":"Diesen Raum ausfüllen","fill.inherit":"Als den Bereich","room.custom_fill_space":"Bereichsfarbe","room.custom_fill_own":"Raumfarbe","room.temp_src_label":"Temperaturquelle","room.hum_src_label":"Luftfeuchtigkeitsquelle","room.src_average":"Durchschnitt über die Sensoren des Raums (Standard)","room.src_pick":"Ein bestimmtes HA-Gerät oder Entität","room.src_ph":"Quelle wählen…","toast.room_updated":"Raum aktualisiert","space.card_font":"Schriftgröße der Raumkarte (gesamter Bereich)","room.sizes_section":"Schriftgrößen","room.name_scale":"Raumname Größe","room.label_scale":"Metrikgröße","preview.room_name":"Wohnzimmer","toast.cfg_reload_failed":"Der Plan konnte nicht vom Server neu geladen werden: {err}","toast.locale_load_failed":"Das Sprachpaket konnte nicht geladen werden. Bis zum Neuladen der Seite wird Englisch verwendet.","room.settings_short":"Raumeinstellungen","room.unnamed":"Unbenannter Raum","marker.use_climate_temp":"Geräte-Temperatur im Raum berücksichtigen","marker.use_climate_temp_tip":"Fügt die aktuelle Temperatur einer Klimaanlage oder eines Thermostats zum Raumdurchschnitt hinzu. Das externe Wert-Label unten separat konfigurieren.","marker.value_badge_title":"Wert-Label","marker.value_badge.help":"Zeigt einen ausgewählten Wert neben dem Symbol an. Dies beeinflusst nicht die Raumkennzahlen, Licht, Glanz oder die Tippaktion.","marker.value_badge.help.aria":"Hilfe: Gerätewert-Badge","marker.value_badge_enabled":"Zeige ein Wert-Badge","marker.value_badge_source":"Wert","marker.value_badge_source.help":"Wählen Sie einen bestimmten Zustand, ein unterstütztes Attribut oder einen abgeleiteten Wert dieses Geräts.","marker.value_badge_source.help.aria":"Hilfe: Quelle des Wert-Badges","marker.value_badge_position":"Position","marker.value_badge_position.help":"Die gewählte Seite bleibt fest und dreht sich nicht automatisch an einem Planrand.","marker.value_badge_position.help.aria":"Hilfe: Position des Wert-Badges","marker.value_badge_right":"Rechts","marker.value_badge_bottom":"Unten","marker.value_badge_left":"Links","marker.value_badge_top":"Oben","marker.value_badge_empty":"Für dieses Gerät stehen keine Werte zur Verfügung","marker.value_badge_static":"Ein statisches Symbol zeigt keine Live-Werte an. Die Einstellung wird beibehalten.","marker.value_badge_missing":"Quelle nicht verfügbar","marker.value_badge_missing_hint":"Die gespeicherte Quelle ist derzeit nicht verfügbar. Behalten Sie sie bei, deaktivieren Sie das Badge oder wählen Sie einen anderen Wert.","marker.value_badge_duplicate":"Derselbe Wert wird bereits im Symbol angezeigt.","marker.value_badge_state":"Status · {name}","marker.value_badge_marker_state":"Lichtquellstatus · {name}","marker.value_badge_lqi":"Durchschnittliche Zigbee-Signalqualität","marker.value_badge_attr_current_temperature":"Aktuelle Temperatur · {name}","marker.value_badge_attr_temperature":"Zieltemperatur · {name}","marker.value_badge_attr_current_humidity":"Aktuelle Luftfeuchtigkeit · {name}","marker.value_badge_attr_humidity":"Luftfeuchtigkeit · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Geschwindigkeit · {name}","marker.value_badge_attr_brightness":"Helligkeit · {name}","marker.value_badge_attr_volume_level":"Lautstärke · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Lüftergeschwindigkeit · {name}","marker.light_role_label":"Ist dieses Gerät eine Lichtquelle?","marker.light_role.help":"Steuert, ob dieser Marker selbst eine räumliche Lichtquelle ist. Verknüpfte Lichter oben bleiben unabhängige Raumlichtsteuerungen.","marker.light_role.help.aria":"Hilfe: ob dieses Gerät eine Lichtquelle ist","marker.light_role_auto_yes":"Automatisch (Lichtquelle)","marker.light_role_auto_no":"Automatisch (keine Lichtquelle)","marker.light_role_always":"Immer eine Lichtquelle","marker.light_role_never":"Nie eine Lichtquelle","marker.light_entity_label":"Führende Lichtereinheit","marker.light_entity.help":"Für ein Verbundgerät wählen Sie die Entität, deren Zustand, Farbe und Helligkeit diese Planquelle darstellen. Der automatische Rückgriff bleibt mit älteren Plänen kompatibel.","marker.light_entity.help.aria":"Hilfe: führende Lichtentität","marker.light_entity_auto":"Automatisch ({entity})","marker.light_entity_none":"keine steuerbare Entität","marker.light_entity_missing":"Die gespeicherte Entität {entity} ist nicht verfügbar. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.toggle_entity_label":"Zu toggelnde Entität","marker.toggle_entity.help":"Für ein Verbundgerät wählen Sie das genaue eigene Licht oder den Schalter, der durch Tippen bedient wird. Automatisch behält die vorherigen Zielregeln bei und ist unabhängig von der führenden Lichtentität.","marker.toggle_entity.help.aria":"Hilfe: zu toggelnde Entität","marker.toggle_entity_auto":"Automatisch ({entity})","marker.toggle_entity_none":"keine eigene steuerbare Entität","marker.toggle_entity_missing":"Die gespeicherte Entität {entity} ist nicht mehr unter den auswählbaren Kanälen dieses Markers. House Plan verwendet vorübergehend {fallback}; die gespeicherte Auswahl wird wiederhergestellt, wenn sie zurückkehrt.","marker.glow_color_label":"Leuchtfarbe und Helligkeit","marker.glow_mode.help":"Verwenden Sie Live-Quellwerte, überschreiben Sie nur deren Farbe oder fixieren Sie sowohl Farbe als auch Helligkeit. Das Minimum beträgt 1 %; um die Quelle zu deaktivieren, wählen Sie „Nie“.","marker.glow_mode.help.aria":"Hilfe: Leuchtfarbe und Helligkeit","marker.glow_mode_auto":"Von Quelle","marker.glow_mode_color":"Farbe einstellen","marker.glow_mode_fixed":"Farbe und Helligkeit einstellen","marker.glow_color":"Leuchtfarbe","marker.glow_brightness":"Helligkeit","marker.glow_disabled_never":"Leuchteinstellungen sind nicht verfügbar, da dieser Marker ausdrücklich keine Lichtquelle ist.","marker.glow_disabled_auto":"Im Automatikmodus wurde keine räumliche Lichtquelle für diesen Marker gefunden.","marker.glow_disabled_no_entity":"Keine aktive steuerbare Home Assistant-Entität ist für diese räumliche Quelle verfügbar.","marker.glow_passive_hint":"Diese Quelle hat keine eigenen Home Assistant-Daten. Stellen Sie ihre Farbe und Helligkeit manuell ein; ihr Radius bleibt verfügbar.","marker.control_broken":"Gespeicherte Quelle fehlt oder ist nicht mehr als Licht markiert","marker.control_missing_label":"Fehlendes Planlicht","marker.control_passive":"passive Quelle","toast.marker_control_cycle":"Dieser Link würde eine kreisförmige Kette von Lichtsteuerungen erzeugen.","toast.marker_binding_required":"Wählen Sie ein Home Assistant-Gerät, bevor Sie eine andere Lichtquelle verlinken.","confirm.unlock":"„{name}“ entsperren?","toast.files_migrate_failed":"Anhänge konnten nicht zur neuen Bindung verschoben werden, Links zeigen weiterhin auf die alten Dateien: {err}","space.pick_saved":"Bereits hochgeladen","space.pick_saved_hint":"Auf dem Server gespeicherte Pläne, einschließlich solcher, die Sie zuvor getrennt haben","space.no_saved":"Noch keine Pläne auf dem Server gespeichert.","space.loading":"Laden…","space.used_by":"in Benutzung: {list}","space.in_use":"Ein Bereich verwendet diesen Plan noch — zuerst die Verknüpfung lösen","btn.use":"Verwenden","confirm.delete_plan":"Plandatei „{name}“ vom Server löschen? Dies kann nicht rückgängig gemacht werden.","toast.plans_list_failed":"Konnte die gespeicherten Pläne nicht auflisten: {err}","toast.plan_delete_failed":"Konnte den Plan nicht löschen: {err}","marker.hide":"Ausblenden","marker.hide_tip":'Das Gerät wird nach dem Speichern aus dem Plan verschwinden, aber weiterhin zum Signalsignal des Raums zählen. Stellen Sie es über den Katalog "Geräte" im Geräte-Editor wieder her.',"marker.show":"Anzeigen","marker.show_tip":"Das Gerät wird nach dem Speichern wieder im Plan erscheinen.","marker.hidden_ghost":"Vom Benutzer ausgeblendetes Gerät","marker.ha_disabled_device":"Das Gerät ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_entity":"Die Entität ist in Home Assistant deaktiviert und im Plan ausgeblendet.","marker.ha_disabled_all_entities":"Das Gerät hat keine aktiven Home Assistant-Entitäten, daher ist es im Plan ausgeblendet.","marker.ha_registry_limited":"Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.","marker.delete_tip":"Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.","tap.run":"Führen Sie Automation/Skript/Szene aus","marker.run_target_label":"Was ausführen","marker.run_search_ph":"Suchen: Automation, Skript oder Szene…","marker.run_target_gone":"Ziel {id} nicht gefunden — erneut auswählen","marker.tap_confirm":"Um Bestätigung bitten","marker.tap_confirm_tip":"Vor der Ausführung ein Bestätigungsdialogfeld anzeigen — eine Vorsichtsmaßnahme gegen versehentliche Berührungen.","marker.toggle_hint_single":"Ziel: {name} ({id}).","marker.virtual_light_target":"Virtuelles Licht: {name}.","marker.virtual_light_current":"Manueller Zustand: {state} → {effect}.","marker.virtual_light_state_on":"ein","marker.virtual_light_state_off":"aus","marker.toggle_hint_group":"Wird {count}-Quelle(n) umschalten: {names}.","marker.toggle_hint_current":"Jetzt: {state} → {effect}.","marker.toggle_hint_group_current":"Derzeit an: {on} von {count} → {effect}.","marker.toggle_hint_skipped":"Wird übersprungen ({count}): {targets}.","marker.toggle_effect_turn_on":"wird eingeschaltet","marker.toggle_effect_turn_off":"wird ausgeschaltet","marker.toggle_effect_open":"wird geöffnet","marker.toggle_effect_close":"wird geschlossen","marker.toggle_effect_stop":"wird gestoppt","marker.toggle_effect_toggle":"Zustand wird umgeschaltet","marker.toggle_skip_missing":"nicht gefunden","marker.toggle_skip_ha_disabled":"in HA deaktiviert","marker.toggle_skip_unavailable":"nicht verfügbar","marker.toggle_skip_unsupported":"Umschalten wird nicht unterstützt","marker.toggle_skip_secure":"aus Sicherheitsgründen blockiert","marker.toggle_none_no_binding":"Dieses Gerät hat keine Bindung, daher gibt es keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_no_actionable_entity":"Dieses Gerät hat keinen Zustand, der umgeschaltet werden könnte. Tippen bewirkt nichts.","marker.toggle_none_configured_targets_missing":"Die konfigurierten Ziele sind nicht verfügbar. Die eigene Entität des Geräts wird nicht ersetzt.","marker.toggle_none_ha_disabled":"Das Ziel ist in Home Assistant deaktiviert und kann im Plan nicht verwendet werden.","marker.toggle_none_unavailable":"Das Ziel ist derzeit nicht verfügbar. Tippen bewirkt nichts.","marker.toggle_none_unsupported":"Home Assistant bietet keine sichere Umschaltfunktion für dieses Gerät. Tippen bewirkt nichts.","marker.toggle_none_secure":"Das Umschalten von Schlössern, Alarmen und gesicherten Toren über den Plan ist aus Sicherheitsgründen gesperrt.","run.automation":"Automatisierung","run.script":"Skript","run.scene":"Szene","confirm.tap_run":'"{name}" ausführen?',"confirm.tap_toggle":'"{name}" umschalten?',"confirm.current_state":"Aktueller Zustand: {state}","confirm.expected_state":"Nach dem Umschalten: {state}","confirm.group_current":"an {on} von {total}","confirm.group_all_on":"alle sind eingeschaltet","confirm.group_all_off":"alle sind ausgeschaltet","confirm.unavailable_targets":"Nicht verfügbar: {count}","confirm.expected_by_ha":"Home Assistant ermittelt den Zustand","confirm.state_on":"Ein","confirm.state_off":"Aus","confirm.state_open":"Offen","confirm.state_closed":"Geschlossen","confirm.state_opening":"Wird geöffnet","confirm.state_closing":"Wird geschlossen","confirm.state_stopped":"Gestoppt","confirm.state_unknown":"Unbekannt","toast.run_started":"Gestartet: {name}","toast.run_target_missing":"Ausführungsziel nicht gefunden — überprüfen Sie die Geräteeinstellungen","toast.run_target_required":"Wählen Sie eine Automatisierung, ein Skript oder eine Szene aus","toast.tap_target_changed":"Das Ziel der Aktion hat sich geändert. Versuchen Sie es erneut.","toast.virtual_light_toggle_failed":"Das virtuelle Licht konnte nicht umgeschaltet werden: {err}","toast.value_badge_source_required":"Einen Wert für den Badge auswählen","btn.run":"Ausführen","vac.section":"Roboterstaubsauger: aktuelle Position","vac.autocal":"Automatisch einrichten","vac.live":"Aktuelle Position im Plan","vac.trail":"Roboterpfad anzeigen","vac.cal_maps":"Kalibrierte Karten: {maps}","vac.autocal_no_rooms":'Die Integration meldet keine Raumliste — öffnen Sie "Manuell anpassen"',"vac.autocal_no_match":'Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie "Manuell anpassen"',"vac.autocal_done":"Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_need_pos":"Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie","vac.cal_done":"Kalibrierung gespeichert. Eine Reinigung starten und das Ergebnis prüfen.","vac.cal_cancelled":"Kalibrierung abgebrochen","vac.fit":"Manuell anpassen","vac.fit_hint":"Roboterkarte an die richtige Position ziehen und an den Ecken skalieren","vac.fit_rotate":"90° drehen","vac.fit_mirror":"Spiegeln","vac.trail_never":"Nie","vac.trail_cleaning":"Beim Reinigen","vac.trail_always":"Immer","gs.bg_group":"Bühnenhintergrund","gs.bg_color":"Hintergrund um den Plan","gs.bg_default":"Standardthema","gs.bg_theme":"Standardthema","gs.bg_mode":"Planhintergrund","gs.bg_static":"Statische Farbe","gs.bg_daynight":"Folgt der Sonne","gs.bg_daynight_hint":"Der Hintergrund wechselt durch Morgendämmerung, Tag, Abenddämmerung und Nacht mithilfe von sun.sun oder Ihrer lokalen Uhr, wenn keine Sonnendaten verfügbar sind. Für den Hintergrund ist kein Kompass erforderlich.","gs.sun_group":"Sonne","gs.sun_missing":"Die sun.sun-Entität wurde nicht gefunden — der Hintergrund folgt Ihrer lokalen Uhr; Fensterstrahlen sind nicht verfügbar.","gs.north":"Norden auf dem Plan","gs.north_ph":"nicht gesetzt","gs.north_hint":"Norden nur für Fensterstrahlen einstellen (1° Schritte, 15° mit Shift); der Sonnenhintergrund funktioniert auch ohne ihn.","gs.north_clear":"Löschen","gs.north_letter":"N","gs.sun_rays":"Sonnenlicht durch Fenster","gs.about_group":"Über","gs.about_version":"House Plan Card v{v}","gs.about_github":"GitHub · Dokumentation & Probleme","gs.about_telegram":"Telegram-Chat","space.bg_color":"Hintergrund rund um den Plan","space.bg_inherit":"Allgemein erben","space.bg_inherited":"erbt allgemeine Einstellungen","space.bg_mode":"Planhintergrund","space.north":"Norden auf dem Plan (überschreiben)","space.north_inherited":"geerbt: {v}","space.sun_rays":"Sonnenlicht durch Fenster","space.sun_inherit":"Allgemein erben","space.sun_on":"Ein","space.sun_off":"Aus","canvas.far_objects":"{n} Objekt(e) weit vom Plan entfernt","canvas.show_far":"Anzeigen","canvas.home_tip":"Der Plan ist dort drüben — klicken, um ihn anzupassen","gs.grid_group":"Planwartung","gs.grid_hint":"Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.","gs.align_all":"Pläne optimieren","gs.align_title":"Pläne optimieren","gs.align_none":"Alle Pläne verwenden bereits das aktuelle optimierte Datenmodell.","gs.optimize_no_automatic_changes":"Es gibt keine automatischen Änderungen, die angewendet werden müssen. Überprüfen Sie die unten stehenden Punkte.","gs.align_count":"{n} von {total} Elementen werden sich um höchstens {cm} cm bewegen.","gs.align_where":"Die größte Verschiebung ist in „{s}“.","gs.align_turned":"Öffnungen, deren Winkel korrigiert wurde: {n}.","gs.align_removed_drafts":"Ungültige Umrisse vom Raster zusammengeklappt und entfernt: {n}.","gs.optimize_redundant_drafts":"Gespeicherte Wandketten, die durch feste Raumwände verdeckt waren, wurden entfernt: {n}.","gs.align_preflight_failed":"Die Geometrie der folgenden Bereiche konnte nicht sicher überprüft werden: {spaces}{more}.","gs.align_preflight_hint":"Pläne wurden nicht geändert. Kopieren Sie die Diagnosen mit dem untenstehenden Button und fügen Sie sie zusammen mit einem Raumexport dem Fehlerbericht bei.","gs.preflight_reason_prepare-exception":"Die Bereichsgeometrie konnte nicht vorbereitet werden (Ausnahme beim Erstellen des Modells)","gs.preflight_reason_wall-null":"Der Wandkörper wurde nicht erstellt (die Vereinigung lieferte leer zurück)","gs.preflight_reason_wall-degraded-extra":"Der Wandkörper wurde mit zusätzlicher Geometrie beschädigt","gs.preflight_reason_wall-failed-core":"Der Kern des Wandkörpers konnte nicht zusammengesetzt werden","gs.preflight_reason_wall-exception":"Die Wandkonstruktion warf eine Ausnahme","gs.preflight_reason_floor-null":"Der Fußbodenumriss wurde nicht erstellt","gs.preflight_reason_floor-exception":"Bodenaufbau hat eine Ausnahme ausgelöst","gs.preflight_copy":"Diagnosen kopieren","gs.preflight_copied":"Diagnosen kopiert","gs.preflight_update_hint":"Die Karten- und Integrationsversionen unterscheiden sich — Hausplan aktualisieren und erneut versuchen.","gs.align_preflight_space":"Bereich {n}","gs.align_preflight_more":", und {n} mehr","gs.optimize_changes":"Modellmigrationen: {m}; aktualisierte Bereiche: {c}; entfernte verrauschte Koordinatenwerte: {p}; zusammengeführte Fragmente realer Wände: {w}; zusammengeführte Fragmente von Wänden ohne Dicke: {s}; unabhängige Wände: {i}.","gs.zero_walls_migrated":"Virtuelle Wandspannweiten konvertiert: {n}.","gs.wall_segments_migrated":"Wandsegmente stabilisiert: {n}.","gs.optimize_lattice_summary":"Rauschen in den Koordinatenwerten kanonisiert: {n}; maximale Bewegung: {cm} cm.","gs.optimize_lattice_space":"{space}: Koordinatenwerte kanonisiert: {n}; außerhalb des Rasters liegende Werte unverändert gelassen: {far}.","gs.optimize_coincident_partitions":"Versteckte unabhängige Wandabschnitte in Raumwände integriert: {n}.","gs.optimize_openings_rehosted":"Öffnungen wieder an Raumwände angebracht: {n}.","gs.optimize_walls_straightened":"Wände begradigt: {n}; maximale Bewegung: {cm} cm.","gs.optimize_walls_straightened_where":"Größte Wandkorrektur: {s}.","gs.optimize_walls_straighten_skipped":"Nahe Achsenwände unverändert gelassen, da sie nicht sicher repariert werden konnten: {n}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} Bereiche → keine Datenfüllung + unabhängiges Glow; {rooms} Räume → geerbte Datenfüllung + unabhängiges Glow.","gs.optimize_references":"Referenzen repariert: Bereiche — {spaces}; Räume — {rooms}; Positionen — {positions}; Geräte aus fehlenden Bereichen gelöst — {detached}.","gs.optimize_reference_more":", und {n} mehr","gs.optimize_orphans_removed":"Vergessene Einträge entfernt: {total} — Raumbezeichnungen: {rooms}; Geräte: {devices}; Gruppenmarkierungen: {groups}. Sie gehörten zu zuvor gelöschten Bereichen.","gs.optimize_live_positions":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie werden beibehalten.","gs.optimize_live_positions_remove":"Alte Positionen in gelöschten Bereichen gehören zu vorhandenen Objekten: {n}{names}. Sie sind zur Entfernung ausgewählt.","gs.optimize_live_names":": {names}{more}","gs.optimize_live_remove":"Alte Positionen entfernen","gs.optimize_live_keep":"Alte Positionen beibehalten","gs.optimize_live_selected":"Alte Positionen werden nach der Anwendung von Optimieren entfernt.","gs.optimize_unverified":"Positionen konnten nicht sicher überprüft werden: {n}. Sie wurden unverändert belassen.","gs.optimize_registry_limited":"Voller Administratorzugriff auf die Home Assistant-Registries ist für eine sichere Überprüfung erforderlich.","gs.optimize_vacuum_warning":"Raumzuordnungen, die noch überprüft werden müssen: {n}.","gs.optimize_details":"Details","gs.optimize_details_more":"Und {n} weitere Einträge.","gs.optimize_detail_removed":"wird entfernt","gs.optimize_detail_live":"wird beibehalten","gs.optimize_detail_unverified":"nicht überprüft","gs.optimize_detail_room_label":"Raumkennzeichnung","gs.optimize_detail_device":"Gerät","gs.optimize_detail_group":"Gruppenkennzeichen","gs.optimize_detail_unknown":"unbekannter Eigentümer","gs.optimize_detail_item":"{status}: {kind} {id}; alter Bereich {space}","gs.align_warn":"Absichtlich zwischen Rasterknoten platzierte Elemente werden sich bewegen. Nach der Operation ist nur eine Rückgängig-Funktion verfügbar, und zwar nur bis zur nächsten Planbearbeitung.","gs.align_run":"Optimieren","gs.align_done":"Pläne optimiert: {n} Elemente bewegt, {m} Datensätze beibehalten, {r} Verweise repariert","gs.optimize_undo":"Letzte Optimierung rückgängig machen","gs.optimize_undone":"Die letzte Optimierung wurde rückgängig gemacht","decor.furniture":"Möbel","furn.title":"Möbelbibliothek","furn.symbol":"Symbol","furn.group_furniture":"Möbel","furn.group_appliance":"Geräte","furn.group_sanitary":"Sanitär","furn.group_other":"Sonstiges","furn.width":"Breite","furn.depth":"Tiefe","furn.back_to_categories":"Alle Kategorien","furn.pick_hint":"Wählen Sie eine Kategorie und dann ein Symbol.","furn.place_hint":"Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.","furn.cat_air_conditioner":"Klimaanlagen","furn.cat_armchair":"Sessel","furn.cat_bathtub":"Badewannen","furn.cat_bed":"Betten","furn.cat_bidet":"Bidets","furn.cat_boiler":"Warmwasserbereiter","furn.cat_chair":"Stühle","furn.cat_coffee_table":"Couchtische","furn.cat_cooktop":"Kochfelder","furn.cat_dining_table":"Esstische","furn.cat_dishwasher":"Geschirrspüler","furn.cat_dryer":"Wäschetrockner","furn.cat_fireplace":"Kamine","furn.cat_fridge":"Kühlschränke","furn.cat_kitchen_cabinet":"Küchenschränke","furn.cat_kitchen_sink":"Küchenspülen","furn.cat_nightstand":"Schränke","furn.cat_plant":"Pflanzen","furn.cat_rug":"Teppiche","furn.cat_shelving":"Regale","furn.cat_shower":"Duschen","furn.cat_sink":"Waschbecken","furn.cat_sofa":"Sofas","furn.cat_stairs":"Treppen","furn.cat_toilet":"Toiletten","furn.cat_tv":"Fernseher","furn.cat_wardrobe":"Kleiderschränke","furn.cat_washer":"Waschmaschinen","furn.cat_work_table":"Schreibtische","furn.sym_sofa":"Zweisitzer-Sofa","furn.sym_sofa_three_seat":"Dreisitzer-Sofa","furn.sym_sofa_corner_right":"Ecksofa rechts","furn.sym_armchair":"Polstersessel","furn.sym_armchair_office":"Bürostuhl","furn.sym_coffee_table":"Rechteckiger Couchtisch","furn.sym_coffee_table_round":"Runder Couchtisch","furn.sym_coffee_table_oval":"Ovaler Couchtisch","furn.sym_coffee_table_rounded":"Abgerundeter Couchtisch","furn.sym_table_dining":"Rechteckiger Esstisch","furn.sym_table_round":"Rundtisch","furn.sym_table_dining_oval":"Ovaler Esstisch","furn.sym_table_dining_rounded":"Abgerundeter Esstisch","furn.sym_chair":"Stuhl","furn.sym_chair_bar":"Barhocker","furn.sym_desk":"Rechteckiger Schreibtisch","furn.sym_desk_corner":"Eckschreibtisch","furn.sym_bed_double":"Doppelbett","furn.sym_bed_single":"Einzelbett","furn.sym_nightstand":"Nachttisch","furn.sym_cabinet_tv":"TV-Schrank","furn.sym_cabinet_shoe":"Schuhschrank","furn.sym_cabinet_sink":"Waschtischunterschrank","furn.sym_wardrobe":"Kleiderschrank","furn.sym_bookshelf":"Bücherschrank","furn.sym_wall_unit":"Schrankwand","furn.sym_kitchen_floor":"Küchen-Unterschrank","furn.sym_kitchen_floor_corner":"Küchen-Eckunterschrank","furn.sym_kitchen_wall":"Küchen-Hängeschrank","furn.sym_kitchen_wall_corner":"Küchen-Eckhängeschrank","furn.sym_shelf_floor":"Standregal","furn.sym_shelf_wall":"Wandregal","furn.sym_fridge":"Kühlschrank","furn.sym_stove":"Vierflammiges Kochfeld","furn.sym_cooktop_two":"Zweiflammiges Kochfeld","furn.sym_dishwasher":"Geschirrspüler","furn.sym_washer":"Waschmaschine","furn.sym_dryer":"Wäschetrockner","furn.sym_tv":"Fernseher mit Standfuß","furn.sym_tv_wall":"Wandfernseher","furn.sym_ac":"Klimaanlage","furn.sym_water_heater":"Wassererhitzer","furn.sym_toilet":"Stand-WC","furn.sym_toilet_built_in":"Wand-WC","furn.sym_bathtub":"Rechteckige Badewanne","furn.sym_bathtub_corner":"Eckbadewanne","furn.sym_shower":"Dusche","furn.sym_sink":"Waschbecken","furn.sym_kitchen_sink":"Einzel-Küchenspüle","furn.sym_kitchen_sink_double":"Doppel-Küchenspüle","furn.sym_bidet":"Stand-Bidet","furn.sym_bidet_built_in":"Wand-Bidet","furn.sym_stairs":"Treppe","furn.sym_fireplace":"Kamin","furn.sym_plant":"Pflanze","furn.sym_rug":"Teppich","common.yes":"Ja","common.no":"Nein","vac.diag_source":"Quelle","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Räume","vac.diag_rooms_value":"{total} · {matched} Namen stimmen überein · {readiness}","vac.autocal_ready":"Auto-Kalibrierung verfügbar","vac.autocal_not_ready":"3 übereinstimmende Namen erforderlich","vac.diag_path":"Integrationspfad","vac.diag_map":"Karten-ID","vac.source_none":"nicht ausgewählt","vac.source_status_ok":"Bereit","vac.source_status_missing":"Fehlend","vac.source_status_disabled":"In Home Assistant deaktiviert","vac.source_status_unavailable":"Nicht verfügbar","vac.source_status_unverified":"Kann mit den aktuellen Berechtigungen nicht verifizieren","vac.source_status_unsupported":"Keine Positionsdaten","vac.source_status_none":"Keine Quelle","vac.source_banner_missing":"Die gespeicherte Quelle existiert nicht mehr. Sie wurde nicht automatisch ersetzt; wählen Sie eine andere Quelle oder stellen Sie sie in Home Assistant wieder her.","vac.source_banner_disabled":"Die gespeicherte Quelle ist in Home Assistant deaktiviert. Aktivieren Sie sie dort oder wählen Sie eine andere Quelle.","vac.source_banner_unverified":"Die aktuellen Home Assistant-Berechtigungen können diese gespeicherte Quelle nicht verifizieren. Sie bleibt angepinnt und wird nicht automatisch ersetzt.","vac.choose_source":"Quelle wählen","vac.source_auto":"Automatisch","vac.source_auto_hint":"Verwenden Sie eine kompatible Entität von diesem Gerät","vac.all_cameras":"Alle Kameras","vac.all_cameras_warn":"Eine Kamera liefert möglicherweise keine Roboter-Daten. Wählen Sie eine nur, wenn es Ihre Saugroboter-Karte ist.","vac.all_cameras_empty":"Keine anderen Kamera-Entitäten gefunden.","vac.platform_unknown":"unbekannte Integration","vac.cap_position":"Position","vac.cap_rooms_short":"Räume","vac.cap_path":"Pfad","vac.cap_map":"Karten-ID","vac.cap_none":"Keine Roboter-Daten erkannt","vac.xcme_hint":"Aktivieren Sie diese Xiaomi Cloud Map Extractor-Attribute:","vac.documentation":"Dokumentation","vac.residual_title":"Automatische Kalibrierung überprüfen","vac.residual_message":"Die zugeordneten Räume weichen um bis zu {error} voneinander ab. Diese ungefähre Kalibrierung anwenden, manuell verfeinern oder ohne Änderung der gespeicherten Einrichtung abbrechen.","vac.apply_proposal":"Anwenden","gs.backup_group":"Sichern und übertragen","gs.backup_hint":"Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.","backup.export_open":"Exportieren","backup.import_open":"Importieren","backup.export_title":"House Plan exportieren","backup.import_title":"House Plan importieren","backup.export_hint":"Wählen Sie, ob das Backup die gesamte House Plan-Konfiguration oder nur den aktuellen Bereich enthalten soll.","backup.full":"Vollständiges Backup","backup.current_space":"Aktueller Bereich","backup.current_space_title":"Aktueller Bereich: {title}","backup.no_current_space":"Kein aktueller Bereich","backup.plan_only":"Nur Plan","backup.plan_only_hint":"Räume, Wände, Öffnungen, Dekor und Raumbezeichnungspositionen ohne Geräte oder Home Assistant-Bindungen beibehalten.","backup.plan_only_preview":"Diese Datei enthält nur den Plan.","backup.privacy_warning":"Das Archiv enthält Namen, Home Assistant-Kennungen und genaue Koordinaten. Interne Pläne und Anhänge werden referenziert, nicht eingebettet; Laufzeitzustände und Staubsaugerspuren sind nicht enthalten.","backup.download":"JSON herunterladen","backup.export_done":"Backup heruntergeladen","backup.reading":"Überprüfe das Backup…","backup.revalidated":"Der Plan wurde nach dieser Vorschau geändert. Die Zusammenfassung wurde aktualisiert; überprüfen Sie sie und bestätigen Sie erneut.","backup.error.unauthorized":"Sie haben keine Berechtigung, diesen Plan zu exportieren oder zu importieren.","backup.error.not_ready":"Der Hausplan ist noch nicht fertig. Versuchen Sie es in einem Moment erneut.","backup.error.too_large":"Das Backup überschreitet das 8-MiB-Limit.","backup.error.invalid_json":"Die ausgewählte Datei ist kein gültiges JSON.","backup.error.invalid_format":"Die ausgewählte Datei ist kein House Plan-Backup.","backup.error.unsupported_export_version":"Dieses Backup-Format wird von der installierten Version nicht unterstützt.","backup.error.future_model":"Das Backup wurde mit einem neueren House-Plan-Datenmodell erstellt.","backup.error.invalid_config":"Das Backup enthält eine ungültige House-Plan-Konfiguration.","backup.error.wall_model_migration_blocked":"Das Backup-Wandmodell konnte nicht sicher aktualisiert werden. Optimieren Sie zuerst den Quellplan und exportieren Sie ihn dann erneut.","backup.error.invalid_layout":"Das Backup enthält ungültige Objektpositionen.","backup.error.invalid_content":"Das Backup enthält ungültige oder inkonsistente Inhaltsreferenzen.","backup.error.space_not_found":"Der ausgewählte Bereich existiert nicht mehr.","backup.error.capacity_exceeded":"Durch das Hinzufügen dieses Backups würden die Planlimits überschritten.","backup.error.preview_expired":"Die Vorschau ist abgelaufen. Wählen Sie die Backup-Datei erneut aus.","backup.error.preview_owner_mismatch":"Diese Vorschau gehört zu einem anderen Home-Assistant-Benutzer.","backup.error.conflict":"Der Plan hat sich nach der Vorschau geändert. Überprüfen Sie die aktualisierte Zusammenfassung.","backup.error.content_confirmation_required":"Bestätigen Sie, dass nicht verfügbare lokale Inhalte getrennt werden können.","backup.error.commit_failed":"Das Backup konnte nicht sicher angewendet werden. Der vorherige Plan wurde wiederhergestellt oder wartet auf die Wiederherstellung.","backup.error.missing_plan":"Eine referenzierte Plan-Datei ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.missing_content":"Ein referenziertes lokales Anhangselement ist nach der Vorschau verschwunden. Überprüfen Sie das Backup erneut.","backup.error.marker_control_missing":"Ein verknüpftes Planlicht fehlt im Backup.","backup.error.marker_control_not_light":"Ein verknüpftes Planziel ist nicht mehr als Lichtquelle markiert.","backup.error.marker_control_self":"Eine Lichtquelle kann sich nicht selbst steuern.","backup.error.marker_control_cycle":"Das Backup enthält eine kreisförmige Kette von Lichtsteuerungen.","backup.error.duplicate_marker_control":"Das Backup enthält eine doppelte Plan-Licht-Verknüpfung.","backup.error.no_backup":"Es gibt keinen Import- oder Optimierungssnapshot zur Wiederherstellung.","backup.same_source":"Erstellt auf dieser Home Assistant-Instanz","backup.foreign_source":"Erstellt auf einer anderen Home Assistant-Instanz","backup.created":"Erstellt: {value}","backup.versions":"Karte {card}; Integration {integration}; Datenmodell {model}","backup.count_spaces":"Bereiche: {n}","backup.count_rooms":"Räume: {n}","backup.count_walls":"Wände: {n}","backup.count_openings":"Öffnungen: {n}","backup.count_decor":"Dekorationsobjekte: {n}","backup.count_markers":"Geräte: {n}","backup.count_layout":"Positionen: {n}","backup.bindings":"Verknüpfungen — Geräte: {device}, Entitäten: {entity}, virtuell: {virtual}; Positionen ohne Bereich: {legacy}","backup.binding_status":"Zielstatus — aktiv: {active}, deaktiviert: {disabled}, fehlt: {missing}","backup.missing_areas":"Auf dem Ziel fehlende Bereiche: {areas}","backup.dropped_marker_links":"Planlicht-Verknüpfungen außerhalb dieses übertragenen Bereichs wurden ausgelassen: {n}.","backup.repaired_target_refs":"Durch den Import dieses Bereichs wiederhergestellte vorhandene Referenzen: {n}.","backup.preserved_unresolved_refs":"Referenzen, die nicht eindeutig wiederhergestellt werden konnten, wurden beibehalten: {n}.","backup.preserved_unresolved_hint":"Es wurden keine Daten geraten oder gelöscht. Führen Sie nach dem Import Optimierungspläne aus, um die verbleibenden Referenzen zu überprüfen.","backup.import_details":"Importieren von Referenzdetails","backup.import_detail.incoming_remapped":"Referenzen innerhalb der importierten Kopie aktualisiert: {n}","backup.import_detail.target_repaired":"Vorhandene Referenzen wiederhergestellt: {n}","backup.import_detail.preserved_unresolved":"Unaufgelöste Referenzen beibehalten: {n}","backup.import_detail.collisions":"Zielkonflikte sicher beibehalten: {n}","backup.import_detail.dropped_links":"Eingehende Links durch Übertragungsregeln ausgelassen: {n}","backup.import_detail.bounded_lineages":"Übermäßig verschachtelte Kennungen begrenzt belassen: {n}","backup.replace_warning":"Dies ersetzt die aktuelle Konfiguration und das Layout. Hochgeladene Dateien werden niemals gelöscht. Eine Rückgängig-Möglichkeit bleibt bis zur nächsten Planbearbeitung verfügbar.","backup.foreign_bookkeeping":"Instanzspezifische bekannte/neue Gerätebuchführung wird nicht importiert.","backup.final_name":"Name des neuen Bereichs","backup.target_settings":"Die globalen Einstellungen der Zielinstanz bleiben unverändert.","backup.duplicates":"Bindungen bereits vorhanden: {n}","backup.skip":"Doppelte Geräte überspringen","backup.virtual_copy":"Sichere statische virtuelle Kopien hinzufügen","backup.content":"Referenzierter Inhalt","backup.content_available":"lokal verfügbar","backup.content_external":"externer Link","backup.content_detach_required":"wird getrennt","backup.confirm_detach":"Ich verstehe, dass nicht verfügbare interne Pläne und Anhänge von der importierten Konfiguration getrennt werden.","backup.replace":"Ersetzen","backup.add":"Bereich hinzufügen","backup.space_done":"Bereich importiert: {rooms} Räume, {markers} Geräte, {refs} bestehende Referenzen wiederhergestellt","backup.full_done":"Backup wiederhergestellt: {spaces} Bereiche, {rooms} Räume, {markers} Geräte","backup.undo_import":"Letzten vollständigen Import rückgängig machen","backup.import_undone":"Vollständiger Import rückgängig gemacht"};const n="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";export{e as dictionary,n as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/editor-BBdPV7Z-.js b/dist/houseplan-assets/editor-Bet9LJop.js similarity index 93% rename from custom_components/houseplan/frontend/houseplan-assets/editor-BBdPV7Z-.js rename to dist/houseplan-assets/editor-Bet9LJop.js index cd509ec3..94ddc9ab 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/editor-BBdPV7Z-.js +++ b/dist/houseplan-assets/editor-Bet9LJop.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-DJctcs0x.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,A=()=>D.createComment(""),O=e=>null===e||"object"!=typeof e&&"function"!=typeof e,z=Array.isArray,P="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${P}(?:([^\\s"'>=/]+)(${P}*=${P}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,j=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),B=j(1),W=j(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!z(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const Z=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=F:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class X{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=Z(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iz(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==G&&O(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){z(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(A()),this.O(A()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!O(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(A(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=2,pe=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:me}=se,ge=e=>e,fe=()=>document.createComment(""),ve=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(fe(),r),o=n.insertBefore(fe(),r);i=new me(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=ge(e).nextSibling;ge(n).insertBefore(e,r),e=t}}}return i},ye=(e,t,i=e)=>(e._$AI(t,i),e),be={},we=(e,t=be)=>e._$AH=t,ke=e=>{e._$AR(),e._$AA.remove()},xe=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},$e=pe(class extends _e{constructor(e){if(super(e),e.type!==ue)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=ye(r[d],o[p]),d++,p++;else if(a[u]===s[_])l[_]=ye(r[u],o[_]),u--,_--;else if(a[d]===s[_])l[_]=ye(r[d],o[_]),ve(e,l[_+1],r[d]),d++,_--;else if(a[u]===s[p])l[p]=ye(r[u],o[p]),ve(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=xe(s,p,_),h=xe(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=ve(e,r[d]);ye(t,o[p]),l[p]=t}else l[p]=ye(i,o[p]),ve(e,r[d],i),r[t]=null;p++}else ke(r[u]),u--;else ke(r[d]),d++;for(;p<=_;){const t=ve(e,l[_+1]);ye(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&ke(e)}return this.ut=s,we(e,l),U}}),Se=new WeakMap;let Me=0;class Re extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Me,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Se.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Se.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?Se.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=Se.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Se.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=B` - ${this.icon?B``:G} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",f=p.reactiveElementPolyfillSupport,g=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(g("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(g("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[g("elementProperties")]=new Map,w[g("finalized")]=new Map,f?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",R=`lit$${Math.random().toFixed(9).slice(2)}$`,T="?"+R,C=`<${T}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,O=Array.isArray,P="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,H=RegExp(`>|${P}(?:([^\\s"'>=/]+)(${P}*=${P}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),N=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,W=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),j=W(1),B=W(2),V=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!O(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const Z=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=F;for(let t=0;t"===l[0]?(s=r??F,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?H:'"'===l[3]?L:N):s===L||s===N?s=H:s===I||s===E?s=F:(s=H,r=void 0);const d=s===H&&e[t+1].startsWith("/>")?" ":"";o+=s===F?i+C:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+R+d):i+R+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class X{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=Z(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iO(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==U&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new X(e)),t}k(e){O(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=U}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==V,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return V}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=2,pe=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const{I:me}=se,fe=e=>e,ge=()=>document.createComment(""),ve=(e,t,i)=>{const n=e._$AA.parentNode,r=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=n.insertBefore(ge(),r),o=n.insertBefore(ge(),r);i=new me(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,s=o!==e;if(s){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==r||s){let e=i._$AA;for(;e!==t;){const t=fe(e).nextSibling;fe(n).insertBefore(e,r),e=t}}}return i},ye=(e,t,i=e)=>(e._$AI(t,i),e),be={},we=(e,t=be)=>e._$AH=t,ke=e=>{e._$AR(),e._$AA.remove()},xe=(e,t,i)=>{const n=new Map;for(let r=t;r<=i;r++)n.set(e[r],r);return n},$e=pe(class extends _e{constructor(e){if(super(e),e.type!==ue)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let n;void 0===i?i=t:void 0!==t&&(n=t);const r=[],o=[];let s=0;for(const t of e)r[s]=n?n(t,s):s,o[s]=i(t,s),s++;return{values:o,keys:r}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,n]){const r=(e=>e._$AH)(e),{values:o,keys:s}=this.dt(t,i,n);if(!Array.isArray(r))return this.ut=s,o;const a=this.ut??=[],l=[];let c,h,d=0,u=r.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===r[d])d++;else if(null===r[u])u--;else if(a[d]===s[p])l[p]=ye(r[d],o[p]),d++,p++;else if(a[u]===s[_])l[_]=ye(r[u],o[_]),u--,_--;else if(a[d]===s[_])l[_]=ye(r[d],o[_]),ve(e,l[_+1],r[d]),d++,_--;else if(a[u]===s[p])l[p]=ye(r[u],o[p]),ve(e,r[d],r[u]),u--,p++;else if(void 0===c&&(c=xe(s,p,_),h=xe(a,d,u)),c.has(a[d]))if(c.has(a[u])){const t=h.get(s[p]),i=void 0!==t?r[t]:null;if(null===i){const t=ve(e,r[d]);ye(t,o[p]),l[p]=t}else l[p]=ye(i,o[p]),ve(e,r[d],i),r[t]=null;p++}else ke(r[u]),u--;else ke(r[d]),d++;for(;p<=_;){const t=ve(e,l[_+1]);ye(t,o[p]),l[p++]=t}for(;d<=u;){const e=r[d++];null!==e&&ke(e)}return this.ut=s,we(e,l),V}}),Se=new WeakMap;let Me=0;class Re extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Me,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._useHaDialog)return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Se.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Se.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?Se.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=Se.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Se.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=j` + ${this.icon?j``:U} ${this.title} - `;return this._useHaDialog?B``;return this._useHaDialog?j`${e} -
`:B`
`:j`0||e===t?t:t-1}function Ye(e){for(var t,i,n=1,r=e.length,o=e[0]+"";nc^i?1:-1;for(a=(l=r.length)<(c=o.length)?l:c,s=0;so[s]^i?1:-1;return l==c?0:l>c^i?1:-1}function Xe(e,t,i,n){if(ei||e!==He(e))throw Error(Le+(n||"Argument")+("number"==typeof e?ei?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function Je(e){var t=e.c.length-1;return Ke(e.e/Be)==t&&e.c[t]%2!=0}function Qe(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function et(e,t,i){var n,r;if(t<0){for(r=i+".";++t;r+=i);e=r+e}else if(++t>(n=e.length)){for(r=i,t-=n;--t;r+=i);e+=r}else ty?u.c=u.e=null:e.e=10;l/=10,a++);return void(a>y?u.c=u.e=null:(u.e=a,u.c=[e]))}d=String(e)}else{if(!Ee.test(d=String(e)))return r(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Xe(t,2,$.length,"Base"),10==t&&S)return D(u=new M(e),_+u.e+1,m);if(d=String(e),c="number"==typeof e){if(0*e!=0)return r(u,d,c,t);if(u.s=1/e<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(qe+e)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,t),a=l=0,h=d.length;la){a=h;continue}}else if(!s&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){s=!0,l=-1,a=0;continue}return r(u,String(e),c,t)}c=!1,(a=(d=n(d,t,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(e>We||e!==He(e)))throw Error(qe+u.s*e);if((a=a-l-1)>y)u.c=u.e=null;else if(a=f)?Qe(l,s):et(l,s,"0");else if(o=(e=D(new M(e),t,i)).e,a=(l=Ye(e.c)).length,1==n||2==n&&(t<=o||o<=g)){for(;as),l=et(l,o,"0"),o+1>a){if(--t>0)for(l+=".";t--;l+="0");}else if((t+=o-a)>0)for(o+1==a&&(l+=".");t--;l+="0");return e.s<0&&r?"-"+l:l}function T(e,t){for(var i,n,r=1,o=new M(e[0]);r=10;r/=10,n++);return(i=n+i*Be-1)>y?e.c=e.e=null:i=10;a/=10,r++);if((o=t-r)<0)o+=Be,s=t,l=d[c=0],h=He(l/u[r-s-1]%10);else if((c=Ne((o+1)/Be))>=d.length){if(!n)break e;for(;d.length<=c;d.push(0));l=h=0,r=1,s=(o%=Be)-Be+1}else{for(l=a=d[c],r=1;a>=10;a/=10,r++);h=(s=(o%=Be)-Be+r)<0?0:He(l/u[r-s-1]%10)}if(n=n||t<0||null!=d[c+1]||(s<0?l:l%u[r-s-1]),n=i<4?(h||n)&&(0==i||i==(e.s<0?3:2)):h>5||5==h&&(4==i||n||6==i&&(o>0?s>0?l/u[r-s]:0:d[c-1])%10&1||i==(e.s<0?8:7)),t<1||!d[0])return d.length=0,n?(t-=e.e+1,d[0]=u[(Be-t%Be)%Be],e.e=-t||0):d[0]=e.e=0,e;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=u[Be-o],d[c]=s>0?He(l/u[r-s]%u[s])*a:0),n)for(;;){if(0==c){for(o=1,s=d[0];s>=10;s/=10,o++);for(s=d[0]+=a,a=1;s>=10;s/=10,a++);o!=a&&(e.e++,d[0]==je&&(d[0]=1));break}if(d[c]+=a,d[c]!=je)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}e.e>y?e.c=e.e=null:e.e=f?Qe(t,i):et(t,i,"0"),e.s<0?"-"+t:t)}return M.clone=e,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(e){var t,i;if(null!=e){if("object"!=typeof e)throw Error(Le+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(Xe(i=e[t],0,Ve,t),_=i),e.hasOwnProperty(t="ROUNDING_MODE")&&(Xe(i=e[t],0,8,t),m=i),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((i=e[t])&&i.pop?(Xe(i[0],-Ve,0,t),Xe(i[1],0,Ve,t),g=i[0],f=i[1]):(Xe(i,-Ve,Ve,t),g=-(f=i<0?-i:i))),e.hasOwnProperty(t="RANGE"))if((i=e[t])&&i.pop)Xe(i[0],-Ve,-1,t),Xe(i[1],1,Ve,t),v=i[0],y=i[1];else{if(Xe(i,-Ve,Ve,t),!i)throw Error(Le+t+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(e.hasOwnProperty(t="CRYPTO")){if((i=e[t])!==!!i)throw Error(Le+t+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Le+"crypto unavailable");b=i}else b=i}if(e.hasOwnProperty(t="MODULO_MODE")&&(Xe(i=e[t],0,9,t),w=i),e.hasOwnProperty(t="POW_PRECISION")&&(Xe(i=e[t],0,Ve,t),k=i),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(i=e[t]))throw Error(Le+t+" not an object: "+i);x=i}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(i=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Le+t+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!M.DEBUG)return!0;var t,i,n=e.c,r=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(n)){if((1===o||-1===o)&&r>=-Ve&&r<=Ve&&r===He(r)){if(0===n[0]){if(0===r&&1===n.length)return!0;break e}if((t=(r+1)%Be)<1&&(t+=Be),String(n[0]).length==t){for(t=0;t=je||i!==He(i))break e;if(0!==i)return!0}}}else if(null===n&&null===r&&(null===o||1===o||-1===o))return!0;throw Error(Le+"Invalid BigNumber: "+e)},M.maximum=M.max=function(){return T(arguments,-1)},M.minimum=M.min=function(){return T(arguments,1)},M.random=(o=9007199254740992,s=Math.random()*o&2097151?function(){return He(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,i,n,r,o,a=0,l=[],c=new M(p);if(null==e?e=_:Xe(e,0,Ve),r=Ne(e/Be),b)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(r*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),t[a]=i[0],t[a+1]=i[1]):(l.push(o%1e14),a+=2);a=r/2}else{if(!crypto.randomBytes)throw b=!1,Error(Le+"crypto unavailable");for(t=crypto.randomBytes(r*=7);a=9e15?crypto.randomBytes(7).copy(t,a):(l.push(o%1e14),a+=7);a=r/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==s[r+1]&&(s[r+1]=0),s[r+1]+=s[r]/i|0,s[r]%=i)}return s.reverse()}return function(n,r,o,s,a){var l,c,h,d,u,p,g,f,v=n.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,n=n.replace(".",""),p=(f=new M(r)).pow(n.length-v),k=d,f.c=t(et(Ye(p.c),p.e,"0"),10,o,e),f.e=f.c.length),h=d=(g=t(n,r,o,a?(l=$,e):(l=e,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=s,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])n=u?et(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,n="";v<=d;n+=l.charAt(g[v++]));n=et(n,h,l.charAt(0))}return n}}(),i=function(){function e(e,t,i){var n,r,o,s,a=0,l=e.length,c=t%Ge,h=t/Ge|0;for(e=e.slice();l--;)a=((r=c*(o=e[l]%Ge)+(n=h*o+(s=e[l]/Ge|0)*c)%Ge*Ge+a)/i|0)+(n/Ge|0)+h*s,e[l]=r%i;return a&&(e=[a].concat(e)),e}function t(e,t,i,n){var r,o;if(i!=n)o=i>n?1:-1;else for(r=o=0;rt[r]?1:-1;break}return o}function i(e,t,i,n){for(var r=0;i--;)e[i]-=r,r=e[i]1;e.splice(0,1));}return function(n,r,o,s,a){var l,c,h,d,u,p,_,m,g,f,v,y,b,w,k,x,$,S=n.s==r.s?1:-1,R=n.c,T=r.c;if(!(R&&R[0]&&T&&T[0]))return new M(n.s&&r.s&&(R?!T||R[0]!=T[0]:T)?R&&0==R[0]||!T?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=o+(c=n.e-r.e)+1,a||(a=je,c=Ke(n.e/Be)-Ke(r.e/Be),S=S/Be|0),h=0;T[h]==(R[h]||0);h++);if(T[h]>(R[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=R.length,x=T.length,h=0,S+=2,(u=He(a/(T[0]+1)))>1&&(T=e(T,u,a),R=e(R,u,a),x=T.length,w=R.length),b=x,v=(f=R.slice(0,x)).length;v=a/2&&k++;do{if(u=0,(l=t(T,f,x,v))<0){if(y=f[0],x!=v&&(y=y*a+(f[1]||0)),(u=He(y/k))>1)for(u>=a&&(u=a-1),_=(p=e(T,u,a)).length,v=f.length;1==t(p,f,_,v);)u--,i(p,x<_?$:T,_,a),_=p.length,l=1;else 0==u&&(l=u=1),_=(p=T.slice()).length;if(_=10;S/=10,h++);D(m,o+(m.e=h+c*Be-1)+1,s,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,r=function(e,t,i,n){var r,o=i?t:t.replace(d,"");if(h.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(e,t,i){return r="x"==(i=i.toLowerCase())?16:"b"==i?2:8,n&&n!=r?e:t}),n&&(r=n,o=o.replace(l,"$1").replace(c,"0.$1")),t!=o))return new M(o,r);if(M.DEBUG)throw Error(Le+"Not a"+(n?" base "+n:"")+" number: "+t);e.s=null}e.c=e.e=null},u.absoluteValue=u.abs=function(){var e=new M(this);return e.s<0&&(e.s=1),e},u.comparedTo=function(e,t){return Ze(this,new M(e,t))},u.decimalPlaces=u.dp=function(e,t){var i,n,r,o=this;if(null!=e)return Xe(e,0,Ve),null==t?t=m:Xe(t,0,8),D(new M(o),e+o.e+1,t);if(!(i=o.c))return null;if(n=((r=i.length-1)-Ke(this.e/Be))*Be,r=i[r])for(;r%10==0;r/=10,n--);return n<0&&(n=0),n},u.dividedBy=u.div=function(e,t){return i(this,new M(e,t),_,m)},u.dividedToIntegerBy=u.idiv=function(e,t){return i(this,new M(e,t),0,1)},u.exponentiatedBy=u.pow=function(e,t){var i,n,r,o,s,a,l,c,h=this;if((e=new M(e)).c&&!e.isInteger())throw Error(Le+"Exponent not an integer: "+A(e));if(null!=t&&(t=new M(t)),s=e.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!e.c||!e.c[0])return c=new M(Math.pow(+A(h),s?e.s*(2-Je(e)):+A(e))),t?c.mod(t):c;if(a=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new M(NaN);(n=!a&&h.isInteger()&&t.isInteger())&&(h=h.mod(t))}else{if(e.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||s&&h.c[1]>=24e7:h.c[0]<8e13||s&&h.c[0]<=9999975e7)))return o=h.s<0&&Je(e)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Ne(k/Be+2))}for(s?(i=new M(.5),a&&(e.s=1),l=Je(e)):l=(r=Math.abs(+A(e)))%2,c=new M(p);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):n&&(c=c.mod(t))}if(r){if(0===(r=He(r/2)))break;l=r%2}else if(D(e=e.times(i),e.e+1,1),e.e>14)l=Je(e);else{if(0===(r=+A(e)))break;l=r%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):n&&(h=h.mod(t))}return n?c:(a&&(c=p.div(c)),t?c.mod(t):o?D(c,k,m,void 0):c)},u.integerValue=function(e){var t=new M(this);return null==e?e=m:Xe(e,0,8),D(t,t.e+1,e)},u.isEqualTo=u.eq=function(e,t){return 0===Ze(this,new M(e,t))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(e,t){return Ze(this,new M(e,t))>0},u.isGreaterThanOrEqualTo=u.gte=function(e,t){return 1===(t=Ze(this,new M(e,t)))||0===t},u.isInteger=function(){return!!this.c&&Ke(this.e/Be)>this.c.length-2},u.isLessThan=u.lt=function(e,t){return Ze(this,new M(e,t))<0},u.isLessThanOrEqualTo=u.lte=function(e,t){return-1===(t=Ze(this,new M(e,t)))||0===t},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(e,t){var i,n,r,o,s=this,a=s.s;if(t=(e=new M(e,t)).s,!a||!t)return new M(NaN);if(a!=t)return e.s=-t,s.plus(e);var l=s.e/Be,c=e.e/Be,h=s.c,d=e.c;if(!l||!c){if(!h||!d)return h?(e.s=-t,e):new M(d?s:NaN);if(!h[0]||!d[0])return d[0]?(e.s=-t,e):new M(h[0]?s:3==m?-0:0)}if(l=Ke(l),c=Ke(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,r=h):(c=l,r=d),r.reverse(),t=a;t--;r.push(0));r.reverse()}else for(n=(o=(a=h.length)<(t=d.length))?a:t,a=t=0;t0)for(;t--;h[i++]=0);for(t=je-1;n>a;){if(h[--n]=0;){for(i=0,u=y[r]%g,p=y[r]/g|0,o=r+(s=l);o>r;)i=((c=u*(c=v[--s]%g)+(a=p*c+(h=v[s]/g|0)*u)%g*g+_[o]+i)/m|0)+(a/g|0)+p*h,_[o--]=c%m;_[o]=i}return i?++n:_.splice(0,1),C(e,_,n)},u.negated=function(){var e=new M(this);return e.s=-e.s||null,e},u.plus=function(e,t){var i,n=this,r=n.s;if(t=(e=new M(e,t)).s,!r||!t)return new M(NaN);if(r!=t)return e.s=-t,n.minus(e);var o=n.e/Be,s=e.e/Be,a=n.c,l=e.c;if(!o||!s){if(!a||!l)return new M(r/0);if(!a[0]||!l[0])return l[0]?e:new M(a[0]?n:0*r)}if(o=Ke(o),s=Ke(s),a=a.slice(),r=o-s){for(r>0?(s=o,i=l):(r=-r,i=a),i.reverse();r--;i.push(0));i.reverse()}for((r=a.length)-(t=l.length)<0&&(i=l,l=a,a=i,t=r),r=0;t;)r=(a[--t]=a[t]+l[t]+r)/je|0,a[t]=je===a[t]?0:a[t]%je;return r&&(a=[r].concat(a),++s),C(e,a,s)},u.precision=u.sd=function(e,t){var i,n,r,o=this;if(null!=e&&e!==!!e)return Xe(e,1,Ve),null==t?t=m:Xe(t,0,8),D(new M(o),e,t);if(!(i=o.c))return null;if(n=(r=i.length-1)*Be+1,r=i[r]){for(;r%10==0;r/=10,n--);for(r=i[0];r>=10;r/=10,n++);}return e&&o.e+1>n&&(n=o.e+1),n},u.shiftedBy=function(e){return Xe(e,-9007199254740991,We),this.times("1e"+e)},u.squareRoot=u.sqrt=function(){var e,t,n,r,o,s=this,a=s.c,l=s.s,c=s.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?s:1/0);if(0==(l=Math.sqrt(+A(s)))||l==1/0?(((t=Ye(a)).length+c)%2==0&&(t+="0"),l=Math.sqrt(+t),c=Ke((c+1)/2)-(c<0||c%2),n=new M(t=l==1/0?"5e"+c:(t=l.toExponential()).slice(0,t.indexOf("e")+1)+c)):n=new M(l+""),n.c[0])for((l=(c=n.e)+h)<3&&(l=0);;)if(o=n,n=d.times(o.plus(i(s,o,h,1))),Ye(o.c).slice(0,l)===(t=Ye(n.c)).slice(0,l)){if(n.e0&&_>0){for(o=_%a||a,h=p.substr(0,o);o<_;o+=a)h+=c+p.substr(o,a);l>0&&(h+=c+p.slice(o)),u&&(h="-"+h)}n=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+n+(i.suffix||"")},u.toFraction=function(e){var t,n,r,o,s,a,l,c,h,d,u,_,g=this,f=g.c;if(null!=e&&(!(l=new M(e)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Le+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+A(l));if(!f)return new M(g);for(t=new M(p),h=n=new M(p),r=c=new M(p),_=Ye(f),s=t.e=_.length-g.e-1,t.c[0]=Ue[(a=s%Be)<0?Be+a:a],e=!e||l.comparedTo(t)>0?s>0?t:h:l,a=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,t,0,1),1!=(o=n.plus(d.times(r))).comparedTo(e);)n=r,r=o,h=c.plus(d.times(o=h)),c=o,t=l.minus(d.times(o=t)),l=o;return o=i(e.minus(n),r,0,1),c=c.plus(o.times(h)),n=n.plus(o.times(r)),c.s=h.s=g.s,u=i(h,r,s*=2,m).minus(g).abs().comparedTo(i(c,n,s,m).minus(g).abs())<1?[h,r]:[c,n],y=a,u},u.toNumber=function(){return+A(this)},u.toPrecision=function(e,t){return null!=e&&Xe(e,1,Ve),R(this,e,t,2)},u.toString=function(e){var t,i=this,r=i.s,o=i.e;return null===o?r?(t="Infinity",r<0&&(t="-"+t)):t="NaN":(null==e?t=o<=g||o>=f?Qe(Ye(i.c),o):et(Ye(i.c),o,"0"):10===e&&S?t=et(Ye((i=D(new M(i),_+o+1,m)).c),i.e,"0"):(Xe(e,2,$.length,"Base"),t=n(et(Ye(i.c),o,"0"),10,e,r,!0)),r<0&&i.c[0]&&(t="-"+t)),t},u.valueOf=u.toJSON=function(){return A(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=t&&M.set(t),M}(),it=class{key;left=null;right=null;constructor(e){this.key=e}},nt=class extends it{constructor(e){super(e)}},rt=class{size=0;modificationCount=0;splayCount=0;splay(e){const t=this.root;if(null==t)return this.compare(e,e),-1;let i=null,n=null,r=null,o=null,s=t;const a=this.compare;let l;for(;;)if(l=a(s.key,e),l>0){let t=s.left;if(null==t)break;if(l=a(t.key,e),l>0&&(s.left=t.right,t.right=s,s=t,t=s.left,null==t))break;null==i?n=s:i.left=s,i=s,s=t}else{if(!(l<0))break;{let t=s.right;if(null==t)break;if(l=a(t.key,e),l<0&&(s.right=t.left,t.left=s,s=t,t=s.right,null==t))break;null==r?o=s:r.right=s,r=s,s=t}}return null!=r&&(r.right=s.left,s.left=o),null!=i&&(i.left=s.right,s.right=n),this.root!==s&&(this.root=s,this.splayCount++),l}splayMin(e){let t=e,i=t.left;for(;null!=i;){const e=i;t.left=e.right,e.right=t,t=e,i=t.left}return t}splayMax(e){let t=e,i=t.right;for(;null!=i;){const e=i;t.right=e.left,e.left=t,t=e,i=t.right}return t}_delete(e){if(null==this.root)return null;if(0!=this.splay(e))return null;let t=this.root;const i=t,n=t.left;if(this.size--,null==n)this.root=t.right;else{const e=t.right;t=this.splayMax(n),t.right=e,this.root=t}return this.modificationCount++,i}addNewRoot(e,t){this.size++,this.modificationCount++;const i=this.root;null!=i?(t<0?(e.left=i,e.right=i.right,i.right=null):(e.right=i,e.left=i.left,i.left=null),this.root=e):this.root=e}_first(){const e=this.root;return null==e?null:(this.root=this.splayMin(e),this.root)}_last(){const e=this.root;return null==e?null:(this.root=this.splayMax(e),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(e){return this.validKey(e)&&0==this.splay(e)}defaultCompare(){return(e,t)=>et?1:0}wrap(){return{getRoot:()=>this.root,setRoot:e=>{this.root=e},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:e=>{this.splayCount=e},splay:e=>this.splay(e),has:e=>this.has(e)}}},ot=class e extends rt{root=null;compare;validKey;constructor(e,t){super(),this.compare=e??this.defaultCompare(),this.validKey=t??(e=>null!=e&&null!=e)}delete(e){return!!this.validKey(e)&&null!=this._delete(e)}deleteAll(e){for(const t of e)this.delete(t)}forEach(e){const t=this[Symbol.iterator]();let i;for(;i=t.next(),!i.done;)e(i.value,i.value,this)}add(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new nt(e),t),this}addAndReturn(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new nt(e),t),this.root.key}addAll(e){for(const t of e)this.add(t)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)<0)return this.root.key;let t=this.root.left;if(null==t)return null;let i=t.right;for(;null!=i;)t=i,i=t.right;return t.key}firstAfter(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)>0)return this.root.key;let t=this.root.right;if(null==t)return null;let i=t.left;for(;null!=i;)t=i,i=t.left;return t.key}retainAll(t){const i=new e(this.compare,this.validKey),n=this.modificationCount;for(const e of t){if(n!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(e)&&0==this.splay(e)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(e){if(!this.validKey(e))return null;return 0!=this.splay(e)?null:this.root.key}intersection(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)&&i.add(e);return i}difference(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)||i.add(e);return i}union(e){const t=this.clone();return t.addAll(e),t}clone(){const t=new e(this.compare,this.validKey);return t.size=this.size,t.root=this.copyNode(this.root),t}copyNode(e){if(null==e)return null;const t=new nt(e.key);return function e(t,i){let n,r;do{if(n=t.left,r=t.right,null!=n){const t=new nt(n.key);i.left=t,e(n,t)}if(null!=r){const e=new nt(r.key);i.right=e,t=r,i=e}}while(null!=r)}(e,t),t}toSet(){return this.clone()}entries(){return new lt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new at(this.wrap())}[Symbol.toStringTag]="[object Set]"},st=class{tree;path=new Array;modificationCount=null;splayCount;constructor(e){this.tree=e,this.splayCount=e.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const e=this.path[this.path.length-1];return this.getValue(e)}rebuildPath(e){this.path.splice(0,this.path.length),this.tree.splay(e),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(e){for(;null!=e;)this.path.push(e),e=e.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let e=this.tree.getRoot();for(;null!=e;)this.path.push(e),e=e.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let e=this.path[this.path.length-1],t=e.right;if(null!=t){for(;null!=t;)this.path.push(t),t=t.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===e;)e=this.path.pop();return this.path.length>0}},at=class extends st{getValue(e){return e.key}},lt=class extends st{getValue(e){return[e.key,e.key]}},ct=e=>()=>e,ht=e=>{const t=e?(t,i)=>i.minus(t).abs().isLessThanOrEqualTo(e):ct(!1);return(e,i)=>t(e,i)?0:e.comparedTo(i)};function dt(e){const t=e?(t,i,n,r,o)=>t.exponentiatedBy(2).isLessThanOrEqualTo(r.minus(i).exponentiatedBy(2).plus(o.minus(n).exponentiatedBy(2)).times(e)):ct(!1);return(e,i,n)=>{const r=e.x,o=e.y,s=n.x,a=n.y,l=o.minus(a).times(i.x.minus(s)).minus(r.minus(s).times(i.y.minus(a)));return t(l,r,o,s,a)?0:l.comparedTo(0)}}var ut=e=>e,pt=e=>{if(e){const t=new ot(ht(e)),i=new ot(ht(e)),n=(e,t)=>t.addAndReturn(e),r=e=>({x:n(e.x,t),y:n(e.y,i)});return r({x:new tt(0),y:new tt(0)}),r}return ut},_t=e=>({set:e=>{mt=_t(e)},reset:()=>_t(e),compare:ht(e),snap:pt(e),orient:dt(e)}),mt=_t(),gt=(e,t)=>e.ll.x.isLessThanOrEqualTo(t.x)&&t.x.isLessThanOrEqualTo(e.ur.x)&&e.ll.y.isLessThanOrEqualTo(t.y)&&t.y.isLessThanOrEqualTo(e.ur.y),ft=(e,t)=>{if(t.ur.x.isLessThan(e.ll.x)||e.ur.x.isLessThan(t.ll.x)||t.ur.y.isLessThan(e.ll.y)||e.ur.y.isLessThan(t.ll.y))return null;const i=e.ll.x.isLessThan(t.ll.x)?t.ll.x:e.ll.x,n=e.ur.x.isLessThan(t.ur.x)?e.ur.x:t.ur.x;return{ll:{x:i,y:e.ll.y.isLessThan(t.ll.y)?t.ll.y:e.ll.y},ur:{x:n,y:e.ur.y.isLessThan(t.ur.y)?e.ur.y:t.ur.y}}},vt=(e,t)=>e.x.times(t.y).minus(e.y.times(t.x)),yt=(e,t)=>e.x.times(t.x).plus(e.y.times(t.y)),bt=e=>yt(e,e).sqrt(),wt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return vt(r,n).div(bt(r)).div(bt(n))},kt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return yt(r,n).div(bt(r)).div(bt(n))},xt=(e,t,i)=>t.y.isZero()?null:{x:e.x.plus(t.x.div(t.y).times(i.minus(e.y))),y:i},$t=(e,t,i)=>t.x.isZero()?null:{x:i,y:e.y.plus(t.y.div(t.x).times(i.minus(e.x)))},St=class e{point;isLeft;segment;otherSE;consumedBy;static compare(t,i){const n=e.comparePoints(t.point,i.point);return 0!==n?n:(t.point!==i.point&&t.link(i),t.isLeft!==i.isLeft?t.isLeft?1:-1:zt.compare(t.segment,i.segment))}static comparePoints(e,t){return e.x.isLessThan(t.x)?-1:e.x.isGreaterThan(t.x)?1:e.y.isLessThan(t.y)?-1:e.y.isGreaterThan(t.y)?1:0}constructor(e,t){void 0===e.events?e.events=[this]:e.events.push(this),this.point=e,this.isLeft=t}link(e){if(e.point===this.point)throw new Error("Tried to link already linked events");const t=e.point.events;for(let e=0,i=t.length;e{const n=i.otherSE;t.set(i,{sine:wt(this.point,e.point,n.point),cosine:kt(this.point,e.point,n.point)})};return(e,n)=>{t.has(e)||i(e),t.has(n)||i(n);const{sine:r,cosine:o}=t.get(e),{sine:s,cosine:a}=t.get(n);return r.isGreaterThanOrEqualTo(0)&&s.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:r.isLessThan(0)&&s.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:s.isLessThan(r)?-1:s.isGreaterThan(r)?1:0}}},Mt=class e{events;poly;_isExteriorRing;_enclosingRing;static factory(t){const i=[];for(let n=0,r=t.length;n0&&(e=i)}let t=e.segment.prevInResult(),i=t?t.prevInResult():null;for(;;){if(!t)return null;if(!i)return t.ringOut;if(i.ringOut!==t.ringOut)return i.ringOut?.enclosingRing()!==t.ringOut?t.ringOut:t.ringOut?.enclosingRing();t=i.prevInResult(),i=t?t.prevInResult():null}}},Rt=class{exteriorRing;interiorRings;constructor(e){this.exteriorRing=e,e.poly=this,this.interiorRings=[]}addInterior(e){this.interiorRings.push(e),e.poly=this}getGeom(){const e=this.exteriorRing.getGeom();if(null===e)return null;const t=[e];for(let e=0,i=this.interiorRings.length;e0?(this.tree.delete(t),i.push(e)):(this.segments.push(t),t.prev=n)}else{if(n&&r){const e=n.getIntersection(r);if(null!==e){if(!n.isAnEndpoint(e)){const t=this._splitSafely(n,e);for(let e=0,n=t.length;e0)return-1;const n=t.comparePoint(e.rightSE.point);return 0!==n?n:-1}if(i.isGreaterThan(n)){if(s.isLessThan(a)&&s.isLessThan(c))return-1;if(s.isGreaterThan(a)&&s.isGreaterThan(c))return 1;const i=t.comparePoint(e.leftSE.point);if(0!==i)return i;const n=e.comparePoint(t.rightSE.point);return n<0?1:n>0?-1:1}if(s.isLessThan(a))return-1;if(s.isGreaterThan(a))return 1;if(r.isLessThan(o)){const i=t.comparePoint(e.rightSE.point);if(0!==i)return i}if(r.isGreaterThan(o)){const i=e.comparePoint(t.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!r.eq(o)){const e=l.minus(s),t=r.minus(i),h=c.minus(a),d=o.minus(n);if(e.isGreaterThan(t)&&h.isLessThan(d))return 1;if(e.isLessThan(t)&&h.isGreaterThan(d))return-1}return r.isGreaterThan(o)?1:r.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:e.idt.id?1:0}constructor(e,t,i,n){this.id=++Ot,this.leftSE=e,e.segment=this,e.otherSE=t,this.rightSE=t,t.segment=this,t.otherSE=e,this.rings=i,this.windings=n}static fromRing(t,i,n){let r,o,s;const a=St.comparePoints(t,i);if(a<0)r=t,o=i,s=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${t.x}, ${t.y}]`);r=i,o=t,s=-1}const l=new St(r,!0),c=new St(o,!1);return new e(l,c,[n],[s])}replaceRightSE(e){this.rightSE=e,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const e=this.leftSE.point.y,t=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:e.isLessThan(t)?e:t},ur:{x:this.rightSE.point.x,y:e.isGreaterThan(t)?e:t}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(e){return e.x.eq(this.leftSE.point.x)&&e.y.eq(this.leftSE.point.y)||e.x.eq(this.rightSE.point.x)&&e.y.eq(this.rightSE.point.y)}comparePoint(e){return mt.orient(this.leftSE.point,e,this.rightSE.point)}getIntersection(e){const t=this.bbox(),i=e.bbox(),n=ft(t,i);if(null===n)return null;const r=this.leftSE.point,o=this.rightSE.point,s=e.leftSE.point,a=e.rightSE.point,l=gt(t,s)&&0===this.comparePoint(s),c=gt(i,r)&&0===e.comparePoint(r),h=gt(t,a)&&0===this.comparePoint(a),d=gt(i,o)&&0===e.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&r.x.eq(a.x)&&r.y.eq(a.y)?null:r;if(l)return d&&o.x.eq(s.x)&&o.y.eq(s.y)?null:s;if(d&&h)return null;if(d)return o;if(h)return a;const u=((e,t,i,n)=>{if(t.x.isZero())return $t(i,n,e.x);if(n.x.isZero())return $t(e,t,i.x);if(t.y.isZero())return xt(i,n,e.y);if(n.y.isZero())return xt(e,t,i.y);const r=vt(t,n);if(r.isZero())return null;const o={x:i.x.minus(e.x),y:i.y.minus(e.y)},s=vt(o,t).div(r),a=vt(o,n).div(r),l=e.x.plus(a.times(t.x)),c=i.x.plus(s.times(n.x)),h=e.y.plus(a.times(t.y)),d=i.y.plus(s.times(n.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(r,this.vector(),s,e.vector());return null===u?null:gt(n,u)?mt.snap(u):null}split(t){const i=[],n=void 0!==t.events,r=new St(t,!0),o=new St(t,!1),s=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(r);const a=new e(r,s,this.rings.slice(),this.windings.slice());return St.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),St.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),n&&(r.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const e=this.rightSE;this.rightSE=this.leftSE,this.leftSE=e,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let e=0,t=this.windings.length;e0){const e=i;i=n,n=e}if(i.prev===n){const e=i;i=n,n=e}for(let e=0,t=n.rings.length;e1===e.length&&e[0].isSubject;this._isInResult=i(e)!==i(t);break}}return this._isInResult}},Pt=class{poly;isExterior;segments;bbox;constructor(e,t,i){if(!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=t,this.isExterior=i,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const n=mt.snap({x:new tt(e[0][0]),y:new tt(e[0][1])});this.bbox={ll:{x:n.x,y:n.y},ur:{x:n.x,y:n.y}};let r=n;for(let t=1,i=e.length;tAt.run("union",e,t),Nt=(e,...t)=>At.run("intersection",e,t),Ht=(e,...t)=>At.run("difference",e,t);mt.set;const Lt=/^#[0-9a-fA-F]{6}$/;function qt(e,t){return"string"==typeof e&&Lt.test(e)?e:t}const jt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",Bt=new RegExp(`^rgb\\(${jt}, ${jt}, ${jt}\\)$`);function Wt(e){if(!Array.isArray(e)||e.length<3)return null;const t=e.slice(0,3);if(!t.every(e=>"number"==typeof e&&Number.isFinite(e)))return null;const[i,n,r]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${n}, ${r})`}function Ut(e){const t=Math.max(0,Math.min(120,(e-40)/140*120));return`hsl(${Math.round(t)}, 85%, 55%)`}function Gt(e,t){if(!(Number.isFinite(e)&&t>0))return e;const i=Math.round(e/t)*t;return Math.abs(i-e)<=1e-9*t?e:i}function Vt(e,t,i,n=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const r=t[0]-e[0],o=t[1]-e[1];if(Math.abs(r)+Math.abs(o)<=1e-12)return[e[0],e[1]];const s=Math.PI/4;let a=Math.atan2(o,r);a<0&&(a+=2*Math.PI);const l=Math.floor(a/s+.5)%8,[c,h]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=c*c+h*h;let u=Math.max(0,Math.round((r*c+o*h)/(i*d)));if(Number.isFinite(n)&&n>=0){const t=(e,t)=>{if(!t)return 1/0;const r=t>0?n-e:e+n;return Math.max(0,Math.floor((r+1e-9*i)/i))};u=Math.min(u,t(e[0],c),t(e[1],h))}return[e[0]+c*u*i,e[1]+h*u*i]}function Kt(e,t){if(t){const t=e/2.54;let i=Math.floor(t/12),n=Math.round(t-12*i);return 12===n&&(i+=1,n=0),`${i}′ ${n}″`}return`${(e/100).toFixed(2)} m`}function Yt(e,t,i=1){const n=e[0].toFixed(i),r=e[1].toFixed(i),o=t[0].toFixed(i),s=t[1].toFixed(i),a=n=3?e.poly:e&&null!=e.x&&null!=e.y&&null!=e.w&&null!=e.h?[[e.x,e.y],[e.x+e.w,e.y],[e.x+e.w,e.y+e.h],[e.x,e.y+e.h]]:null}function Xt(e){const t=[];for(const i of e||[])i?.poly?.length>=3?t.push({poly:i.poly.map(e=>e.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&t.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return t}function Jt(e){const t=[],i=new Set;for(const n of e||[]){const e=Zt(n);if(e)for(let n=0;n=90?e-=180:e<-90&&(e+=180),n.step&&n.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(n.length||0,0)/2,i/2);let a=Math.round(u*i/n.step)*n.step;Math.abs(u*i-i/2)<=n.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;r={x:t+l*c,y:s+l*h,angle:e}}else r={x:p[0],y:p[1],angle:e}}}return r}function ei(e,t,i){let n=null,r=1/0;for(let o=0;o=r)continue;r=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;n=[s+g*h,a+g*d]}return n}function ti(e,t,i,n,r,o=1){const s=t*Math.PI/180,a=[Math.cos(s),Math.sin(s)];let l=null,c=o;for(const t of Jt(n)){const i=[[t[0],t[1]],[t[2],t[3]]],n=t=>Math.abs(a[0]*(t[1]-e[1])-a[1]*(t[0]-e[0]));if(n(i[0])>o||n(i[1])>o)continue;const r=(i[0][0]-e[0])*a[0]+(i[0][1]-e[1])*a[1],s=(i[1][0]-e[0])*a[0]+(i[1][1]-e[1])*a[1],h=Math.min(r,s),d=Math.max(r,s),u=h>0?h:d<0?-d:0;u[e[0]+a[0]*t,e[1]+a[1]*t],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:p,sideB:_,midA:m((h-u)/2),midB:m((u+d)/2),wallCenter:m(g),centered:Math.abs(g)<=r}}function ii(e,t,i=!1){if("passage"===e)return 1;if(null==t||"unavailable"===t||"unknown"===t)return"window"===e?0:1;const n=function(e){return["on","open","home","detected","playing","cleaning"].includes(String(e))}(t)!==!!i;return n?1:0}function ni(e){return"passage"===e.type?[]:[e.contact,e.lock].filter(e=>"string"==typeof e&&e.length>0)}function ri(e,t,i=200){const n=t.trim().toLowerCase();return(n?e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)):e).slice(0,Math.max(0,i))}function oi(e,t,i=.001){return Math.abs(e[0]-t[0])e[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s)+o&&(i=!i)}return i}function ai(e,t,i){const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;let s=o?((e[0]-t[0])*n+(e[1]-t[1])*r)/o:0;return s=Math.max(0,Math.min(1,s)),Math.hypot(e[0]-(t[0]+s*n),e[1]-(t[1]+s*r))}function li(e,t){if(!t||t.length<2)return null;let i=null,n=1/0;for(let r=0;rr&&s<-r||o<-r&&s>r)&&(a>r&&l<-r||a<-r&&l>r)}function pi(e,t=24){const i=e.map(e=>e[0]),n=e.map(e=>e[1]),r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1;let c=0,h=0,d=0;for(let t=0;t1e-9?[h/(3*c),d/(3*c)]:[(r+o)/2,(s+a)/2],p=(t,i)=>{const n=((t,i)=>{if(!si([t,i],e))return-1/0;let n=1/0;for(let r=0;rm&&(m=c,_=[n,l])}if(_){const[e,i]=_,n=(o-r)/t,l=(a-s)/t;for(let t=-4;t<=4;t++)for(let r=-4;r<=4;r++){const o=e+n*t/4,s=i+l*r/4,a=p(o,s);a>m&&(m=a,_=[o,s])}}return _||_i(e)||e[0]}function _i(e,t=1e-6){if(!e||e.length<3)return null;const i=e.length,n=[e.reduce((e,t)=>e+t[0],0)/i,e.reduce((e,t)=>e+t[1],0)/i];if(hi(n,e,t))return n;for(let n=0;ngi(e,t,i));return n.filter(e=>!n.some(t=>t!==e&&gi(t,e,i)))}function yi(e){if(!e||e.length<3)return 0;let t=0;for(let i=0;i[e[0],e[1]]),[e[0][0],e[0][1]]]]}function wi(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=Et(bi(e),bi(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const n=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return n.length>=3?n:null}function ki(e,t,i){for(let n=0;n1&&oi(i[0],i[i.length-1],t)&&i.pop(),i}function $i(e,t,i=1e-6){if(!e||e.length<3||!t||t.length<2)return null;const n=t[0],r=t[t.length-1];if(oi(n,r,i))return null;const o=ki(e,n,i),s=ki(e,r,i);if(o<0||s<0)return null;const a=t.slice(1,-1);for(const t of a)if(!hi(t,e,i))return null;for(let i=0;i{const s=[t];let a=(n+1)%e.length;for(let t=0;t<=e.length&&(s.push(e[a]),a!==o);t++)a=(a+1)%e.length;return s.push(r),xi(s,i)};let c,h;if(o===s){const s=xi([...t],i);if(s.length<3||yi(s)<=i)return null;const a=[];for(let i=0;i=0?t:[...t].reverse();for(const e of i)a.push(e)}c=xi(a,i),h=s}else c=xi([...l(n,o,r,s),...[...a].reverse()],i),h=xi([...l(r,s,n,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(e))>Math.max(i,1e-6*yi(e))?null:[c,h]}function Si(e,t,i){const[n,r]=e.split(":");return"device"===n?r:"entity"===n?"lg_"+r:t&&t.startsWith("v_")?t:i()}function Mi(e){return e.length?Math.round(e.reduce((e,t)=>e+t,0)/e.length):null}function Ri(e,t){if(t>e[2]/e[3]){const i=e[3],n=e[3]*t;return{x:e[0]-(n-e[2])/2,y:e[1],w:n,h:i}}const i=e[2],n=e[2]/t;return{x:e[0],y:e[1]-(n-e[3])/2,w:i,h:n}}function Ti(e,t,i,n){if(e.length<2)return;const r=t.x+n,o=t.x+t.w-n,s=t.y+n,a=t.y+t.h-n;for(let t=0;t<60;t++){let t=!1;for(let n=0;n=0)i=t.slice(0,r).trim(),n=t.slice(r+1).trim();else{const e=t.split(".");e.length>2&&(i=e.slice(0,2).join("."),n=e.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?r>=0&&!n||n&&!/^[a-zA-Z0-9_.-]+$/.test(n)?null:n?{entity:i,attr:n}:{entity:i}:null}function Li(e,t){const i=String(e??"").trim(),n=String(t??"").trim(),r=Hi(n?`${i}:${n}`:i);return r?`{${r.entity}${r.attr?`:${r.attr}`:""}}`:""}function qi(e,t,i){const n=String(t??"").trim();if(!n)return null;const r=e?.states?.[n];if(!r)return null;const o=String(i??"").trim(),s=e=>e.slice(0,60);if(o){const t=function(e){if(null==e)return null;if(Array.isArray(e)){const t=e.map(e=>null==e?"":String(e)).join(", ");return t?t.slice(0,60):null}if("object"==typeof e)return null;const t=String(e);return""===t?null:t.slice(0,60)}(r.attributes?.[o]);if(null===t)return null;const i=e?.formatEntityAttributeValue;if("function"==typeof i)try{const t=i.call(e,r,o);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:t,formatted:!1}}const a=r.state;if(null==a||""===a)return null;const l=e?.formatEntityState;if("function"==typeof l)try{const t=l.call(e,r);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:s(String(a)),formatted:!1}}function ji(e,t,i){const n=String(t??"").trim(),r=String(i??"").trim()||n;if(!r)return e.text;const o=e.formatted&&n?function(e,t){if(!t)return e;const i=e.replace(/\s+$/,"");return i.endsWith(t)?i.slice(0,i.length-t.length).replace(/\s+$/,""):e}(e.text,n):e.text;return`${o} ${r}`}function Bi(e,t){const i=(t?.entity||"").trim();if(!i)return"";const n=e?.states?.[i],r=n?.state;if(!n||null==r||""===r||"unavailable"===r||"unknown"===r)return Ei;const o=(t?.attr||"").trim(),s=qi(e,i,o||null);if(null===s)return Ei;return ji(s,o?"":String(n.attributes?.unit_of_measurement??"").trim(),t?.unit)}function Wi(e,t,i,n=()=>!0){const r=e??"";let o=!1;const s=r.replace(/\{([^{}\r\n]+)\}/g,(e,t)=>{const r=Hi(t);return r?(o=!0,n(r.entity||"")?Bi(i,r):Ei):e});if(o)return s;const a=(t?.entity||"").trim();if(!a)return r;if(!n(a)){const e=r.indexOf(Ni);return e>=0?r.slice(0,e)+Ei+r.slice(e+2):r?`${r} ${Ei}`:Ei}const l=Bi(i,t),c=r.indexOf(Ni);return c>=0?r.slice(0,c)+l+r.slice(c+2):r?`${r} ${l}`:l}const Ui=20;function Gi(e){const t=Number(e?.scale);if(Number.isFinite(t)&&t>0)return Math.min(20,Math.max(.15,t));return{s:.7,m:1,l:1.5}[String(e?.size??"")]??1}function Vi(e,t){if(!t)return e;let i=e;for(const[e,n]of Object.entries(t))i=i.split("{"+e+"}").join(String(n));return i}const Ki="#55606c",Yi=.55,Zi=20,Xi=25,Ji={c:"#607d8b",a:.18};function Qi(e,t=Ji){const i=e&&"object"==typeof e?e:null,n=i?.a;return{c:qt(i?.c,t.c),a:"number"==typeof n&&Number.isFinite(n)?Math.min(1,Math.max(0,n)):t.a}}function en(e,t){const i=Qi(e),n=t?.settings?.custom_fill;return n&&"object"==typeof n?Qi(n,i):i}function tn(e){const t=e?.settings||{},i=!e?.plan_url,n="glow"===t.fill_mode;return{showBorders:t.show_borders??i,showNames:t.show_names??i,color:qt(t.room_color,Ki),opacity:"number"==typeof t.room_opacity?Math.min(1,Math.max(0,t.room_opacity)):.55,fill:["lqi","light","temp","custom"].includes(t.fill_mode)?t.fill_mode:"none",customFill:Qi(t.custom_fill),glow:"boolean"==typeof t.glow_enabled?t.glow_enabled:n,tempMin:"number"==typeof t.temp_min?t.temp_min:20,tempMax:"number"==typeof t.temp_max?t.temp_max:25,showLqi:"boolean"==typeof t.show_lqi?t.show_lqi:null,cardFontScale:"number"==typeof t.card_font_scale&&t.card_font_scale>0?Math.min(3,Math.max(.5,t.card_font_scale)):1,labelTemp:!0===t.label_temp,labelHum:!0===t.label_hum,labelLqi:!0===t.label_lqi,labelLight:!0===t.label_light,bgColor:qt(t.bg_color,null),hideDecor:!0===t.hide_decor,hideOpenings:!0===t.hide_openings}}function nn(e,t){if(t.bgColor)return t.bgColor;const i=e?.bg_color;return qt(i,"")}const rn={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function on(e){const t={},i=e?.fill_colors||{};for(const e of Object.keys(rn)){const n=rn[e],r=i[e];t[e]={c:qt(r?.c,n.c),a:r&&"number"==typeof r.a?Math.min(1,Math.max(0,r.a)):n.a}}return t}function sn(e,t,i){const n=Math.min(1,Math.max(0,i)),r=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),s=r.map((e,t)=>Math.round(e+(o[t]-e)*n));return"#"+s.map(e=>e.toString(16).padStart(2,"0")).join("")}function an(e,t,i,n,r,o,s,a=Ji){const l=function(e,t,i,n,r,o,s,a=Ji){if("custom"===e)return Qi(a);if("lqi"===e){if(null==t)return null;const e=(t-40)/140;return{c:sn(s.lqi_low.c,s.lqi_high.c,e),a:s.lqi_low.a+(s.lqi_high.a-s.lqi_low.a)*Math.min(1,Math.max(0,e))}}if("light"===e)return"none"===i?s.light_none.a>0?s.light_none:null:"on"===i?s.light_on:s.light_off;if("temp"===e){if(null==n)return null;const e=Math.min(r,o),t=Math.max(r,o);return nt?s.temp_hot:s.temp_ok}return null}(e,t,i,n,r,o,s,a);return l?{color:l.c,opacity:l.a,mode:e}:null}const ln={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},cn=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function hn(e){for(const t of[...Object.values(ln),...cn])if(e===t[0]||e===t[1])return t;return null}function dn(e,t,i,n,r){if(!n||"unavailable"===n||"unknown"===n)return e;if(r){const i="cover"===t?hn(e):null;return i?"closed"===n?i[0]:i[1]:e}if("binary_sensor"===t){if("door"===i)return"on"===n?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===n?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===n?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===t){const t=ln[String(i||"")];if(t)return"closed"===n?t[0]:t[1];const r=hn(e);return r?"closed"===n?r[0]:r[1]:e}return"lock"===t?"locked"===n?"mdi:lock":"mdi:lock-open-variant":"light"===t&&"mdi:lightbulb"===e&&"on"===n?"mdi:lightbulb-on":e}function un(e){const t=Math.min(4e4,Math.max(1e3,e))/100,i=t<=66?255:329.698727446*Math.pow(t-60,-.1332047592),n=t<=66?99.4708025861*Math.log(t)-161.1195681661:288.1221695283*Math.pow(t-60,-.0755148492),r=t>=66?255:t<=19?0:138.5177312231*Math.log(t-10)-305.0447927307,o=e=>Math.round(Math.min(255,Math.max(0,e)));return[o(i),o(n),o(r)]}const pn=1/2.2;function _n(e){const t=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(e);return t?"#"+t.slice(1).map(e=>Math.min(255,Number(e)).toString(16).padStart(2,"0")).join(""):null}function mn(e){if(!e||"object"!=typeof e||Array.isArray(e))return null;const t=e;if(Object.keys(t).some(e=>"c"!==e&&"bri"!==e))return null;const i=qt(t.c,null);return i?void 0===t.bri||null===t.bri?{c:i}:"number"!=typeof t.bri||!Number.isFinite(t.bri)||t.bri<.01||t.bri>1?null:{c:i,bri:t.bri}:null}function gn(e,t,i){const n=mn(t),r=e?.attributes||{},o=n?.bri??function(e){const t=e?.attributes?.brightness,i="number"==typeof t?t:"string"==typeof t&&""!==t.trim()?Number(t):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(e);if(n)return{c:n.c,bri:o};const s=Wt(r.rgb_color);if(s)return{c:_n(s)||qt(i,"#ffd9a0"),bri:o};const a=Number(r.color_temp_kelvin)||(Number(r.color_temp)>0?1e6/Number(r.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=un(a),"#"+l.slice(0,3).map(e=>Math.min(255,Math.max(0,Math.round(Number(e)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:qt(i,"#ffd9a0"),bri:o};var l}function fn(e,t,i){return"on"===e?.state?gn(e,t,i):null}function vn(e,t=1){const i=Math.max(0,Math.min(1,Number.isFinite(e)?e:1)),n=.7*Math.max(0,Math.min(1,Number.isFinite(t)?t:1))*(.4+.6*Math.pow(i,pn));return Math.max(0,Math.min(1,n))}function yn(e){return e.startsWith("light.")||e.startsWith("switch.")}function bn(e,t,i=1e-6){const n=[];if(!e||!t||e.length<3||t.length<3)return n;for(let r=0;ru||l>u)continue;const p=(r[0]-o[0])*h+(r[1]-o[1])*d,_=(s[0]-o[0])*h+(s[1]-o[1])*d,m=Math.max(0,Math.min(p,_)),g=Math.min(c,Math.max(p,_));g-m>i&&n.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return n}function wn(e,t,i=1e-6){const n=[];for(const r of e){const e=[r[0],r[1]],o=[r[2],r[3]],s=o[0]-e[0],a=o[1]-e[1],l=Math.hypot(s,a);if(lo||r>o)continue;const s=(n[0]-e[0])*c+(n[1]-e[1])*h,a=(n[2]-e[0])*c+(n[3]-e[1])*h,u=Math.max(0,Math.min(s,a)),p=Math.min(l,Math.max(s,a));p-u>i&&d.push([u,p])}if(!d.length){n.push([e[0],e[1],o[0],o[1]]);continue}d.sort((e,t)=>e[0]-t[0]);let u=0;for(const[t,r]of d)t-u>i&&n.push([e[0]+c*u,e[1]+h*u,e[0]+c*t,e[1]+h*t]),u=Math.max(u,r);l-u>i&&n.push([e[0]+c*u,e[1]+h*u,o[0],o[1]])}return n}function kn(e,t,i=1e-6){const n=[];for(let t=0;t{if("string"!=typeof e||!e)return;const i=Mn(e);i.startsWith("/api/houseplan/content/")&&t.add(i)};for(const t of e?.spaces||[]){i(t?.plan_url);for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url)}for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url);return t}function Mn(e){return e?e.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+e.slice(23):e.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+e.slice(23):e:""}function Rn(e,t){const i=t?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:e}function Tn(e,t){const i=t?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||e}function Cn(e,t,i,n){if(!t||!i||t===i)return e;const r="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(r))return e;const t=e.url.split(r)[1]||"",[i,s]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(n){const t=n[decodeURIComponent(i)]??n[i];return t?{...e,url:e.url.split(r+i)[0]+o+encodeURIComponent(t)+s}:e}return{...e,url:e.url.split(r).join(o)}})}function Dn(e,t=1){const i=Number(e);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):t}function An(e,t,i){let n=null,r=null;for(const o of t){if(!(Math.abs(o[0]-e[0])<1e-6&&Math.abs(o[1]-e[1])<1e-6)){if(Math.abs(o[0]-e[0])<=i){const t=Math.abs(o[1]-e[1]);t>1e-6&&(!n||t1e-6&&(!r||t"number"==typeof e&&Number.isFinite(e),Wn=(e,t,i)=>Math.min(i,Math.max(t,e));function Un(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function Gn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;return t>=300&&t<480?"dawn":t>=480&&t<1080?"day":t>=1080&&t<1260?"dusk":"night"}function Vn(e){const t=Un(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-Wn(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:Wn((e.elevation+6)/12,0,1)}}function Kn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;if(t<300||t>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(t-300)/960,n=(t-300)/120,r=(1260-t)/120;return{sunX:8+84*i,sunY:78-64*Math.sin(i*Math.PI),sunOpacity:Math.max(.18,Math.min(n,r,1))}}function Yn(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&Bn(t.azimuth)&&Bn(t.elevation)&&"boolean"==typeof t.rising?{azimuth:Ln(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:Un(i),source:"sun",...Vn(i)};const n="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:Gn(n),source:"clock",...Kn(n)}}function Zn(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const Xn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],Jn=e=>Math.min(1,Math.max(0,e));function Qn(e,t,i=6){const n=e.angle*Math.PI/180,r=[Math.sin(n),-Math.cos(n)],o=n=>{const o=[e.x+r[0]*i*n,e.y+r[1]*i*n];return t.find(e=>e.poly.length>=3&&si(o,e.poly))||null},s=o(1),a=o(-1);return s&&a?null:s||a?s?{normal:[-r[0],-r[1]],roomId:s.id}:{normal:r,roomId:a.id}:null}function er(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function tr(e,t,i,n){return[[e[0],e[1]],[t[0],t[1]],[t[0]+i[0]*n,t[1]+i[1]*n],[e[0]+i[0]*n,e[1]+i[1]*n]]}function ir(e,t){try{const i=Nt([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]),n=[];for(const e of i){const t=e?.[0];!Array.isArray(t)||t.length<4||n.push(t.slice(0,t.length-1).map(e=>[e[0],e[1]]))}return n}catch{return[]}}function nr(e,t,i,n,r,o,s){if(!(n>0))return[];const a=qn(i,r),l=[-a[0],-a[1]],c=function(e){const t=Math.min(90,Math.max(0,e));return.7*(.8+1.7*Math.pow(1-t/90,1.6))}(n),h=[];for(const i of t){if(!(i.length>0))continue;const t=Qn(i,e);if(!t||!er(t.normal,a,n))continue;const r=e.find(e=>e.id===t.roomId);if(!r)continue;const d=o&&o[t.roomId]||r.poly,u=i.angle*Math.PI/180,p=i.length/2,_=[-t.normal[0],-t.normal[1]],m=Math.max(0,s?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,x=ir(tr(b,w,l,k),d);if(!x.length)continue;const $=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:t.roomId,polys:x,a:b,b:w,dir:l,len:k,normal:_,depth:k*$})}return h}const rr=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function or(e,t){const i=rr(t?.north_deg);return null!==i?i:rr(e?.north_deg)}function sr(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function ar(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function lr(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),n=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(n)?{azimuth:i,elevation:n}:null}const cr=["dawn","day","dusk","night"];function hr(e){if(!e)return"";const t=jn[e.phase];return[`--hp-day-cycle-outline-near:${t.outlineNear}`,`--hp-day-cycle-outline-mid:${t.outlineMid}`,`--hp-day-cycle-outline-far:${t.outlineFar}`].join(";")}function dr(e,t=1){if(!e)return G;const i=[`--hp-day-cycle-sun-x:${e.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${e.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${e.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,t)).toFixed(4)}`].join(";");return B``:U}
this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -4875,163 +4884,163 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> ${dr(F,I)} - ${this._editorRuntime?this._renderEditorSecondary():G} + ${this._editorRuntime?this._renderEditorSecondary():U}
- ${h&&m?.structural?W``:G} - `:U} + - + ${""} - ${this._wallHatchDefs(_.color)}${W`${this._paperShapes(s.rooms).map(e=>"path"in e?W``:"poly"in e?W``:W``)}`} - ${this._editing?this._renderMarkupDefs(l):G} + ${this._wallHatchDefs(_.color)}${B`${this._paperShapes(s.rooms).map(e=>"path"in e?B``:"poly"in e?B``:B``)}`} + ${this._editing?this._renderMarkupDefs(l):U} ${""} - ${this._editing&&!this._markup&&this._gridLevels()?W``:G} - ${s.bg&&O?W``:U} + ${s.bg&&A?B`this._onBackdropLoaded(s.bg.href,O)} - transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:G} + @load=${()=>this._onBackdropLoaded(s.bg.href,A)} + transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:U} @dblclick=${e=>this._openBackdropDialog(e)} - preserveAspectRatio="none" />`:G} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Zt(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Rn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=f.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},d=r(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),p=d&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return d.some((e,i)=>Pn(t,[e[0],e[1],...d[(i+1)%d.length]])<=.08*this._gridPitch)}):[],m=u?[]:t,g=p.concat(m);g.length&&(o+=" noedge");const v=this._spaceWalls.length&&n.id&&d&&this._innerRoomContour(s,n.id,e,i)||d,b=v?vi(v,(w=n,s.rooms.filter(e=>e!==w).map(r).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=v?this._cleanFloor(n,v,s).path:"",$=n.id||G,S=n.area||G,M=x&&v?W``:U} + ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Zt(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Rn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=g.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},d=r(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),p=d&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return d.some((e,i)=>Pn(t,[e[0],e[1],...d[(i+1)%d.length]])<=.08*this._gridPitch)}):[],m=u?[]:t,f=p.concat(m);f.length&&(o+=" noedge");const v=this._spaceWalls.length&&n.id&&d&&this._innerRoomContour(s,n.id,e,i)||d,b=v?vi(v,(w=n,s.rooms.filter(e=>e!==w).map(r).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=v?this._cleanFloor(n,v,s).path:"",$=n.id||U,S=n.area||U,M=x&&v?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:b.length&&v?W`this._clearTransientHover()}>`:b.length&&v?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:v&&v!==d?W`this._clearTransientHover()}>`:v&&v!==d?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:n.poly?W`this._clearTransientHover()}>`:n.poly?B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`:W`this._clearTransientHover()}>`:B`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @pointermove=${h} - @pointerleave=${()=>this._clearTransientHover()}>`,R=g.length&&d?kn(d,g,.02*this._gridPitch):null,T=R?W`this._clearTransientHover()}>`,R=f.length&&d?kn(d,f,.02*this._gridPitch):null,T=R?B``:G;return W`${M}${T}`})})()} - ${this._renderRoomHoverFill(A)} - ${this._renderOpeningTunnelFills(s,f)} + style=${this._markup?U:`stroke:${_.color};stroke-opacity:${_.showBorders?_.opacity:0}`}>`:U;return B`${M}${T}`})})()} + ${this._renderRoomHoverFill(z)} + ${this._renderOpeningTunnelFills(s,g)} ${this._renderGlowBaseRooms(s,v)} ${this._renderOpeningTunnelFills(s,v,"glow-base")} ${""} - ${_.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()} - ${N?this._renderGlowLayer(s,_):G} + ${_.hideDecor&&"decor"!==this._mode?U:this._renderDecorLayer()} + ${H?this._renderGlowLayer(s,_):U} ${this._renderSunRays(s)} - ${this._editing?W`${this._renderAlignGuides()}`:G} - ${this._markup?W`${this._renderMarkupLayer(l)}`:G} + ${this._editing?B`${this._renderAlignGuides()}`:U} + ${this._markup?B`${this._renderMarkupLayer(l)}`:U} ${""} ${""} - ${this._editing?G:this._renderZeroWalls(_)} + ${this._editing?U:this._renderZeroWalls(_)} ${this._renderWallBodies(_)} - ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():G} - ${this._renderRoomHoverOutline(A)} + ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():U} + ${this._renderRoomHoverOutline(z)} ${""} - ${this._editing?this._renderZeroWalls(_):G} - ${this._markup?W`${this._renderHiddenWallDiagnosticOverlay()}`:G} - ${this._markup?W`${this._renderOpeningPlacementPreview()}`:G} - ${M?this._renderOpeningDimensionGuides(M):G} - ${M?.guide?this._renderOpeningCenterTick(M.guide):G} + ${this._editing?this._renderZeroWalls(_):U} + ${this._markup?B`${this._renderHiddenWallDiagnosticOverlay()}`:U} + ${this._markup?B`${this._renderOpeningPlacementPreview()}`:U} + ${M?this._renderOpeningDimensionGuides(M):U} + ${M?.guide?this._renderOpeningCenterTick(M.guide):U} ${""} ${""} - ${this._markup?W`${this._renderActiveChainInk()}`:G} - ${this._markup?W`${this._renderPlanSnapOverlay()}`:G} - ${_.hideOpenings&&!this._markup||m&&!m.floorSymbols?G:this._renderOpenings(_)} + ${this._markup?B`${this._renderActiveChainInk()}`:U} + ${this._markup?B`${this._renderPlanSnapOverlay()}`:U} + ${_.hideOpenings&&!this._markup||m&&!m.floorSymbols?U:this._renderOpenings(_)} ${this._renderWallThickUi()} - ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):G} + ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):U} ${""} ${this._renderBackdropFrame(x)} ${this._renderTextFrame(x)} - ${h&&m?.structural?W` `:G} + ${this._renderIsoWalls(m,f)} + `:U} ${""}
${u.map(e=>this._renderDevice(e,x,y))} ${this._renderVacuums(u,x)} ${this._renderVacFit(x)} ${this._renderOpeningLocks(x)} - ${_.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,_)):G} - ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):G} + ${_.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,_)):U} + ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):U}
- ${this._measureAnchor?B`
${this._renderMeasureLabel(x)}
`:G} - ${this._resize?.liveLabels?B`
${this._resize?.liveLabels?.map(e=>B`
${this._renderMeasureLabel(x)}
`:U} + ${this._resize?.liveLabels?j`
${this._resize?.liveLabels?.map(e=>j`
${e.text}
`)}
`:G} - ${M?B`
${M.labels.map(e=>B`
${e.text}
`)}
`:U} + ${M?j`
${M.labels.map(e=>j`
${e.text}
`)}
`:G} - ${this._wallDialog?B`
${this._renderWallThickDialog()}
`:G} - ${R?B`
${e.text}
`)}
`:U} + ${this._wallDialog?j`
${this._renderWallThickDialog()}
`:U} + ${R?j`
${R.text}
`:G} - ${C?B`
${C.map(e=>B`
${R.text}
`:U} + ${C?j`
${C.map(e=>j`
${e.text}
`)}
`:G} - ${T?B`
${e.text}
`)}
`:U} + ${T?j`
${T.text}
`:G} + style="left:${((T.x-x.x)/x.w*100).toFixed(2)}%;top:${((T.y-x.y)/x.h*100).toFixed(2)}%">${T.text}
`:U}
- ${this._zoom>1?B`
${Math.round(100*this._zoom)}%
`:G} + ${this._zoom>1?j`
${Math.round(100*this._zoom)}%
`:U} ${this._renderFarHint()} ${this._renderHomeArrow()} ${this._renderEditorRuntimeLoading()} ${this._renderRecoveryOverlay()} - ${this._booting||this._bootFading?B``:U}
- ${this._roomDialog&&this._editorRuntime?this._renderRoomDialog():G} - ${this._mergeDialog&&this._editorRuntime?this._renderMergeDialog():G} - ${this._openingDialog&&this._editorRuntime?this._renderOpeningDialog():G} - ${this._physicalDialog&&this._editorRuntime?this._renderPhysicalDialog():G} - ${this._partitionDeleteDialog&&this._editorRuntime?this._renderPartitionDeleteDialog():G} - ${this._roomDeleteDialog&&this._editorRuntime?this._renderRoomDeleteDialog():G} - ${this._openingInfo?this._renderOpeningInfoCard():G} - ${this._decorTextDialog&&this._editorRuntime?this._renderDecorTextDialog():G} - ${this._decorShapeDialog&&this._editorRuntime?this._renderDecorShapeDialog():G} - ${this._backdropDialog&&this._editorRuntime?this._renderBackdropDialog():G} - ${this._decorEraseConfirm&&this._editorRuntime?this._renderDecorEraseConfirm():G} - ${this._spaceDialog&&(this._onboardingRuntime||this._editorRuntime)?this._renderSpaceDialog():G} - ${this._deviceInbox&&this._editorRuntime?this._renderDeviceInbox():G} - ${this._markerDialog&&this._editorRuntime?this._renderMarkerDialog():G} - ${this._vacCalConfirm?B`this._vacCalConfirm=null}>
@@ -5045,38 +5054,38 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this._t("vac.apply_proposal")}
-
`:G} - ${this._infoCard?this._renderInfoCard():G} - ${this._rulesDialog&&this._editorRuntime?this._renderRulesDialog():G} - ${this._settingsDialog&&this._editorRuntime?this._renderSettingsDialog():G} - ${this._alignDialog&&this._editorRuntime?this._renderAlignDialog():G} - ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():G} - ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():G} - ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():G} - ${this._tip?B`
- ${this._tip.title}${this._tip.meta?B`${this._tip.meta}`:G} - ${null!=this._tip.temp?B`${this._t("tip.temp_avg")} ${this._tip.temp}°`:G} - ${null!=this._tip.hum?B`${this._t("tip.hum_avg")} ${this._tip.hum}%`:G} - ${null!=this._tip.lqi?B`${this._t("tip.lqi")} - ${this._tip.lqi}`:G} -
`:G} - ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?B`
- ${this._model.map(e=>B``)} -
`:G} - ${this._kioskDialog?this._renderKioskDialog():G} - ${this._vacFit?B`
+ `:U} + ${this._infoCard?this._renderInfoCard():U} + ${this._rulesDialog&&this._editorRuntime?this._renderRulesDialog():U} + ${this._settingsDialog&&this._editorRuntime?this._renderSettingsDialog():U} + ${this._alignDialog&&this._editorRuntime?this._renderAlignDialog():U} + ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():U} + ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():U} + ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():U} + ${this._tip?j`
+ ${this._tip.title}${this._tip.meta?j`${this._tip.meta}`:U} + ${null!=this._tip.temp?j`${this._t("tip.temp_avg")} ${this._tip.temp}°`:U} + ${null!=this._tip.hum?j`${this._t("tip.hum_avg")} ${this._tip.hum}%`:U} + ${null!=this._tip.lqi?j`${this._t("tip.lqi")} + ${this._tip.lqi}`:U} +
`:U} + ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?j`
+ ${this._model.map(e=>j``)} +
`:U} + ${this._kioskDialog?this._renderKioskDialog():U} + ${this._vacFit?j`
${this._t("vac.fit_hint")} -
`:G} - ${this._tapConfirm?B``:U} + ${this._tapConfirm?j`this._tapConfirm=null}>
- ${"run"===this._tapConfirm.kind?B`

${this._tapConfirm.text}

`:this._tapConfirm.lines.map((e,t)=>B` + ${"run"===this._tapConfirm.kind?j`

${this._tapConfirm.text}

`:this._tapConfirm.lines.map((e,t)=>j`

${e}

`)}
@@ -5086,32 +5095,32 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this._t("btn.run")}
-
`:G} - ${this._toast?B``:G} +
`:U} + ${this._toast?j``:U} - `}_vacCandidateStatus(e,t,i=this._planHass){const n=this._bindingStatus("entity:"+e);if("ha_disabled"===n.kind)return"disabled";if("orphaned"===n.kind)return"missing";if("unverified"===n.kind)return"unverified";const r=i?.states?.[e];return"unavailable"===r?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,n]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const r=Gl(e,n,t[e]);r&&i.push(r)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const n=e.marker?.vacuum,r="string"==typeof n?.source&&!!n.source,o=new Set(e.entities||[]),s=new Set(o);r&&s.add(n.source);const a=i?.entities||{},l=[];for(const e of s){const t=i?.states?.[e],o=Gl(e,t,a[e]);o?l.push(o):r&&e===n.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:a[e]?.platform?String(a[e].platform):null,category:e.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(t&&this._vacAllCameraCache?.devId===e.id){const e=new Set(l.map(e=>e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Mh(this._planHass,this._devices,null,this._virtualLights)){const i=Nu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:qu(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=Ap(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),zp(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Mh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==Ai(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Op(a,s,o,window.clearTimeout.bind(window)):(a=Ap(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==Ai(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Pp(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Ai(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Xl(this.hass.states[i||""]?.state),r=Bl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==nc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Zl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){return this._editorRuntimeOrThrow()._vacMapId(e,t,i)}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):G}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===Ai(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??Bl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=nc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,g=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===u&&g&&g.points.length>1){const e=g.points.map(([e,t])=>{const[i,n]=zl(c,e,t),r=this._scenePoint([i,n]);return r[0].toFixed(1)+","+r[1].toFixed(1)}).join(" ");o.push(W``)}if(p){const e=Ll(a,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:ql(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[n,r]=zl(c,e,t),o=this._scenePoint([n,r]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=zl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(W``)}}}if(!d||!a.pos)continue;const[f,v]=zl(c,a.pos.x,a.pos.y),y=this._scenePoint([f,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(B`
e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Th(this._planHass,this._devices,null,this._virtualLights)){const i=Lu(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:ju(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=Op(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),Fp(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Th(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==zi(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&Pp(a,s,o,window.clearTimeout.bind(window)):(a=Op(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==zi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=Ip(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===zi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=Ql(this.hass.states[i||""]?.state),r=Vl(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==oc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=Jl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=Jl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){return this._editorRuntimeOrThrow()._vacMapId(e,t,i)}_vacSaveMatrix(e,t,i,n){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):U}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(e,t){if(this._markup||"decor"===this._mode)return U;const i=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,n=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const r=[],o=[];for(const i of e){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===zi(i.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${i.id}`),s=e?.source??this._vacSource(i,this._renderPlanHass);if(!s)continue;const a=e?.telemetry??Vl(this._renderPlanHass?.states[s]?.attributes);if(!a)continue;const l=String(e?.mapId??this._vacMapId(i,a,this._renderPlanHass)),c=i.marker?.vacuum?.calibration?.[l];if(!c||6!==c.length)continue;const h=e?.runtime??this._vacRt.get(i.id),d=h?.moving??!1,u=oc(i.marker?.vacuum),p="always"===u||"cleaning"===u&&d,_=e?.server??this._vacSrvTrails[i.id],m=_?.current?.map_id===l&&Array.isArray(_.current.points)?_.current:null,f=_?.previous?.map_id===l&&Array.isArray(_.previous.points)?_.previous:null;if("always"===u&&f&&f.points.length>1){const e=f.points.map(([e,t])=>{const[i,n]=Fl(c,e,t),r=this._scenePoint([i,n]);return r[0].toFixed(1)+","+r[1].toFixed(1)}).join(" ");o.push(B``)}if(p){const e=Wl(a,m,h?.trail||[]),t=!d||"integration"!==e.source&&"server"!==e.source?e.path:jl(e.path);if(t.length){const e=t.map(e=>e.map(([e,t],i)=>{const[n,r]=Fl(c,e,t),o=this._scenePoint([n,r]);return`${i?"L":"M"} ${o[0].toFixed(1)} ${o[1].toFixed(1)}`}).join(" ")).join(" ");o.push(B``);const n=t[t.length-1];if(d&&n?.length>=2){const e=n[n.length-1],[t,r]=Fl(c,e[0],e[1]),s=this._scenePoint([t,r]),a=s[0].toFixed(1),l=s[1].toFixed(1);o.push(B``)}}}if(!d||!a.pos)continue;const[g,v]=Fl(c,a.pos.x,a.pos.y),y=this._scenePoint([g,v]),b=(y[0]-t.x)/t.w*100,w=(y[1]-t.y)/t.h*100,k=h&&h.lastTs>0&&Date.now()-h.lastTs>6e4,x=i.marker?.icon||i.icon||"mdi:robot-vacuum";r.push(j`
{e.stopPropagation();const t=this._vacEntity(i);t&&this._openMoreInfo(t)}}> -
`)}return this._vacLastView=t,r.length&&!this._vacRaf&&this._vacRafLoop(),r.length||o.length?B` - ${o.length?W`${o}`:G} - ${r}`:G}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...Yu(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Ou(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",uu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),_=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return B`
`)}return this._vacLastView=t,r.length&&!this._vacRaf&&this._vacRafLoop(),r.length||o.length?j` + ${o.length?B`${o}`:U} + ${r}`:U}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...Xu(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Pu(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",_u(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),_=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return j`
this._clickDevice(t,e)} @keydown=${t=>this._keyDevice(t,e)} @@ -5123,35 +5132,35 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointerup=${t=>this._pointerUp(t,e)} @pointercancel=${t=>this._pointerUp(t,e)} > - ${Xu(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} -
`}_roomArea(e){const t=Zt(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Hn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Gh(this._renderPlanHass,t,"temp",this._markers);const i=Kh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Gh(this._renderPlanHass,t,"hum",this._markers);const i=Kh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Zh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*km,y:i.y*km};const n=this._roomCenter(e),r=this._gridPitch;return{x:qa(Gt(n[0],r)),y:qa(Gt(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return G;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(B`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(B`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(B`${t}`)}if(n.labelLight){const t=(d=Mh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(B`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return B`
`}_roomArea(e){const t=Zt(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return Nn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return Kh(this._renderPlanHass,t,"temp",this._markers);const i=Zh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return Kh(this._renderPlanHass,t,"hum",this._markers);const i=Zh(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const n=Jh(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:n},n}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*$m,y:i.y*$m};const n=this._roomCenter(e),r=this._gridPitch;return{x:qa(Ut(n[0],r)),y:qa(Ut(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return U;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(j`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(j`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(j`${t}`)}if(n.labelLight){const t=(d=Th(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(j`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return j`
this._labelDown(i,e,t.id)} @pointermove=${i=>this._labelMove(i,e,t.id)} @pointerup=${()=>this._labelUp(e)} @pointercancel=${()=>this._labelUp(e)} - >${e.name||(this._markup?this._t("room.unnamed"):"")}${u?B`{t.stopPropagation(),this._clickRoom(e)}:G} - @pointerdown=${p?e=>e.stopPropagation():G}>`:G} - ${h.length?B`${h}`:G} - ${"plan"===this._mode?["tl","tr","bl","br"].map(i=>B`${e.name||(this._markup?this._t("room.unnamed"):"")}${u?j`{t.stopPropagation(),this._clickRoom(e)}:U} + @pointerdown=${p?e=>e.stopPropagation():U}>`:U} + ${h.length?j`${h}`:U} + ${"plan"===this._mode?["tl","tr","bl","br"].map(i=>j`this._rlResizeDown(i,e,t.id)} @pointermove=${e=>this._rlResizeMove(e)} @pointerup=${()=>this._rlResizeUp()} - @pointercancel=${()=>this._rlResizeUp()}>`):G} -
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=On(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return B`
- ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=On(e.a,e.b);return{x:o,y:s,on45:zn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Hn(vr(Math.abs(n-t),this._cellCm,this._gridPitch)*vr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=km,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return ii(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!qh(this._fullRegistryHass,e,Lh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!qh(this._renderPlanHass,e,Lh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Vc(e)){return!!t&&"active"===th(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return W`${t.map(e=>{if(e.orphanReason)return W`this._rlResizeUp()}>`):U} +
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=An(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return j`
+ ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=An(e.a,e.b);return{x:o,y:s,on45:On(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${Nn(vr(Math.abs(n-t),this._cellCm,this._gridPitch)*vr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=$m,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]?.state:null;return ii(e.type,t,!!e.invert)}_planEntityAvailable(e){return!!e&&(!jh(this._fullRegistryHass,e,Wh(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!jh(this._renderPlanHass,e,Wh(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=Yc(e)){return!!t&&"active"===nh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return B``;const i=this._spaceModel();if(!i)return B``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return B`${t.map(e=>{if(e.orphanReason)return B`{t.stopPropagation(),this._editOpening(e)}}> ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=cu(l),u=xa(10,this._cellCm),p=xa(12,this._cellCm);return W``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=du(l),u=xa(10,this._cellCm),p=xa(12,this._cellCm);return B` - ${hu(l)} + ${uu(l)} @@ -5162,7 +5171,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return B``;const i=this._spaceModel();if(!i)return B``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return B`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=xa(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,_=(u[1]-e.y)/e.h*100;return B`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return j`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=xa(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,_=(u[1]-e.y)/e.h*100;return j`
{e.stopPropagation(),"view"===this._mode&&(this._openingInfo=t)}}> -
`})}`}_lockAction(e,t){if(this._openingEntityAvailable(e)){if("unlock"===t){const t=this.hass?.states?.[e]?.attributes?.friendly_name||e;if(!confirm(this._t("confirm.unlock",{name:t})))return}this.hass?.callService?.("lock",t,{entity_id:e})}}_renderOpeningInfoCard(){const e=this._openingInfo,t="passage"!==e.type&&e.contact&&this._openingEntityAvailable(e.contact)?e.contact:null,i=("door"===e.type||"gate"===e.type)&&e.lock&&this._openingEntityAvailable(e.lock)?e.lock:null,n=t?this.hass.states[t]?.state:null,r=this._openingAmt(e),o=i?this.hass.states[i]?.state:null,s="door"===e.type?"opening.door":"gate"===e.type?"opening.gate":"passage"===e.type?"opening.passage":"opening.window",a="door"===e.type?"mdi:door":"gate"===e.type?"mdi:gate":"passage"===e.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===e.type?r>0?"mdi:gate-open":"mdi:gate":r>0?"mdi:door-open":"mdi:door-closed",c=(e,t,i,n="")=>B`
${t}${i}
`;return B``})}`}_lockAction(e,t){if(this._openingEntityAvailable(e)){if("unlock"===t){const t=this.hass?.states?.[e]?.attributes?.friendly_name||e;if(!confirm(this._t("confirm.unlock",{name:t})))return}this.hass?.callService?.("lock",t,{entity_id:e})}}_renderOpeningInfoCard(){const e=this._openingInfo,t="passage"!==e.type&&e.contact&&this._openingEntityAvailable(e.contact)?e.contact:null,i=("door"===e.type||"gate"===e.type)&&e.lock&&this._openingEntityAvailable(e.lock)?e.lock:null,n=t?this.hass.states[t]?.state:null,r=this._openingAmt(e),o=i?this.hass.states[i]?.state:null,s="door"===e.type?"opening.door":"gate"===e.type?"opening.gate":"passage"===e.type?"opening.passage":"opening.window",a="door"===e.type?"mdi:door":"gate"===e.type?"mdi:gate":"passage"===e.type?"mdi:arch":"mdi:window-closed-variant",l="gate"===e.type?r>0?"mdi:gate-open":"mdi:gate":r>0?"mdi:door-open":"mdi:door-closed",c=(e,t,i,n="")=>j`
${t}${i}
`;return j`this._openingInfo=null}>
- ${t?c(l,this._t("opening.contact_label"),"unavailable"===n||null==n?this._t("opening.state_unknown"):this._t(r>0?"opening.open":"opening.closed"),r>0?"warn":"ok"):G} - ${i?c("locked"===o?"mdi:lock":"mdi:lock-open-variant",this._t("opening.lock_label"),"locked"===o?this._t("opening.locked"):["unlocked","open"].includes(String(o))?this._t("opening.unlocked"):this._t("opening.state_unknown"),"locked"===o?"ok":"warn"):G} - ${i&&("locked"===o||["unlocked","open"].includes(String(o)))?B``:i&&["locking","unlocking"].includes(String(o))?B``:i&&["locking","unlocking"].includes(String(o))?j``:G} - ${t||i?G:B`

${this._t("opening.no_entities")}

`} + `:U} + ${t||i?U:j`

${this._t("opening.no_entities")}

`}
-
`}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Mh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?qi(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(yn).filter(e=>this._planEntityAvailable(e));return B``}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Th(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?qi(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(yn).filter(e=>this._planEntityAvailable(e));return j`
- ${(()=>{const t=this._cardEntities(e);return t.length?B`
- ${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?qi(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return B`
+ ${(()=>{const t=this._cardEntities(e);return t.length?j`
+ ${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?qi(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return j`
${n} - ${"toggle"===t?B``:"open"===t?B``:B`${r}`} + ${"toggle"===t?j``:"open"===t?j``:j`${r}`}
`})} -
`:G})()} - ${e.model?B`
${this._t("info.model")}${e.model}
`:G} - ${i&&!this._cardEntities(e).length?B`
${this._t("info.state")}${i}
`:G} - ${Ci(e.link)?B`
${this._t("info.link")} - ${e.link}
`:G} - ${e.description?B`
${e.description}
`:G} - ${e.pdfs&&e.pdfs.length?B`
${this._t("info.manuals")} - ${e.pdfs.map(e=>B` - ${e.name}`)}
`:G} - ${n.length?B`
${this._t("info.controls")} +
`:U})()} + ${e.model?j`
${this._t("info.model")}${e.model}
`:U} + ${i&&!this._cardEntities(e).length?j`
${this._t("info.state")}${i}
`:U} + ${Ci(e.link)?j`
${this._t("info.link")} + ${e.link}
`:U} + ${e.description?j`
${e.description}
`:U} + ${e.pdfs&&e.pdfs.length?j`
${this._t("info.manuals")} + ${e.pdfs.map(e=>j` + ${e.name}`)}
`:U} + ${n.length?j`
${this._t("info.controls")} - ${n.map(e=>{const t=this.hass.states[e],i="on"===t?.state;return B` + ${n.map(e=>{const t=this.hass.states[e],i="on"===t?.state;return j` ${t?.attributes?.friendly_name||e}`})} -
`:G} - ${e.model||i||e.link||e.description||e.pdfs&&e.pdfs.length||n.length?G:B`
${this._t("info.none")}
`} +
`:U} + ${e.model||i||e.link||e.description||e.pdfs&&e.pdfs.length||n.length?U:j`
${this._t("info.none")}
`}
- ${e.primary?B``:G} + `:U}
- `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return this._editorRuntimeOrThrow()._toggleIntent(e,t)}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){return this._editorRuntimeOrThrow()._toggleStateText(e,t)}_toggleConfirmationStateText(e){return this._editorRuntimeOrThrow()._toggleConfirmationStateText(e)}_toggleConfirmationLines(e){return this._editorRuntimeOrThrow()._toggleConfirmationLines(e)}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return B`
+ `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return this._editorRuntimeOrThrow()._toggleIntent(e,t)}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){return this._editorRuntimeOrThrow()._toggleStateText(e,t)}_toggleConfirmationStateText(e){return this._editorRuntimeOrThrow()._toggleConfirmationStateText(e)}_toggleConfirmationLines(e){return this._editorRuntimeOrThrow()._toggleConfirmationLines(e)}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return j`
${this._t("preview.room_name")} @@ -5235,4 +5244,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d 45% · ${this._t("roomcard.light_partial",{on:1,total:3})} -
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Dm.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Dm.ZOOM_MAX=8,Dm.ZOOM_MIN=1/3,Dm.styles=[Ud,Hp],customElements.get("houseplan-card")||customElements.define("houseplan-card",Dm),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${sm} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{Ao as $,G as A,Qt as B,Ia as C,le as D,U as E,Zt as F,Na as G,Dd as H,Nd as I,gs as J,lo as K,Id as L,Td as M,No as N,vs as O,Ad as P,Bs as Q,Gs as R,vo as S,oi as T,ao as U,xo as V,np as W,rp as X,ss as Y,Wr as Z,zo as _,o as a,ts as a$,no as a0,oo as a1,qr as a2,pp as a3,Ra as a4,Fo as a5,Ea as a6,Oo as a7,Ca as a8,Da as a9,ea as aA,Al as aB,_p as aC,Rl as aD,Yp as aE,_s as aF,Ns as aG,ks as aH,qp as aI,hi as aJ,si as aK,$i as aL,va as aM,sa as aN,oa as aO,Pa as aP,Io as aQ,Eo as aR,Ls as aS,Kt as aT,tl as aU,ma as aV,_a as aW,Nn as aX,Hn as aY,pi as aZ,is as a_,Ui as aa,Gi as ab,Hi as ac,Li as ad,za as ae,ms as af,ns as ag,qs as ah,qo as ai,Xs as aj,ba as ak,mp as al,Qs as am,fs as an,ar as ao,sr as ap,or as aq,nn as ar,Ce as as,lu as at,z_ as au,fo as av,qa as aw,Gt as ax,Vt as ay,Cr as az,B as b,Yi as b$,W as b0,Bp as b1,Wp as b2,kr as b3,Mr as b4,Sr as b5,vr as b6,ja as b7,_r as b8,gr as b9,Ed as bA,wi as bB,li as bC,ei as bD,ci as bE,Lo as bF,Si as bG,mn as bH,$u as bI,Su as bJ,uh as bK,gd as bL,Ai as bM,Ii as bN,En as bO,Cn as bP,Eh as bQ,ro as bR,Hh as bS,Ih as bT,tn as bU,Qi as bV,Ji as bW,Sa as bX,hp as bY,Xi as bZ,Zi as b_,br as ba,On as bb,pr as bc,fr as bd,mr as be,xr as bf,gl as bg,dl as bh,ml as bi,ul as bj,bl as bk,sl as bl,hl as bm,Ho as bn,_o as bo,xa as bp,cu as bq,Fd as br,im as bs,Pd as bt,Vs as bu,X_ as bv,nm as bw,op as bx,ti as by,Od as bz,he as c,co as c$,Ki as c0,$a as c1,Kp as c2,jp as c3,rn as c4,Kr as c5,lr as c6,De as c7,Fe as c8,Ae as c9,bd as cA,$d as cB,Md as cC,Rh as cD,Mh as cE,kh as cF,_h as cG,yn as cH,dh as cI,Nh as cJ,gn as cK,Bu as cL,_u as cM,xh as cN,$h as cO,mh as cP,dd as cQ,Oi as cR,Di as cS,mu as cT,Ci as cU,Ma as cV,zi as cW,Pi as cX,so as cY,cl as cZ,vl as c_,Bl as ca,Kl as cb,Vi as cc,nc as cd,jl as ce,El as cf,Yl as cg,Wl as ch,Ul as ci,ec as cj,tc as ck,Ql as cl,ic as cm,zl as cn,Dn as co,An as cp,ii as cq,J_ as cr,hu as cs,Z_ as ct,nl as cu,ys as cv,Vo as cw,Ko as cx,Mu as cy,Uh as cz,Ud as d,ri as d0,Fa as d1,ou as d2,Yd as d3,eu as d4,Zd as d5,Dm as d6,pe as e,Ku as f,Yu as g,ui as h,_e as i,Tr as j,fi as k,su as l,Ol as m,Nt as n,yi as o,gi as p,Lh as q,Xu as r,qt as s,au as t,qh as u,uu as v,Tu as w,Jt as x,wn as y,Pn as z}; +
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Am.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_furnCategory:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Am.ZOOM_MAX=8,Am.ZOOM_MIN=1/3,Am.styles=[Gd,qp],customElements.get("houseplan-card")||customElements.define("houseplan-card",Am),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${lm} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{zo as $,U as A,Qt as B,Ia as C,le as D,V as E,Zt as F,Ha as G,Ad as H,Ld as I,fs as J,lo as K,Hd as L,Dd as M,Ho as N,vs as O,Od as P,js as Q,Us as R,vo as S,oi as T,ao as U,xo as V,op as W,sp as X,ss as Y,Br as Z,Oo as _,o as a,ts as a$,no as a0,oo as a1,qr as a2,mp as a3,Ra as a4,Fo as a5,Ea as a6,Ao as a7,Ca as a8,Da as a9,ea as aA,Ol as aB,fp as aC,Cl as aD,Xp as aE,_s as aF,Hs as aG,ks as aH,jp as aI,hi as aJ,si as aK,$i as aL,va as aM,sa as aN,oa as aO,Pa as aP,Io as aQ,Eo as aR,Ls as aS,Kt as aT,tl as aU,ma as aV,_a as aW,Hn as aX,Nn as aY,pi as aZ,is as a_,Vi as aa,Ui as ab,Ni as ac,Li as ad,Oa as ae,ms as af,ns as ag,qs as ah,qo as ai,Xs as aj,ba as ak,gp as al,Qs as am,gs as an,ar as ao,sr as ap,or as aq,nn as ar,Ce as as,hu as at,F_ as au,go as av,qa as aw,Ut as ax,Gt as ay,Cr as az,j as b,Yi as b$,B as b0,Vp as b1,Up as b2,kr as b3,Mr as b4,Sr as b5,vr as b6,Wa as b7,_r as b8,fr as b9,Nd as bA,wi as bB,li as bC,ei as bD,ci as bE,Lo as bF,Si as bG,mn as bH,Mu as bI,Ru as bJ,_h as bK,vd as bL,zi as bM,Ii as bN,En as bO,Cn as bP,Nh as bQ,ro as bR,qh as bS,Hh as bT,tn as bU,Qi as bV,Ji as bW,Sa as bX,up as bY,Xi as bZ,Zi as b_,br as ba,An as bb,pr as bc,gr as bd,mr as be,xr as bf,vl as bg,pl as bh,gl as bi,_l as bj,kl as bk,sl as bl,ul as bm,No as bn,_o as bo,xa as bp,du as bq,Ed as br,rm as bs,Id as bt,Gs as bu,Q_ as bv,om as bw,ap as bx,ti as by,Pd as bz,he as c,ri as c$,Ki as c0,$a as c1,Zp as c2,Bp as c3,rn as c4,Kr as c5,lr as c6,De as c7,Fe as c8,ze as c9,kd as cA,Md as cB,Td as cC,Ch as cD,Th as cE,$h as cF,fh as cG,yn as cH,ph as cI,Lh as cJ,fn as cK,Vu as cL,fu as cM,Sh as cN,Mh as cO,gh as cP,pd as cQ,Ai as cR,Di as cS,gu as cT,Ci as cU,Ma as cV,Oi as cW,Pi as cX,so as cY,bl as cZ,co as c_,Vl as ca,Zl as cb,Gi as cc,oc as cd,Bl as ce,Nl as cf,Xl as cg,Ul as ch,Gl as ci,ic as cj,nc as ck,tc as cl,rc as cm,Fl as cn,Dn as co,zn as cp,ii as cq,em as cr,uu as cs,J_ as ct,nl as cu,ys as cv,Go as cw,Ko as cx,Tu as cy,Gh as cz,Gd as d,Fa as d0,au as d1,Xd as d2,iu as d3,Jd as d4,Am as d5,pe as e,Zu as f,Xu as g,ui as h,_e as i,Tr as j,gi as k,lu as l,Pl as m,Ht as n,yi as o,fi as p,Wh as q,Qu as r,qt as s,cu as t,jh as u,_u as v,Du as w,Jt as x,wn as y,Pn as z}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js b/dist/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js similarity index 74% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js rename to dist/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js index 2615950d..8fad2f54 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-B7Z35HO6.js +++ b/dist/houseplan-assets/houseplan-editor-runtime-BD-85n_Z.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as l,m as h,d as c,l as d,t as p,w as _,f as u,g as m,v as g,r as f,h as b,j as y,p as v,k as $,n as w,o as k,q as x,u as S,x as M,y as D,z as C,B as P,G as I,C as T,F as R,H as F,I as N,J as z,K as A,L as O,M as L,N as E,O as B,P as H,Q as G,R as W,S as j,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as _t,ae as ut,af as mt,ag as gt,ah as ft,ai as bt,aj as yt,ak as vt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as Ft,ay as Nt,az as zt,aA as At,aB as Ot,aC as Lt,aD as Et,aE as Bt,aF as Ht,aG as Gt,aH as Wt,aI as jt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as _e,b4 as ue,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ye,ba as ve,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as Fe,bo as Ne,bp as ze,bq as Ae,br as Oe,bs as Le,bt as Ee,bu as Be,bv as He,bw as Ge,bx as We,by as je,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as _s,bW as us,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as ys,c0 as vs,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as Fs,ce as Ns,cf as zs,cg as As,ch as Os,ci as Ls,cj as Es,ck as Bs,cl as Hs,cm as Gs,cn as Ws,co as js,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as _i,cM as ui,cN as mi,cO as gi,cP as fi,cQ as bi,cR as yi,cS as vi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti}from"./houseplan-card-DJctcs0x.js";import{i as Ri,c as Fi,a as Ni,s as zi,t as Ai}from"./space-deletion-BIiYcPJz.js";const Oi={},Li=t(class extends e{constructor(){super(...arguments),this.ot=Oi}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Ei=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Bi(t){return Number.isFinite(t)?(t%360+360)%360:0}function Hi(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Gi(t){const e=Hi(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function Wi(t){const e=Ei(t.r,0,255)/255,s=Ei(t.g,0,255)/255,i=Ei(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Bi(n),s:0===o?0:a/o*100,v:100*o}}function ji(t){return function(t){const e=t=>Math.round(Ei(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Bi(t.h),s=Ei(t.s,0,100)/100,i=Ei(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function qi(t){const e=t.visualViewport;return e&&Number.isFinite(e.width)&&Number.isFinite(e.height)?{left:Number(e.offsetLeft)||0,top:Number(e.offsetTop)||0,width:Math.max(0,Number(e.width)||0),height:Math.max(0,Number(e.height)||0)}:{left:0,top:0,width:t.innerWidth,height:t.innerHeight}}function Ui(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),l=Math.max(n,s.top+s.height-o),h=Math.max(0,r-a),c=Math.max(0,l-n),d=Math.min(Math.max(0,e.width),h),p=Math.min(Math.max(0,e.height),c);let _=t.left;_+d>r&&(_=t.right-d),_=Math.min(Math.max(a,_),Math.max(a,r-d));const u=t.bottom+i,m=t.top-i-p;let g="bottom",f=u;return u+p>l&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,l-p))):f=Math.min(Math.max(n,u),Math.max(n,l-p)),{left:Math.round(_),top:Math.round(f),side:g,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class Ki{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(t=!1){const e=this.window();return!t&&!!e&&function(t){const e=t.HTMLElement?.prototype;return"function"==typeof e?.showPopover&&"function"==typeof e?.hidePopover}(e)}get hasFallback(){return!!this.host}containsPath(t){return t.includes(this.owner)||!!this.host&&t.includes(this.host)}ownsActiveElement(){let t=this.owner.ownerDocument.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return!(!t||t!==this.owner&&!this.owner.shadowRoot?.contains(t)&&!this.root?.contains(t))}renderFallback(t,e){const s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const t=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!t)return null;const e=this.owner.ownerDocument.createElement("div");return e.dataset.hpOverlay=this.name,e.style.display="contents",this.keydown&&e.addEventListener("keydown",this.keydown,!0),t.append(e),this.host=e,this.root=e.attachShadow({mode:"open"}),this.root}}const Vi={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ji extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Vi,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Ki(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Hi(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Ji.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=qi(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=Ui(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Vi;return{title:t.title||Vi.title,hue:t.hue||Vi.hue,saturation:t.saturation||Vi.saturation,value:t.value||Vi.value,hex:t.hex||Vi.hex,invalidHex:t.invalidHex||Vi.invalidHex}}_syncFromColor(t,e){const s=Hi(l(t,"#607d8b"))||"#607d8b",i=Gi(s);if(!i)return;const o=Wi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(ji({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Hi(e),i=s?Gi(s):null;if(!s||!i)return;const o=Wi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Hi(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Hi(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=ji({h:this._hue,s:this._saturation,v:this._value}),i=ji({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),l=Math.round(this._saturation),h=Math.round(this._value);return o` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";import{e as t,i as e,E as s,D as i,b as o,A as a,a as n,c as r,s as h,m as l,d as c,l as d,t as p,w as _,f as u,g as m,v as g,r as f,h as b,j as v,p as y,k as $,n as w,o as k,q as x,u as M,x as S,y as C,z as D,B as P,G as I,C as T,F as R,H as z,I as F,J as A,K as N,L as H,M as O,N as L,O as E,P as B,Q as W,R as G,S as j,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as ht,a9 as lt,aa as ct,ab as dt,ac as pt,ad as _t,ae as ut,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as Mt,aq as St,ar as Ct,as as Dt,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as Ft,az as At,aA as Nt,aB as Ht,aC as Ot,aD as Lt,aE as Et,aF as Bt,aG as Wt,aH as Gt,aI as jt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as he,a$ as le,b0 as ce,b1 as de,b2 as pe,b3 as _e,b4 as ue,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Me,bg as Se,bh as Ce,bi as De,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Fe,bp as Ae,bq as Ne,br as He,bs as Oe,bt as Le,bu as Ee,bv as Be,bw as We,bx as Ge,by as je,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as hs,bR as ls,bS as cs,bT as ds,bU as ps,bV as _s,bW as us,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ms,c6 as Ss,c7 as Cs,c8 as Ds,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as Fs,cf as As,cg as Ns,ch as Hs,ci as Os,cj as Ls,ck as Es,cl as Bs,cm as Ws,cn as Gs,co as js,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as hi,cH as li,cI as ci,cJ as di,cK as pi,cL as _i,cM as ui,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Mi,cY as Si,cZ as Ci,c_ as Di,c$ as Pi,d0 as Ii}from"./houseplan-card-VbP9hCtk.js";import{i as Ti,c as Ri,a as zi,s as Fi,t as Ai}from"./space-deletion-Dh9IGz13.js";const Ni={},Hi=t(class extends e{constructor(){super(...arguments),this.ot=Ni}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),Oi=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Li(t){return Number.isFinite(t)?(t%360+360)%360:0}function Ei(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Bi(t){const e=Ei(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function Wi(t){const e=Oi(t.r,0,255)/255,s=Oi(t.g,0,255)/255,i=Oi(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Li(n),s:0===o?0:a/o*100,v:100*o}}function Gi(t){return function(t){const e=t=>Math.round(Oi(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Li(t.h),s=Oi(t.s,0,100)/100,i=Oi(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}function ji(t){const e=t.visualViewport;return e&&Number.isFinite(e.width)&&Number.isFinite(e.height)?{left:Number(e.offsetLeft)||0,top:Number(e.offsetTop)||0,width:Math.max(0,Number(e.width)||0),height:Math.max(0,Number(e.height)||0)}:{left:0,top:0,width:t.innerWidth,height:t.innerHeight}}function qi(t,e,s,i=7,o=8){const a=s.left+o,n=s.top+o,r=Math.max(a,s.left+s.width-o),h=Math.max(n,s.top+s.height-o),l=Math.max(0,r-a),c=Math.max(0,h-n),d=Math.min(Math.max(0,e.width),l),p=Math.min(Math.max(0,e.height),c);let _=t.left;_+d>r&&(_=t.right-d),_=Math.min(Math.max(a,_),Math.max(a,r-d));const u=t.bottom+i,m=t.top-i-p;let g="bottom",f=u;return u+p>h&&m>=n?(g="top",f=Math.min(Math.max(n,m),Math.max(n,h-p))):f=Math.min(Math.max(n,u),Math.max(n,h-p)),{left:Math.round(_),top:Math.round(f),side:g,maxWidth:Math.round(l),maxHeight:Math.round(c)}}class Ui{constructor(t,e,s){this.owner=t,this.name=e,this.keydown=s,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(t=!1){const e=this.window();return!t&&!!e&&function(t){const e=t.HTMLElement?.prototype;return"function"==typeof e?.showPopover&&"function"==typeof e?.hidePopover}(e)}get hasFallback(){return!!this.host}containsPath(t){return t.includes(this.owner)||!!this.host&&t.includes(this.host)}ownsActiveElement(){let t=this.owner.ownerDocument.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return!(!t||t!==this.owner&&!this.owner.shadowRoot?.contains(t)&&!this.root?.contains(t))}renderFallback(t,e){const s=this.ensureFallback();return s?(i(o`${t}`,s),s):null}surface(t,e){return e?this.owner.shadowRoot?.querySelector(t)||null:this.root?.querySelector(t)||null}destroy(){this.root&&i(a,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const t=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!t)return null;const e=this.owner.ownerDocument.createElement("div");return e.dataset.hpOverlay=this.name,e.style.display="contents",this.keydown&&e.addEventListener("keydown",this.keydown,!0),t.append(e),this.host=e,this.root=e.attachShadow({mode:"open"}),this.root}}const Ki={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Vi extends r{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=Ki,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Ui(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Ei(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Vi.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=ji(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=qi(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||Ki;return{title:t.title||Ki.title,hue:t.hue||Ki.hue,saturation:t.saturation||Ki.saturation,value:t.value||Ki.value,hex:t.hex||Ki.hex,invalidHex:t.invalidHex||Ki.invalidHex}}_syncFromColor(t,e){const s=Ei(h(t,"#607d8b"))||"#607d8b",i=Bi(s);if(!i)return;const o=Wi(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Gi({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Ei(e),i=s?Bi(s):null;if(!s||!i)return;const o=Wi(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Ei(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Ei(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=Gi({h:this._hue,s:this._saturation,v:this._value}),i=Gi({h:this._hue,s:100,v:100}),n=this._labels(),r=Math.round(this._hue),h=Math.round(this._saturation),l=Math.round(this._value);return o` `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=l(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return o` +
`}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=h(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return o` ${this.label?o`${this.label}`:a} ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):a} - `}}Ji.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Ji.styles=n` + `}}Vi.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Vi.styles=n` :host { position: relative; display: inline-flex; @@ -322,9 +322,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d box-shadow: none; } } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",Ji);let Yi=0;class Xi extends r{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++Yi,this._surfacePointerEnter=()=>{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=this._floating.containsPath(t.composedPath()),s=t.target,i=this._dialog(),o=s instanceof Node&&(s===i||s instanceof Element&&s.contains(this));var a;this._open&&(a=o,!e&&a)&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{if(!this._open||t.defaultPrevented)return;if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const e=this.renderRoot.querySelector(".trigger");if(!e||!t.composedPath().includes(e))return;const s=this._surface();if(!s||s.scrollHeight<=s.clientHeight)return;const i=Math.max(40,.8*s.clientHeight),o={ArrowDown:40,ArrowUp:-40,PageDown:i,PageUp:-i};if("Home"===t.key)s.scrollTop=0;else if("End"===t.key)s.scrollTop=s.scrollHeight;else{if(!(t.key in o))return;s.scrollBy({top:o[t.key],behavior:"auto"})}t.preventDefault(),t.stopImmediatePropagation()},this._floating=new Ki(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const t=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("pointerHover")&&!this.pointerHover){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return t=this.text,e=this.ariaLabel,"string"==typeof t&&t.trim().length>0&&"string"==typeof e&&e.trim().length>0;var t,e}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();this.pointerHover&&t&&!this._open&&!this._openTimer&&(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){this.pointerHover&&"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const t=this._window();t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const t=this._window();t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".tooltip");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return o`
{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=this._floating.containsPath(t.composedPath()),s=t.target,i=this._dialog(),o=s instanceof Node&&(s===i||s instanceof Element&&s.contains(this));var a;this._open&&(a=o,!e&&a)&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{if(!this._open||t.defaultPrevented)return;if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const e=this.renderRoot.querySelector(".trigger");if(!e||!t.composedPath().includes(e))return;const s=this._surface();if(!s||s.scrollHeight<=s.clientHeight)return;const i=Math.max(40,.8*s.clientHeight),o={ArrowDown:40,ArrowUp:-40,PageDown:i,PageUp:-i};if("Home"===t.key)s.scrollTop=0;else if("End"===t.key)s.scrollTop=s.scrollHeight;else{if(!(t.key in o))return;s.scrollBy({top:o[t.key],behavior:"auto"})}t.preventDefault(),t.stopImmediatePropagation()},this._floating=new Ui(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const t=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("pointerHover")&&!this.pointerHover){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return t=this.text,e=this.ariaLabel,"string"==typeof t&&t.trim().length>0&&"string"==typeof e&&e.trim().length>0;var t,e}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();this.pointerHover&&t&&!this._open&&!this._openTimer&&(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){this.pointerHover&&"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const t=this._window();t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const t=this._window();t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".tooltip");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return o``}_renderFallback(){const t=Xi.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=qi(t);if(s.style.maxWidth=`${Math.max(0,Math.min(320,i.width-16))}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._usesPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=Ui(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}render(){if(!this._hasContent())return a;const t=this.ariaLabel.trim();return o` + @pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}
`}_renderFallback(){const t=Yi.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=ji(t);if(s.style.maxWidth=`${Math.max(0,Math.min(320,i.width-16))}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._usesPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=qi(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}render(){if(!this._hasContent())return a;const t=this.ariaLabel.trim();return o` ${this.text} ${this._usesPopover()?this._tooltipTemplate(!0):a} - `}}Xi.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Xi.styles=n` + `}}Yi.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Yi.styles=n` :host { display: none; flex: none; @@ -461,7 +461,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d transition: none; } } - `,customElements.get("hp-help")||customElements.define("hp-help",Xi);const Zi=new WeakMap;function Qi(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Zi.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Zi.set(e,s)),s}function to(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function eo(t,e){const s=Qi(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,to(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,to(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,to(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function so(t){const e=Qi(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function io(t,e,s=so(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function oo(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:io(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function ao(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function no(t,e,s,i=so(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return oo(t,o.platform||n,"entity-platform",i,a)}class ro extends r{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=eo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return d(this.hass)}_t(t,e){return p(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=so(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=no(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return oo(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return ao(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(no(t,e,s,i));return h.some(Boolean)?ao(h):ao((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return oo(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,so(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=no(this.hass,t,this.registry,so(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return a;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=_(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),n="none"!==t.pulse.kind,r=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let l=-r/2,h=r/2,c=-r/2,d=r/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(h=Math.max(h,i+t)),"left"===s.valueBadge.position&&(l=Math.min(l,-i-t)),"top"===s.valueBadge.position&&(c=Math.min(c,-i-e)),"bottom"===s.valueBadge.position&&(d=Math.max(d,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);d=Math.max(d,t)}const p=Math.min(1,2.35/Math.max(1,h-l),2.35/Math.max(1,d-c)),b=Math.round(100*p),y=[`left:calc(50% - ${54*((l+h)/2)*p}px)`,`top:calc(50% - ${54*((c+d)/2)*p}px)`,p<1?`transform:scale(${p})`:""].filter(Boolean).join(";"),v=["dev",u(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),$=m(s).join(";"),w=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),k=[this.deviceName,this._providerSummary(i),this._stateSummary(t),w,t.valueFullText||"",g(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return o`
+ `,customElements.get("hp-help")||customElements.define("hp-help",Yi);const Xi=new WeakMap;function Zi(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Xi.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Xi.set(e,s)),s}function Qi(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function to(t,e){const s=Zi(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Qi(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Qi(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,Qi(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function eo(t){const e=Zi(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function so(t,e,s=eo(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function io(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:so(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function oo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function ao(t,e,s,i=eo(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return io(t,o.platform||n,"entity-platform",i,a)}class no extends r{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=to(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return d(this.hass)}_t(t,e){return p(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=eo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=ao(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const h=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return io(t,s?.domain,"registry-owner",i,e)});if(h.some(Boolean))return oo(h);const l=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&l.push(ao(t,e,s,i));return l.some(Boolean)?oo(l):oo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return io(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,eo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=ao(this.hass,t,this.registry,eo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return a;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=_(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),n="none"!==t.pulse.kind,r=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let h=-r/2,l=r/2,c=-r/2,d=r/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(l=Math.max(l,i+t)),"left"===s.valueBadge.position&&(h=Math.min(h,-i-t)),"top"===s.valueBadge.position&&(c=Math.min(c,-i-e)),"bottom"===s.valueBadge.position&&(d=Math.max(d,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);d=Math.max(d,t)}const p=Math.min(1,2.35/Math.max(1,l-h),2.35/Math.max(1,d-c)),b=Math.round(100*p),v=[`left:calc(50% - ${54*((h+l)/2)*p}px)`,`top:calc(50% - ${54*((c+d)/2)*p}px)`,p<1?`transform:scale(${p})`:""].filter(Boolean).join(";"),y=["dev",u(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),$=m(s).join(";"),w=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),k=[this.deviceName,this._providerSummary(i),this._stateSummary(t),w,t.valueFullText||"",g(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return o`
${this._t("marker.preview.title")} @@ -470,8 +470,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d
`}}ro.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ro.styles=[c,n` +
`}}no.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},no.styles=[c,n` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -581,7 +581,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",ro);const lo=(t,e)=>[t[0]-e[0],t[1]-e[1]],ho=(t,e)=>[t[0]+e[0],t[1]+e[1]],co=(t,e)=>t[0]*e[0]+t[1]*e[1],po=t=>Math.hypot(t[0],t[1]);function _o(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function go(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(u),f(m))}}return i}function bo(t,e,s){if($(t,e,s))return!0;if(v(t,e,s)||v(e,t,s))return!1;let i=0;try{const s=w([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=k(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function yo(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function vo(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function $o(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function wo(t,e,s,i,o){return $o(t,s,o)&&$o(e,i,o)||$o(t,i,o)&&$o(e,s,o)}function ko(t,e,s,i,o){const a=vo(t,e,o);return a&&vo(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function xo(t,e,s,i){const o=t.length,a=vo(t[(e-1+o)%o],t[e],i),n=vo(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function So(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function Mo(t,e,s){const i=So(t),o=So(e);return i[2]o}const Co=(t,e)=>"h"===e?t[0]:t[1],Po=(t,e)=>"h"===e?t[1]:t[0];function Io(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=vo(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=Co(h,l),p=Co(c,l),_=Po(h,l),u=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(Po(r,e)-s)>i)continue;const l=Co(n,e),h=Co(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,_,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(u,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:_,fixed:d,moving:p,intervals:u,baseline:m}}function To(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(vo(n,r,s)!==t.axis||Math.abs(Po(n,t.axis)-t.line)>s||Math.abs(Po(r,t.axis)-t.line)>s)continue;const l=Co(n,t.axis),h=Co(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function Ro(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function Fo(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(vo(o,a,s)!==i.axis)return!1;const n=Co(0===i.movedEndpoint?o:a,i.axis),r=To(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),_=Math.min(i.fixed,i.moving),u=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1_-s&&am-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function No(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?ho(t,o):[...t]),a.movedSpans[e]=[[ho(i.poly[n],o),ho(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function zo(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(uo([t.x,t.y],e,s)>2*i)return!1;const o=po(lo(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(uo([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return po(lo([t.x,t.y],l))>=t.length/2+n-r}function Ao(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?uo(t.center,e,s)2)return!1;const r=No(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!Fo(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!go(h))return!1;const d=l.poly.length,p=(c-1+d)%d,_=[p,c,(c+1)%d];for(const t of _)if(y(h[t],h[(t+1)%d]))return!1;const u=_o(l.poly),m=_o(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!Mo(s.poly,o.poly,a)||!Mo(i,t,a))if(v(s.poly,o.poly,a)){if(!v(i,t,a))return!1}else if(v(o.poly,s.poly,a)){if(!v(t,i,a))return!1}else if(v(i,t,a)||v(t,i,a)||bo(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],_=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(Ao(t,p,_,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!yo({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const Lo=new WeakMap,Eo=t=>"number"==typeof t&&Number.isFinite(t),Bo=(t,e)=>{const s=new Map;for(const i of t||[])!Eo(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Ho=t=>[String(t),"см"].join(" ");function Go(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=Bo(t,i),a=Bo(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Ho(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Ho(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Ho(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Wo{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=Lo.get(s);h&&h.opts===a||(h={opts:a,values:new Map},Lo.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=Oo(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=No(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Go(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if(Oo(e.rooms,e.openings,e.plan,e.delta,e.options))if(Go(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;return!s||void 0!==e&&s.pointerId!==e?{kind:"no-op"}:(this._session=null,this._eligibility=null,{kind:"cancelled",restoreWallUnion:t===s.snapshotIdentity?s.wallUnionBefore:null})}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const jo=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function qo(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=jo(r,o),p=jo(t.gearCenter,o),_=Math.max(34,7.2*i.length+12),u=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,_/2+4):Math.max(28,13),m=d[0]+l[0]*u,g=d[1]+l[1]*u,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=_,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const y=l[0]*u+c[0]*b,v=l[1]*u+c[1]*b,$=Math.hypot(y,v)||1,w=v/$*12,k=[r[0]+y/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:y,offsetYPx:v,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const Uo=1e-9,Ko=1e7,Vo=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*Ko+.5)/Ko;return Object.is(e,-0)?0:e},Jo=t=>`${Vo(t[0])},${Vo(t[1])}`,Yo=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Xo=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),Zo=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),Qo=t=>(t||[]).filter(t=>Zo(t?.a)&&Zo(t?.b)&&Yo(t.a,t.b)>Uo);function ta(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of Qo(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(Jo(t))||[];e.push(Xo(t,s)),i.set(Jo(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tUo&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,sa=(t,e,s=1)=>{const i=Math.abs(ea(t)-ea(e));return Math.min(i,180-i)<=s},ia=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(Jo(t));i?i.push(s):e.set(Jo(t),[s])}return e};function oa(t,e,s){const i=Qo(e),o=s??ia(i),a=new Set([t]);let n=Yo(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(Jo(e))||[])a.has(s)||sa(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=Yo(s.a,s.b),r.push(s.a,s.b))}return n}function aa(t,e,s,{minLengthCm:i=20}={}){const o=[],a=Qo(t),n=ia(a);for(const t of a){const r=oa(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=Uo?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function ra(t,e,s,{minDistanceCm:i=5}={}){const o=Qo(t),a=new Map;for(const t of o)a.set(Jo(t.a),t.a),a.set(Jo(t.b),t.b);const n=((t,e,s)=>t/(e||1)*s)(i,e,s),r=n>Uo?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,_]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=Yo(o,_);a<=2e-7||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return lt.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(S(e,t,l)&&!h.has(i))continue;const o=ha.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(S(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,_=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${_?` · ${_}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const da={kind:"active",enabledEntityIds:[],allEntityIds:[]};function pa(t){return t.startsWith("entity:")?"entity":"device"}function _a(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const ua=.001;function ma(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function ga(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function fa(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=ba(t.a,t.b);return{a:e,b:s,key:`hidden|${va(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${ya(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function xa(t){const e=t.epsilon??ua,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!ma(e.a)||!ma(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of M(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1wa(s,t,e)))continue;const t=ga(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function Sa(t,e,s){return e.some(e=>ma(e)&&fa(t,e,s))}function Ma(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function Da(t,e,s){const i=s.epsilon??ua,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>ma(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(Sa(t.point,o,i))continue;const e=ga(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function Ca(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function Pa(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function Ia(t,e,s){if(!(ma(e)&&ma(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=Da(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=Ca(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||Sa(d,s.excludePoints||[],i)||!Ma(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const Ta=.001;function Ra(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function za(t,e,s,i){return t*i-e*s}function Aa(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function Ha(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function Ga(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function Wa(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function ja(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=Wa(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function qa(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=za(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=za(h,c,r,l)/d,e=za(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(za(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=ja(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=ja(s,e,o);null!=t&&i.push(t)}}}function Ua(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function Ka(t,e){return t.minY-e.minY||t.index-e.index}function Va(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Ja(t){const e=t.right;return t.right=e.left,e.left=Va(t),Va(e)}function Ya(t){const e=t.left;return t.left=e.right,e.right=Va(t),Va(e)}function Xa(t,e){return t?(Ka(e,t.item)<0?(t.left=Xa(t.left,e),t.left.priority0)t.right=Za(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&Qa(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&Qa(t.right,e,s,i))}function tn(t,e=.001){const s=Number.isFinite(e)&&e>0?e:Ta,i=t.filter(t=>Na(t.a)&&Na(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)qa(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=Xa(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function en(t,e=.001){const s=Number.isFinite(e)&&e>0?e:Ta,i=tn(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=La(t.a,s),i=La(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[La(t.a,s),La(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,_=a,u=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,_);if(l.has(s)){u=p===t&&_===a;break}l.add(s),e.push(p),d.push(Ea(p,_));const i=r.get(_)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=_,_=i[(o-1+i.length)%i.length],p===t&&_===a){u=!0;break}}if(!u||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=Aa(m);if(!(g>s*s))continue;const f=Ba(Ha(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const y={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,y)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function sn(t,e,s=.001){if(!Na(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function on(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function an(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function nn(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(an(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&on(e.a,t.a,t.b)<=s&&on(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:an(e.angle,s)?on([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function rn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const ln=t=>!t.key.startsWith("static:room|");function hn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function cn(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function dn(t,e,s){if(ln(t.source)!==ln(e.source))return ln(t.source)?t:e;const i=cn(t.point,s),o=cn(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function pn(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function _n(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function un(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=dn(n,r,e.gridStep||0),c=h===n?r:n;if(!ln(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(pn(d),d)}for(const a of o)if(ln(a.source))for(const o of t){if(o===a.source)continue;const t=hn(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(pn(n),n)}const a=[];for(const o of i.values()){const i=en(_n(t,o),s),n=e.point?sn(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||pn(t.proposal).localeCompare(pn(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${pn(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const mn=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class gn{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,a=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return o`
-
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(mn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=mn(t,t=>t.classList.contains("editor-secondary")),i=mn(t,t=>!!t.dataset.editorGroup),o=mn(t,t=>!!t.dataset.editorPalette),a=mn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const fn=t=>t.stopPropagation(),bn=1e-6*I,yn=6*I;function vn(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const $n=(t,e,s,i)=>Math.hypot(s-t,i-e),wn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),kn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,_=Math.max($n(t+h,e+c,o+d,a+p),$n(t-h,e-c,o-d,a-p)),u=Math.max($n(t+h,e+c,o-d,a-p),$n(t-h,e-c,o+d,a+p));return Math.min(_,u)},xn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Sn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=bn&&c++},_=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},u={};let m=5;for(const t of s){const e=xn(t);null!=t?.id&&(u[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>bn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,l=s)};for(const t of s){const e=xn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[vn(e[0]),vn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),_(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=vn(e+o),r=vn(s+a),l=vn(e),h=vn(s),c=Math.max(I,n-l),d=Math.max(I,r-h);t=wn(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[vn(e[0]),vn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)_(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[vn(o[0]),vn(o[1])],l=[vn(n[0]),vn(n[1])];let h=Math.max($n(o[0],o[1],r[0],r[1]),$n(n[0],n[1],l[0],l[1]));const c=$n(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-bn&&i+e/2<=c+bn});c>bn&&d?(i.a=r,i.b=l,_(o,r),_(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[vn(t[0]),vn(t[1])],n=$n(t[0],t[1],o[0],o[1]);i.center=o,_(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[vn(e[0]),vn(e[1])],a=[vn(s[0]),vn(s[1])];t=Math.max($n(e[0],e[1],o[0],o[1]),$n(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],_(e,o),_(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=vn(e),r=vn(s);if(null!=i.w&&null!=i.h){const l=vn(e+o),h=vn(s+a),c=Math.max(I,l-n),d=Math.max(I,h-r);t=wn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=$n(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=bn)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),_=!(Number.isFinite(p)&&p===d),u=kn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,_&&h++,g(u,e,s,_);continue}const o=P([i.x,i.y],t.rooms||[],yn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=kn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=vn(s.x),n=vn(s.y),r=$n(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,u[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function Mn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Dn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Cn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function Pn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Cn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function In(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Dn(i.a)&&Dn(i.b)&&(Cn(t,i.a)<=o||Cn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Cn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function Rn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Dn(t.a)&&Dn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tCn(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/_:0,m=_>0?(d[1]-c[1])/_:0,g=t=>_>0?((t[0]-c[0])*u+(t[1]-c[1])*m)/_:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function Fn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Mn(t.t,n)},a++;const r=F(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,N(s,r,i.coordScale))}return a}function Nn(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const zn=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),An=(t,e)=>t[0]-e[0]||t[1]-e[1],On=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],Ln=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,En=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=Ln(s,t,l,h),p=Ln(i,t,l,h),_=Math.max(0,Math.min(d,p)),u=Math.min(r,Math.max(d,p));return u-_>o?{lo:_,hi:u}:null},Bn=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Hn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Gn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!zn(o))return!0;const a=Ln(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},Wn=(t,e,s)=>{if(t.host){const i=F(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},jn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Un=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Vn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,_=r;const u=z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:A;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const y=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Hn(t))continue;const s=y.get(t.id);if(!(s&&zn(s.a)&&zn(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=An(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const u=r/h,g=l/h,v=z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=v.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=En(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=_.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:F(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=Ln(e.center,a,u,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>qn(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=On(a,u,g,i),l=On(a,u,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],_=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:A)),f=p[0]?.kind,b=[...d.keys()].sort(),y=1===_.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),v={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!En(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:A:0,I=y?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tBn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&O(e,v,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=L(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)Bn(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>Bn(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=L(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)Bn(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=An(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=E(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=B(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const K=new Set(p.map(t=>t.id));K.delete(s.id);const V=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Un(s.id,i,a,K);return K.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...V);const X=Y.map(t=>qn(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=N(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=S.get(t.id),a=V[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=Ln(e.center,n.a,r/h,l/h),p={...N(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!H(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=_.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>Wn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=Wn(t,X,o);e&&st.some(t=>jn(t,e,c))&&(it=!0)}if(it)continue;const at=G(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=W(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,_=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:_,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Jn=t=>JSON.parse(JSON.stringify(t)),Yn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Xn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Zn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Qn=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const tr=1e-9;class er extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const sr=t=>JSON.parse(JSON.stringify(t)),ir=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),or=t=>[Number(t[0]),Number(t[1])],ar=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,nr=(t,e)=>{const s=ar(t),i=ar(e);return sar(t)<=ar(e)?[or(t),or(e)]:[or(e),or(t)],lr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),hr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},dr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},pr=(t,e,s)=>{const i=Math.max(0,Math.min(1,dr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=tr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=dr(s,t,e),c=dr(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},ur=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,_]=r;for(let t=0;t<64;t++){const a=_+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;_=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+_>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},gr=(t,e,s,i,o="")=>{const[a,n]=rr(e,s),r=`${t}|${ar(a)}|${ar(n)}|${[...i].sort().join(",")}${o}`;return`wall-${mr(ur(r)).slice(0,20)}`};let fr=0;const br=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||mr(ur(`${Date.now()}|${fr++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new er("duplicate-id","id factory exhausted")},yr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&ir(s.a)&&ir(s.b)){if(e.has(s.id))throw new er("duplicate-id",s.id);e.set(s.id,s)}return e},vr=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=tr&&Math.abs(e[1]-i[1]-a)<=tr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},$r=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>pr([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(pr(i,t,e)>.02*I)return!1;const o=lr(t,e),a=dr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-tr&&a-n<=o+tr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new er("invalid-room",r);const _=Y(s,r,n,I,1,[...t.walls||[],...l]);if(!_||_.poly.length<3)throw new er("invalid-room",r);const u=Array.isArray(i.wall_ids)?i.wall_ids:[],m=u.length===p.length;let g=null,f=m;if(f)for(let t=0;ttr||Math.abs(i[1]-g[1])>tr)){f=!1;break}g=i}const b=[];for(let t=0;t<_.poly.length;t++){const s=_.poly[t],i=_.poly[(t+1)%_.poly.length];if(lr(s,i)<=tr)throw new er("zero-length",r);const n=nr(s,i);let l=c.get(n);if(!l){const[t,e]=rr(s,i);l={key:n,a:t,b:e,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},c.set(n,l)}if(l.owners.add(r),l.owners.size>2)throw new er("third-owner",n);const d=m?u[_.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>tr?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:or(p[_.parent[t]]),b:or(p[(_.parent[t]+1)%p.length])})}const g=_.parent[t];l.parentKeys.add(K(p[g],p[(g+1)%p.length],I));const y=[(s[0]+i[0])/2,(s[1]+i[1])/2],v=t=>t.some(t=>pr(y,[t[0],t[1]],[t[2],t[3]])<=.04*I);l.zeroWall||=v(a)||v(o)&&!h(s,i),b.push(n)}const y={...i};delete y.wall_ids,d.push({...y,poly:_.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},wr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[nr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new er("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new er("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>tr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||lr(e.a,e.b)-lr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new er("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=pr(o,t.a,t.b)<=tr?0:1,a=pr(o,e.a,e.b)<=tr?0:1;if(s!==a)return s-a;return(pr(i.a,t.a,t.b)<=tr?0:1)-(pr(i.a,e.a,e.b)<=tr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new er("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=rr(e.a,e.b),o=`${String(t.id||"")}|${ar(s)}|${ar(i)}|${[...e.owners].sort().join(",")}`,a=mr(ur(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new er("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=br(h),h.add(t.id)},kr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=dr(s,e.a,e.b),o=lr(e.a,e.b),a=Number(t.length)/2;return i>=-tr&&i<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-tr&&i*o+a<=o+tr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,dr(s,n.a,n.b)))}},xr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=dr(s,e.a,e.b),a=lr(e.a,e.b);return o>=-tr&&o<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-tr&&o*a+i<=a+tr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>pr(s,t.a,t.b)-pr(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,dr(s,a.a,a.b)))}:null},Sr=(t,e,s)=>{const i=yr(t),{atoms:o,rooms:a}=$r(t,i);wr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=K(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!ir(t.a)||!ir(t.b))&&_r(e.a,e.b,t.a,t.b)>=lr(e.a,e.b)-tr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new er("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:K(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=kr(t,e);s&&(t.host=s);continue}const i=kr(t,e)??(s?xr(t,e):null);if(i)t.host=i;else{if(!s)throw new er("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Mr(t,e={}){const s=JSON.stringify(t),i=sr(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Sr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Dr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Cr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Dr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Dr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):sr(e)}return o[e,e in t?s(t[e],i):sr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return sr(e)};return s(t,e)}const Pr=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Ir=(t,e,s)=>Math.min(s,Math.max(e,t)),Tr=t=>ut({spaces:[t]})[0],Rr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of R(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const _=mt(t,n.id,e,s,i,o,a,r);if(_)for(let t=0;t<_.orig.length;t++){const e=[];for(let s=0;s<_.parent.length;s++)_.parent[s]===t&&e.push(s);for(let t=1;t+10&&u>0&&n===m&&u!==n))continue;const g=_.poly[i],f=_.poly[(i+1)%_.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const y=Rr(g,f,r),v=p.get(y);v?v.targets.add(n):p.set(y,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let _=e.slice();const u=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(u(s,e.a)&&u(i,e.b)||u(s,e.b)&&u(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Rr(t.a,t.b,r).localeCompare(Rr(e.a,e.b,r)))){const e=[...t.targets][0],s=_.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;_=_.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(_=E(_,t.a,t.b,e,i,r))}return _}const Nr=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=Zn("space",t);e.layers>0&&Xn("space",e.root)&&Qn(d,e.root,t)}const p=new Map,_=new Map,u=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Zn("room",t);o.layers>0&&Xn("room",o.root)&&Qn(s,o.root,t),_.has(t)||_.set(t,e),u.has(t)||u.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(_.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const y={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},v=new Set,$=t=>c.has(t)?null:(()=>{const e=Zn("space",t).root;if(!Xn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Zn("room",t).root;if(!Xn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,y.roomRefsRemapped++):s?(t.room_id=s.roomId,y.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,y.roomRefsRemapped++):y.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,y.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,y.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,y.spaceRefsRemapped++):(delete t.space,h=null,y.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},y.positionsRemapped++,v.add(e))}for(const t of Object.keys(o)){if(v.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Yn(o,s)||(o[s]={...e,s:i}),delete o[t],y.roomRefsRemapped++,y.positionsRemapped++;continue}}o[t]={...e,s:i},y.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?y.orphanRoomLabelsRemoved++:"group"===t?y.orphanGroupPositionsRemoved++:y.orphanDevicePositionsRemoved++},F=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",u.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(y.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],y.liveMissingPositionsRemoved++,R(a.kind),y.removedPositions.push(a);continue}}else y.unverifiedPositions.push(a);y.positionsUnresolved++,F.add(i)}else delete o[t],R(a.kind),y.removedPositions.push(a)}return y.deadSpaceIds=[...F].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:y,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Pr(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Pr(o,"segments")&&(delete o.segments,e++),Pr(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=lt&&t<=ht&&(Pr(o,"plan_scale_x")||(o.plan_scale_x=t),Pr(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Pr(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Ir(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Pr(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Pr(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Pr(t,"width")){const s=Number(t.width);(Pr(t,"width_cm")||Number.isFinite(s))&&(Pr(t,"width_cm")||(t.width_cm=Number(Ir(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Pr(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Pr(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Ir(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Pr(t,"scale")||Pr(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Pr(t,"entity")||Pr(t,"attr")||Pr(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":_t(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const _=(u=n.spaces||[],JSON.parse(JSON.stringify(u)));var u;const m=Sn(n.spaces||[],a.layout);let g=0,f=0,b=0,y="";const v=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,y=String(t?.id||"")),e.space}),$=g?Sn(v,m.layout):{...m,spaces:v};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Tr(e),r=Tr(s);if(!a||!r)continue;const l=Nr(a.rooms),h=Nr(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:K(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),u=it(e,a.rooms,ot,c);x+=u.length;for(const t of u){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=K(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,l,h,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const y=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...y,...b.map(({key:t,...e})=>e)];const v=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,l,h,I,ot);w=Fr(r.rooms,w,v,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=B(r.rooms,w,v,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,v.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=Rn(s.partitions||[],{pitch:I,geometry:Nn(s)});R.merged&&(M+=R.merged,s.partitions=R.partitions,Fn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const F=Tr(s);if(F){const t=o(s,F,s.walls||[],v,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Mr(t);return Cr(t,e.config),{...e,config:t}}(n).migratedSegments);const F=U(n),N=et($.layout),z=JSON.stringify(F)!==l||JSON.stringify(N)!==h;c<9&&z&&(F.model_version=9);const A=JSON.stringify(F)!==l||JSON.stringify(N)!==h,O=Number.isInteger(Number(F.model_version))?Number(F.model_version):c,L=A?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},E=A?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:F,layout:N,report:{...L,modelFrom:c,modelTo:O,migrated:A?p:0,glowSpacesMigrated:A?d.glowSpaces:0,glowRoomsMigrated:A?d.glowRooms:0,canonicalized:A?T:0,wallSegmentsMigrated:A?R:0,legacyZeroWallsMigrated:A?x:0,wallsMerged:A?k:0,spansMerged:A?S:0,partitionsMerged:A?M:0,partitionsReconciled:A?D:0,openingsRehosted:A?C:0,redundantDraftsRemoved:A?P:0,wallsStraightened:A?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:A?b:0,maxStraightenSpace:A?y:"",latticeCoordinatesCanonicalized:A?r.canonicalized:0,latticeCoordinatesFar:A?r.far:0,latticeMaxShift:A?r.maxShift:0,latticeMaxShiftCm:A?r.maxShiftCm:0,latticeSpaces:A?r.spaces:[],...E},changed:A}}const Ar=(t,e)=>t[0]*e[1]-t[1]*e[0],Or=(t,e)=>t[0]*e[0]+t[1]*e[1],Lr=(t,e)=>[t[0]-e[0],t[1]-e[1]],Er=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Br(t,e,s){return Math.abs(Ar(t,e))<=s}function Hr(t,e,s){return Math.abs(Ar(Lr(t,e),s))}function Gr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Br(l,[i/n,a/n],1e-6)))return[];if(Hr(e.a,t,l)>1.1*Math.max(s,o)||Hr(e.b,t,l)>1.1*Math.max(s,o))return[];return[[Or(Lr(e.a,t),l),Or(Lr(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=bt({a:Er(t,l,e),b:Er(t,l,i),halfDepth:s});return o?[o]:[]})}function Wr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Er([t.x,t.y],s,-i),b:Er([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Gr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Gr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function jr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Er(s.center,a,o),r=[];for(let e=0;ei&&Br(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(Hr(o,n,s.axis)>i||Hr(a,n,s.axis)>i)continue;const c=Or(Lr(o,n),s.axis),d=Or(Lr(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=Er(n,s.axis,-s.half),p=Er(n,s.axis,s.half),_=Math.max(0,-s.half-h),u=Math.max(0,c-s.half),m=_>0?Er(n,s.axis,h):d,g=u>0?Er(n,s.axis,c):p,f=Or(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,_),right:b(p,g,u)}}function qr(t){const e=Er(t.center,t.axis,-t.half),s=Er(t.center,t.axis,t.half),i=Er(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Er(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ur(t,e,s,i){const o=Lr(s,e),a=Or(o,o);if(!(a>i*i))return Math.hypot(...Lr(t,e))<=i;const n=Or(Lr(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Er(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Lr(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function Vr(t,e,s,i,o,a){const n=Lr(i,s),r=Ar(e,n);if(Math.abs(r)<=a)return null;const l=Lr(s,t),h=Ar(l,n)/r,c=Ar(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Jr(t,e,s){const i=qr(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iOr(Lr(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Lr(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&Br(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Hr(a,e.center,e.axis)>s||Hr(n,e.center,e.axis)>s)continue;const h=Or(Lr(a,e.center),e.axis),c=Or(Lr(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||_2)return qr(s);if(i.length){const t=i.map(({room:t,edge:i})=>jr(t,i,s,e.epsilon));if(t.some(t=>!t))return qr(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Jr(t,e,s)}const Xr="1.69.0-beta.1",Zr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Qr={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},tl=2e3,el=1e3,sl=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},il=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},ol=1e3,al=2e3,nl=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},rl="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";class ll{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=sl(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?yt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?yt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=sl(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&vt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=sl(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),l)},this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=nn(z(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>al)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*ol,Number(e.a?.[1])*ol],i=[Number(e.b?.[0])*ol,Number(e.b?.[1])*ol];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/ol,a.interval.a[1]/ol],b:[a.interval.b[0]/ol,a.interval.b[1]/ol],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*ol,i.a[1]*ol],b:[i.b[0]*ol,i.b[1]*ol]};e.host={kind:"partition",id:i.id,t:rn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>al)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=Ra(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,A);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Dt(this.host._settings,{bgColor:null})||null,northDeg:Mt(this.host._settings,{}),bgMode:St(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Ct).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new gn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Wo}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const _=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(_,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&_===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(A,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=Rn(s,{pitch:I,seedIds:e,geometry:Nn(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,Fn(t.openings,t.partitions,i.openingMoves,{coordScale:ol,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=Fa(this.host._path,Ra(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,A)),s=e.length;if((t.partitions||[]).length+s>al)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=al:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(Ft(t[0],e)),Rt(Ft(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Nt(s,t,this.host._gridPitch,Ti):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=W(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=xa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=ka({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ia(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ma(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=s.excludePoints||[],a=Ca(Da(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=C([e[0],e[1]],t);if(r>s.tolerance)continue;const l=Pa(a,e,s.gridStep);!Sa(l,o,i)&&Ma(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?zt(i,l):l;return{point:h,candidate:r&&q(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:At(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=hr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ot(ut({spaces:[t]}),t.id),s=e?Lt(t,e,ol,.02*nt).contour.map(t=>t.map(t=>t/ol)):V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Et(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Bt(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=Mi(this.host._serverCfg),e=Et(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Et(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof er?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ta(a),...aa(a,o,I),...ra(a,o,I)];let r=null;try{r=Ht(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let l="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!l&&r&&r.nodes.length)try{const t=Gt(i.rooms||[],i.walls||[],[],[],I,o,I,1);l="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{l=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Wt(i.rooms||[],e,i.walls||[],[],I,o,I,1,l??void 0,r)}catch{s=null}n.push(...la(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Mr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let l=[];try{l=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(l,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new er("invalid-room",e.spaceId);const s=Mr(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new er("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=$r(s,yr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=R(l),c=R(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;atr||pr(r,s,i)>tr)continue;const l=dr(n,s,i),h=dr(r,s,i);l<-tr||h>1+tr||h<=l+tr||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/ol,t[1]/ol]):[];i.push(...s.map(t=>[t[0],t[1]])),h=cr(t)<=cr(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Cr(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),_=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:_?.before||l,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Mr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Cr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Cr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!go(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>al)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=zt(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),_=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],u=[...p,...null==d?[]:[{cm:d}],...l],m=_.length>=4&&this._samePt(_[0],_[_.length-1]),g=_,f=u;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const y=this._geometrySnapshot(),v=this.host._activeDraftId||t.draft.id,$={...a||n,id:v,points:g.map(t=>[t[0]/ol,t[1]/ol]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=v,this.host._resumeDraftBySpace[this.host._space]=v,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),v),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),y),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=Ra(i,e,this.host._drawWallCm,A);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*ol,Number(t?.[1])*ol]):[],o=Array.isArray(e?.segments)?e.segments:[];return Ra(Math.max(0,t.length-1),s,this.host._drawWallCm,A).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=en(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=un(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Kt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=sn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=un(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=un(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(yt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>vt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/ol,e[1]/ol]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?At(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),nl(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Vt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Vt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*ol-Math.min(...o),Math.min(Xt*ol-Math.max(...o),s[0])),Math.max(-Xt*ol-Math.min(...a),Math.min(Xt*ol-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/ol,o.a[1]/ol],t.b=[o.b[0]/ol,o.b[1]/ol];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=F(t,[o],ol,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,N(t,s,ol))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&vt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/ol,(o.center[1]+e.delta[1])/ol])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),nl(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:At(e.angle),angle:At(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=At(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&vt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*ol,t.a[1]*this.host._spaceH],b:[t.b[0]*ol,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!go(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=vo(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!xo(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Do(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=po(lo(l,r)),d=[],p=new Set;let _=!1,u=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!go(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!xo(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(uo([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),y=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Io(t,e,o,1,a),l=Io(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};y.push(r,l)}const v={roomId:s,edge:i,a:[...r],b:[...l],n:mo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:y};if(!Oo(t,e,v,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>Oo(t,e,v,s,o))){if(!s.some(s=>Fo(No(t,e,v,s),v,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>Oo(t,e,v,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>Oo(t,[],v,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:v}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/ol,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/ol,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*ol,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/ol,i[t][1]/ol],o.b=[i[(t+1)%i.length][0]/ol,i[(t+1)%i.length][1]/ol])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,h,c,this.host._wallKeyPitch,ol,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=p(t);_.set(e,(_.get(e)||0)+1)}const u=[];for(const t of r.walls||[]){const e=p(t),s=_.get(e)||0;s?_.set(e,s-1):u.push(t)}if(Qt(u,d,this.host._wallKeyPitch,ol,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Et([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};il(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));nl(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};il(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),_=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[u,m]=this.host._lastValidStageSize||[_.w,_.h],g={..._,stageWidth:Math.max(1,u),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,y=f?se(b>8?2.5:b,f,_.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,v=Math.max(10,.77*y),$=this.host._t("room.settings_short"),w=v*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),h=ne(r,l),p=qo({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:v});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=le(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,ol);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>he(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
`}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(mn(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=mn(t,t=>t.classList.contains("editor-secondary")),i=mn(t,t=>!!t.dataset.editorGroup),o=mn(t,t=>!!t.dataset.editorPalette),a=mn(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const fn=t=>t.stopPropagation(),bn=1e-6*I,vn=6*I;function yn(t){return Number.isFinite(t)?Math.round(t/I)*I:t}const $n=(t,e,s,i)=>Math.hypot(s-t,i-e),wn=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),kn=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,h=Math.PI/180,l=Math.cos(s*h)*r,c=Math.sin(s*h)*r,d=Math.cos(n*h)*r,p=Math.sin(n*h)*r,_=Math.max($n(t+l,e+c,o+d,a+p),$n(t-l,e-c,o-d,a-p)),u=Math.max($n(t+l,e+c,o-d,a-p),$n(t-l,e-c,o+d,a+p));return Math.min(_,u)},xn=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Mn(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,h="",l=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=bn&&c++},_=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},u={};let m=5;for(const t of s){const e=xn(t);null!=t?.id&&(u[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>bn||i))return;o++,t>n&&(n=t);const a=t*T*e;a>r&&(r=a,h=s)};for(const t of s){const e=xn(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),_(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=yn(e+o),r=yn(s+a),h=yn(e),l=yn(s),c=Math.max(I,n-h),d=Math.max(I,r-l);t=wn(e,s,o,a,h,l,c,d),i.x=h,i.y=l,i.w=c,i.h=d,p(e,h),p(s,l),p(e+o,h+c),p(s+a,l+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[yn(e[0]),yn(e[1])];return t=Math.max(t,$n(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],h=n.length?[[o[0],n[0]]]:[],l=[];for(let t=0;t+1=2)for(const[t,e]of h)_(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[yn(o[0]),yn(o[1])],h=[yn(n[0]),yn(n[1])];let l=Math.max($n(o[0],o[1],r[0],r[1]),$n(n[0],n[1],h[0],h[1]));const c=$n(r[0],r[1],h[0],h[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-bn&&i+e/2<=c+bn});c>bn&&d?(i.a=r,i.b=h,_(o,r),_(n,h)):l=0,g(l,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[yn(t[0]),yn(t[1])],n=$n(t[0],t[1],o[0],o[1]);i.center=o,_(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[yn(e[0]),yn(e[1])],a=[yn(s[0]),yn(s[1])];t=Math.max($n(e[0],e[1],o[0],o[1]),$n(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],_(e,o),_(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=yn(e),r=yn(s);if(null!=i.w&&null!=i.h){const h=yn(e+o),l=yn(s+a),c=Math.max(I,h-n),d=Math.max(I,l-r);t=wn(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=$n(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=bn)continue;const h=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),_=!(Number.isFinite(p)&&p===d),u=kn(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,h,c,d);i.x=h,i.y=c,i.angle=d,_&&l++,g(u,e,s,_);continue}const o=P([i.x,i.y],t.rooms||[],vn,{step:I,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),h=kn(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&l++,g(h,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=yn(s.x),n=yn(s.y),r=$n(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const h="string"==typeof s.s?s.s:"";g(r,u[h]??m,h)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:h,rotated:l,removedDrafts:d},changed:o>0||c>0}}function Sn(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Cn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Dn=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function Pn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Dn(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function In(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Cn(i.a)&&Cn(i.b)&&(Dn(t,i.a)<=o||Dn(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Dn(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function Rn(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Cn(t.a)&&Cn(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tDn(e,t.at)>s);let c=l[0]??r.a,d=l[l.length-1]??h.b;if(l.length<2&&(c=r.a,d=h.b),d[0]0?(d[0]-c[0])/_:0,m=_>0?(d[1]-c[1])/_:0,g=t=>_>0?((t[0]-c[0])*u+(t[1]-c[1])*m)/_:0;for(const t of[r,h]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function zn(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Sn(t.t,n)},a++;const r=z(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,F(s,r,i.coordScale))}return a}function Fn(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>R(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const An=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Nn=(t,e)=>t[0]-e[0]||t[1]-e[1],Hn=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],On=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,Ln=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const h=a/r,l=n/r,c=e=>Math.abs((e[0]-t[0])*l-(e[1]-t[1])*h);if(c(s)>o||c(i)>o)return null;const d=On(s,t,h,l),p=On(i,t,h,l),_=Math.max(0,Math.min(d,p)),u=Math.min(r,Math.max(d,p));return u-_>o?{lo:_,hi:u}:null},En=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Bn=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},Wn=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,h=a/n,l=j(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!An(o))return!0;const a=On(o,t.a,r,h),c=Math.abs((o[0]-t.a[0])*h-(o[1]-t.a[1])*r),d=j(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=l+d+i})},Gn=(t,e,s)=>{if(t.host){const i=z(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},jn=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),h=Math.sin(n);if(Math.abs(o*h-a*r)>1e-6)return!1;const l=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(l*a-c*o)>s)return!1;const d=l*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Un=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Kn=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Vn(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],h=Array.isArray(t?.room_drafts)?t.room_drafts:[],l={walls:a,partitions:n,openings:r,roomDrafts:h,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!h.length)return l;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,_=r;const u=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const h=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(h>i)){e=!1;break}const l=s.flatMap(t=>{const e=t.cm>0?t.cm:N;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of l){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=h-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Bn(t))continue;const s=v.get(t.id);if(!(s&&An(s.a)&&An(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Nn(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],h=n[1]-a[1],l=Math.hypot(r,h);if(!(l>c))continue;const u=r/l,g=h/l,y=A(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=Ln(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=_.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:z(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,l];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),M=new Map;for(const{opening:t,resolved:e}of w){const s=On(e.center,a,u,g),i={lo:s-e.length/2,hi:s+e.length/2};M.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(l,i.hi))}const S=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=l+c).map(t=>Math.max(0,Math.min(l,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),C=p.map(t=>qn(t,o.coordScale)).filter(t=>t.id!==s.id),D=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Hn(a,u,g,i),h=Hn(a,u,g,n),l=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of l)d.set(t.roomId,t);const p=[...d.values()],_=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:N)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===_.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:h,cm:s.cm},w=C.some(t=>!!Ln(r,h,t.a,t.b,c)),k=D.some(t=>t.points.some((e,s)=>s+10?p[0].cm:N:0,I=v?Math.max(M,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:h,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:M,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=M.get(t.id),a=k.some(t=>t>i.lo+c&&tEn(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&H(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const h=O(s,o.cellCm,o.gridPitch),l={lo:i.lo-h,hi:i.hi+h};for(const t of P)En(t,l,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=M.get(t),a=P.filter(t=>En(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=O(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)En(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Kn(P,c),j=R.filter(t=>t.safe),q=R.filter(t=>!t.safe);if(!j.length)continue;if(p.length-1+q.length>2e3)continue;let U=d;for(const t of j){const[e,i]=Nn(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];U=L(U,e,i,t.finalCm,o.pitch,o.coordScale)}if(U=E(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),U.length>500)continue;const K=new Set(p.map(t=>t.id));K.delete(s.id);const V=q.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Un(s.id,i,a,K);return K.add(n),{id:n,a:i,b:a,cm:s.cm}}),J=p.findIndex(t=>t.id===s.id),Y=p.slice();Y.splice(J,1,...V);const X=Y.map(t=>qn(t,o.coordScale)),Z=new Map;let Q=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=F(t,s.resolved,o.coordScale),{host:i,...a}=e;Z.set(t.id,a);continue}const i=M.get(t.id),a=V[q.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&X.find(t=>t.id===a.id);if(!a||!n){Q=!1;break}const r=n.b[0]-n.a[0],h=n.b[1]-n.a[1],l=Math.hypot(r,h),d=On(e.center,n.a,r/l,h/l),p={...F(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/l}};if(!B(p,X,o.coordScale,o.cellCm,o.gridPitch).resolved){Q=!1;break}Z.set(t.id,p)}if(!Q)continue;const tt=_.map(t=>Z.get(t.id)||t),et=[...I.keys()].map(t=>Z.get(t)),st=et.map(t=>Gn(t,X,o));if(st.some(t=>!t))continue;let it=!1;for(let t=0;tt.id));for(const t of tt){if(ot.has(t.id))continue;const e=Gn(t,X,o);e&&st.some(t=>jn(t,e,c))&&(it=!0)}if(it)continue;const at=W(e.rooms,U,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=Z.get(t),i=G(at,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=U,p=Y,_=tt,f+=j.length,b+=I.size)}return{walls:d,partitions:p,openings:_,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Jn=t=>JSON.parse(JSON.stringify(t)),Yn=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Xn=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function Zn(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Qn=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const tr=1e-9;class er extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const sr=t=>JSON.parse(JSON.stringify(t)),ir=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),or=t=>[Number(t[0]),Number(t[1])],ar=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,nr=(t,e)=>{const s=ar(t),i=ar(e);return sar(t)<=ar(e)?[or(t),or(e)]:[or(e),or(t)],hr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),lr=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/I;Math.abs(i-Math.round(i))>=X&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of R(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},dr=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},pr=(t,e,s)=>{const i=Math.max(0,Math.min(1,dr(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},_r=(t,e,s,i,o=tr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const h=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(h(s)>o||h(i)>o)return 0;const l=dr(s,t,e),c=dr(i,t,e);return Math.max(0,Math.min(1,Math.max(l,c))-Math.max(0,Math.min(l,c)))*r},ur=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),h=new Uint32Array(64),l=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=l(s,17)^l(s,19)^s>>>10;h[t]=h[t-16]+i+h[t-7]+o>>>0}let[e,s,i,o,c,d,p,_]=r;for(let t=0;t<64;t++){const a=_+(l(c,6)^l(c,11)^l(c,25))+(c&d^~c&p)+n[t]+h[t]>>>0,r=e&s^e&i^s&i;_=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((l(e,2)^l(e,13)^l(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+_>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},gr=(t,e,s,i,o="")=>{const[a,n]=rr(e,s),r=`${t}|${ar(a)}|${ar(n)}|${[...i].sort().join(",")}${o}`;return`wall-${mr(ur(r)).slice(0,20)}`};let fr=0;const br=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||mr(ur(`${Date.now()}|${fr++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new er("duplicate-id","id factory exhausted")},vr=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&ir(s.a)&&ir(s.b)){if(e.has(s.id))throw new er("duplicate-id",s.id);e.set(s.id,s)}return e},yr=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=tr&&Math.abs(e[1]-i[1]-a)<=tr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},$r=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:J(s,null,1,.04*I,!0),a=[];for(const t of s){const s=R(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>pr([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*I)):[],h=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),l=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!Z(t,e,Number(s.angle)))return!1;if(pr(i,t,e)>.02*I)return!1;const o=hr(t,e),a=dr(i,t,e)*o,n=Number(s.length)/2;return a+n>=-tr&&a-n<=o+tr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=R(i);if(!r||!p||p.length<3)throw new er("invalid-room",r);const _=Y(s,r,n,I,1,[...t.walls||[],...h]);if(!_||_.poly.length<3)throw new er("invalid-room",r);const u=Array.isArray(i.wall_ids)?i.wall_ids:[],m=u.length===p.length;let g=null,f=m;if(f)for(let t=0;ttr||Math.abs(i[1]-g[1])>tr)){f=!1;break}g=i}const b=[];for(let t=0;t<_.poly.length;t++){const s=_.poly[t],i=_.poly[(t+1)%_.poly.length];if(hr(s,i)<=tr)throw new er("zero-length",r);const n=nr(s,i);let h=c.get(n);if(!h){const[t,e]=rr(s,i);h={key:n,a:t,b:e,owners:new Set,preferredIds:new Set,positionalIds:new Set,preferredCarriers:new Map,parentKeys:new Set,zeroWall:!1},c.set(n,h)}if(h.owners.add(r),h.owners.size>2)throw new er("third-owner",n);const d=m?u[_.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||_r(s,i,o.a,o.b)>tr?h.preferredIds.add(d):h.positionalIds.add(d),h.preferredCarriers.set(d,{a:or(p[_.parent[t]]),b:or(p[(_.parent[t]+1)%p.length])})}const g=_.parent[t];h.parentKeys.add(K(p[g],p[(g+1)%p.length],I));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>pr(v,[t[0],t[1]],[t[2],t[3]])<=.04*I);h.zeroWall||=y(a)||y(o)&&!l(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:_.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},wr=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[nr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new er("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new er("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],h=i?s.get(i):void 0;if(h){r.set(t,h);continue}const l=i?t.preferredCarriers.get(i):void 0;if(i&&l){r.set(t,{id:i,a:l.a,b:l.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>_r(t.a,t.b,e.a,e.b)>tr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||hr(e.a,e.b)-hr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const h=new Map;for(const[t,e]of r){const s=h.get(e.id)||[];s.push(t),h.set(e.id,s)}for(const[t,e]of h){const i=s.get(t)||r.get(e[0]);if(!i)throw new er("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=pr(o,t.a,t.b)<=tr?0:1,a=pr(o,e.a,e.b)<=tr?0:1;if(s!==a)return s-a;return(pr(i.a,t.a,t.b)<=tr?0:1)-(pr(i.a,e.a,e.b)<=tr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const l=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(l.has(t.id))throw new er("duplicate-id",t.id);l.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=rr(e.a,e.b),o=`${String(t.id||"")}|${ar(s)}|${ar(i)}|${[...e.owners].sort().join(",")}`,a=mr(ur(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new er("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;l.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,l.add(o)}}else for(const t of c)t.id=br(l),l.add(t.id)},kr=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=dr(s,e.a,e.b),o=hr(e.a,e.b),a=Number(t.length)/2;return i>=-tr&&i<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-tr&&i*o+a<=o+tr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,dr(s,n.a,n.b)))}},xr=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=dr(s,e.a,e.b),a=hr(e.a,e.b);return o>=-tr&&o<=1+tr&&pr(s,e.a,e.b)<=.02*I&&Z(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-tr&&o*a+i<=a+tr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>pr(s,t.a,t.b)-pr(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,dr(s,a.a,a.b)))}:null},Mr=(t,e,s)=>{const i=vr(t),{atoms:o,rooms:a}=$r(t,i);wr(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=K(e.a,e.b,I),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!ir(t.a)||!ir(t.b))&&_r(e.a,e.b,t.a,t.b)>=hr(e.a,e.b)-tr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new er("thickness-conflict",e.key);if(a.length)return a[0];const n=Q(i,e.a,e.b,I,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:K(t.a,t.b,I),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=kr(t,e);s&&(t.host=s);continue}const i=kr(t,e)??(s?xr(t,e):null);if(i)t.host=i;else{if(!s)throw new er("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Sr(t,e={}){const s=JSON.stringify(t),i=sr(t);U(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Mr(t,a,s)}return i.model_version=9,U(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Cr=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Dr(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Cr(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Cr(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):sr(e)}return o[e,e in t?s(t[e],i):sr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return sr(e)};return s(t,e)}const Pr=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Ir=(t,e,s)=>Math.min(s,Math.max(e,t)),Tr=t=>ut({spaces:[t]})[0],Rr=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,h=Math.max(i*r*.02,1e-9),l=[];for(const e of t||[])for(const t of R(e)||[])l.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h),p=new Map;for(const n of t||[]){if(!n?.id)continue;const _=mt(t,n.id,e,s,i,o,a,r);if(_)for(let t=0;t<_.orig.length;t++){const e=[];for(let s=0;s<_.parent.length;s++)_.parent[s]===t&&e.push(s);for(let t=1;t+10&&u>0&&n===m&&u!==n))continue;const g=_.poly[i],f=_.poly[(i+1)%_.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>h&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,l)&&d(f,l))continue;const v=Rr(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let _=e.slice();const u=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*h,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(u(s,e.a)&&u(i,e.b)||u(s,e.b)&&u(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>Rr(t.a,t.b,r).localeCompare(Rr(e.a,e.b,r)))){const e=[...t.targets][0],s=_.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;_=_.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(_=L(_,t.a,t.b,e,i,r))}return _}const Fr=t=>{const e=[];for(const s of t||[]){const t=R(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(l),d=new Map;for(const t of l){const e=Zn("space",t);e.layers>0&&Xn("space",e.root)&&Qn(d,e.root,t)}const p=new Map,_=new Map,u=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=Zn("room",t);o.layers>0&&Xn("room",o.root)&&Qn(s,o.root,t),_.has(t)||_.set(t,e),u.has(t)||u.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(_.keys()),f=new Map,b=new Map;for(const t of h)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=Zn("space",t).root;if(!Xn("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=Zn("room",t).root;if(!Xn("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of h){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),h=!0===t.removed;let l=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,l=i,p=!0,v.spaceRefsRemapped++):h||"virtual"===t.binding?h||(delete t.space,l=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,l=d.spaceId,v.spaceRefsRemapped++):(delete t.space,l=null,v.markersDetached++))}h&&!p||x(t,l,d),r&&p&&n===s&&l&&(o[e]={...a,s:l},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Yn(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const M=s.ownerRoster,S=!0===M?.authoritative,C=new Set(M?.deviceIds||[]),D=new Set(M?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(M?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),h=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(h)a=T(t,i,"device",String(h.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",u.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=D.has(e)?"live":S?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else C.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=S?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=tt(n,a.layout);U(n),et(a.layout);const h=JSON.stringify(t||{}),l=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&Pr(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(Pr(o,"segments")&&(delete o.segments,e++),Pr(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=ht&&t<=lt&&(Pr(o,"plan_scale_x")||(o.plan_scale_x=t),Pr(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);Pr(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?Ir(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)Pr(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Pr(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(Pr(t,"width")){const s=Number(t.width);(Pr(t,"width_cm")||Number.isFinite(s))&&(Pr(t,"width_cm")||(t.width_cm=Number(Ir(s/nt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Pr(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Pr(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Ir(ct*dt(t)/nt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Pr(t,"scale")||Pr(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!pt(t[1]));if(!(Pr(t,"entity")||Pr(t,"attr")||Pr(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":_t(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const _=(u=n.spaces||[],JSON.parse(JSON.stringify(u)));var u;const m=Mn(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=st(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/I*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?Mn(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,M=0,S=0,C=0,D=0,P=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=Tr(e),r=Tr(s);if(!a||!r)continue;const h=Fr(a.rooms),l=Fr(r.rooms),c=.02*nt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:K(t.a,t.b,I),cm:0})),p=new Set(d.map(t=>t.key)),u=it(e,a.rooms,ot,c);x+=u.length;for(const t of u){const e=[t[0]/ot,t[1]/ot],s=[t[2]/ot,t[3]/ot],i=K(e,s,I);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=at(d,h,l,I,ot),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*ot),s=t.b.map(t=>t*ot);return l.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});M+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*ot,t.a[1]*ot,t.b[0]*ot,t.b[1]*ot]),$=Array.isArray(e.walls)?e.walls.length:0;let w=at(e.walls,h,l,I,ot);w=zr(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=E(r.rooms,w,y,I,Number(s.cell_cm)>0?Number(s.cell_cm):5,nt,ot),w=rt(w,s.rooms||[],I,1,y.map(t=>[t[0]/ot,t[1]/ot,t[2]/ot,t[3]/ot])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const R=Rn(s.partitions||[],{pitch:I,geometry:Fn(s)});R.merged&&(S+=R.merged,s.partitions=R.partitions,zn(s.openings,s.partitions,R.openingMoves,{coordScale:ot,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt}));const z=Tr(s);if(z){const t=o(s,z,s.walls||[],y,{pitch:I,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:nt,coordScale:ot});t.partitionsReconciled&&(C+=t.partitionsReconciled,D+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let R=0;c<=9&&(R=function(t){const e=Sr(t);return Dr(t,e.config),{...e,config:t}}(n).migratedSegments);const z=U(n),F=et($.layout),A=JSON.stringify(z)!==h||JSON.stringify(F)!==l;c<9&&A&&(z.model_version=9);const N=JSON.stringify(z)!==h||JSON.stringify(F)!==l,H=Number.isInteger(Number(z.model_version))?Number(z.model_version):c,O=N?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},L=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:z,layout:F,report:{...O,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?R:0,legacyZeroWallsMigrated:N?x:0,wallsMerged:N?k:0,spansMerged:N?M:0,partitionsMerged:N?S:0,partitionsReconciled:N?C:0,openingsRehosted:N?D:0,redundantDraftsRemoved:N?P:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?b:0,maxStraightenSpace:N?v:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...L},changed:N}}const Nr=(t,e)=>t[0]*e[1]-t[1]*e[0],Hr=(t,e)=>t[0]*e[0]+t[1]*e[1],Or=(t,e)=>[t[0]-e[0],t[1]-e[1]],Lr=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Er(t,e,s){return Math.abs(Nr(t,e))<=s}function Br(t,e,s){return Math.abs(Nr(Or(t,e),s))}function Wr(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const h=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Er(h,[i/n,a/n],1e-6)))return[];if(Br(e.a,t,h)>1.1*Math.max(s,o)||Br(e.b,t,h)>1.1*Math.max(s,o))return[];return[[Hr(Or(e.a,t),h),Hr(Or(e.b,t),h)]]}),o).flatMap(([e,i])=>{const o=bt({a:Lr(t,h,e),b:Lr(t,h,i),halfDepth:s});return o?[o]:[]})}function Gr(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=mt(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=gt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:Lr([t.x,t.y],s,-i),b:Lr([t.x,t.y],s,i)}}),a=[];for(const i of ft(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Wr(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=j(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Wr(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function jr(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=Lr(s.center,a,o),r=[];for(let e=0;ei&&Er(s.axis,[h[0]/l,h[1]/l],1e-6)))continue;if(Br(o,n,s.axis)>i||Br(a,n,s.axis)>i)continue;const c=Hr(Or(o,n),s.axis),d=Hr(Or(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const h=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!h)return null;const[l,c]=h,d=Lr(n,s.axis,-s.half),p=Lr(n,s.axis,s.half),_=Math.max(0,-s.half-l),u=Math.max(0,c-s.half),m=_>0?Lr(n,s.axis,l):d,g=u>0?Lr(n,s.axis,c):p,f=Hr(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,_),right:b(p,g,u)}}function qr(t){const e=Lr(t.center,t.axis,-t.half),s=Lr(t.center,t.axis,t.half),i=Lr(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=Lr(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ur(t,e,s,i){const o=Or(s,e),a=Hr(o,o);if(!(a>i*i))return Math.hypot(...Or(t,e))<=i;const n=Hr(Or(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=Lr(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Or(t,r))<=i}function Kr(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=l>t[1]&&t[0]<(h-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Vr(t,e,s,i,o,a){const n=Or(i,s),r=Nr(e,n);if(Math.abs(r)<=a)return null;const h=Or(s,t),l=Nr(h,n)/r,c=Nr(h,e)/r;return l<-a||l>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,l))}function Jr(t,e,s){const i=qr(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Kr(t,r.body,a))return 0;for(let i=0;iHr(Or(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Or(n,a),h=Math.hypot(r[0],r[1]);if(!(h>s&&Er(e.axis,[r[0]/h,r[1]/h],1e-6)))continue;if(Br(a,e.center,e.axis)>s||Br(n,e.center,e.axis)>s)continue;const l=Hr(Or(a,e.center),e.axis),c=Hr(Or(n,e.center),e.axis),d=Math.min(l,c),p=Math.max(l,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||_2)return qr(s);if(i.length){const t=i.map(({room:t,edge:i})=>jr(t,i,s,e.epsilon));if(t.some(t=>!t))return qr(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Jr(t,e,s)}const Xr="1.69.0-beta.1",Zr={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Qr={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},th=2e3,eh=1e3,sh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},ih=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},oh=1e3,ah=2e3,nh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},rh="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";class hh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=sh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?vt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?vt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=sh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&yt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&$t(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=wt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=sh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const h=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,h||this.host.requestUpdate()},this._deletePhysicalSelection=()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._curSpaceCfg;if(!t||!e||!confirm(this.host._t("confirm.delete_draft")))return;const s=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftSegment=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this.host._t("confirm.delete_draft_segment")))return;const s=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(s<0)return;const i=e.room_drafts[s],o=Math.max(0,Math.min(i.segments.length-1,t.segment||0)),a=[],n=i.points.slice(0,o+1),r=i.points.slice(o+1);if(n.length>=2&&a.push({id:i.id,points:n,segments:i.segments.slice(0,o)}),r.length>=2&&a.push({id:a.length?`${i.id}-${Date.now().toString(36)}`:i.id,points:r,segments:i.segments.slice(o+1)}),2===a.length&&e.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot();e.room_drafts.splice(s,1,...a),e.room_drafts.length||delete e.room_drafts,this.host._activeDraftId===i.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=a.length?{kind:"draft",id:a[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),h)},this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=nn(A(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const h=this._geometrySnapshot(),l=kt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*oh,Number(e.a?.[1])*oh],i=[Number(e.b?.[0])*oh,Number(e.b?.[1])*oh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/oh,a.interval.a[1]/oh],b:[a.interval.b[0]/oh,a.interval.b[1]/oh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*oh,i.a[1]*oh],b:[i.b[0]*oh,i.b[1]*oh]};e.host={kind:"partition",id:i.id,t:rn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(l,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),h),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>ah)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=Ra(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,N);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Ct(this.host._settings,{bgColor:null})||null,northDeg:St(this.host._settings,{}),bgMode:Mt(this.host._settings,{}),sunRays:xt(this.host._settings,{}),busy:!1}},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Dt).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},t._editorSecondary=new gn({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Wo}_help(t){const e=`${t}.aria`,s=d(this.host.hass,this.host._config?.language);return Pt(s,t)&&Pt(s,e)?o``:a}_setMode(t,e=!0){this.host._endTabDrag(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if(this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const h=!s.bg&&"view"===t!=("view"===i),l=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=l?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=l?l.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=l?l.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}h&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?It([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const _=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(_,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&_===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=Tt(N,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:Tt(e,this.host._imperial),max:Tt(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=Rn(s,{pitch:I,seedIds:e,geometry:Fn(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,zn(t.openings,t.partitions,i.openingMoves,{coordScale:oh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=za(this.host._path,Ra(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,N)),s=e.length;if((t.partitions||[]).length+s>ah)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=ah:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[Rt(zt(t[0],e)),Rt(zt(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?Ft(s,t,this.host._gridPitch,Ii):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=G(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=xa({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=ka({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=xa({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?Ia(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(ma(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??ua,o=s.excludePoints||[],a=Da(Ca(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=D([e[0],e[1]],t);if(r>s.tolerance)continue;const h=Pa(a,e,s.gridStep);!Ma(h,o,i)&&Sa(r,a.key,n)&&(n={kind:"line",point:h,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,h=r?[...r.point]:this._snapDrawPoint(t,e),l=i?At(i,h):h;return{point:l,candidate:r&&q(l,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return q(t,e)}_dropLegacySegments(){for(const t of this.host._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this.host._serverCfg?.spaces||[]){delete t.segments;const e=new Set,s=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),i=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Xt);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:wt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Nt(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=lr(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Ht(ut({spaces:[t]}),t.id),s=e?Ot(t,e,oh,.02*nt).contour.map(t=>t.map(t=>t/oh)):V(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=rt(t.walls,t.rooms||[],I,1,s),t.walls.length||delete t.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=Lt(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_writeConfig(){this.host._writesPending++,this.host._writeChain=Et(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;this._dropLegacySegments();const t=Si(this.host._serverCfg),e=Lt(t),s=[...this.host._pendingPhysicalWrites.entries()];for(const[e,i]of s){const s=t.spaces.find(t=>t.id===e),o=jt(s);if(o===i.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(t,e).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(i.before),this.host._pendingPhysicalWrites.delete(e),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});i.fingerprint=o}e!==Lt(this.host._serverCfg)&&(this.host._serverCfg=t),this.host._cfgContentFingerprint=e;try{await this.host._sendConfigCandidate(t)}catch(t){if(!this._rollbackRejectedPhysicalWrites(s))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of s)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),h=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=h.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof er?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...ta(a),...aa(a,o,I),...ra(a,o,I)];let r=null;try{r=Bt(i.rooms||[],i.walls||[],[],I,o,I,1)}catch{r=null}let h="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!h&&r&&r.nodes.length)try{const t=Wt(i.rooms||[],i.walls||[],[],[],I,o,I,1);h="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{h=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=Gt(i.rooms||[],e,i.walls||[],[],I,o,I,1,h??void 0,r)}catch{s=null}n.push(...ha(e,s,o,I))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=jt(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:Sr(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let h=[];try{h=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(h,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||jt(e)===jt(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let h=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new er("invalid-room",e.spaceId);const s=Sr(t).config;h=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new er("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),l=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of l?.wall_segments||[])t.cm=Q(o.walls,t.a,t.b,I,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=$r(s,vr(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=R(e),h=(s.rooms||[]).find(e=>e.id===t.id),l=R(h),c=R(n);if(r&&l&&c&&r.length===l.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;atr||pr(r,s,i)>tr)continue;const h=dr(n,s,i),l=dr(r,s,i);h<-tr||l>1+tr||l<=h+tr||o.push({start:h,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,l[e],l[(e+1)%l.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/oh,t[1]/oh]):[];i.push(...s.map(t=>[t[0],t[1]])),l=cr(t)<=cr(h,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{l=!1}if(!l)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Dr(i,n),this._recordGeometry(t,h);const p=i.spaces.find(t=>t.id===e.spaceId),_=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:_?.before||h,fingerprint:jt(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=jt(s)!==jt(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Sr(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Dr(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:jt(n)})}else Dr(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&qt(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=R(e);return!!s&&$(t,s)})}_pointInRoom(t,e){return e.poly?Ut(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!mo(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>ah)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=At(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const h=this.host._drawWallCm;if(null==h)return void this._showPhysicalRange(100);const l=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,h],this._persistActiveDraftSegment(),this._offerWallFaces(l)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),h=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),l=this.host._path[this.host._path.length-1],c=this._samePt(l,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),_=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],u=[...p,...null==d?[]:[{cm:d}],...h],m=_.length>=4&&this._samePt(_[0],_[_.length-1]),g=_,f=u;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||k(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/oh,t[1]/oh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=Ra(i,e,this.host._drawWallCm,N);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*oh,Number(t?.[1])*oh]):[],o=Array.isArray(e?.segments)?e.segments:[];return Ra(Math.max(0,t.length-1),s,this.host._drawWallCm,N).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/oh,t[1]/oh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=en(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=un(r,{requiredSourceKey:o,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=R(t);if(!t.id||!e)continue;const s=Kt(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=k(i)>=k(o)?i:o,r=a===i?o:i,h=[...n].sort((t,e)=>Math.abs(t.area-k(r))-Math.abs(e.area-k(r))||t.key.localeCompare(e.key))[0];if(!h)return;return void this._beginWallFaceBatch([{...h,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const h=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));h.length&&this._beginWallFaceBatch(h)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=sn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=un(this._wallGraphSources([]),{point:t,maxDistance:j(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=un(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(vt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:wt(s),angle:0},a=(i.wall_columns||[]).find(t=>yt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/oh,e[1]/oh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:Tt(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?Nt(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:Tt(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),nh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Vt(o,Jt(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=Yt(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Vt(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),h=this.host._physicalPickCycle,l=a.length>1&&h?.signature===n&&r-h.at>380&&r-h.at<=1200&&Math.hypot(t.clientX-h.x,t.clientY-h.y)<=10?(h.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:l,x:t.clientX,y:t.clientY,at:r},e=a[l].kind,s=a[l].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Xt*oh-Math.min(...o),Math.min(Xt*oh-Math.max(...o),s[0])),Math.max(-Xt*oh-Math.min(...a),Math.min(Xt*oh-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/oh,o.a[1]/oh],t.b=[o.b[0]/oh,o.b[1]/oh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=z(t,[o],oh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,F(t,s,oh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/oh,(o.center[1]+e.delta[1])/oh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),nh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:Nt(e.angle),angle:Nt(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=Nt(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&yt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?R(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:j(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*oh,t.a[1]*this.host._spaceH],b:[t.b[0]*oh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?j(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!mo(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],h=n.poly[(i+1)%n.poly.length],l=vo(r,h,a);if(!l)return{enabled:!1,reason:"diagonal"};if(!ko(n.poly,i,l,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(So(t,r,h,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=co(ro(h,r)),d=[],p=new Set;let _=!1,u=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!mo(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!ko(t.room.poly,t.edge,l,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(_o([t.x,t.y],r,h)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=Po(t,e,o,1,a),h=Po(t,e,n,0,a);if(!r||!h)return{enabled:!1,reason:"partial-shared"};v.push(r,h)}const y={roomId:s,edge:i,a:[...r],b:[...h],n:uo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!No(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>No(t,e,y,s,o))){if(!s.some(s=>Ro(zo(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>No(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>No(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const h=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/oh,t[1]/h]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/oh,s.y=e[1]/h)}const l=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*oh,t[1]*h]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/oh,i[t][1]/oh],o.b=[i[(t+1)%i.length][0]/oh,i[(t+1)%i.length][1]/oh])}}if(l.length&&Array.isArray(r.walls)&&r.walls.length){const t=Zt(r.walls,l,c,this.host._wallKeyPitch,oh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=R(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),_=new Map;for(const t of n.walls||[]){const e=p(t);_.set(e,(_.get(e)||0)+1)}const u=[];for(const t of r.walls||[]){const e=p(t),s=_.get(e)||0;s?_.set(e,s-1):u.push(t)}if(Qt(u,d,this.host._wallKeyPitch,oh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=te(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:Lt([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};ih(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));nh(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,h=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,l=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:l,before:JSON.parse(l),wallUnionBefore:h})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};ih(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):ee(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const h="mi"===this.host.hass?.config?.unit_system?.length,l=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),_=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[u,m]=this.host._lastValidStageSize||[_.w,_.h],g={..._,stageWidth:Math.max(1,u),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?se(b>8?2.5:b,f,_.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of l){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?ie(oe(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:ae(n,this.host._gridPitch,this.host._cellCm),l=ne(r,h),p=jo({poly:a,edge:e.edgeByRoom[s],text:l,view:g,gearCenter:re(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:l,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=he(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,oh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>le(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return a;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return ce`
this.host._markerDialog={...t,glowRadius:e.target.value}} /> ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")}
- ${M||C?o`

+ ${S||D?o`

${I}

`:a} @@ -2139,7 +2158,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d @@ -2166,20 +2185,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${$?o`

${$.technical}

`:a} - ${v?o`

+ ${y?o`

${this.host._t("marker.value_badge_missing_hint")}

`:a} - ${"value"===t.display&&y===w?o`

+ ${"value"===t.display&&v===w?o`

${this.host._t("marker.value_badge_duplicate")}

`:a}
@@ -2199,7 +2218,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d .hass=${this.host.hass} .presentation=${_} .registry=${this.host._haRegistry} - .deviceName=${t.name.trim()||r?.name||L||""}> + .deviceName=${t.name.trim()||r?.name||O||""}> `:o`
${this.host._t("marker.preview.select_source")} @@ -2292,7 +2311,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${"file"===t.source?o`
@@ -2312,7 +2331,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${t.pickSaved?this._renderServerPlans(t):a}`:a} @@ -2320,9 +2339,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d
{const s=e.target.value,i=sl(s),o=null==i?null:ki(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(el,o)):t.cellCm}}} /> + @input=${e=>{const s=e.target.value,i=sh(s),o=null==i?null:ki(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(eh,o)):t.cellCm}}} /> ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")}
@@ -2386,10 +2405,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d - ${"static"===(t.bgMode??St(this.host._settings,{}))?o`
+ ${"static"===(t.bgMode??Mt(this.host._settings,{}))?o`
{this.host._spaceDialog={...t,bgColor:e.detail.color}}}> ${t.bgColor?o`
{const s=sl(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMin:s})}} /> + @input=${e=>{const s=sh(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMin:s})}} /> – {const s=sl(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMax:s})}} /> + @input=${e=>{const s=sh(e.target.value);null!=s&&(this.host._spaceDialog={...t,tempMax:s})}} /> °C `:a} @@ -2505,7 +2524,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${t.label}${t.sub}
`)}
-
`:a}`:a}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),n=ps(this.host._curSpaceCfg),r=this.host._roomFill||n.fill,l=this.host._roomCustomFill||n.customFill,h=[...this.host._freeAreas];if(t&&this.host._areaSel&&!h.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&h.unshift(t)}return o``:a}`:a}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),n=ps(this.host._curSpaceCfg),r=this.host._roomFill||n.fill,h=this.host._roomCustomFill||n.customFill,l=[...this.host._freeAreas];if(t&&this.host._areaSel&&!l.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&l.unshift(t)}return o`this._roomDialogCancel()}>
@@ -2520,12 +2539,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d - ${[["","fill.inherit"],...Si.map(t=>[t,"fill."+t])].map(([t,e])=>o`
`:a} @@ -2569,4 +2588,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d ${this.host._t("btn.save")} `}
- `}}export{rl as EDITOR_RUNTIME_FINGERPRINT,ll as HouseplanEditorRuntime}; + `}}export{rh as EDITOR_RUNTIME_FINGERPRINT,hh as HouseplanEditorRuntime}; diff --git a/dist/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js b/dist/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js similarity index 98% rename from dist/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js rename to dist/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js index f11f6dc2..ff1054a5 100644 --- a/dist/houseplan-assets/houseplan-onboarding-runtime-Bh9X5uQb.js +++ b/dist/houseplan-assets/houseplan-onboarding-runtime-UI_A2NLN.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as n,b$ as c,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-DJctcs0x.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-BIiYcPJz.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";class C{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:c,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return p`
${this.host._t("space.no_saved")}
`;return p`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as n,b$ as c,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-VbP9hCtk.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-Dh9IGz13.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";class C{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,c="none"===h.fill?{...n||e,a:0}:n;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:c,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:c,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:n,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:n,name:e.name}}}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
${this.host._t("space.loading")}
`;const s=t.saved||[];if(!s.length)return p`
${this.host._t("space.no_saved")}
`;return p`
${s.map(s=>{return p`
diff --git a/dist/houseplan-assets/space-deletion-BIiYcPJz.js b/dist/houseplan-assets/space-deletion-Dh9IGz13.js similarity index 56% rename from dist/houseplan-assets/space-deletion-BIiYcPJz.js rename to dist/houseplan-assets/space-deletion-Dh9IGz13.js index 7b87fb44..02209851 100644 --- a/dist/houseplan-assets/space-deletion-BIiYcPJz.js +++ b/dist/houseplan-assets/space-deletion-Dh9IGz13.js @@ -1 +1 @@ -function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function a(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),a=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),t=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&a.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:t,count:t.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";export{a,e as c,o as i,r as s,s as t}; +function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";export{t as a,e as c,o as i,r as s,s as t}; diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index be3bdd10..f3376912 100644 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="967d2749e25a30292223ad85baf8d03e0d0d2840b00ee6e6a5876ab3292a4f76";try{await import("./houseplan-assets/houseplan-card-DJctcs0x.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="9cb687ae4f1d1abc741993db3312043f8b5750ab41c58aceb4c51212998c2b85";try{await import("./houseplan-assets/houseplan-card-VbP9hCtk.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 07cc7c9c..a821a2d9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- The Background furniture library now has 56 top-view symbols and a compact + two-level picker: choose a front-view category, then the exact plan variant. + Existing furniture keeps its saved geometry while 18 drawings receive the + new original artwork ([#159](https://github.com/Matysh/houseplan-card/issues/159)). + - If the optional language pack (currently German) cannot be downloaded, the card now says so with a toast instead of silently switching to English; the locale runtime the card ships is now the same code the i18n test suite diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index 84b3c9af..bee7f319 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,11 @@ ## Не выпущено +- В библиотеке мебели редактора подложки теперь 56 видов сверху и компактный + двухуровневый выбор: сначала категория с видом спереди, затем точный вариант + для плана. Уже размещённая мебель сохраняет геометрию, а 18 рисунков получают + новый авторский дизайн ([#159](https://github.com/Matysh/houseplan-card/issues/159)). + - Если необязательный языковой пакет (сейчас — немецкий) не загрузился, карточка говорит об этом тостом, а не молча переключается на английский; рантайм локалей в продакшне теперь тот же код, что доказывает тестовый diff --git a/docs/FURNITURE.md b/docs/FURNITURE.md index df61891a..a3fd353b 100644 --- a/docs/FURNITURE.md +++ b/docs/FURNITURE.md @@ -1,263 +1,93 @@ -# The furniture library — top-view symbols in the decor layer +# Furniture library -Status: **implemented (v1.59.0-beta.3).** Code: `src/furniture.ts` (the -table, the geometry, the wall magnet, the resize — all pure and unit-tested), -`src/houseplan-card.ts` (the `furniture` tool, `_renderFurnPalette`, -`_furnPlace`, `_furnMoveUpdate`, the shared `_dt*` frame), -`custom_components/houseplan/validation.py` (`DECOR_SCHEMA`, furniture -branch). Tests: `test/furniture.test.mjs`, -`tests_backend/test_validation.py::test_decor_furniture`; smoke: -`demo/smoke_furniture.mjs`. +Status: **implemented and expanded by issue #159**. The Background editor +stores furniture as ordinary decor: -The shape: `{kind:'furniture', symbol, x, y, w, h, color, opacity, width_cm, angle?}` — -a **new** decor kind, so no existing plan carries one, nothing is migrated, -and every plan written before this validates and renders byte-for-byte as -before. - -## 1. Why a plan needs furniture and not icons - -A house plan with rooms, doors and devices still does not look like a plan of -a *home*. What tells a stranger which room is the bathroom is not the label -«Bathroom» — it is a bath, a toilet and a basin drawn where they stand. The -competing card solves this by making the user bring their own SVG; we already -draw the walls, so the pieces are ours to draw too. - -**The symbols are ours, drawn in code.** This is the one design decision worth -arguing, so: every general-purpose icon set — Material Design Icons included, -and `@mdi/js` is already in this bundle — draws a **pictogram**, a sofa seen -from the front inside a 24 × 24 square. A plan is drawn **from above** and -**to scale**: a sofa is a 2.2 × 0.9 m rectangle with a back along one long -side. Stretching a 24 × 24 front view into that rectangle produces a drawing -that was never meant to be stretched; on the plan it reads as an icon lying on -the floor. It also fights the renderer: a non-uniform `scale()` distorts the -stroke, and the usual cure (`vector-effect: non-scaling-stroke`) freezes the -stroke in screen pixels, so a furniture outline would stop scaling with zoom -while every other decor shape kept scaling. - -So `src/furniture.ts` holds ~30 symbols as primitives in a **unit box**, and -`furniturePathD(symbol, w, h)` generates one `d` string at the piece's real -size. The stroke is then an ordinary `stroke-width` in render units, exactly -like a decor rectangle. The whole library costs a few kilobytes of source and -needs no asset pipeline, no build step and no second set of preview images — -the palette draws its thumbnails with the same function the plan uses. - -Two conventions every symbol obeys, and they are the whole contract: - -- the box is `0..1 × 0..1`, `x` right, `y` **down** (SVG); -- **`y = 0` is the BACK** — the side that goes against a wall. A sofa's back, - a bed's headboard, a wardrobe's rear panel, a worktop's edge and a toilet's - cistern are all "the back", and that is what makes §5 mean something; -- nothing is filled. A plan is a drawing. - -## 2. The licence, and what we did not use - -The project is MIT and the symbols ship inside the bundle and through HACS, so -only a licence permitting commercial use and modification **without attribution -in the UI** is usable: public domain / CC0 / MIT / Apache-2.0 / BSD / ISC / -SIL OFL. What was checked, from the repositories and licence pages rather than -from hearsay: - -| Set | Licence | Attribution in the UI? | Top view? | -|---|---|---|---| -| Material Design Icons (`@mdi/js`, Pictogrammers) | Apache-2.0 (`Pictogrammers Free License`: icons Apache-2.0, code MIT) | not required ("appreciated in your about screen") | **no** — 24×24 pictograms | -| Tabler / Lucide / Phosphor / Iconoir | MIT / ISC / MIT / MIT | no | **no** — pictograms | -| Font Awesome Free | CC BY 4.0 | **yes** | no | -| Game-icons.net | CC BY 3.0 | **yes** | no | -| Flaticon / Freepik / Vecteezy / Adobe Stock / Noun Project | proprietary or CC BY | yes / unusable | mixed | -| [`1337GameDev/FloorPlanSVGSymbols`](https://github.com/1337GameDev/FloorPlanSVGSymbols) | **MIT** | no | **yes** | - -So a ready-made, correctly licensed **top-view** set does exist — exactly one, -a 70-file MIT repository by Richard Duerr. It was read and **not used**, and -the reason is not the licence: - -- coverage is partial and shaped for a different job (electrical receptacles, - smoke detectors, five window types; no wardrobe, no desk, no single bed, and - a sofa that is assembled from `CouchLeft` + `CouchMiddle` + `CouchRight`); -- the files are raw Inkscape output — `sodipodi` namespaces, unused gradient - `defs`, absolute `translate(-1366,-1443)` transforms, hard-coded - `stroke:#000000` inline styles, arbitrary `mm` viewBoxes. Every one would - need stripping, re-origining, re-normalising and re-colouring to - `currentColor` before it could be used, which is the same work as drawing it - — without the freedom to pick the real-world proportions. - -The set is recorded here because its licence is genuinely compatible: if we -ever want a symbol we do not have (an electrical panel, a fan), taking it from -there is legally free — MIT, one copyright line in `LICENSE`, no UI credit. -**Nothing from it is in the bundle today.** Our symbols are original, and the -licence question is therefore closed rather than managed. - -`@mdi/js` stays where it is — the *toolbar button* for the library is -`mdi:sofa-outline`, an icon in a toolbar, which is what pictograms are for. - -## 3. The palette - -The decor bar gains a seventh tool, **Мебель / Furniture**, next to -line/rect/oval/text. Choosing it opens a panel between the bar and the plan — -not a modal: where a sofa goes is a question about the plan, and the plan must -stay visible while it is answered. - -- symbols are **grouped**, and the groups are the point: `мебель` (12), - `техника` (8), `сантехника` (6), `прочее` (4 — stairs, fireplace, plant, - rug). No search box: thirty items in four labelled rows are faster to scan - than to type at. -- every tile draws the real symbol through `furniturePathD`, fitted into - 40 × 40 **keeping its real proportions** — so a sofa reads as a sofa and a - toilet does not become a square. -- picking a symbol **arms** it and fills the two size fields with its default. -- **Width** and **Depth** are in metres or feet, by the HA unit system - (`hass.config.unit_system.length`), and are editable **before** the click. - The config stores centimetres-through-`cell_cm` either way: a unit system is - how a user reads a plan, never what the plan is (docs/STYLING-HOOKS.md §6). - -### The symbols and their default real sizes - -Widths are measured **along the back edge**, depths away from it. Where the -owner's list gave a pair without an axis, the pair is kept and the axes follow -from the back edge — a toilet is 0.4 m wide and 0.7 m deep, not the other way -round. - -| Group | Symbol | W × D, m | | Symbol | W × D, m | -|---|---|---|---|---|---| -| мебель | Диван `sofa` | 2.2 × 0.9 | | Кресло `armchair` | 0.9 × 0.85 | -| | Журнальный столик `coffee_table` | 1.1 × 0.6 | | Обеденный стол `table_dining` | 1.4 × 0.8 | -| | Круглый стол `table_round` | 1.2 × 1.2 | | Стул `chair` | 0.45 × 0.45 | -| | Письменный стол `desk` | 1.2 × 0.6 | | Двуспальная кровать `bed_double` | 1.6 × 2.0 | -| | Односпальная кровать `bed_single` | 0.9 × 2.0 | | Тумбочка `nightstand` | 0.45 × 0.4 | -| | Шкаф `wardrobe` | 1.0 × 0.6 | | Стеллаж `bookshelf` | 0.8 × 0.3 | -| техника | Холодильник `fridge` | 0.6 × 0.65 | | Плита `stove` | 0.6 × 0.6 | -| | Посудомоечная машина `dishwasher` | 0.6 × 0.6 | | Стиральная машина `washer` | 0.6 × 0.6 | -| | Сушильная машина `dryer` | 0.6 × 0.6 | | Телевизор `tv` | 1.2 × 0.3 | -| | Кондиционер `ac` | 0.9 × 0.25 | | Бойлер `water_heater` | 0.45 × 0.45 | -| сантехника | Унитаз `toilet` | 0.4 × 0.7 | | Ванна `bathtub` | 1.7 × 0.75 | -| | Душ `shower` | 0.9 × 0.9 | | Раковина `sink` | 0.6 × 0.45 | -| | Кухонная мойка `kitchen_sink` | 0.8 × 0.6 | | Биде `bidet` | 0.4 × 0.55 | -| прочее | Лестница `stairs` | 1.0 × 2.8 | | Камин `fireplace` | 1.2 × 0.4 | -| | Растение `plant` | 0.4 × 0.4 | | Ковёр `rug` | 2.0 × 1.4 | - -Sizes are **defaults, not limits**: the fields overrule them before the click -and the corner handles overrule them after. - -## 4. Placing: the tool is a stamp - -Press on the plan → the piece appears **centred on the press**, at its real -size, selected, and the editor **switches back to `select`**. One pick, one -piece: the owner asked for «сразу выделен», and a tool that keeps stamping -until you disarm it is a tool that stamps a sofa the next time you meant to -pan. The palette is disarmed with it, and leaving the tool disarms it too. - -Real size is `cell_cm`, the one scale this card has: -`w_norm = (cm / cell_cm) × GRID_PITCH / 1000`. A 2.2 m sofa on a plan drawn at -5 cm per cell is 44 cells; on a plan drawn at 10 cm per cell it is 22, and it -covers the same 2.2 m of wall in both. - -Under this tool existing shapes are **inert**, like under every drawing tool -(the rule from 2026-08-04): the press must reach the stage even when it lands -on a sofa that is already there. Without an armed symbol the press does -nothing at all and the plane still pans — a click must not stamp whatever was -chosen last week. `Esc` disarms the symbol first, then clears the selection, -then leaves the tool, then leaves the editor. - -## 5. The wall magnet - -While a piece is **placed** or **dragged**, the nearest wall within six grid -cells (≈ 30 cm on a default plan) claims it: the piece's **back edge lands on -the wall** and the piece is **turned to the wall's direction**, with its body -on the side the finger is holding it (so a sofa dragged along the inside of a -wall never flips through it). The offset **along** the wall is still quantised -to the grid, so a row of kitchen units lines up. - -Walls are the **derived room edges** (`roomEdges`) — the same walls an opening -snaps to. There is no wall entity to be bound to and no room id is stored: a -piece keeps absolute coordinates and survives every later edit of the rooms. - -The magnet and grid quantisation are mandatory (docs/CANVAS.md §9.4). Out -of the magnet's reach the drag is the ordinary grid snap on the shape's own -anchor, and the angle the piece already had is kept: dragging a turned -wardrobe into the middle of a room must not straighten it. - -## 6. Selecting: one frame, two shapes - -A selected piece wears **the text block's frame** — dashed box, four corner -handles, one rotate handle on a stem, with the hit radius at 1.8 % of the -visible view and the visible bead at a quarter of that (docs/LIVE-TEXT.md §3, -the owner's «уменьшить в 4 раза»). Reusing it is deliberate: it is the same -question ("how big, which way round?") asked about two different things, and a -third set of chrome would only be a third set of bugs. What differs is -confined to two places: - -- **the pivot.** A label has an anchor and must never walk away from it; a - piece of furniture has a box, and turning a sofa about its top-left corner - is not what anyone means by turning a sofa. So: the anchor for text, the - **centre** for furniture (`_dtPivot`). -- **what a corner means.** For a label it is one physical font size. For - furniture it is width and depth about the opposite corner. The common - controller preserves the current ratio by default; hold `Shift` for - independent axes. Each extent and the stored top-left coordinate remains - grid-bound. - -**Rotation** is the handle above the box, in **5° steps**, Shift past the step -— the same step a device icon and a text block turn in. Turning back to zero -removes the field, so a straight piece stores no angle at all. - -Double click opens the common properties dialog. It exposes the symbol itself, -physical width/depth, angle, contour colour/opacity and physical line width; -changing the symbol keeps the current box and transform. - -**Live measurements.** While a corner is dragged, two badges show the piece's -real width and depth in the HA unit system, centred on the edges they measure. -They are `.measurelabel`, fed by the same `_fmtLen` (`segmentCm` over -`cell_cm`) the wall ruler, the room resize and the backdrop badge use. There is -one way this card ever states a length. - -The frame needs **no measuring pass**: a piece's box is its config, so the -frame appears in the same render as the selection, not one after it (a text -label still needs `getBBox`, because only the browser knows how wide a word -came out). - -## 7. Styling hooks - -Per docs/STYLING-HOOKS.md §3, every piece carries -`data-hp="decor"`, `data-id=""`, `data-kind="furniture"` and -`data-symbol=""`. So card-mod can colour all the plumbing, or -one particular bed, without us shipping a CSS field: - -```css -/* every bathroom fixture in blue */ -.decorlayer [data-kind="furniture"][data-symbol="toilet"], -.decorlayer [data-kind="furniture"][data-symbol="bathtub"] { stroke: #4fc3f7; } +```text +{ kind: "furniture", symbol, x, y, w, h, color, opacity, width_cm, angle? } ``` -The colour, opacity and physical line width are the decor style's, chosen in -the bar like any other shape's, and are stored per piece. +The schema and saved coordinates did not change. Existing furniture therefore +keeps its position, size, rotation and styling; only 18 built-in drawings gain +the new artwork after an update. -## 8. Backend +## Library and source -`DECOR_SCHEMA` gains a fourth branch: `symbol` required, `^[a-z0-9_]+$`, -≤ 32 characters; `x`/`y` the usual ±`CANVAS_LIMIT`; `w`/`h` strictly positive -and capped by the same limit (a size, not a coordinate); `angle` optional, -−360…360. `color`/`opacity`/`width_cm` come from `_DECOR_COMMON`; legacy -render-unit `width` remains accepted until the object is edited or optimised. +The public library contains **56** top-view symbols: -The symbol is **not** validated against the card's list. The backend must -accept a plan written by a NEWER card, and a card that has learnt a new symbol -must not wait for the integration to be updated before the user can save. An -id this build has never heard of simply renders as nothing — `furnitureSymbol` -returns `null`, `furniturePathD` returns `''`, and the shape is skipped. +- 44 designer symbols from `assets/furniture/houseplan-0.3.0/svg/plan`; +- 12 retained built-ins: refrigerator, dishwasher, washer, dryer, air + conditioner, water heater, shower, sink, stairs, fireplace, plant and rug. -## 9. What this is not +The designer pack also contains 33 front-view category illustrations in +`svg/menu`. Four of them (`computer`, `oven`, `hood`, `exercise`) currently +have no top-view symbol and are intentionally hidden instead of opening an +empty category. -- **Not a device.** A piece of furniture has no entity, no tap action, no - state and no part in room aggregation. A sofa that switches the light is a - device marker standing on a sofa. -- **Not a 3D or an interior planner.** No layers, no z-order beyond - configuration order, no collision detection, no "does the door open into the - bed" check. A plan is a drawing, not a CAD model. -- **Not a user-extensible library.** No symbol upload, no custom paths in the - config: an arbitrary path from a config is an SVG injection surface, and the - card already refuses to be a place where users paste markup. -- **Not a wall-hugging constraint.** The magnet is a *placement aid*, not a - binding: once placed, a piece keeps absolute coordinates and does not follow - a wall that is later moved. Binding furniture to walls is what would make a - room resize (docs/RESIZE.md) start dragging sofas around, and that is a - bigger promise than a decor layer should make. -- **Not a stamp that keeps stamping.** See §4. +`pack.json` is the source of truth for stable ids, categories, names, +default centimetre dimensions and SVG paths. Run: + +```powershell +npm run furniture:generate +npm run furniture:check +``` + +The generator validates a deliberately small, inert SVG subset and produces: + +- `src/furniture-plan-art.generated.ts` for the initial View graph; +- `src/furniture-menu-art.generated.ts` for the lazy editor graph only. + +Generated files are never edited manually. The 77 drawings were created by +Sergey Matyushin (`Matysh`) and granted to the project under its MIT License in +[issue #159](https://github.com/Matysh/houseplan-card/issues/159#issuecomment-5454085168). +No separate attribution is required in the interface. + +## Palette interaction + +Furniture is a two-level non-modal palette in the editor context tray: + +1. The first level shows front-view **categories**, grouped as Furniture, + Appliances, Plumbing and Other. +2. Every category opens a second level, including categories that have only + one variant. +3. The second level shows the real top-view drawings. Back returns to the + category list and clears any armed symbol. +4. Picking a variant arms one placement and reveals editable Width and Depth + in the Home Assistant length unit. +5. Clicking the plan places the object and returns to Select. Shift keeps the + established free-placement behaviour. + +The properties dialog remains a flat native select, grouped by category. Its +optgroup label includes both the parent group and category because HTML selects +cannot nest optgroups. + +## Rendering contract + +Each object is still one `` and one erase hit path. Designer artwork +keeps its native SVG `viewBox`; the renderer applies the user's stored width +and depth with a non-uniform transform and uses +`vector-effect="non-scaling-stroke"`. Consequently: + +- resizing changes the physical object box without distorting line weight; +- the user's decor colour, opacity and physical line width remain authoritative; +- `data-hp="decor"`, `data-kind="furniture"`, `data-id` and `data-symbol` + remain stable for card-mod; +- an id unknown to an older card remains valid data and simply renders + nothing instead of breaking the plan. + +The top edge of every drawing is BACK (`y = 0`). Placement and dragging use +the established wall magnet, absolute saved coordinates and the shared +selection/resize/rotate frame. Furniture is decor: it has no entity, state, +room aggregation, collision model or automatic binding to later wall edits. + +## Compatibility and performance + +No backend schema, configuration version or migration was added. The 18 +replacement ids preserve their identity; the remaining old ids remain +available. Default dimensions change only for a newly picked replacement—an +already saved object's `w` and `h` are never rewritten. + +Plan artwork is available in View and kiosk. Front-view menu art is imported +only after the editor runtime is requested. Touch View/kiosk support is +blocking; editor ergonomics on touch remain best effort under +`docs/TOUCH-SUPPORT.md`. diff --git a/docs/STATUS.md b/docs/STATUS.md index 5d7efa03..fee2b99f 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -30,6 +30,7 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate. | HACS | **In the default catalog since 2026-08-25** (hacs/default#9004 merged). Install = plain HACS search. Post-merge checklist: run the manual zip workflow on the next stable tag after merging to main; forum/4pda announcement | | 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/de (src/i18n/*.json), everything user-visible localized incl. kiosk popover; German is loaded lazily through the registry introduced by #62 | +| Furniture | #159 replaces the flat ~30-item picker with a two-level category/variant palette and 56 top-view symbols. The reviewed 77-SVG MIT source pack is vendored under `assets/furniture/houseplan-0.3.0`; plan art stays in the initial View graph, front-view category art stays in the lazy editor graph, and existing saved geometry is unchanged. | | Tests | Four layers: Node unit (`npm test`: frontend pure modules + tooling policy), pure backend (`pytest tests_backend`, runs anywhere), HA-harness backend (same folder, CI only — needs py3.13 + pytest-homeassistant-custom-component), and browser smokes (`demo/smoke_*.mjs`, headless chromium). **Counts are not written down here** — they went stale within two releases while the version line beside them was kept current, which reads as less coverage than exists (review R5-2). Run `npm run inventory` for the current numbers, or read them off the last CI run | | Input support | Owner's rule since 2026-08-08: View and kiosk are fully supported and release-blocking on touch. All three editors are desktop-first; touch editing is best effort and may be awkward, reduced or absent when parity is expensive. `docs/TOUCH-SUPPORT.md` defines the non-negotiable safety floor and documentation/test rules | | Vacuums | Live puck, server-side trails and fit calibration are shipped. The local v1.61 Stage 1 contract in docs/VACUUM.md adds explicit Dreame/XCME/Valetudo coverage, registry-less source selection, capability diagnostics, path-gap preservation and source-health warnings; #205's issue branch resumes one ended same-map run through an inclusive 30-minute station/pause grace. Roomba remains Stage 2 | diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 03a76ade..61f85960 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -688,13 +688,18 @@ The authoritative technical interaction contract is | Rectangle | Drag diagonal; Shift makes a square | Stroke plus independent fill, size and angle | | Oval | Drag bounds; Shift makes a circle | Stroke plus independent fill, radii and angle | | Text | Click to open dialog | Multiline text, HA tokens, colour, physical size and angle | -| Furniture | Pick symbol, then click | Symbol, size, colour, outline and wall magnet | +| Furniture | Pick a front-view category, pick a top-view variant, then click | Symbol, size, colour, outline and wall magnet | | Erase | Click an item | Confirmed deletion, undoable | Creation and transform snap to the grid plus nearby room/background anchors. The plan image is interactive only with Backdrop selected. Undo/Redo shares the 50-command editor history. +The Furniture palette always uses two levels: categories first, then the +available plan variants. **All categories** returns to the first level and +disarms the current symbol. Existing placed furniture keeps its saved size and +position when the built-in artwork is updated. + ![Selected line in the Background editor](images/07-background-editor.png) Live text accepts `{sensor.entity}` and diff --git a/docs/USER-GUIDE.ru.md b/docs/USER-GUIDE.ru.md index 82904b18..0370b2f7 100644 --- a/docs/USER-GUIDE.ru.md +++ b/docs/USER-GUIDE.ru.md @@ -1237,7 +1237,7 @@ Power=`off`/`unavailable` подавляет даже устаревший ак | Прямоугольник | Drag по диагонали; `Shift` фиксирует квадрат | Контур и независимая заливка с прозрачностью; показываются ширина × высота и площадь | | Овал | Drag по габаритам; `Shift` фиксирует круг | Контур и независимая заливка с прозрачностью; показывается `R` либо `Rx × Ry` | | Текст | Клик открывает диалог | Многострочный текст, переменные HA, цвет/прозрачность, физический размер и угол; масштаб и поворот ручками | -| Мебель | Выбор символа и клик по центру | Символ, размер, цвет, толщина, поворот; магнитится к стене | +| Мебель | Выбор категории спереди, затем варианта сверху и клик по центру | Символ, размер, цвет, толщина, поворот; магнитится к стене | | Стереть | Клик по объекту | После подтверждения удаляет объект; действие можно отменить через Undo | Новые фигуры создаются не меньше половины клетки. После создания объект остаётся выбранным. Создание, движение и изменение размера всегда квантуются по сетке. При рисовании и перемещении действует лёгкий магнит к углам, серединам, центрам и рёбрам других объектов декора и комнат; устройства, проёмы и картинка-подложка не являются целями магнита. @@ -1255,6 +1255,11 @@ Power=`off`/`unavailable` подавляет даже устаревший ак Редактор подложки использует тот же именованный Undo/Redo на 50 команд, что и геометрия плана. `Esc` отменяет только незавершённое рисование или текущий drag/resize/поворот; после отпускания указателя используйте `Ctrl+Z`/`Cmd+Z`. Повтор — `Ctrl+Shift+Z` либо `Ctrl+Y`. +Палитра мебели всегда двухуровневая: сначала категории, затем доступные виды +сверху. Кнопка **Все категории** возвращает на первый уровень и снимает выбор +символа. Уже размещённая мебель сохраняет размеры и положение при обновлении +встроенных рисунков. + ### Двойной клик в «Выбрать» | Объект | Что открывается | diff --git a/package.json b/package.json index 390a741d..b113f513 100755 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "release:prerelease": "node scripts/release-prerelease.mjs", "release:notes": "node scripts/release-notes.mjs", "prepare": "node scripts/install-hooks.mjs", - "benchmark:junction-limits": "tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node demo/benchmark_junction_limits.mjs" + "benchmark:junction-limits": "tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node demo/benchmark_junction_limits.mjs", + "furniture:generate": "node scripts/generate-furniture-assets.mjs", + "furniture:check": "node scripts/generate-furniture-assets.mjs --check" }, "devDependencies": { "@mdi/js": "^7.4.47", diff --git a/scripts/generate-furniture-assets.mjs b/scripts/generate-furniture-assets.mjs new file mode 100644 index 00000000..cc4df3ce --- /dev/null +++ b/scripts/generate-furniture-assets.mjs @@ -0,0 +1,171 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const PACK = path.join(ROOT, 'assets', 'furniture', 'houseplan-0.3.0'); +// Deliberately not named *manifest.json: HACS rejects a repository that +// contains any second file matching that suffix outside the integration. +const MANIFEST = path.join(PACK, 'pack.json'); +const PLAN_OUT = path.join(ROOT, 'src', 'furniture-plan-art.generated.ts'); +const MENU_OUT = path.join(ROOT, 'src', 'furniture-menu-art.generated.ts'); +const CHECK = process.argv.includes('--check'); + +const fail = (message) => { throw new Error(`Furniture pack: ${message}`); }; +const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key); +const plainObject = (value, label) => { + if (!value || typeof value !== 'object' || Array.isArray(value)) fail(`${label} must be an object`); +}; +const exactKeys = (value, required, optional, label) => { + plainObject(value, label); + for (const key of required) if (!own(value, key)) fail(`${label}.${key} is required`); + const allowed = new Set([...required, ...optional]); + for (const key of Object.keys(value)) if (!allowed.has(key)) fail(`${label}.${key} is not allowed`); +}; +const id = (value, label) => { + if (typeof value !== 'string' || !/^[a-z][a-z0-9_]*$/.test(value)) fail(`${label} is not a stable id`); + return value; +}; +const text = (value, label) => { + if (typeof value !== 'string' || !value.trim()) fail(`${label} must be non-empty text`); + return value; +}; +const positive = (value, label) => { + if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) fail(`${label} must be positive`); + return value; +}; +const relSvg = (value, prefix, label) => { + text(value, label); + if (!new RegExp(`^svg/${prefix}/[a-z][a-z0-9_]*\\.svg$`).test(value)) fail(`${label} has an unsafe path`); + return value; +}; + +function parseAttrs(source, label) { + const attrs = Object.create(null); + const re = /([:\w-]+)\s*=\s*"([^"]*)"/g; + let scrubbed = source; + for (const match of source.matchAll(re)) { + if (own(attrs, match[1])) fail(`${label} repeats ${match[1]}`); + attrs[match[1]] = match[2]; + scrubbed = scrubbed.replace(match[0], ''); + } + if (scrubbed.trim()) fail(`${label} contains malformed attributes`); + return attrs; +} + +function svgArt(file, expectedViewBox) { + const label = path.relative(ROOT, file).replaceAll('\\', '/'); + const source = fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '').trim(); + if (/]*)>/g)]; + if (!tags.length || tags[0][1] !== 'svg' || !source.endsWith('')) fail(`${label} is not one SVG root`); + for (const tag of tags) if (!['svg', 'g', 'path'].includes(tag[1])) fail(`${label} contains <${tag[1]}>`); + + const root = source.match(/^]*)>/); + if (!root) fail(`${label} has no SVG root attributes`); + const svgAttrs = parseAttrs(root[1], `${label} `); + const svgKeys = Object.keys(svgAttrs).sort().join(','); + if (svgKeys !== 'viewBox,xmlns' || svgAttrs.xmlns !== 'http://www.w3.org/2000/svg') fail(`${label} has unsupported SVG attributes`); + const view = svgAttrs.viewBox.trim().split(/\s+/).map(Number); + if (view.length !== 4 || view.some((n) => !Number.isFinite(n)) || view[0] !== 0 || view[1] !== 0 || view[2] <= 0 || view[3] <= 0) { + fail(`${label} has an invalid viewBox`); + } + if (expectedViewBox && (view[2] !== expectedViewBox[0] || view[3] !== expectedViewBox[1])) { + fail(`${label} viewBox does not match manifest dimensions`); + } + + for (const match of source.matchAll(/]*)>/g)) { + if (match[1].trim()) fail(`${label} attributes are not allowed`); + } + const paths = []; + for (const match of source.matchAll(/]*)\/?\s*>/g)) { + const attrs = parseAttrs(match[1].replace(/\/$/, ''), `${label} `); + const allowed = new Set(['d', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin']); + for (const key of Object.keys(attrs)) if (!allowed.has(key) || key.startsWith('on')) fail(`${label} path attribute ${key} is not allowed`); + if (attrs.fill !== 'none' || attrs.stroke !== 'currentColor') fail(`${label} paths must use fill=none and stroke=currentColor`); + if (!/^\d+(?:\.\d+)?$/.test(attrs['stroke-width'] || '')) fail(`${label} has an invalid stroke width`); + if (attrs['stroke-linecap'] !== 'round' || attrs['stroke-linejoin'] !== 'round') fail(`${label} paths must use rounded joins`); + const d = text(attrs.d, `${label} path.d`).trim().replace(/\s+/g, ' '); + if (!/^[0-9eE+.,\-\sMmZzLlHhVvCcSsQqTtAa]+$/.test(d)) fail(`${label} path.d contains unsupported data`); + paths.push(d); + } + if (!paths.length) fail(`${label} has no paths`); + return { d: paths.join(' '), viewW: view[2], viewH: view[3] }; +} + +function loadPack() { + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + exactKeys(manifest, + ['schema_version', 'view_schema', 'pack_id', 'pack_version', 'title_ru', 'title_en', 'author', 'license', 'source_url', 'menu_icons', 'symbols'], + [], 'manifest'); + if (manifest.schema_version !== 1 || manifest.view_schema !== 2 || manifest.pack_id !== 'houseplan' || manifest.pack_version !== '0.3.0') fail('unsupported manifest identity'); + if (manifest.author !== 'Sergey Matyushin (Matysh)' || manifest.license !== 'MIT') fail('release provenance is incomplete'); + text(manifest.title_ru, 'manifest.title_ru'); text(manifest.title_en, 'manifest.title_en'); text(manifest.source_url, 'manifest.source_url'); + if (!Array.isArray(manifest.menu_icons) || manifest.menu_icons.length !== 33) fail('exactly 33 menu icons are required'); + if (!Array.isArray(manifest.symbols) || manifest.symbols.length !== 44) fail('exactly 44 plan symbols are required'); + + const menuIds = new Set(); + const menu = manifest.menu_icons.map((entry, index) => { + exactKeys(entry, ['id', 'name_ru', 'name_en', 'group', 'file'], [], `menu_icons[${index}]`); + const entryId = id(entry.id, `menu_icons[${index}].id`); + if (menuIds.has(entryId)) fail(`duplicate menu icon ${entryId}`); menuIds.add(entryId); + if (!['furniture', 'appliance', 'sanitary', 'other'].includes(entry.group)) fail(`menu icon ${entryId} has an invalid group`); + relSvg(entry.file, 'menu', `menu icon ${entryId}.file`); + const base = path.basename(entry.file, '.svg'); + if (base !== entryId) fail(`menu icon ${entryId} filename must match its id`); + return { id: entryId, group: entry.group, nameEn: text(entry.name_en, `${entryId}.name_en`), nameRu: text(entry.name_ru, `${entryId}.name_ru`), art: svgArt(path.join(PACK, entry.file)) }; + }); + + const symbolIds = new Set(); + const symbols = manifest.symbols.map((entry, index) => { + exactKeys(entry, ['id', 'operation', 'name_ru', 'name_en', 'group', 'width_cm', 'depth_cm', 'back', 'file', 'menu_icon', 'notes'], [], `symbols[${index}]`); + const entryId = id(entry.id, `symbols[${index}].id`); + if (symbolIds.has(entryId)) fail(`duplicate plan symbol ${entryId}`); symbolIds.add(entryId); + if (!['replace', 'add'].includes(entry.operation)) fail(`symbol ${entryId} has an invalid operation`); + if (!['furniture', 'appliance', 'sanitary', 'other'].includes(entry.group)) fail(`symbol ${entryId} has an invalid group`); + if (entry.back !== 'top') fail(`symbol ${entryId} must keep BACK at the top`); + const width = positive(entry.width_cm, `${entryId}.width_cm`); + const depth = positive(entry.depth_cm, `${entryId}.depth_cm`); + relSvg(entry.file, 'plan', `symbol ${entryId}.file`); + if (path.basename(entry.file, '.svg') !== entryId) fail(`symbol ${entryId} filename must match its id`); + if (!menuIds.has(entry.menu_icon)) fail(`symbol ${entryId} refers to unknown menu icon ${entry.menu_icon}`); + const menuGroup = menu.find((item) => item.id === entry.menu_icon)?.group; + if (menuGroup !== entry.group) fail(`symbol ${entryId} and its menu icon disagree on group`); + return { id: entryId, operation: entry.operation, group: entry.group, category: entry.menu_icon, + w: width, h: depth, nameEn: text(entry.name_en, `${entryId}.name_en`), nameRu: text(entry.name_ru, `${entryId}.name_ru`), + art: svgArt(path.join(PACK, entry.file), [width, depth]) }; + }); + + for (const folder of ['menu', 'plan']) { + const declared = new Set((folder === 'menu' ? manifest.menu_icons : manifest.symbols).map((entry) => path.basename(entry.file))); + const actual = fs.readdirSync(path.join(PACK, 'svg', folder)).filter((file) => file.endsWith('.svg')); + if (actual.length !== declared.size || actual.some((file) => !declared.has(file))) fail(`svg/${folder} contains undeclared SVG files`); + } + return { menu, symbols }; +} + +const header = `/* Generated by scripts/generate-furniture-assets.mjs. DO NOT EDIT. */\n`; +function renderPlan(symbols) { + const runtime = symbols.map(({ id, group, category, w, h, art }) => ({ + id, group, category, w, h, art, + })); + return header + `export const GENERATED_FURNITURE_PLAN = ${JSON.stringify(runtime)} as const;\n`; +} +function renderMenu(menu) { + const runtime = menu.map(({ id, group, art }) => ({ id, group, art })); + return header + `export const GENERATED_FURNITURE_MENU = ${JSON.stringify(runtime)} as const;\n`; +} +function output(file, content) { + const normalized = content.replaceAll('\\/', '/'); + if (CHECK) { + if (!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== normalized) fail(`${path.relative(ROOT, file)} is stale; run npm run furniture:generate`); + } else { + fs.writeFileSync(file, normalized, 'utf8'); + } +} + +const pack = loadPack(); +output(PLAN_OUT, renderPlan(pack.symbols)); +output(MENU_OUT, renderMenu(pack.menu)); +console.log(`Furniture pack OK: ${pack.symbols.length} plan symbols, ${pack.menu.length} menu icons${CHECK ? ' (generated files current)' : ''}.`); diff --git a/src/furniture-menu-art.generated.ts b/src/furniture-menu-art.generated.ts new file mode 100644 index 00000000..79305e32 --- /dev/null +++ b/src/furniture-menu-art.generated.ts @@ -0,0 +1,2 @@ +/* Generated by scripts/generate-furniture-assets.mjs. DO NOT EDIT. */ +export const GENERATED_FURNITURE_MENU = [{"id":"air_conditioner","group":"appliance","art":{"d":"M20.5 39h69a2.5 2.5 0 0 1 2.5 2.5V69a2.5 2.5 0 0 1-2.5 2.5h-69A2.5 2.5 0 0 1 18 69V41.5a2.5 2.5 0 0 1 2.5-2.5 M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v8.5H25z M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v3.5H25z","viewW":110,"viewH":110}},{"id":"armchair","group":"furniture","art":{"d":"M42.5 29h25a10 10 0 0 1 10 10v26.5a10 10 0 0 1-10 10h-25a10 10 0 0 1-10-10V39a10 10 0 0 1 10-10 M77.5 48.5a6 6 0 0 1 2 11.657V71.5a4 4 0 0 1-4 4H34a4 4 0 0 1-4-4V60.157A6.001 6.001 0 0 1 32 48.5a6 6 0 0 1 5.419 3.424A4 4 0 0 1 38 54v7h33.5v-6.5q0-.107.005-.213A6 6 0 0 1 77.5 48.5 M42.75 57h24.5A4.75 4.75 0 0 1 72 61.75a4.75 4.75 0 0 1-4.75 4.75h-24.5A4.75 4.75 0 0 1 38 61.75 4.75 4.75 0 0 1 42.75 57M36.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 41.392 82H38a1.5 1.5 0 0 1-1.5-1.5zm36 0h-8l1.674 5.441A1.5 1.5 0 0 0 67.608 82H71a1.5 1.5 0 0 0 1.5-1.5z","viewW":110,"viewH":110}},{"id":"bathtub","group":"sanitary","art":{"d":"M28 75h8l-1.674 5.441a1.5 1.5 0 0 1-1.434 1.059H29.5A1.5 1.5 0 0 1 28 80zm53 0h-8l1.674 5.441a1.5 1.5 0 0 0 1.434 1.059H79.5A1.5 1.5 0 0 0 81 80zM19.5 46.5H90a2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5H19.5A2.5 2.5 0 0 1 17 49a2.5 2.5 0 0 1 2.5-2.5 M19.5 51.5H90L86.633 68a10 10 0 0 1-9.798 8h-44.17a10 10 0 0 1-9.798-8zM30 36v-2a5 5 0 1 0-10 0v12.5","viewW":110,"viewH":110}},{"id":"bed","group":"furniture","art":{"d":"M16 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 20.892 82H17.5a1.5 1.5 0 0 1-1.5-1.5zm78.5 0h-8l1.674 5.441A1.5 1.5 0 0 0 89.608 82H93a1.5 1.5 0 0 0 1.5-1.5zM25 29h60.5a2.5 2.5 0 0 1 2.5 2.5V68a2.5 2.5 0 0 1-2.5 2.5H25a2.5 2.5 0 0 1-2.5-2.5V31.5A2.5 2.5 0 0 1 25 29 M16 59.5c0-5.523 4.477-10 10-10h58.5c5.523 0 10 4.477 10 10v6H16zm-1 6h80.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H15a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1m15.5-20a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4h-21zm28.5 0a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4H59z","viewW":110,"viewH":110}},{"id":"bidet","group":"sanitary","art":{"d":"M36 57.5c0 5.523 4.477 10 10 10h18.5c5.523 0 10-4.477 10-10V53H36zm8.5 22a1 1 0 0 0 1 1H65a1 1 0 0 0 1-1v-12H44.5zm-8.5-32a1 1 0 0 1 1-1h36.5a1 1 0 0 1 1 1V53H36zM50.5 41a1 1 0 0 1 1-1H59a1 1 0 0 1 1 1v5.5h-9.5zM53 30a1 1 0 0 1 1-1h2.5a1 1 0 0 1 1 1v10H53zm-4 6a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 1-1h4V36z","viewW":110,"viewH":110}},{"id":"boiler","group":"appliance","art":{"d":"M46.5 81.5h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zm12.5 0h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zM44 23h22.5a8 8 0 0 1 8 8v42.5a8 8 0 0 1-8 8H44a8 8 0 0 1-8-8V31a8 8 0 0 1 8-8 M36 31a8 8 0 0 1 8-8h22.5a8 8 0 0 1 8 8v42H36z M48.5 42.5a7 7 0 1 0 14 0 7 7 0 1 0-14 0m6.5 0 3.5-3.5","viewW":110,"viewH":110}},{"id":"chair","group":"furniture","art":{"d":"M40.5 39.5C40.5 32.596 46.096 27 53 27h4c6.904 0 12.5 5.596 12.5 12.5v13a5 5 0 0 1-5 5h-19a5 5 0 0 1-5-5z M41.25 54h27.5A3.25 3.25 0 0 1 72 57.25a3.25 3.25 0 0 1-3.25 3.25h-27.5A3.25 3.25 0 0 1 38 57.25 3.25 3.25 0 0 1 41.25 54m28.25 6.5H64l1.88 21.63a1.5 1.5 0 0 0 1.495 1.37H68a1.5 1.5 0 0 0 1.5-1.5zm-29 0H46l-1.88 21.63a1.5 1.5 0 0 1-1.495 1.37H42a1.5 1.5 0 0 1-1.5-1.5z","viewW":110,"viewH":110}},{"id":"coffee_table","group":"furniture","art":{"d":"M25 51h5.238l-1.534 15.646A1.5 1.5 0 0 1 27.211 68H26.5a1.5 1.5 0 0 1-1.5-1.5zm60.238 0H80l1.534 15.646A1.5 1.5 0 0 0 83.027 68h.711a1.5 1.5 0 0 0 1.5-1.5z M24.34 43.485A2.5 2.5 0 0 1 26.625 42h56.75a2.5 2.5 0 0 1 2.285 1.485l2.715 6.109A1 1 0 0 1 87.461 51H22.54a1 1 0 0 1-.914-1.406z M87.5 51h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z","viewW":110,"viewH":110}},{"id":"computer","group":"appliance","art":{"d":"M24.5 38h40a2.5 2.5 0 0 1 2.5 2.5v22a2.5 2.5 0 0 1-2.5 2.5h-40a2.5 2.5 0 0 1-2.5-2.5v-22a2.5 2.5 0 0 1 2.5-2.5m50 0h11a2.5 2.5 0 0 1 2.5 2.5v29a2.5 2.5 0 0 1-2.5 2.5h-11a2.5 2.5 0 0 1-2.5-2.5v-29a2.5 2.5 0 0 1 2.5-2.5M43 65h5v7h-5zm-6 7h16m23-12h8m-8-17h8 M35 56V46h5m-5 7h5 M39.5 53a3.5 3.5 0 1 0 0-7m15.036 8.536a5 5 0 1 1 0-7.071","viewW":110,"viewH":110}},{"id":"cooktop","group":"appliance","art":{"d":"M27.5 29h55a2.5 2.5 0 0 1 2.5 2.5v47a2.5 2.5 0 0 1-2.5 2.5h-55a2.5 2.5 0 0 1-2.5-2.5v-47a2.5 2.5 0 0 1 2.5-2.5 M33.5 66.5a8 8 0 1 0 16 0 8 8 0 1 0-16 0m26.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0m3 23a5 5 0 1 0 10 0 5 5 0 1 0-10 0m-29.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0","viewW":110,"viewH":110}},{"id":"dining_table","group":"furniture","art":{"d":"M83.5 43H78l6.742 30.82A1.5 1.5 0 0 0 86.207 75h.54a1.5 1.5 0 0 0 1.482-1.732zm-56 0H33l-6.742 30.82A1.5 1.5 0 0 1 24.793 75h-.54a1.5 1.5 0 0 1-1.482-1.732zm-2.62-7.406A2.5 2.5 0 0 1 27.21 34h56.58a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044A1 1 0 0 1 87.538 43H23.462a1 1 0 0 1-.932-1.362z M88.5 43h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z","viewW":110,"viewH":110}},{"id":"dishwasher","group":"appliance","art":{"d":"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5M43 42h24m-1.5-11a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5","viewW":110,"viewH":110}},{"id":"dryer","group":"appliance","art":{"d":"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5 M55.994 55.5s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m5.979-11s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m-8.519-11s-.886 1.81-.986 3.143c-.132 1.761 1.066 2.554.986 4.321-.067 1.479-.986 3.536-.986 3.536","viewW":110,"viewH":110}},{"id":"exercise","group":"other","art":{"d":"m60.752 62.03-5.379-2.312 11.066-25.75a1.5 1.5 0 0 1 1.97-.786l2.624 1.127a1.5 1.5 0 0 1 .786 1.97zm-18.648-4.512-4.353 1.189-4.474-16.382a1.5 1.5 0 0 1 1.052-1.842l1.459-.398a1.5 1.5 0 0 1 1.842 1.052z M41.297 38.173a2.215 2.215 0 0 1 0 4.43H29.781a2.215 2.215 0 0 1 0-4.43zm33.219 38.091a2.657 2.657 0 1 1 0 5.315H28.453a2.657 2.657 0 1 1 0-5.315zm8.641-51.366a1.795 1.795 0 0 1 .68 2.41l-3.03 5.59a5 5 0 0 1-4.396 2.618H65.657a1.772 1.772 0 1 1 0-3.544h9.61a3 3 0 0 0 2.598-1.5l2.84-4.917a1.795 1.795 0 0 1 2.452-.657M29.339 65.787c0-6.699 6.457-11.503 12.873-9.579l24.291 7.288a10 10 0 0 1 7.127 9.578v3.19H29.339zm0 15.852h7.086l-1.394 3.486a1.5 1.5 0 0 1-1.393.943h-2.8a1.5 1.5 0 0 1-1.5-1.5zm44.291 0h-7.087l1.395 3.486a1.5 1.5 0 0 0 1.392.943h2.8a1.5 1.5 0 0 0 1.5-1.5z M35.54 66.077a4.872 4.872 0 1 0 9.743 0 4.872 4.872 0 1 0-9.744 0","viewW":110,"viewH":110}},{"id":"fireplace","group":"other","art":{"d":"M26.672 34.425h55.99v39.47h-55.99Z M23 74.894a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v5.802a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zM23 29a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v4.425a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zm15.145 21.44a5 5 0 0 1 5-5h23.043a5 5 0 0 1 5 5v23.454H38.145z M44.57 65.633a2.754 2.754 0 0 1 2.754-2.753H62.01v5.507H47.324a2.754 2.754 0 0 1-2.754-2.754m20.194 5.507a2.754 2.754 0 0 0-2.754-2.753H47.324v5.507H62.01a2.754 2.754 0 0 0 2.754-2.754 M44.57 71.14a2.754 2.754 0 1 0 5.508 0 2.754 2.754 0 1 0-5.508 0m14.688-5.507a2.754 2.754 0 1 0 5.507 0 2.754 2.754 0 1 0-5.507 0","viewW":110,"viewH":110}},{"id":"fridge","group":"appliance","art":{"d":"M35.5 18H75a2.5 2.5 0 0 1 2.5 2.5v64A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5v-64a2.5 2.5 0 0 1 2.5-2.5 M33 46.5h44.5v38A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5zm2.5 44a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V87h-39zm3-49.5v-9.5m0 30.5v-9.5","viewW":110,"viewH":110}},{"id":"hood","group":"appliance","art":{"d":"M44.308 57h21.384L83 69H27zM44 37.5a2.5 2.5 0 0 1 2.5-2.5h17a2.5 2.5 0 0 1 2.5 2.5V57H44zM80.5 74h-51a2.5 2.5 0 0 1-2.5-2.5V70a1 1 0 0 1 1-1h54a1 1 0 0 1 1 1v1.5a2.5 2.5 0 0 1-2.5 2.5","viewW":110,"viewH":110}},{"id":"kitchen_cabinet","group":"furniture","art":{"d":"M38 88a1 1 0 0 0 1 1h32a1 1 0 0 0 1-1v-3H38zm17-68h16.5a2.5 2.5 0 0 1 2.5 2.5v17a2.5 2.5 0 0 1-2.5 2.5H55zm-19 2.5a2.5 2.5 0 0 1 2.5-2.5H55v22H38.5a2.5 2.5 0 0 1-2.5-2.5zM59 33v5m-8-5v5m4 47h16.5a2.5 2.5 0 0 0 2.5-2.5v-17a2.5 2.5 0 0 0-2.5-2.5H55zm-19-2.5a2.5 2.5 0 0 0 2.5 2.5H55V63H38.5a2.5 2.5 0 0 0-2.5 2.5zM67 67h-5m-14 0h-5","viewW":110,"viewH":110}},{"id":"kitchen_sink","group":"sanitary","art":{"d":"M21.5 84h68a2.5 2.5 0 0 0 2.5-2.5v-35a2.5 2.5 0 0 0-2.5-2.5h-68a2.5 2.5 0 0 0-2.5 2.5v35a2.5 2.5 0 0 0 2.5 2.5 M26 78h26.5a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H26a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m23-24v5m9.5 19H85a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H58.5a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m-6-39.5a1 1 0 0 0-1-1H44a1 1 0 0 0-1 1V44h9.5zM58 33v-2a5 5 0 1 0-10 0v6.5M62 54v5","viewW":110,"viewH":110}},{"id":"nightstand","group":"furniture","art":{"d":"M76.5 73.5h-8l1.674 5.441A1.5 1.5 0 0 0 71.608 80H75a1.5 1.5 0 0 0 1.5-1.5zm-42.5 0h8l-1.674 5.441A1.5 1.5 0 0 1 38.892 80H35.5a1.5 1.5 0 0 1-1.5-1.5z M34.5 30.5h41A2.5 2.5 0 0 1 78 33v40.5a2.5 2.5 0 0 1-2.5 2.5h-41a2.5 2.5 0 0 1-2.5-2.5V33a2.5 2.5 0 0 1 2.5-2.5 M32 39.5h46v18H32Zm18.5 9H60M50.5 66H60","viewW":110,"viewH":110}},{"id":"oven","group":"appliance","art":{"d":"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5 M40.5 47h29a2.5 2.5 0 0 1 2.5 2.5v20a2.5 2.5 0 0 1-2.5 2.5h-29a2.5 2.5 0 0 1-2.5-2.5v-20a2.5 2.5 0 0 1 2.5-2.5m25-16a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5M43 42h24","viewW":110,"viewH":110}},{"id":"plant","group":"other","art":{"d":"M45.952 75.849a2.5 2.5 0 0 0 2.485 2.231H63.43a2.5 2.5 0 0 0 2.485-2.231l1.571-14.532H44.381zM60.236 44.78C58.423 36.174 66.805 29.605 77 31.87c0 8.155-8.609 16.763-16.763 12.912m-9.761 5.147c1.517-8.665-7.085-14.943-17.195-12.33.28 8.15 9.178 16.458 17.195 12.33 M55.706 55.88v-2.944m0 0c1.133-5.89 7.09-10.956 12.912-15.404M55.706 52.936c-4.938-4.146-7.982-6.16-14.044-9.061m1.453 12.005h25.636a1 1 0 0 1 1 1v3.437a1 1 0 0 1-1 1H43.115a1 1 0 0 1-1-1v-3.436a1 1 0 0 1 1-1","viewW":110,"viewH":110}},{"id":"rug","group":"other","art":{"d":"M90.27 33.874h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.154h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20M28.12 32h54.025a2.5 2.5 0 0 1 2.5 2.5v40.439a2.5 2.5 0 0 1-2.5 2.5H28.12a2.5 2.5 0 0 1-2.5-2.5V34.5a2.5 2.5 0 0 1 2.5-2.5 M25.62 34.5a2.5 2.5 0 0 1 2.5-2.5h1.717v45.439H28.12a2.5 2.5 0 0 1-2.5-2.5zm59.026 0a2.5 2.5 0 0 0-2.5-2.5H80.43v45.439h1.716a2.5 2.5 0 0 0 2.5-2.5zM37.8 54.485l17.801-8.432 17.801 8.432-17.8 8.432z","viewW":110,"viewH":110}},{"id":"shelving","group":"furniture","art":{"d":"M28 58.5h53V39H28Z M64 58.5h5V47a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1zM52.5 39h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-12 0h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-5 29.5V71a1 1 0 0 1 1 1H49a1 1 0 0 1 1-1v-2.5a1 1 0 0 1-1-1H36.5a1 1 0 0 1-1 1m33.5-10h7v-14a1 1 0 0 0-1-1h-5a1 1 0 0 0-1 1zM47.5 39h-7V25a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1zm-12 34v4a1 1 0 0 1 1 1h14a1 1 0 0 1 1-1v-4a1 1 0 0 1-1-1h-14a1 1 0 0 1-1 1M28 78h53v7H28Z M28 89.5V23a2.5 2.5 0 0 1 2.5-2.5h48A2.5 2.5 0 0 1 81 23v66.5","viewW":110,"viewH":110}},{"id":"shower","group":"sanitary","art":{"d":"M31 84h49a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H31a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m31.5-47.6v-4.686a11.7 11.7 0 0 0-3.441-8.283A11.77 11.77 0 0 0 50.75 20a11.77 11.77 0 0 0-8.309 3.431A11.7 11.7 0 0 0 39 31.714V84 M72 39.25a10.25 10.25 0 1 0-20.5 0zm-17.25 5v2.5m7.25-2.5v2.5m7.25-2.5v2.5m-14.5 3.75V53M62 50.5V53m7.25-2.5V53m-14.5 3.75v2.5m7.25-2.5v2.5m7.25-2.5v2.5M54.75 63v2.5M62 63v2.5m7.25-2.5v2.5","viewW":110,"viewH":110}},{"id":"sink","group":"sanitary","art":{"d":"M30 57c0 5.523 4.477 10 10 10h29.5c5.523 0 10-4.477 10-10v-6.5A2.5 2.5 0 0 0 77 48H32.5a2.5 2.5 0 0 0-2.5 2.5zm14 22a1 1 0 0 0 1 1h19.5a1 1 0 0 0 1-1V67H44zm6-36.5a1 1 0 0 1 1-1h7.5a1 1 0 0 1 1 1V48H50zM45 37v-2a5 5 0 1 1 10 0v6.5","viewW":110,"viewH":110}},{"id":"sofa","group":"furniture","art":{"d":"M59.5 29h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5m-34 0h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5 M18.5 44a5 5 0 0 1 5 5v12H86V49a5 5 0 0 1 5-5h.5a5 5 0 0 1 5 5v21.5a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5V49a5 5 0 0 1 5-5z M25.25 57h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 25.25 57m34 0h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 59.25 57M18.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 23.392 82H20a1.5 1.5 0 0 1-1.5-1.5zm72 0h-8l1.674 5.441A1.5 1.5 0 0 0 85.608 82H89a1.5 1.5 0 0 0 1.5-1.5z","viewW":110,"viewH":110}},{"id":"stairs","group":"other","art":{"d":"M73.47 46.365v-17.62m-10.571 25.55V36.674M52.326 61.783V45.484M42.193 69.273v-15.86M28.979 77.761a1 1 0 0 1 1-1h53.624v6.489a1 1 0 0 1-1 1H29.98a1 1 0 0 1-1-1z M26.777 57.497a1 1 0 0 1 1-1h2.846a1 1 0 0 1 1 1V83.25a1 1 0 0 1-1 1h-2.846a1 1 0 0 1-1-1z m73.113 28.633-41.492 32.93v-4.405l39.9-32.007a.7.7 0 0 1 .428-.151h3.724a1.762 1.762 0 1 1 0 3.524h-2.248a.5.5 0 0 0-.312.109M25.895 53.414a3.084 3.084 0 1 0 6.167 0 3.084 3.084 0 1 0-6.167 0m14.978 16.859a1 1 0 0 1 1-1h41.73v7.488h-42.73zm10.131-7.489a1 1 0 0 1 1-1h31.598v7.488H51.004z M61.576 55.294a1 1 0 0 1 1-1h21.026v7.49H61.576z M72.15 47.806a1 1 0 0 1 1-1h9.454a1 1 0 0 1 1 1v6.489H72.15z","viewW":110,"viewH":110}},{"id":"toilet","group":"sanitary","art":{"d":"M32 26.5a2.5 2.5 0 0 1 2.5-2.5H45a2.5 2.5 0 0 1 2.5 2.5V57H32zM47.5 52h29a2.5 2.5 0 0 1 2.5 2.5V57H47.5zm17.037 20.102C73.95 70.288 79 62.01 79 57H32c.5 7.5 8.072 7 7 11l-2.342 16.862a1 1 0 0 0 .99 1.138H66.72a1 1 0 0 0 .97-1.243z","viewW":110,"viewH":110}},{"id":"tv","group":"appliance","art":{"d":"M23.5 31H86a2.5 2.5 0 0 1 2.5 2.5v34A2.5 2.5 0 0 1 86 70H23.5a2.5 2.5 0 0 1-2.5-2.5v-34a2.5 2.5 0 0 1 2.5-2.5M50 70h9.5v8H50Zm-10 8h30M42.5 45.5h5m5 0h-5m0 0v9.75 m56.75 45.5 5 10 5-10","viewW":110,"viewH":110}},{"id":"wardrobe","group":"furniture","art":{"d":"M28 23a2.5 2.5 0 0 1 2.5-2.5h24V85h-24a2.5 2.5 0 0 1-2.5-2.5zm2 65.5a1 1 0 0 0 1 1h47a1 1 0 0 0 1-1V85H30zM81 23a2.5 2.5 0 0 0-2.5-2.5h-24V85h24a2.5 2.5 0 0 0 2.5-2.5zM50.5 48v9.5m8-9.5v9.5","viewW":110,"viewH":110}},{"id":"washer","group":"appliance","art":{"d":"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5","viewW":110,"viewH":110}},{"id":"work_table","group":"furniture","art":{"d":"M88.5 47h-20v14h20zm-12 4h4m7.984-8h-66v4h66zm-66 4h4v26.5a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5z M24.866 35.597A2.5 2.5 0 0 1 27.197 34h56.577a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044a1 1 0 0 1-.93 1.362H23.46a1 1 0 0 1-.933-1.361zM88.5 61h-20v12.5A1.5 1.5 0 0 0 70 75h17a1.5 1.5 0 0 0 1.5-1.5zm-12 4h4","viewW":110,"viewH":110}}] as const; diff --git a/src/furniture-plan-art.generated.ts b/src/furniture-plan-art.generated.ts new file mode 100644 index 00000000..3cb8994c --- /dev/null +++ b/src/furniture-plan-art.generated.ts @@ -0,0 +1,2 @@ +/* Generated by scripts/generate-furniture-assets.mjs. DO NOT EDIT. */ +export const GENERATED_FURNITURE_PLAN = [{"id":"coffee_table","group":"furniture","category":"coffee_table","w":120,"h":60,"art":{"d":"M3.84 5.884c0-1.21 1.048-2.192 2.34-2.192h107.64c1.293 0 2.34.981 2.34 2.192v48.23c0 1.212-1.047 2.193-2.34 2.193H6.18c-1.292 0-2.34-.981-2.34-2.192z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877","viewW":120,"viewH":60}},{"id":"coffee_table_round","group":"furniture","category":"coffee_table","w":80,"h":80,"art":{"d":"M3.765 40a36.235 36.235 0 1 0 72.47 0 36.235 36.235 0 1 0-72.47 0 m56.26 26.96-3.843-3.843a.906.906 0 0 1 0-1.28l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.843 3.844a.906.906 0 0 1 0 1.28l-3.843 3.844a.906.906 0 0 1-1.281 0m-34.423-34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.844a.906.906 0 0 1 1.281 0l3.844 3.844a.906.906 0 0 1 0 1.28l-3.844 3.844a.906.906 0 0 1-1.28 0m-.001 34.66-3.843-3.843a.906.906 0 0 1 0-1.281l3.843-3.843a.906.906 0 0 1 1.281 0l3.844 3.843a.906.906 0 0 1 0 1.281l-3.844 3.843a.906.906 0 0 1-1.28 0","viewW":80,"viewH":80}},{"id":"coffee_table_oval","group":"furniture","category":"coffee_table","w":120,"h":60,"art":{"d":"M3.84 30c0-14.53 12.572-26.308 28.08-26.308h56.16c15.509 0 28.08 11.778 28.08 26.308s-12.571 26.307-28.08 26.307H31.92C16.412 56.307 3.84 44.53 3.84 30 M80.592 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877M31.92 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876m0 35.077v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.876h-5.616a.936.877 0 0 1-.936-.876","viewW":120,"viewH":60}},{"id":"coffee_table_rounded","group":"furniture","category":"coffee_table","w":120,"h":60,"art":{"d":"M3.84 12.461c0-4.843 4.19-8.77 9.36-8.77h93.6c5.17 0 9.36 3.927 9.36 8.77v35.077c0 4.843-4.19 8.77-9.36 8.77H13.2c-5.17 0-9.36-3.927-9.36-8.77z M103.056 15.093V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m-93.6-35.077V9.83a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877m0 35.078v-5.262a.936.877 0 0 1 .936-.877h5.616a.936.877 0 0 1 .936.877v5.262a.936.877 0 0 1-.936.877h-5.616a.936.877 0 0 1-.936-.877","viewW":120,"viewH":60}},{"id":"table_dining","group":"furniture","category":"dining_table","w":160,"h":90,"art":{"d":"M3.879 6.08c0-1.265 1.065-2.29 2.378-2.29h147.484c1.314 0 2.38 1.025 2.38 2.29v77.84c0 1.265-1.066 2.29-2.38 2.29H6.257c-1.313 0-2.378-1.025-2.378-2.29z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915","viewW":160,"viewH":90}},{"id":"table_round","group":"furniture","category":"dining_table","w":110,"h":110,"art":{"d":"M3.826 55a51.174 51.174 0 1 0 102.349 0A51.174 51.174 0 1 0 3.826 55 m81.005 32.303-3.947-3.947a.93.93 0 0 1 0-1.316l3.947-3.948a.93.93 0 0 1 1.316 0l3.948 3.948a.93.93 0 0 1 0 1.316l-3.948 3.947a.93.93 0 0 1-1.316 0m0 54.897-3.947-3.948a.93.93 0 0 1 0-1.316l3.947-3.947a.93.93 0 0 1 1.316 0l3.948 3.947a.93.93 0 0 1 0 1.316L82.32 87.2a.93.93 0 0 1-1.316 0M27.04 32.304l-3.947-3.948a.93.93 0 0 1 0-1.315l3.948-3.948a.93.93 0 0 1 1.315 0l3.948 3.948a.93.93 0 0 1 0 1.315l-3.948 3.948a.93.93 0 0 1-1.315 0m-.001 54.604-3.947-3.948a.93.93 0 0 1 0-1.316l3.948-3.947a.93.93 0 0 1 1.315 0l3.948 3.947a.93.93 0 0 1 0 1.316l-3.948 3.948a.93.93 0 0 1-1.315 0","viewW":110,"viewH":110}},{"id":"table_dining_oval","group":"furniture","category":"dining_table","w":180,"h":100,"art":{"d":"M3.892 50c0-25.51 21.418-46.19 47.838-46.19h76.54c26.42 0 47.839 20.68 47.839 46.19S154.69 96.19 128.27 96.19H51.73C25.31 96.19 3.892 75.51 3.892 50 M122.53 19.514V13.97a.957.924 0 0 1 .956-.924h5.74a.957.924 0 0 1 .958.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m.001 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.957-.924M49.816 19.515v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .958.924v5.542a.957.924 0 0 1-.957.924h-5.74a.957.924 0 0 1-.958-.924m0 66.516v-5.543a.957.924 0 0 1 .957-.924h5.74a.957.924 0 0 1 .957.924v5.543a.957.924 0 0 1-.957.923h-5.74a.957.924 0 0 1-.957-.924","viewW":180,"viewH":100}},{"id":"table_dining_rounded","group":"furniture","category":"dining_table","w":160,"h":90,"art":{"d":"M3.879 17.527c0-7.587 6.39-13.737 14.272-13.737h123.696c7.883 0 14.273 6.15 14.273 13.737v54.947c0 7.587-6.39 13.737-14.273 13.737H18.151c-7.882 0-14.272-6.15-14.272-13.737z M138.994 19.358v-5.495a.952.916 0 0 1 .951-.915h5.71a.952.916 0 0 1 .95.915v5.495a.952.916 0 0 1-.95.916h-5.71a.952.916 0 0 1-.951-.916m-.001 56.778v-5.495a.952.916 0 0 1 .952-.915h5.709a.952.916 0 0 1 .951.915v5.495a.952.916 0 0 1-.951.916h-5.71a.952.916 0 0 1-.95-.916m-125.6-56.778v-5.495a.952.916 0 0 1 .951-.916h5.71a.952.916 0 0 1 .95.916v5.495a.952.916 0 0 1-.95.915h-5.71a.952.916 0 0 1-.951-.915m-.001 56.779v-5.495a.952.916 0 0 1 .952-.916h5.709a.952.916 0 0 1 .951.916v5.495a.952.916 0 0 1-.951.915h-5.71a.952.916 0 0 1-.95-.915","viewW":160,"viewH":90}},{"id":"desk","group":"furniture","category":"work_table","w":140,"h":70,"art":{"d":"M3.862 5.967c0-1.234 1.057-2.234 2.362-2.234h127.552c1.305 0 2.362 1 2.362 2.234v58.066c0 1.234-1.057 2.233-2.362 2.233H6.224c-1.305 0-2.362-1-2.362-2.233z M9.531 15.346v-5.36a.945.893 0 0 1 .945-.893h5.67a.945.893 0 0 1 .944.893v5.36a.945.893 0 0 1-.945.894h-5.669a.945.893 0 0 1-.945-.894m113.379.001v-5.36a.945.893 0 0 1 .945-.894h5.67a.945.893 0 0 1 .944.894v5.36a.945.893 0 0 1-.945.893h-5.669a.945.893 0 0 1-.945-.893M9.53 60.013v-5.36a.945.893 0 0 1 .946-.893h5.669a.945.893 0 0 1 .945.893v5.36a.945.893 0 0 1-.945.894h-5.67a.945.893 0 0 1-.944-.894m113.38.001v-5.36a.945.893 0 0 1 .945-.894h5.669a.945.893 0 0 1 .945.894v5.36a.945.893 0 0 1-.945.893h-5.67a.945.893 0 0 1-.944-.893m-97.318-2.681h88.814","viewW":140,"viewH":70}},{"id":"desk_corner","group":"furniture","category":"work_table","w":160,"h":160,"art":{"d":"M156.12 66.202a2.38 2.38 0 0 1-2.379 2.38H94.272c-13.138 0-23.788 10.65-23.788 23.787v61.372a2.38 2.38 0 0 1-2.379 2.38H6.257a2.38 2.38 0 0 1-2.378-2.38V6.257A2.38 2.38 0 0 1 6.257 3.88h147.484a2.38 2.38 0 0 1 2.38 2.378z M9.588 16.249v-5.71a.95.95 0 0 1 .951-.95h5.71a.95.95 0 0 1 .95.95v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m133.212.001v-5.71a.95.95 0 0 1 .951-.951h5.71a.95.95 0 0 1 .95.951v5.71a.95.95 0 0 1-.95.951h-5.71a.95.95 0 0 1-.951-.951m0 45.672v-5.71a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.95-.951M9.587 149.46v-5.708a.95.95 0 0 1 .952-.952h5.709a.95.95 0 0 1 .951.952v5.709a.95.95 0 0 1-.951.951h-5.71a.95.95 0 0 1-.951-.951m47.576 0v-5.709a.95.95 0 0 1 .952-.951h5.709a.95.95 0 0 1 .951.951v5.71a.95.95 0 0 1-.951.95h-5.71a.95.95 0 0 1-.951-.95m51.382-90.396H95.569a34.6 34.6 0 0 0-34.6 34.6v12.976","viewW":160,"viewH":160}},{"id":"chair","group":"furniture","category":"chair","w":50,"h":50,"art":{"d":"M39.452 12.433H10.548a4.398 4.398 0 1 1 0-8.796h28.904a4.398 4.398 0 0 1 0 8.796m-28.904 0H8.035a4.4 4.4 0 0 0-4.398 4.399V40.08a6.283 6.283 0 0 0 6.283 6.284h30.16a6.283 6.283 0 0 0 6.284-6.284V16.832c0-2.43-1.97-4.399-4.399-4.399h-2.513","viewW":50,"viewH":50}},{"id":"chair_bar","group":"furniture","category":"chair","w":45,"h":48,"art":{"d":"M38.579 17.168c1.326 2.526 1.947 6.015 1.947 9.071 0 10.018-8.07 18.139-18.025 18.139S4.475 36.257 4.475 26.239c0-3.056.586-6.552 1.912-9.078m32.192.007a3.59 3.59 0 0 1-3.333-.976m3.333.976a3.6 3.6 0 0 0 1.765-.976m-33.827.996-.13-.027a3.6 3.6 0 0 1-1.731-.969m30.59 0a18 18 0 0 0-5.848-3.932 17.93 17.93 0 0 0-13.796 0 18 18 0 0 0-5.848 3.932 3.59 3.59 0 0 1-3.237.996m28.729-.996a3.59 3.59 0 0 0 5.098 0m-33.827.996a3.6 3.6 0 0 1-1.861-.996 3.644 3.644 0 0 1 0-5.13 25.2 25.2 0 0 1 8.187-5.505A25.1 25.1 0 0 1 22.5 3.623a25.1 25.1 0 0 1 9.657 1.934 25.2 25.2 0 0 1 8.187 5.505 3.644 3.644 0 0 1 0 5.13","viewW":45,"viewH":48}},{"id":"armchair","group":"furniture","category":"armchair","w":90,"h":90,"art":{"d":"M75.702 13.011q.012-.225.012-.453c0-4.843-3.917-8.768-8.748-8.768H23.034c-4.83 0-8.747 3.925-8.747 8.768q0 .228.011.453M77.132 72.09q.59.091 1.206.092c4.348 0 7.873-3.533 7.873-7.892V20.45c0-4.359-3.525-7.892-7.873-7.892-.924 0-1.812.16-2.636.453a7.89 7.89 0 0 0-5.237 7.438v.148m0 0a8.7 8.7 0 0 1-3.499.73H23.034a8.7 8.7 0 0 1-3.499-.73m50.93 0V64.29a7.89 7.89 0 0 0 6.667 7.8M14.298 13.01a7.89 7.89 0 0 1 5.237 7.438v.148m0 0V64.29a7.89 7.89 0 0 1-6.666 7.8q-.59.091-1.206.092c-4.348 0-7.873-3.533-7.873-7.892V20.45c0-4.359 3.525-7.892 7.873-7.892.924 0 1.811.16 2.635.453M77.132 72.09c-1.585 8.05-8.667 14.12-17.164 14.12H30.032c-8.496 0-15.578-6.07-17.163-14.12","viewW":90,"viewH":90}},{"id":"armchair_office","group":"furniture","category":"armchair","w":65,"h":65,"art":{"d":"M56.968 18.845q0-.368-.05-.72M49.051 13.8H15.95a5.04 5.04 0 0 1-5.038-5.043 5.04 5.04 0 0 1 5.038-5.043h33.103a5.04 5.04 0 0 1 5.038 5.043 5.04 5.04 0 0 1-5.038 5.043m-33.103 0h-2.88a5.04 5.04 0 0 0-4.986 4.323M49.052 13.8h2.879a5.04 5.04 0 0 1 4.986 4.324m.051 24.826a4.32 4.32 0 0 0 4.318-4.323v-16.18a4.32 4.32 0 0 0-4.369-4.322m.051 24.826a4.32 4.32 0 0 1-4.318-4.323V22.447a4.32 4.32 0 0 1 4.267-4.322m-11.84 37.24h4.695c.736 0 1.446-.111 2.115-.317a7.21 7.21 0 0 0 5.081-6.889v-5.208M8.084 18.124h-.051a4.32 4.32 0 0 0-4.318 4.323v16.18a4.32 4.32 0 0 0 4.318 4.324 4.32 4.32 0 0 0 4.317-4.323V22.447a4.32 4.32 0 0 0-4.266-4.323m11.935 37.24h-4.79a7.2 7.2 0 0 1-2.04-.294 7.21 7.21 0 0 1-5.156-6.91v-5.21m37.045 12.414 5.177 5.183c.969.97 2.534.987 3.524.038a2.526 2.526 0 0 0 .038-3.605l-1.93-1.932m-6.81.316H20.02m-6.83-.294-1.909 1.91c-.998 1-.98 2.627.038 3.605.99.949 2.555.932 3.524-.038l5.177-5.183","viewW":65,"viewH":65}},{"id":"sofa","group":"furniture","category":"sofa","w":180,"h":90,"art":{"d":"M90 19.365v57.111m0-57.11V7.683m0 11.681c0 2.151-1.836 3.894-4.1 3.894H30.544a10.7 10.7 0 0 1-4.136-.825M90 19.365c0 2.151 1.836 3.894 4.1 3.894h55.356c1.472 0 2.871-.294 4.137-.825M90 76.476c0 5.376-4.59 9.735-10.25 9.735H30.543c-5.266 0-9.605-3.772-10.185-8.625M90 76.476c0 5.376 4.59 9.735 10.25 9.735h49.206c5.266 0 9.605-3.772 10.185-8.625M90 7.684c0-2.15-1.837-3.894-4.101-3.894H30.544c-5.661 0-10.25 4.358-10.25 9.735q0 .416.036.825M90 7.684c0-2.15 1.836-3.894 4.1-3.894h55.356c5.661 0 10.25 4.358 10.25 9.735q0 .416-.036.825m0 0a10.7 10.7 0 0 1 4.137-.825h2.05c5.662 0 10.252 4.358 10.252 9.734v45.43c0 5.376-4.59 9.734-10.251 9.734h-2.05c-1.484 0-2.894-.3-4.167-.837m0 0c-3.585-1.517-6.084-4.93-6.084-8.898V23.26q0-.417.036-.825c.32-3.622 2.728-6.68 6.077-8.084m-139.34 0c3.35 1.404 5.758 4.462 6.078 8.084q.036.408.036.825v45.43c0 3.967-2.5 7.38-6.085 8.897a10.7 10.7 0 0 1-4.166.837h-2.05c-5.662 0-10.251-4.358-10.251-9.735V23.26c0-5.376 4.59-9.734 10.25-9.734h2.05c1.473 0 2.872.294 4.138.825","viewW":180,"viewH":90}},{"id":"sofa_three_seat","group":"furniture","category":"sofa","w":240,"h":90,"art":{"d":"M86.553 18.593V76.96m0-58.366V7.491m0 11.102c0 2.044-1.762 3.701-3.935 3.701H29.496c-1.413 0-2.756-.28-3.97-.784m61.027-2.917c0 2.044 1.762 3.701 3.935 3.701h59.024c2.174 0 3.935-1.657 3.935-3.7M86.553 76.958c0 5.11-4.404 9.252-9.837 9.252h-47.22c-5.054 0-9.218-3.585-9.775-8.198m66.832-1.054c0 5.11 4.404 9.252 9.837 9.252h47.22c5.433 0 9.837-4.142 9.837-9.252m0-58.366V76.96m0-58.366V7.491m0 11.102c0 2.044 1.762 3.701 3.936 3.701h53.122c1.413 0 2.756-.28 3.97-.784m-61.028 55.449c0 5.11 4.405 9.252 9.838 9.252h47.22c5.054 0 9.218-3.585 9.775-8.198M86.553 7.491c0-2.044-1.762-3.701-3.935-3.701H29.496c-5.433 0-9.838 4.142-9.838 9.252q0 .396.035.784m66.86-6.335c0-2.044 1.762-3.701 3.935-3.701h59.024c2.174 0 3.935 1.657 3.935 3.7m0 0c0-2.043 1.762-3.7 3.936-3.7h53.122c5.433 0 9.838 4.142 9.838 9.252 0 .258.022.517 0 .77m-.063 64.201c1.22.512 2.574.796 3.998.796h1.967c5.433 0 9.837-4.142 9.837-9.252V22.294c0-5.11-4.404-9.252-9.837-9.252h-1.967c-5.153 0-9.38 3.725-9.803 8.468q-.036.387-.035.784v47.263c0 3.771 2.399 7.015 5.84 8.456M19.693 13.826a10.3 10.3 0 0 0-3.97-.784h-1.967c-5.433 0-9.838 4.142-9.838 9.252v47.263c0 5.11 4.405 9.252 9.838 9.252h1.967c1.424 0 2.777-.284 3.998-.796 3.441-1.441 5.84-4.685 5.84-8.456V22.294a9 9 0 0 0-.035-.784c-.307-3.443-2.618-6.349-5.833-7.684","viewW":240,"viewH":90}},{"id":"sofa_corner_right","group":"furniture","category":"sofa","w":260,"h":170,"art":{"d":"M93.673 20.687V83.69m0-63.004v-12.6m0 12.6c0 2.32-1.913 4.2-4.273 4.2H31.704a10.8 10.8 0 0 1-4.311-.89m66.28-3.31c0 2.32 1.914 4.2 4.274 4.2h64.106c2.36 0 4.274-1.88 4.274-4.2M93.673 83.692c0 5.8-4.783 10.501-10.684 10.501H31.704c-5.489 0-10.011-4.068-10.616-9.304m72.585-1.197c0 5.8 4.784 10.501 10.684 10.501h51.286c5.9 0 10.684-4.701 10.684-10.5m0-63.005V83.69m0-63.004v-12.6m0 12.6c0 2.32 1.914 4.2 4.273 4.2h57.696c1.534 0 2.993-.317 4.312-.89m-66.28 59.695v71.923c0 5.799 4.782 10.5 10.684 10.5h51.284c5.49 0 10.012-4.068 10.616-9.304V84.888M93.673 8.086c0-2.32-1.913-4.2-4.273-4.2H31.704c-5.9 0-10.684 4.7-10.684 10.5q0 .45.038.89m72.615-7.19c0-2.32 1.914-4.2 4.274-4.2h64.106c2.36 0 4.274 1.88 4.274 4.2m0 0c0-2.32 1.914-4.2 4.273-4.2h57.696c5.9 0 10.685 4.7 10.685 10.5 0 .293.024.587 0 .874m-.07 69.628c1.327.58 2.797.904 4.344.904h2.136c5.901 0 10.684-4.702 10.684-10.501V24.887c0-5.8-4.783-10.5-10.684-10.5h-2.136c-5.596 0-10.187 4.227-10.647 9.61q-.038.44-.038.89V75.29c0 4.28 2.605 7.961 6.342 9.597M21.059 15.276a10.8 10.8 0 0 0-4.312-.89H14.61c-5.9 0-10.684 4.701-10.684 10.5v50.405c0 5.8 4.783 10.5 10.684 10.5h2.137c1.546 0 3.016-.322 4.342-.903 3.737-1.636 6.342-5.317 6.342-9.597V24.887q0-.45-.037-.89c-.334-3.907-2.844-7.206-6.335-8.72","viewW":260,"viewH":170}},{"id":"bed_single","group":"furniture","category":"bed","w":90,"h":200,"art":{"d":"M3.79 14.024c0 5.59 4.318 10.122 9.645 10.122h63.13c5.328 0 9.646-4.532 9.646-10.122m-22.797 46.93V46.23c0-5.082-3.926-9.201-8.769-9.201h-19.29c-4.842 0-8.768 4.12-8.768 9.201v14.723m-22.797 0h82.42m0 22.085H3.79m8.768 113.06h64.885c4.842 0 8.768-4.12 8.768-9.202V13.104c0-5.082-3.926-9.202-8.768-9.202H12.558c-4.843 0-8.768 4.12-8.768 9.202v173.792c0 5.082 3.925 9.202 8.768 9.202","viewW":90,"viewH":200}},{"id":"bed_double","group":"furniture","category":"bed","w":160,"h":200,"art":{"d":"M3.879 14.073c0 5.618 4.516 10.172 10.088 10.172h132.065c5.571 0 10.088-4.554 10.088-10.172m-81.165 47.16V46.438c0-5.107-4.106-9.247-9.171-9.247H45.607c-5.065 0-9.17 4.14-9.17 9.247v14.795m89.877 0V46.438c0-5.107-4.107-9.247-9.172-9.247H96.966c-5.065 0-9.171 4.14-9.171 9.247v14.795m-83.916 0H156.12m0 22.192H3.88m9.17 112.673h133.9c5.065 0 9.171-4.14 9.171-9.247V13.15c0-5.107-4.106-9.247-9.171-9.247H13.05c-5.064 0-9.17 4.14-9.17 9.247v173.7c0 5.107 4.106 9.247 9.17 9.247","viewW":160,"viewH":200}},{"id":"nightstand","group":"furniture","category":"nightstand","w":50,"h":40,"art":{"d":"M5.773 3.555h38.454a2.136 2.056 0 0 1 2.137 2.056v28.777a2.136 2.056 0 0 1-2.137 2.056H5.773a2.136 2.056 0 0 1-2.136-2.056V5.611a2.136 2.056 0 0 1 2.136-2.056","viewW":50,"viewH":40}},{"id":"cabinet_tv","group":"furniture","category":"nightstand","w":140,"h":45,"art":{"d":"M56.772 17.88h5.315m0 0h5.315m-5.315 0v9.214m9.832-9.214 5.315 9.45 5.314-9.45M6.224 41.4h127.552c1.305 0 2.362-.94 2.362-2.1V5.7c0-1.16-1.057-2.1-2.362-2.1H6.224c-1.305 0-2.362.94-2.362 2.1v33.6c0 1.16 1.057 2.1 2.362 2.1","viewW":140,"viewH":45}},{"id":"cabinet_shoe","group":"furniture","category":"nightstand","w":80,"h":35,"art":{"d":"m39.722 16.231 1.505-1.596m1.134 3.15 1.53-1.738M6.03 31.5h67.94c1.251 0 2.265-.895 2.265-2v-24c0-1.105-1.014-2-2.264-2H6.03c-1.251 0-2.265.895-2.265 2v24c0 1.105 1.014 2 2.265 2m41.966-14.305L46.92 17.1a3.4 3.4 0 0 1-1.813-.706l-4.583-3.62c-1.264-1-3.277-.326-3.475 1.161-.185 1.39-1.983 2.1-3.28 1.299l-1.532-.947c-.968-.513-2.2.103-2.2 1.1v3.463c0 1.559 1.43 2.823 3.196 2.823h14.51c1.404 0 2.542-1.005 2.542-2.245 0-1.153-.99-2.118-2.289-2.233","viewW":80,"viewH":35}},{"id":"cabinet_sink","group":"furniture","category":"nightstand","w":80,"h":50,"art":{"d":"M3.765 5.773c0-1.18 1.014-2.136 2.265-2.136h67.94c1.251 0 2.265.956 2.265 2.136v38.454c0 1.18-1.014 2.137-2.264 2.137H6.03c-1.251 0-2.265-.957-2.265-2.137z M36.088 32.135c2.525 1.88 6.205 1.88 8.73 0s3.167-5.101 1.52-7.637l-5.879-8.043-5.893 8.043c-1.644 2.536-1.003 5.755 1.522 7.637","viewW":80,"viewH":50}},{"id":"bookshelf","group":"furniture","category":"wardrobe","w":100,"h":35,"art":{"d":"M57.12 20.488H45.473c-.514 0-1.008.124-1.372.345s-.568.52-.568.832m4.076-8.236h5.434M6.119 3.5h87.762c1.276 0 2.31.895 2.31 2v24c0 1.105-1.034 2-2.31 2H6.12c-1.275 0-2.31-.895-2.31-2v-24c0-1.105 1.035-2 2.31-2m51 6.4v14.118H45.474a2.04 2.04 0 0 1-1.372-.517 1.7 1.7 0 0 1-.568-1.248V11.665c0-.468.204-.917.568-1.248a2.04 2.04 0 0 1 1.372-.517z","viewW":100,"viewH":35}},{"id":"wall_unit","group":"furniture","category":"wardrobe","w":240,"h":45,"art":{"d":"M6.336 41.4h227.328a2.418 2.1 0 0 0 2.418-2.1V5.7a2.418 2.1 0 0 0-2.418-2.1H6.336a2.418 2.1 0 0 0-2.418 2.1v33.6a2.418 2.1 0 0 0 2.418 2.1m43.744-5.88V9.48m46.432 26.04V9.48m46.433 26.04V9.48m46.433 26.04V9.48","viewW":240,"viewH":45}},{"id":"wardrobe","group":"furniture","category":"wardrobe","w":180,"h":60,"art":{"d":"M93.87 20.25a3.3 3.3 0 0 0-1.072-2.407 3.8 3.8 0 0 0-2.586-.997c-.97 0-1.9.358-2.587.997a3.3 3.3 0 0 0-1.071 2.407c0 2.838 1.225 5.107 3.658 6.81h-.015m0 0 14.58 7.537c.57.295 1.046.726 1.377 1.25.331.523.505 1.12.505 1.727v1.401a3.3 3.3 0 0 1-1.071 2.407 3.8 3.8 0 0 1-2.587.998H77.393a3.8 3.8 0 0 1-2.586-.998 3.3 3.3 0 0 1-1.072-2.407v-1.4c0-.608.174-1.205.505-1.728a3.55 3.55 0 0 1 1.377-1.25zM6.284 3.691h167.433c1.32 0 2.392.981 2.392 2.192v48.23c0 1.212-1.071 2.193-2.392 2.193H6.284c-1.321 0-2.392-.981-2.392-2.192V5.885c0-1.212 1.07-2.193 2.392-2.193","viewW":180,"viewH":60}},{"id":"kitchen_floor","group":"furniture","category":"kitchen_cabinet","w":60,"h":60,"art":{"d":"M3.692 5.884c0-1.21.981-2.192 2.192-2.192h48.23c1.212 0 2.193.981 2.193 2.192v48.23a2.19 2.19 0 0 1-2.192 2.193H5.885a2.19 2.19 0 0 1-2.193-2.192z","viewW":60,"viewH":60}},{"id":"kitchen_floor_corner","group":"furniture","category":"kitchen_cabinet","w":90,"h":90,"art":{"d":"M45.944 41.767a2.29 2.29 0 0 0 2.29 2.29H83.92a2.29 2.29 0 0 1 2.29 2.29V83.92a2.29 2.29 0 0 1-2.29 2.29H6.08a2.29 2.29 0 0 1-2.29-2.29V6.08a2.29 2.29 0 0 1 2.29-2.29h37.576a2.29 2.29 0 0 1 2.29 2.29z","viewW":90,"viewH":90}},{"id":"kitchen_wall","group":"furniture","category":"kitchen_cabinet","w":60,"h":35,"art":{"d":"m4.444 31.033 50.75-27.007m-50.752-.06 50.75 27.008M5.884 31.5h48.23c1.212 0 2.193-.895 2.193-2v-24c0-1.105-.981-2-2.192-2H5.885c-1.212 0-2.193.895-2.193 2v24c0 1.105.981 2 2.192 2","viewW":60,"viewH":35}},{"id":"kitchen_wall_corner","group":"furniture","category":"kitchen_cabinet","w":60,"h":60,"art":{"d":"M3.772 29.59 29.88 4.383M4.57 55.43 55.43 30M4.175 4.383l26.701 23.863m-27.184.877L55.43 55.43M28.41 3.692H5.884c-1.21 0-2.192.981-2.192 2.192v48.23c0 1.212.981 2.193 2.192 2.193h48.23a2.19 2.19 0 0 0 2.193-2.192V31.589c0-1.21-.981-2.192-2.192-2.192h-21.32a2.19 2.19 0 0 1-2.193-2.192V5.885a2.19 2.19 0 0 0-2.192-2.193","viewW":60,"viewH":60}},{"id":"shelf_floor","group":"furniture","category":"shelving","w":100,"h":35,"art":{"d":"M6.12 31.5h87.76a2.31 2 0 0 0 2.31-2v-24a2.31 2 0 0 0-2.31-2H6.12a2.31 2 0 0 0-2.31 2v24a2.31 2 0 0 0 2.31 2 M9.089 25.933c-.494.112-.776.546-.63.97s.664.675 1.157.564l-.263-.767zM90.912 9.067c.493-.112.775-.546.63-.97s-.664-.675-1.158-.564l.264.767zM11.875 26.956c.493-.112.775-.546.63-.97s-.664-.676-1.158-.564l.264.767zm3.988-2.556c-.493.112-.775.546-.63.969s.664.676 1.158.564l-.264-.766zm5.045.511c.493-.112.775-.545.63-.969s-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.111-.775.545-.63.968.146.424.664.677 1.158.565l-.264-.767zm5.044.51c.494-.111.776-.545.63-.968-.145-.424-.664-.676-1.157-.565l.263.767zm3.99-2.555c-.495.112-.777.546-.63.97.145.423.663.675 1.157.563l-.264-.766zm5.043.511c.494-.112.776-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.989-2.555c-.494.111-.776.545-.63.969s.664.676 1.157.564l-.263-.767zm5.044.51c.494-.11.776-.545.63-.968-.146-.424-.664-.676-1.158-.565l.264.767zm3.989-2.555c-.494.112-.776.546-.63.97.145.423.664.675 1.157.564l-.264-.767zm5.044.511c.493-.111.775-.545.63-.969s-.664-.676-1.158-.564l.264.767zm3.988-2.555c-.493.112-.775.545-.63.969s.664.676 1.158.564l-.264-.767zm5.045.51c.493-.11.775-.545.63-.968-.146-.424-.664-.676-1.158-.564l.264.766zm3.988-2.555c-.493.112-.775.546-.63.97s.664.675 1.158.564l-.264-.767zm5.044.511c.494-.111.776-.545.63-.968-.145-.424-.664-.677-1.157-.565l.264.767zm3.99-2.555c-.494.112-.777.545-.63.969.145.423.663.676 1.157.564l-.264-.766zm5.043.511c.494-.112.776-.546.63-.969s-.664-.676-1.157-.564l.263.766zm3.989-2.556c-.494.112-.776.546-.63.97s.664.676 1.158.564l-.264-.767zM9.353 26.7l.263.767 2.259-.511-.264-.767-.264-.767-2.258.511zm6.774-1.533.264.766 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.045.264.767 4.516-1.022-.264-.767-.263-.767-4.517 1.023zm9.033-2.044.264.766 4.516-1.022-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.263.767 4.517-1.022-.264-.767-.264-.767-4.516 1.023zm9.032-2.044.264.767 4.517-1.023-.264-.766-.264-.767-4.516 1.022zm9.033-2.045.264.767 4.517-1.022-.264-.767-.264-.766-4.517 1.022zm9.033-2.044.264.767 4.516-1.023-.263-.766-.264-.767-4.517 1.022zm9.033-2.044.264.766 4.516-1.022-.264-.767-.263-.766-4.517 1.022zM88.39 8.81l.264.767 2.258-.511-.264-.767-.264-.767-2.258.511z M9.089 9.067c-.494-.112-.776-.546-.63-.97s.664-.675 1.157-.564l-.263.767zm81.823 16.866c.493.112.775.546.63.97s-.664.675-1.158.564l.264-.767zM11.875 8.044c.493.112.775.546.63.97s-.664.676-1.158.564l.264-.767zm3.988 2.556c-.493-.112-.775-.546-.63-.969s.664-.676 1.158-.564l-.264.766zm5.045-.511c.493.112.775.545.63.969s-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.111-.775-.545-.63-.968.146-.424.664-.677 1.158-.565l-.264.767zm5.044-.51c.494.111.776.545.63.968-.145.424-.664.676-1.157.565l.263-.767zm3.99 2.555c-.495-.112-.777-.546-.63-.97.145-.423.663-.675 1.157-.563l-.264.766zm5.043-.511c.494.112.776.545.63.969s-.664.676-1.158.564l.264-.767zm3.989 2.555c-.494-.111-.776-.545-.63-.969s.664-.676 1.157-.564l-.263.767zm5.044-.51c.494.11.776.545.63.968-.146.424-.664.676-1.158.565l.264-.767zm3.989 2.555c-.494-.112-.776-.546-.63-.97.145-.423.664-.675 1.157-.564l-.264.767zm5.044-.511c.493.111.775.545.63.969s-.664.676-1.158.564l.264-.767zm3.988 2.555c-.493-.112-.775-.545-.63-.969s.664-.676 1.158-.564l-.264.767zm5.045-.51c.493.11.775.545.63.968-.146.424-.664.676-1.158.564l.264-.766zm3.988 2.555c-.493-.112-.775-.546-.63-.97s.664-.675 1.158-.564l-.264.767zm5.044-.511c.494.111.776.545.63.968-.145.424-.664.677-1.157.565l.264-.767zm3.99 2.555c-.494-.112-.777-.545-.63-.969.145-.423.663-.676 1.157-.564l-.264.766zm5.043-.511c.494.112.776.546.63.969s-.664.676-1.157.564l.263-.766zm3.989 2.556c-.494-.112-.776-.546-.63-.97s.664-.676 1.158-.564l-.264.767zM9.353 8.3l.263-.767 2.259.511-.264.767-.264.767-2.258-.511zm6.774 1.533.264-.766 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.045.264-.767 4.516 1.022-.264.767-.263.767-4.517-1.023zm9.033 2.044.264-.766 4.516 1.022-.264.766-.264.767-4.516-1.022zm9.033 2.045.263-.767 4.517 1.022-.264.767-.264.767-4.516-1.023zm9.032 2.044.264-.767 4.517 1.023-.264.766-.264.767-4.516-1.022zm9.033 2.045.264-.767 4.517 1.022-.264.767-.264.766-4.517-1.022zm9.033 2.044.264-.767 4.516 1.023-.263.766-.264.767-4.517-1.022zm9.033 2.044.264-.766 4.516 1.022-.264.767-.263.766-4.517-1.022zm9.033 2.045.264-.767 2.258.511-.264.767-.264.767-2.258-.511z","viewW":100,"viewH":35}},{"id":"shelf_wall","group":"furniture","category":"shelving","w":100,"h":25,"art":{"d":"M18.36 3.15v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.309-.82-2.309-1.833V3.15m73.905 0v2.567c0 1.012-1.034 1.833-2.31 1.833h-.924c-1.275 0-2.31-.82-2.31-1.833V3.15m-75.29 0H93.65c1.276 0 2.31.82 2.31 1.833V19.65c0 1.012-1.034 1.833-2.31 1.833H5.888c-1.275 0-2.31-.82-2.31-1.833V4.983c0-1.012 1.035-1.833 2.31-1.833","viewW":100,"viewH":25}},{"id":"cooktop_two","group":"appliance","category":"cooktop","w":30,"h":50,"art":{"d":"M15 30.982v-1.71m3.086 5.128h1.542M15 39.527v-1.709M10.372 34.4h1.542M5.84 46.364h18.322c1.331 0 2.41-1.196 2.41-2.67V6.306c0-1.475-1.079-2.67-2.41-2.67H5.839c-1.331 0-2.41 1.195-2.41 2.67v37.386c0 1.475 1.079 2.67 2.41 2.67m14.56-29.91c0 3.304-2.417 5.982-5.4 5.982-2.981 0-5.4-2.678-5.4-5.981s2.419-5.982 5.4-5.982c2.983 0 5.4 2.678 5.4 5.982M18.858 34.4c0 2.36-1.727 4.273-3.857 4.273s-3.857-1.913-3.857-4.273S12.87 30.127 15 30.127s3.857 1.913 3.857 4.273","viewW":30,"viewH":50}},{"id":"stove","group":"appliance","category":"cooktop","w":60,"h":60,"art":{"d":"M41.4 35.261a.877.877 0 1 0-1.754 0zm4.384 6.138a.877.877 0 1 0 0-1.753zM41.4 44.03a.877.877 0 1 0-1.753 0zm-4.384-2.63a.877.877 0 1 0 0-1.754zM5.885 3.691v.877h48.23V2.815H5.885zm50.422 2.192h-.877v48.23h1.754V5.885zm-2.192 50.423v-.877H5.885v1.754h48.23zM3.692 54.115h.877V5.885H2.815v48.23zm2.192 2.192v-.877a1.315 1.315 0 0 1-1.315-1.315H2.815a3.07 3.07 0 0 0 3.07 3.069zm50.423-2.192h-.877c0 .726-.589 1.315-1.315 1.315v1.754a3.07 3.07 0 0 0 3.069-3.07zM54.115 3.692v.877c.726 0 1.315.589 1.315 1.315h1.754a3.07 3.07 0 0 0-3.07-3.069zm-48.23 0v-.877a3.07 3.07 0 0 0-3.07 3.07h1.754c0-.727.589-1.316 1.315-1.316zm19.73 36.83h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.015zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.261h-1.754a7.015 7.015 0 0 0 7.015 7.015zm-6.138-6.138h.877a5.26 5.26 0 0 1 5.261-5.262v-1.754a7.015 7.015 0 0 0-7.015 7.016zm6.138-6.139v.877a5.26 5.26 0 0 1 5.262 5.262h1.754a7.015 7.015 0 0 0-7.016-7.016zm27.185-14.908h-.877a5.26 5.26 0 0 1-5.261 5.262v1.754a7.015 7.015 0 0 0 7.015-7.016zm-6.138 6.139v-.877a5.26 5.26 0 0 1-5.262-5.262h-1.754a7.015 7.015 0 0 0 7.016 7.016zm-6.139-6.139h.877a5.26 5.26 0 0 1 5.262-5.261v-1.754a7.015 7.015 0 0 0-7.016 7.015zm6.139-6.138v.877a5.26 5.26 0 0 1 5.261 5.261h1.754a7.015 7.015 0 0 0-7.015-7.015zm4.384 27.185h-.877a3.51 3.51 0 0 1-3.507 3.507v1.754a5.26 5.26 0 0 0 5.261-5.261zm-4.384 4.384v-.877a3.51 3.51 0 0 1-3.508-3.507H35.26a5.26 5.26 0 0 0 5.262 5.261zm-4.385-4.384h.877a3.51 3.51 0 0 1 3.508-3.508V35.26a5.26 5.26 0 0 0-5.262 5.262zm4.385-4.385v.877a3.51 3.51 0 0 1 3.507 3.508h1.754a5.26 5.26 0 0 0-5.261-5.262zm0 .877h.876V35.26h-1.753v1.754zm3.508 3.508v.876h1.753v-1.753h-1.753zm-3.508 5.261h.876V44.03h-1.753v1.754zm-5.261-5.261v.876h1.753v-1.753h-1.753zm-9.647-21.047h-.877a5.26 5.26 0 0 1-5.262 5.262v1.754a7.015 7.015 0 0 0 7.016-7.016zm-6.139 6.139v-.877a5.26 5.26 0 0 1-5.261-5.262h-1.754a7.015 7.015 0 0 0 7.015 7.016zm-6.138-6.139h.877a5.26 5.26 0 0 1 5.261-5.261v-1.754a7.015 7.015 0 0 0-7.015 7.015zm6.138-6.138v.877a5.26 5.26 0 0 1 5.262 5.261h1.754a7.015 7.015 0 0 0-7.016-7.015z","viewW":60,"viewH":60}},{"id":"tv","group":"appliance","category":"tv","w":120,"h":28,"art":{"d":"M45.134 10.766H6.18c-1.292 0-2.34.848-2.34 1.894v2.68c0 1.045 1.048 1.893 2.34 1.893h38.954m0-6.467V5.288c0-1.046 1.048-1.894 2.34-1.894h26.704c1.292 0 2.34.848 2.34 1.894v5.478m-31.384 0h31.384m0 0h37.302c1.293 0 2.34.848 2.34 1.894v2.68c0 1.045-1.047 1.893-2.34 1.893H76.518m-31.384 0v5.479c0 1.046 1.048 1.894 2.34 1.894h26.704c1.292 0 2.34-.848 2.34-1.894v-5.479m-31.384 0h31.384","viewW":120,"viewH":28}},{"id":"tv_wall","group":"appliance","category":"tv","w":120,"h":15,"art":{"d":"M35.664 11.4c-.517 0-.936.269-.936.6s.419.6.936.6zm49.553 1.2c.517 0 .936-.269.936-.6s-.42-.6-.936-.6zM6.18 3v.6h107.64V2.4H6.18zm109.98 1.5h-.936v1.8h1.872V4.5zm-2.34 3.3v-.6H6.18v1.2h107.64zM3.84 6.3h.936V4.5H2.904v1.8zm2.34 1.5v-.6c-.775 0-1.404-.403-1.404-.9H2.904c0 1.16 1.467 2.1 3.276 2.1zm109.98-1.5h-.936c0 .497-.629.9-1.404.9v1.2c1.81 0 3.276-.94 3.276-2.1zM113.82 3v.6c.775 0 1.404.403 1.404.9h1.872c0-1.16-1.467-2.1-3.276-2.1zM6.18 3v-.6c-1.81 0-3.276.94-3.276 2.1h1.872c0-.497.629-.9 1.404-.9zm61.308 4.2h-.936V12h1.872V7.2zM52.512 12h.936V7.2h-1.872V12zm-16.848 0v.6h49.553v-1.2H35.664z","viewW":120,"viewH":15}},{"id":"toilet","group":"sanitary","category":"toilet","w":40,"h":70,"art":{"d":"M3.555 48.976c0 9.55 7.125 17.29 15.914 17.29h1.061c8.79 0 15.914-7.74 15.914-17.29V5.966c0-1.233-.92-2.233-2.056-2.233H5.611c-1.135 0-2.056 1-2.056 2.234z M7.666 49.12c0 7.003 5.225 12.68 11.67 12.68h1.327c6.445 0 11.67-5.677 11.67-12.68V31.873c0-1.233-.92-2.233-2.056-2.233H9.722c-1.135 0-2.056 1-2.056 2.233zM33.792 3.733c1.465 0 2.652 1.29 2.652 2.882v12.103c0 1.592-1.187 2.882-2.652 2.882H6.208c-1.465 0-2.653-1.29-2.653-2.882V6.615c0-1.591 1.188-2.882 2.653-2.882z M24.11 50.186c0 2.467-1.84 4.467-4.11 4.467s-4.111-2-4.111-4.467 1.84-4.466 4.11-4.466 4.112 2 4.112 4.466","viewW":40,"viewH":70}},{"id":"toilet_built_in","group":"sanitary","category":"toilet","w":40,"h":55,"art":{"d":"M3.555 34.56c0 9.263 7.125 16.774 15.914 16.774h1.061c8.79 0 15.914-7.51 15.914-16.775V5.833c0-1.196-.92-2.166-2.056-2.166H5.611c-1.135 0-2.056.97-2.056 2.166z M7.666 34.7c0 6.793 5.225 12.3 11.67 12.3h1.327c6.445 0 11.67-5.507 11.67-12.3V14.5c0-1.197-.92-2.167-2.056-2.167H9.722c-1.135 0-2.056.97-2.056 2.167z M24.11 35.733c0 2.394-1.84 4.334-4.11 4.334s-4.111-1.94-4.111-4.334 1.84-4.333 4.11-4.333 4.112 1.94 4.112 4.333","viewW":40,"viewH":55}},{"id":"bathtub","group":"sanitary","category":"bathtub","w":170,"h":75,"art":{"d":"M3.885 6c0-1.243 1.068-2.25 2.386-2.25h157.458c1.318 0 2.386 1.007 2.386 2.25v63c0 1.243-1.068 2.25-2.386 2.25H6.27c-1.318 0-2.386-1.007-2.386-2.25z M13.428 37.5c0-13.669 11.75-24.75 26.243-24.75h90.658c14.493 0 26.243 11.081 26.243 24.75s-11.75 24.75-26.243 24.75H39.67c-14.493 0-26.243-11.081-26.243-24.75 M31.56 37.5c0 2.237-1.923 4.05-4.295 4.05s-4.294-1.813-4.294-4.05 1.923-4.05 4.294-4.05c2.372 0 4.295 1.813 4.295 4.05","viewW":170,"viewH":75}},{"id":"bathtub_corner","group":"sanitary","category":"bathtub","w":140,"h":140,"art":{"d":"M3.862 6.224a2.36 2.36 0 0 1 2.362-2.362H133.97c1.197 0 2.168.97 2.168 2.168 0 71.857-58.251 130.108-130.108 130.108a2.17 2.17 0 0 1-2.168-2.168z M13.31 32.207c0-10.437 8.46-18.897 18.897-18.897h87.628a2.13 2.13 0 0 1 2.13 2.13c0 58.833-47.692 106.526-106.524 106.526a2.13 2.13 0 0 1-2.13-2.131z M32.207 27.483a4.724 4.724 0 1 1-9.449 0 4.724 4.724 0 0 1 9.449 0","viewW":140,"viewH":140}},{"id":"bidet","group":"sanitary","category":"bidet","w":40,"h":60,"art":{"d":"M22.466 3.692v10.085c0 1.21-.92 2.192-2.055 2.192h-.822c-1.136 0-2.056-.981-2.056-2.192V3.692m4.933 8.77h2.878c1.135 0 2.056-.982 2.056-2.193v-.877c0-1.21-.92-2.192-2.056-2.192h-2.878m-4.933 5.262h-2.878c-1.135 0-2.055-.982-2.055-2.193v-.877c0-1.21.92-2.192 2.055-2.192h2.878M5.611 3.692h28.777c1.136 0 2.056.981 2.056 2.192v33.45c0 9.374-7.125 16.973-15.914 16.973h-1.06c-8.79 0-15.915-7.599-15.915-16.972V5.885c0-1.212.92-2.193 2.056-2.193m4.11 15.785h20.556c1.136 0 2.056.981 2.056 2.192v17.807c0 6.874-5.225 12.447-11.67 12.447h-1.327c-6.445 0-11.67-5.573-11.67-12.447V21.67c0-1.21.92-2.192 2.056-2.192m13.567 7.892c0 1.937-1.472 3.508-3.288 3.508-1.817 0-3.29-1.57-3.29-3.508s1.473-3.507 3.29-3.507 3.288 1.57 3.288 3.507","viewW":40,"viewH":60}},{"id":"bidet_built_in","group":"sanitary","category":"bidet","w":40,"h":50,"art":{"d":"M17.318 12.182h-8.06c-.88 0-1.592.74-1.592 1.654v16.126c0 6.698 5.225 12.129 11.67 12.129h1.327c6.445 0 11.67-5.43 11.67-12.13V13.837c0-.914-.713-1.654-1.591-1.654H22.68m-.215-8.545v9.827c0 1.18-.92 2.136-2.055 2.136h-.822c-1.136 0-2.056-.956-2.056-2.136V3.637m10.689 8.545V3.637m-16.445 0v8.545M5.611 3.637h28.777c1.136 0 2.056.956 2.056 2.136v24.051c0 9.135-7.125 16.54-15.914 16.54h-1.06c-8.79 0-15.915-7.405-15.915-16.54V5.773c0-1.18.92-2.136 2.056-2.136M23.289 23.29c0 1.888-1.473 3.418-3.29 3.418s-3.288-1.53-3.288-3.418 1.472-3.418 3.289-3.418c1.816 0 3.288 1.53 3.288 3.418","viewW":40,"viewH":50}},{"id":"kitchen_sink","group":"sanitary","category":"kitchen_sink","w":60,"h":50,"art":{"d":"M38.188 8.7h10.598c1.211 0 2.193.956 2.193 2.136v28.2c0 1.18-.982 2.136-2.193 2.136H11.08c-1.21 0-2.192-.956-2.192-2.136v-28.2c0-1.18.981-2.136 2.192-2.136h10.599m10.885-5.128V15.11c0 1.18-.981 2.136-2.192 2.136h-.877c-1.21 0-2.192-.957-2.192-2.136V3.572m5.261 7.691h3.07c1.21 0 2.192-.956 2.192-2.136v-.855c0-1.18-.982-2.136-2.192-2.136h-3.07m-5.261 5.127h-3.07c-1.21 0-2.192-.956-2.192-2.136v-.855c0-1.18.982-2.136 2.193-2.136h3.069M5.817 46.299h48.231c1.21 0 2.192-.956 2.192-2.136V5.709c0-1.18-.981-2.137-2.192-2.137H5.818c-1.211 0-2.193.957-2.193 2.137v38.454c0 1.18.982 2.136 2.192 2.136M33.44 25.79c0 1.888-1.57 3.418-3.507 3.418s-3.508-1.53-3.508-3.418c0-1.887 1.57-3.418 3.508-3.418s3.507 1.53 3.507 3.418","viewW":60,"viewH":50}},{"id":"kitchen_sink_double","group":"sanitary","category":"kitchen_sink","w":90,"h":50,"art":{"d":"M53.173 12.182c-.468 0-.847.354-.847.79s.38.79.847.79zm21.979 1.58c.467 0 .846-.353.846-.79 0-.436-.379-.79-.846-.79zM6.01 3.572v.79h77.842v-1.58H6.01zM86.14 5.71h-.847v38.454h1.694V5.709zM83.85 46.3v-.79H6.01v1.58h77.842zM3.72 44.163h.847V5.709H2.873v38.454zm2.29 2.136v-.79c-.797 0-1.443-.603-1.443-1.346H2.873c0 1.616 1.404 2.926 3.137 2.926zm80.131-2.136h-.847c0 .743-.646 1.346-1.442 1.346v1.58c1.732 0 3.136-1.31 3.136-2.926zm-2.29-40.59v.79c.797 0 1.443.602 1.443 1.346h1.694c0-1.617-1.404-2.927-3.136-2.927zm-77.841 0v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.744.646-1.347 1.443-1.347zm5.494 37.599v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2h-1.694v28.2zm-32.97-28.2h-.846v28.2h1.694v-28.2zM39.895 8.7v-.79h-5.669v1.58h5.669zm-22.721 0v-.79h-5.669v1.58h5.669zm-7.958 2.136h.847c0-.744.645-1.346 1.442-1.346V7.91c-1.732 0-3.136 1.31-3.136 2.926zm32.968 0h.847c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.442.603 1.442 1.347zm-2.29 30.336v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.645 1.346-1.442 1.346zm-28.389 0v-.79c-.797 0-1.442-.603-1.442-1.346H8.368c0 1.616 1.404 2.926 3.136 2.926zm13.737-23.927v.79h.916v-1.58h-.916zm3.205-2.136h.847V3.572H27.6V15.11zM22.952 3.572h-.847V15.11h1.693V3.572zm3.205 13.673v.79c1.732 0 3.136-1.31 3.136-2.926H27.6c0 .743-.646 1.346-1.443 1.346zm-.916 0v-.79c-.797 0-1.443-.603-1.443-1.346h-1.693c0 1.616 1.404 2.926 3.136 2.926zm3.205-5.982v.79h3.206v-1.58h-3.206zm5.495-2.136h.847v-.855h-1.694v.855zm-2.29-2.991v-.79h-3.205v1.58h3.206zm2.29 2.136h.847c0-1.616-1.404-2.926-3.136-2.926v1.58c.796 0 1.442.603 1.442 1.346zm-2.29 2.991v.79c1.733 0 3.137-1.31 3.137-2.926h-1.694c0 .743-.646 1.346-1.442 1.346zm-11.905 0v.79h3.206v-1.58h-3.206zm3.206-5.127v-.79h-3.206v1.58h3.206zm-5.495 2.136h-.847v.855h1.694v-.855zm2.29-2.136v-.79c-1.733 0-3.137 1.31-3.137 2.926h1.694c0-.743.646-1.346 1.442-1.346zm0 5.127v-.79c-.797 0-1.443-.603-1.443-1.346H16.61c0 1.616 1.404 2.926 3.136 2.926zm9.615 14.527h-.847c0 1.452-1.26 2.628-2.816 2.628v1.58c2.49 0 4.51-1.884 4.51-4.208zM25.7 29.208v-.79c-1.555 0-2.816-1.176-2.816-2.628H21.19c0 2.324 2.02 4.209 4.51 4.209zm-3.663-3.418h.847c0-1.451 1.26-2.628 2.816-2.628v-1.58c-2.49 0-4.51 1.884-4.51 4.208zm3.663-3.418v.79c1.555 0 2.816 1.177 2.816 2.628h1.694c0-2.324-2.02-4.208-4.51-4.208zm24.268 18.8v.79h28.39v-1.58h-28.39zm30.68-2.136h.846v-28.2H79.8v28.2zM78.356 8.7v-.79h-28.39v1.58h28.39zm-30.68 2.136h-.846v28.2h1.694v-28.2zm2.29-2.136v-.79c-1.732 0-3.136 1.31-3.136 2.926h1.694c0-.744.646-1.346 1.442-1.346zm30.68 2.136h.846c0-1.616-1.404-2.927-3.136-2.927v1.58c.797 0 1.443.603 1.443 1.347zm-2.29 30.336v.79c1.732 0 3.136-1.31 3.136-2.926H79.8c0 .743-.646 1.346-1.443 1.346zm-28.39 0v-.79c-.796 0-1.442-.603-1.442-1.346H46.83c0 1.616 1.404 2.926 3.136 2.926zm3.206-28.2v.79h21.979v-1.58h-21.98z","viewW":90,"viewH":50}}] as const; diff --git a/src/furniture.ts b/src/furniture.ts index e10b4644..66a4b794 100644 --- a/src/furniture.ts +++ b/src/furniture.ts @@ -10,14 +10,16 @@ * to SCALE: a sofa is a 2.2 x 0.9 m rectangle with a back along one long * side. Putting a 24x24 pictogram into that rectangle stretches a drawing * that was never meant to be stretched, and the result reads as an icon lying - * on the floor, not as a piece of furniture. So the symbols below are drawn - * here, in a UNIT BOX, and generated at the shape's real size — which also - * means the stroke is a plain `stroke-width` in render units, exactly like - * every other decor shape, instead of a `vector-effect` hack fighting a - * non-uniform scale. + * on the floor, not as a piece of furniture. The primary library is therefore + * generated from House Plan's own top-view SVG pack. A small compatibility + * set that is absent from the pack stays expressed as unit-box primitives. + * Both forms resolve to one path plus its native coordinate box; the renderer + * scales that box to the stored real-world size and keeps the configured decor + * stroke stable with `vector-effect="non-scaling-stroke"`. * * THE CONVENTION EVERY SYMBOL OBEYS: - * - the box is `0..1 x 0..1`, x to the right, y DOWN (SVG); + * - x points right and y points DOWN (SVG); generated art keeps its native + * viewBox while legacy primitives use `0..1 x 0..1`; * - `y = 0` is the BACK of the object — the side that goes against a wall. * That is what makes the wall magnet meaningful: a sofa's back, a bed's * headboard, a wardrobe's rear panel and a worktop's edge all mean the @@ -27,6 +29,7 @@ */ import { NORM_W, GRID_PITCH, CANVAS_LIMIT } from './space-geometry'; +import { GENERATED_FURNITURE_PLAN } from './furniture-plan-art.generated'; /** Groups the palette shows, in the order it shows them. */ export const FURNITURE_GROUPS = ['furniture', 'appliance', 'sanitary', 'other'] as const; @@ -53,11 +56,21 @@ export interface FurnitureSymbol { /** Stable id — it is what the config stores and what `data-symbol` carries. */ id: string; group: FurnitureGroup; + /** Category tile in the two-level editor palette. */ + category: string; /** Default REAL size in centimetres: width ALONG the back edge x depth. */ w: number; h: number; - /** The drawing, in the unit box described above. */ - g: Prim[]; + /** Legacy drawing retained for symbols absent from the designer pack. */ + g?: Prim[]; + /** Designer drawing with its native SVG viewBox. */ + art?: FurnitureGraphic; +} + +export interface FurnitureGraphic { + d: string; + viewW: number; + viewH: number; } // The outline every boxy symbol starts from, named once so the table reads. @@ -68,136 +81,136 @@ const box = (): Prim => ['r', 0, 0, 1, 1]; * DEFAULTS, not limits — the palette lets the user type over both numbers * before placing, and the corner handles change them afterwards. */ -export const FURNITURE: FurnitureSymbol[] = [ +const LEGACY_FURNITURE: FurnitureSymbol[] = [ // ------------------------------- мебель -------------------------------- - { id: 'sofa', group: 'furniture', w: 220, h: 90, g: [ + { id: 'sofa', group: 'furniture', category: 'sofa', w: 220, h: 90, g: [ box(), ['l', 0.09, 0.26, 0.91, 0.26], // the back cushion ['l', 0.09, 0.26, 0.09, 1], // armrests ['l', 0.91, 0.26, 0.91, 1], ['l', 0.5, 0.26, 0.5, 1], // two seats ] }, - { id: 'armchair', group: 'furniture', w: 90, h: 85, g: [ + { id: 'armchair', group: 'furniture', category: 'armchair', w: 90, h: 85, g: [ box(), ['l', 0.14, 0.28, 0.86, 0.28], ['l', 0.14, 0.28, 0.14, 1], ['l', 0.86, 0.28, 0.86, 1], ] }, - { id: 'coffee_table', group: 'furniture', w: 110, h: 60, g: [ + { id: 'coffee_table', group: 'furniture', category: 'coffee_table', w: 110, h: 60, g: [ box(), ['r', 0.08, 0.14, 0.84, 0.72], ] }, - { id: 'table_dining', group: 'furniture', w: 140, h: 80, g: [ + { id: 'table_dining', group: 'furniture', category: 'dining_table', w: 140, h: 80, g: [ box(), ['r', 0.06, 0.11, 0.88, 0.78], ] }, - { id: 'table_round', group: 'furniture', w: 120, h: 120, g: [ + { id: 'table_round', group: 'furniture', category: 'dining_table', w: 120, h: 120, g: [ ['e', 0.5, 0.5, 0.5, 0.5], ['e', 0.5, 0.5, 0.41, 0.41], ] }, - { id: 'chair', group: 'furniture', w: 45, h: 45, g: [ + { id: 'chair', group: 'furniture', category: 'chair', w: 45, h: 45, g: [ ['r', 0, 0, 1, 0.18], // the back ['r', 0.06, 0.18, 0.88, 0.8], // the seat ] }, - { id: 'desk', group: 'furniture', w: 120, h: 60, g: [ + { id: 'desk', group: 'furniture', category: 'work_table', w: 120, h: 60, g: [ box(), ['r', 0.63, 0.07, 0.31, 0.86], // the drawer pedestal ['l', 0.63, 0.5, 0.94, 0.5], ] }, - { id: 'bed_double', group: 'furniture', w: 160, h: 200, g: [ + { id: 'bed_double', group: 'furniture', category: 'bed', w: 160, h: 200, g: [ box(), ['r', 0, 0, 1, 0.07], // the headboard, i.e. the back ['r', 0.06, 0.1, 0.4, 0.15], // two pillows ['r', 0.54, 0.1, 0.4, 0.15], ['l', 0, 0.33, 1, 0.33], // the turned-down blanket ] }, - { id: 'bed_single', group: 'furniture', w: 90, h: 200, g: [ + { id: 'bed_single', group: 'furniture', category: 'bed', w: 90, h: 200, g: [ box(), ['r', 0, 0, 1, 0.07], ['r', 0.15, 0.1, 0.7, 0.15], ['l', 0, 0.33, 1, 0.33], ] }, - { id: 'nightstand', group: 'furniture', w: 45, h: 40, g: [ + { id: 'nightstand', group: 'furniture', category: 'nightstand', w: 45, h: 40, g: [ box(), ['r', 0.12, 0.14, 0.76, 0.33], ['r', 0.12, 0.53, 0.76, 0.33], ] }, - { id: 'wardrobe', group: 'furniture', w: 100, h: 60, g: [ + { id: 'wardrobe', group: 'furniture', category: 'wardrobe', w: 100, h: 60, g: [ box(), ['l', 0, 0.72, 1, 0.72], // the hanging rail ['l', 0.5, 0.72, 0.5, 1], // the doors meet here ] }, - { id: 'bookshelf', group: 'furniture', w: 80, h: 30, g: [ + { id: 'bookshelf', group: 'furniture', category: 'wardrobe', w: 80, h: 30, g: [ box(), ['l', 0.34, 0, 0.34, 1], ['l', 0.67, 0, 0.67, 1], ] }, // ------------------------------- техника ------------------------------- - { id: 'fridge', group: 'appliance', w: 60, h: 65, g: [ + { id: 'fridge', group: 'appliance', category: 'fridge', w: 60, h: 65, g: [ box(), ['l', 0, 0.36, 1, 0.36], // freezer / fridge ['l', 0.83, 0.44, 0.83, 0.64], // the handle ] }, - { id: 'stove', group: 'appliance', w: 60, h: 60, g: [ + { id: 'stove', group: 'appliance', category: 'cooktop', w: 60, h: 60, g: [ box(), ['e', 0.29, 0.31, 0.15, 0.15], ['e', 0.71, 0.31, 0.15, 0.15], ['e', 0.29, 0.71, 0.15, 0.15], ['e', 0.71, 0.71, 0.15, 0.15], ] }, - { id: 'dishwasher', group: 'appliance', w: 60, h: 60, g: [ + { id: 'dishwasher', group: 'appliance', category: 'dishwasher', w: 60, h: 60, g: [ box(), ['r', 0.1, 0.12, 0.8, 0.76], ['e', 0.5, 0.5, 0.27, 0.27], ['e', 0.5, 0.5, 0.13, 0.13], // plates ] }, - { id: 'washer', group: 'appliance', w: 60, h: 60, g: [ + { id: 'washer', group: 'appliance', category: 'washer', w: 60, h: 60, g: [ box(), ['l', 0.08, 0.17, 0.92, 0.17], // the control panel ['e', 0.5, 0.57, 0.3, 0.3], ['e', 0.5, 0.57, 0.14, 0.14], // the drum ] }, - { id: 'dryer', group: 'appliance', w: 60, h: 60, g: [ + { id: 'dryer', group: 'appliance', category: 'dryer', w: 60, h: 60, g: [ box(), ['l', 0.08, 0.17, 0.92, 0.17], ['e', 0.5, 0.57, 0.3, 0.3], ['p', 0.36, 0.5, 0.5, 0.64, 0.64, 0.5], // the chevron that is not a drum ] }, - { id: 'tv', group: 'appliance', w: 120, h: 30, g: [ + { id: 'tv', group: 'appliance', category: 'tv', w: 120, h: 30, g: [ ['r', 0, 0, 1, 0.42], // the screen, seen edge-on ['l', 0.5, 0.42, 0.5, 0.72], ['l', 0.3, 0.72, 0.7, 0.72], // the stand ] }, - { id: 'ac', group: 'appliance', w: 90, h: 25, g: [ + { id: 'ac', group: 'appliance', category: 'air_conditioner', w: 90, h: 25, g: [ box(), ['l', 0.05, 0.55, 0.95, 0.55], ['l', 0.05, 0.79, 0.95, 0.79], ] }, - { id: 'water_heater', group: 'appliance', w: 45, h: 45, g: [ + { id: 'water_heater', group: 'appliance', category: 'boiler', w: 45, h: 45, g: [ ['e', 0.5, 0.5, 0.5, 0.5], ['e', 0.5, 0.5, 0.31, 0.31], ] }, // ----------------------------- сантехника ------------------------------ - { id: 'toilet', group: 'sanitary', w: 40, h: 70, g: [ + { id: 'toilet', group: 'sanitary', category: 'toilet', w: 40, h: 70, g: [ ['r', 0.06, 0, 0.88, 0.2], // the cistern, against the wall ['e', 0.5, 0.58, 0.37, 0.35], // the bowl ['e', 0.5, 0.58, 0.22, 0.2], ] }, - { id: 'bathtub', group: 'sanitary', w: 170, h: 75, g: [ + { id: 'bathtub', group: 'sanitary', category: 'bathtub', w: 170, h: 75, g: [ box(), ['r', 0.05, 0.11, 0.77, 0.78], ['e', 0.89, 0.5, 0.045, 0.1], // the drain end ] }, - { id: 'shower', group: 'sanitary', w: 90, h: 90, g: [ + { id: 'shower', group: 'sanitary', category: 'shower', w: 90, h: 90, g: [ box(), ['l', 0, 0, 1, 1], ['l', 1, 0, 0, 1], // the tray, as every plan draws it ['e', 0.5, 0.5, 0.08, 0.08], ] }, - { id: 'sink', group: 'sanitary', w: 60, h: 45, g: [ + { id: 'sink', group: 'sanitary', category: 'sink', w: 60, h: 45, g: [ box(), ['e', 0.5, 0.6, 0.34, 0.3], ['e', 0.5, 0.15, 0.07, 0.07], // the tap ] }, - { id: 'kitchen_sink', group: 'sanitary', w: 80, h: 60, g: [ + { id: 'kitchen_sink', group: 'sanitary', category: 'kitchen_sink', w: 80, h: 60, g: [ box(), ['r', 0.06, 0.24, 0.44, 0.64], ['r', 0.54, 0.24, 0.4, 0.64], ['e', 0.5, 0.12, 0.06, 0.06], ] }, - { id: 'bidet', group: 'sanitary', w: 40, h: 55, g: [ + { id: 'bidet', group: 'sanitary', category: 'bidet', w: 40, h: 55, g: [ ['e', 0.5, 0.5, 0.44, 0.5], ['e', 0.5, 0.5, 0.26, 0.3], ] }, // ------------------------------- прочее -------------------------------- - { id: 'stairs', group: 'other', w: 100, h: 280, g: [ + { id: 'stairs', group: 'other', category: 'stairs', w: 100, h: 280, g: [ box(), ['l', 0, 0.111, 1, 0.111], ['l', 0, 0.222, 1, 0.222], ['l', 0, 0.333, 1, 0.333], ['l', 0, 0.444, 1, 0.444], ['l', 0, 0.556, 1, 0.556], ['l', 0, 0.667, 1, 0.667], @@ -205,21 +218,40 @@ export const FURNITURE: FurnitureSymbol[] = [ ['l', 0.5, 0.93, 0.5, 0.06], // the "up" arrow ['p', 0.38, 0.16, 0.5, 0.06, 0.62, 0.16], ] }, - { id: 'fireplace', group: 'other', w: 120, h: 40, g: [ + { id: 'fireplace', group: 'other', category: 'fireplace', w: 120, h: 40, g: [ box(), ['p', 0.22, 1, 0.22, 0.42, 0.78, 0.42, 0.78, 1], ] }, - { id: 'plant', group: 'other', w: 40, h: 40, g: [ + { id: 'plant', group: 'other', category: 'plant', w: 40, h: 40, g: [ ['e', 0.5, 0.5, 0.22, 0.22], ['l', 0.5, 0.28, 0.5, 0.02], ['l', 0.5, 0.72, 0.5, 0.98], ['l', 0.28, 0.5, 0.02, 0.5], ['l', 0.72, 0.5, 0.98, 0.5], ['l', 0.34, 0.34, 0.13, 0.13], ['l', 0.66, 0.66, 0.87, 0.87], ['l', 0.66, 0.34, 0.87, 0.13], ['l', 0.34, 0.66, 0.13, 0.87], ] }, - { id: 'rug', group: 'other', w: 200, h: 140, g: [ + { id: 'rug', group: 'other', category: 'rug', w: 200, h: 140, g: [ box(), ['r', 0.06, 0.09, 0.88, 0.82], ] }, ]; +const RETAINED_IDS = new Set([ + 'fridge', 'dishwasher', 'washer', 'dryer', 'ac', 'water_heater', + 'shower', 'sink', 'stairs', 'fireplace', 'plant', 'rug', +]); + +/** Complete public library: 44 designer symbols plus the 12 useful existing + * symbols for which the pack intentionally has no top-view replacement. */ +export const FURNITURE: FurnitureSymbol[] = [ + ...GENERATED_FURNITURE_PLAN.map((symbol) => ({ + id: symbol.id, + group: symbol.group as FurnitureGroup, + category: symbol.category, + w: symbol.w, + h: symbol.h, + art: symbol.art, + })), + ...LEGACY_FURNITURE.filter((symbol) => RETAINED_IDS.has(symbol.id)), +]; + const BY_ID = new Map(FURNITURE.map((s) => [s.id, s])); /** The symbol with this id, or null. An unknown id is DATA, not a crash: a @@ -295,9 +327,8 @@ const num = (v: number): string => { * takes a single stroke, a single pointer target and a single `data-symbol`, * and the decor layer's DOM does not grow by ten nodes per sofa. */ -export function furniturePathD(id: string, w: number, h: number): string { - const s = furnitureSymbol(id); - if (!s || !(w > 0) || !(h > 0)) return ''; +function primitivePathD(s: FurnitureSymbol, w: number, h: number): string { + if (!s.g || !(w > 0) || !(h > 0)) return ''; const X = (v: number) => num(v * w); const Y = (v: number) => num(v * h); const out: string[] = []; @@ -325,6 +356,25 @@ export function furniturePathD(id: string, w: number, h: number): string { return out.join(''); } +/** Native one-path artwork and its coordinate box. The renderer scales this + * box non-uniformly to the user's stored dimensions and keeps the decor stroke + * physical with `vector-effect=non-scaling-stroke`. */ +export function furnitureGraphic(id: string): FurnitureGraphic | null { + const symbol = furnitureSymbol(id); + if (!symbol) return null; + if (symbol.art) return symbol.art; + const d = primitivePathD(symbol, 1, 1); + return d ? { d, viewW: 1, viewH: 1 } : null; +} + +/** Compatibility helper for callers that only need a path string. Designer + * artwork remains in its native viewBox; use `furnitureGraphic` when sizing it. */ +export function furniturePathD(id: string, w: number, h: number): string { + const symbol = furnitureSymbol(id); + if (!symbol || !(w > 0) || !(h > 0)) return ''; + return symbol.art?.d || primitivePathD(symbol, w, h); +} + // ------------------------------ the wall magnet ----------------------------- /** How far from a wall the magnet still reaches, in GRID CELLS. Six cells is diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index e40c990f..e5e67237 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -61,7 +61,7 @@ import { import { dayCycleStageVars, renderDayCycleEnvironment } from './day-cycle-render'; import { FURNITURE_GROUPS, furnitureOfGroup, furnitureSymbol, furnitureDefaultCm, - furniturePathD, furnitureCorners, snapFurnitureToWall, + furnitureGraphic, furnitureCorners, snapFurnitureToWall, cmToNorm, clampFurnSize, clampFurnCm, FURN_WALL_CELLS, type FurnitureGroup, } from './furniture'; import { @@ -1033,6 +1033,8 @@ export class HouseplanCard extends LitElement { * a unit system is a display setting, never a stored one. */ private _furnPalette: { symbol: string; w: number; h: number } | null = null; + /** Category currently open in the two-level furniture palette. */ + private _furnCategory: string | null = null; /** The selected shape's own unrotated frame (text is measured from SVG). */ private _dtBox: { id: string; x: number; y: number; w: number; h: number } | null = null; /** @@ -2426,6 +2428,7 @@ export class HouseplanCard extends LitElement { _decorShapeDialog: { state: true }, _backdropDialog: { state: true }, _furnPalette: { state: true }, + _furnCategory: { state: true }, _bdDrag: { state: true }, _dtBox: { state: true }, _dtDrag: { state: true }, @@ -2777,6 +2780,7 @@ export class HouseplanCard extends LitElement { // to Select in one step, regardless of whether a symbol was armed. else if (this._decorTool === 'furniture') { this._furnPalette = null; + this._furnCategory = null; this._decorTool = 'select'; } else if (this._decorSel) this._decorSel = null; @@ -7823,13 +7827,7 @@ export class HouseplanCard extends LitElement { ]; } - /** - * The palette: every symbol of the library, grouped, each drawn with the - * very same `furniturePathD` the plan uses — so what the user picks is what - * the user gets, at any size, and there is no second set of preview assets - * to keep in step. Under it the two size fields, prefilled with the chosen - * symbol's real size and shown in metres or feet by the HA unit system. - */ + /** The lazy editor runtime owns the generated two-level furniture palette. */ private _renderFurnPalette(): TemplateResult { return this._editorRuntimeOrThrow()._renderFurnPalette(); } @@ -8068,24 +8066,25 @@ export class HouseplanCard extends LitElement { transform=${ang ? `rotate(${ang} ${cx} ${cy})` : nothing} @pointerdown=${down}>` : nothing}`; } if (sh.kind === 'furniture') { - // One path per piece, generated at the shape's REAL size, so the - // stroke is an ordinary stroke-width in render units like every other - // decor shape — no non-uniform scale to fight (docs/FURNITURE.md §2). + // One path per piece. Designer art keeps its native viewBox and is + // scaled to the user's stored box; vector-effect keeps the configured + // decor stroke physical under non-uniform resizing. // An unknown symbol renders as nothing: a plan from a newer card must // open in an older one, not break it. const W2 = sh.w * W, H2 = sh.h * H; - const d = furniturePathD(sh.symbol, W2, H2); - if (!d) return nothing; + const art = furnitureGraphic(sh.symbol); + if (!art) return nothing; const ang = Number(sh.angle) || 0; const cx = sh.x * W + W2 / 2, cy = sh.y * H + H2 / 2; - const tr = `${ang ? `rotate(${ang} ${cx} ${cy}) ` : ''}translate(${sh.x * W} ${sh.y * H})`; + const tr = `${ang ? `rotate(${ang} ${cx} ${cy}) ` : ''}translate(${sh.x * W} ${sh.y * H}) scale(${W2 / art.viewW} ${H2 / art.viewH})`; return svg` ${erasing ? svg`` : nothing}`; } if (sh.kind === 'text') { diff --git a/src/houseplan-editor-runtime.ts b/src/houseplan-editor-runtime.ts index 40c83960..90e6222e 100644 --- a/src/houseplan-editor-runtime.ts +++ b/src/houseplan-editor-runtime.ts @@ -63,10 +63,11 @@ import { } from './sun'; import { dayCycleStageVars, renderDayCycleEnvironment } from './day-cycle-render'; import { - FURNITURE_GROUPS, furnitureOfGroup, furnitureSymbol, furnitureDefaultCm, - furniturePathD, furnitureCorners, snapFurnitureToWall, + FURNITURE_GROUPS, furnitureOfGroup, furnitureDefaultCm, + furnitureGraphic, furnitureCorners, snapFurnitureToWall, cmToNorm, clampFurnSize, clampFurnCm, FURN_WALL_CELLS, type FurnitureGroup, } from './furniture'; +import { GENERATED_FURNITURE_MENU } from './furniture-menu-art.generated'; import { degradeWalls, rekeyWallsAfterMoveChecked, wallRecordCarrierViolations, setWallThickness, setWallThicknessForRoom, cmToField, wallCmToUnits, @@ -915,6 +916,7 @@ export interface HouseplanEditorHostPort { _freeAreas: any[]; _fullRegistryHass: any; _furnPalette: { symbol: string; w: number; h: number; } | null; + _furnCategory: string | null; _furnWallReach: number; _furnWalls: number[][]; _gearPtCache: WeakMap; @@ -4661,6 +4663,7 @@ public _furnPlace(raw: number[], free = false): void { // …and the editor goes back to the tool that can move what was just placed this.host._decorTool = 'select'; this.host._furnPalette = null; + this.host._furnCategory = null; this._recordGeometry(this.host._t('history.decor_add'), before); this._saveConfig(); this.host.requestUpdate(); @@ -4726,38 +4729,74 @@ public _decorApplyBox(id: string, box: { x: number; y: number; w: number; h: num public _renderFurnPalette(): TemplateResult { const pal = this.host._furnPalette; + const categoryId = this.host._furnCategory; + const allSymbols = FURNITURE_GROUPS.flatMap((group) => furnitureOfGroup(group)); + const categories = GENERATED_FURNITURE_MENU.filter((item) => + allSymbols.some((symbol) => symbol.category === item.id)); + const category = categories.find((item) => item.id === categoryId) || null; + const variants = category + ? allSymbols.filter((symbol) => symbol.category === category.id) + : []; // the unit the fields are read in — the same two words the glow radius // already uses, because a plan has one unit system, not one per control const unit = this.host._t(this.host._imperial ? 'gs.unit_ft' : 'gs.unit_m'); const preview = (id: string): TemplateResult => { - const s = furnitureSymbol(id)!; - // fit the symbol into a 40×40 box keeping its real proportions, so a - // sofa reads as a sofa and a toilet does not become a square - const k = 36 / Math.max(s.w, s.h); - const w = s.w * k, h = s.h * k; - return svg`` as unknown as TemplateResult; + const art = furnitureGraphic(id); + if (!art) return svg`` as unknown as TemplateResult; + return svg`` as unknown as TemplateResult; + }; + const categoryPreview = (item: (typeof GENERATED_FURNITURE_MENU)[number]): TemplateResult => { + return svg`` as unknown as TemplateResult; }; return html`
e.stopPropagation()}>
${this.host._t('furn.title')}
- ${FURNITURE_GROUPS.map((g) => html` + ${category ? html` + +
+ ${this.host._t(`furn.cat_${category.id}` as I18nKey)} +
+
+ ${variants.map((symbol) => html``)} +
+ ` : FURNITURE_GROUPS.map((g) => html`
${this.host._t(`furn.group_${g}` as any)}
-
- ${furnitureOfGroup(g as FurnitureGroup).map((s) => html``)}
`)}
@@ -5074,6 +5113,7 @@ public _renderDecorSecondary(): EditorSecondaryModel | null { dismiss: () => { if (this.host._decorTool !== 'furniture') return; this.host._furnPalette = null; + this.host._furnCategory = null; this.host._decorTool = 'select'; this.host.requestUpdate(); }, @@ -5195,6 +5235,7 @@ public _renderDecorBar(): TemplateResult { @click=${() => { if (t === 'furniture' && this.host._decorTool === 'furniture') { this.host._furnPalette = null; + this.host._furnCategory = null; this.host._decorTool = 'select'; return; } @@ -5203,7 +5244,12 @@ public _renderDecorBar(): TemplateResult { this.host._decorDraft = null; // the palette belongs to its tool and to nothing else: leaving the // tool disarms whatever was chosen, so no later click can stamp it - if (t !== 'furniture') this.host._furnPalette = null; + if (t !== 'furniture') { + this.host._furnPalette = null; + this.host._furnCategory = null; + } else { + this.host._furnCategory = null; + } }} title=${this.host._t(k)}> ${this.host._t(k)} @@ -5335,8 +5381,13 @@ public _renderDecorShapeDialog(): TemplateResult { @change=${(e: Event) => (this.host._decorShapeDialog = { ...d, symbol: (e.target as HTMLSelectElement).value, })}> - ${FURNITURE_GROUPS.map((group) => html` - ${furnitureOfGroup(group).map((symbol) => html` + ${symbols.map((symbol) => html``)} diff --git a/src/i18n/de.json b/src/i18n/de.json index 2cf8276e..71772698 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -900,34 +900,90 @@ "furn.group_other": "Sonstiges", "furn.width": "Breite", "furn.depth": "Tiefe", - "furn.pick_hint": "Wählen Sie ein Symbol aus und klicken Sie dann auf den Plan.", + "furn.back_to_categories": "Alle Kategorien", + "furn.pick_hint": "Wählen Sie eine Kategorie und dann ein Symbol.", "furn.place_hint": "Klicken Sie auf den Plan — das Element wird an der nächsten Wand platziert. Mit Shift wird es frei platziert.", - "furn.sym_sofa": "Sofa", - "furn.sym_armchair": "Sessel", - "furn.sym_coffee_table": "Couchtisch", - "furn.sym_table_dining": "Esstisch", + "furn.cat_air_conditioner": "Klimaanlagen", + "furn.cat_armchair": "Sessel", + "furn.cat_bathtub": "Badewannen", + "furn.cat_bed": "Betten", + "furn.cat_bidet": "Bidets", + "furn.cat_boiler": "Warmwasserbereiter", + "furn.cat_chair": "Stühle", + "furn.cat_coffee_table": "Couchtische", + "furn.cat_cooktop": "Kochfelder", + "furn.cat_dining_table": "Esstische", + "furn.cat_dishwasher": "Geschirrspüler", + "furn.cat_dryer": "Wäschetrockner", + "furn.cat_fireplace": "Kamine", + "furn.cat_fridge": "Kühlschränke", + "furn.cat_kitchen_cabinet": "Küchenschränke", + "furn.cat_kitchen_sink": "Küchenspülen", + "furn.cat_nightstand": "Schränke", + "furn.cat_plant": "Pflanzen", + "furn.cat_rug": "Teppiche", + "furn.cat_shelving": "Regale", + "furn.cat_shower": "Duschen", + "furn.cat_sink": "Waschbecken", + "furn.cat_sofa": "Sofas", + "furn.cat_stairs": "Treppen", + "furn.cat_toilet": "Toiletten", + "furn.cat_tv": "Fernseher", + "furn.cat_wardrobe": "Kleiderschränke", + "furn.cat_washer": "Waschmaschinen", + "furn.cat_work_table": "Schreibtische", + "furn.sym_sofa": "Zweisitzer-Sofa", + "furn.sym_sofa_three_seat": "Dreisitzer-Sofa", + "furn.sym_sofa_corner_right": "Ecksofa rechts", + "furn.sym_armchair": "Polstersessel", + "furn.sym_armchair_office": "Bürostuhl", + "furn.sym_coffee_table": "Rechteckiger Couchtisch", + "furn.sym_coffee_table_round": "Runder Couchtisch", + "furn.sym_coffee_table_oval": "Ovaler Couchtisch", + "furn.sym_coffee_table_rounded": "Abgerundeter Couchtisch", + "furn.sym_table_dining": "Rechteckiger Esstisch", "furn.sym_table_round": "Rundtisch", + "furn.sym_table_dining_oval": "Ovaler Esstisch", + "furn.sym_table_dining_rounded": "Abgerundeter Esstisch", "furn.sym_chair": "Stuhl", - "furn.sym_desk": "Schreibtisch", + "furn.sym_chair_bar": "Barhocker", + "furn.sym_desk": "Rechteckiger Schreibtisch", + "furn.sym_desk_corner": "Eckschreibtisch", "furn.sym_bed_double": "Doppelbett", "furn.sym_bed_single": "Einzelbett", "furn.sym_nightstand": "Nachttisch", + "furn.sym_cabinet_tv": "TV-Schrank", + "furn.sym_cabinet_shoe": "Schuhschrank", + "furn.sym_cabinet_sink": "Waschtischunterschrank", "furn.sym_wardrobe": "Kleiderschrank", - "furn.sym_bookshelf": "Bücherregal", + "furn.sym_bookshelf": "Bücherschrank", + "furn.sym_wall_unit": "Schrankwand", + "furn.sym_kitchen_floor": "Küchen-Unterschrank", + "furn.sym_kitchen_floor_corner": "Küchen-Eckunterschrank", + "furn.sym_kitchen_wall": "Küchen-Hängeschrank", + "furn.sym_kitchen_wall_corner": "Küchen-Eckhängeschrank", + "furn.sym_shelf_floor": "Standregal", + "furn.sym_shelf_wall": "Wandregal", "furn.sym_fridge": "Kühlschrank", - "furn.sym_stove": "Herd", + "furn.sym_stove": "Vierflammiges Kochfeld", + "furn.sym_cooktop_two": "Zweiflammiges Kochfeld", "furn.sym_dishwasher": "Geschirrspüler", "furn.sym_washer": "Waschmaschine", "furn.sym_dryer": "Wäschetrockner", - "furn.sym_tv": "Fernseher", + "furn.sym_tv": "Fernseher mit Standfuß", + "furn.sym_tv_wall": "Wandfernseher", "furn.sym_ac": "Klimaanlage", "furn.sym_water_heater": "Wassererhitzer", - "furn.sym_toilet": "Toilette", - "furn.sym_bathtub": "Badewanne", + "furn.sym_toilet": "Stand-WC", + "furn.sym_toilet_built_in": "Wand-WC", + "furn.sym_bathtub": "Rechteckige Badewanne", + "furn.sym_bathtub_corner": "Eckbadewanne", "furn.sym_shower": "Dusche", "furn.sym_sink": "Waschbecken", - "furn.sym_kitchen_sink": "Küchenspüle", - "furn.sym_bidet": "Bidet", + "furn.sym_kitchen_sink": "Einzel-Küchenspüle", + "furn.sym_kitchen_sink_double": "Doppel-Küchenspüle", + "furn.sym_bidet": "Stand-Bidet", + "furn.sym_bidet_built_in": "Wand-Bidet", "furn.sym_stairs": "Treppe", "furn.sym_fireplace": "Kamin", "furn.sym_plant": "Pflanze", diff --git a/src/i18n/en.json b/src/i18n/en.json index 15bef3f0..574deb1c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -900,34 +900,90 @@ "furn.group_other": "Other", "furn.width": "Width", "furn.depth": "Depth", - "furn.pick_hint": "Pick a symbol, then click on the plan.", + "furn.back_to_categories": "All categories", + "furn.pick_hint": "Choose a category, then a symbol.", "furn.place_hint": "Click on the plan — the piece lands against the nearest wall. Shift places it free.", - "furn.sym_sofa": "Sofa", - "furn.sym_armchair": "Armchair", - "furn.sym_coffee_table": "Coffee table", - "furn.sym_table_dining": "Dining table", + "furn.cat_air_conditioner": "Air conditioners", + "furn.cat_armchair": "Armchairs", + "furn.cat_bathtub": "Bathtubs", + "furn.cat_bed": "Beds", + "furn.cat_bidet": "Bidets", + "furn.cat_boiler": "Water heaters", + "furn.cat_chair": "Chairs", + "furn.cat_coffee_table": "Coffee tables", + "furn.cat_cooktop": "Cooktops", + "furn.cat_dining_table": "Dining tables", + "furn.cat_dishwasher": "Dishwashers", + "furn.cat_dryer": "Dryers", + "furn.cat_fireplace": "Fireplaces", + "furn.cat_fridge": "Refrigerators", + "furn.cat_kitchen_cabinet": "Kitchen cabinets", + "furn.cat_kitchen_sink": "Kitchen sinks", + "furn.cat_nightstand": "Cabinets", + "furn.cat_plant": "Plants", + "furn.cat_rug": "Rugs", + "furn.cat_shelving": "Shelving", + "furn.cat_shower": "Showers", + "furn.cat_sink": "Sinks", + "furn.cat_sofa": "Sofas", + "furn.cat_stairs": "Stairs", + "furn.cat_toilet": "Toilets", + "furn.cat_tv": "Televisions", + "furn.cat_wardrobe": "Wardrobes", + "furn.cat_washer": "Washing machines", + "furn.cat_work_table": "Desks", + "furn.sym_sofa": "Two-seat sofa", + "furn.sym_sofa_three_seat": "Three-seat sofa", + "furn.sym_sofa_corner_right": "Right sectional sofa", + "furn.sym_armchair": "Soft armchair", + "furn.sym_armchair_office": "Office chair", + "furn.sym_coffee_table": "Rectangular coffee table", + "furn.sym_coffee_table_round": "Round coffee table", + "furn.sym_coffee_table_oval": "Oval coffee table", + "furn.sym_coffee_table_rounded": "Rounded coffee table", + "furn.sym_table_dining": "Rectangular dining table", "furn.sym_table_round": "Round table", + "furn.sym_table_dining_oval": "Oval dining table", + "furn.sym_table_dining_rounded": "Rounded dining table", "furn.sym_chair": "Chair", - "furn.sym_desk": "Desk", + "furn.sym_chair_bar": "Bar stool", + "furn.sym_desk": "Rectangular desk", + "furn.sym_desk_corner": "Corner desk", "furn.sym_bed_double": "Double bed", "furn.sym_bed_single": "Single bed", - "furn.sym_nightstand": "Nightstand", + "furn.sym_nightstand": "Bedside table", + "furn.sym_cabinet_tv": "TV cabinet", + "furn.sym_cabinet_shoe": "Shoe cabinet", + "furn.sym_cabinet_sink": "Sink cabinet", "furn.sym_wardrobe": "Wardrobe", - "furn.sym_bookshelf": "Bookshelf", + "furn.sym_bookshelf": "Bookcase", + "furn.sym_wall_unit": "Wall unit", + "furn.sym_kitchen_floor": "Kitchen floor module", + "furn.sym_kitchen_floor_corner": "Kitchen floor corner module", + "furn.sym_kitchen_wall": "Kitchen wall module", + "furn.sym_kitchen_wall_corner": "Kitchen wall corner module", + "furn.sym_shelf_floor": "Floor shelving unit", + "furn.sym_shelf_wall": "Wall shelf", "furn.sym_fridge": "Fridge", - "furn.sym_stove": "Cooker", + "furn.sym_stove": "Four-burner cooktop", + "furn.sym_cooktop_two": "Two-burner cooktop", "furn.sym_dishwasher": "Dishwasher", "furn.sym_washer": "Washing machine", "furn.sym_dryer": "Tumble dryer", - "furn.sym_tv": "TV", + "furn.sym_tv": "TV on stand", + "furn.sym_tv_wall": "Wall-mounted TV", "furn.sym_ac": "Air conditioner", "furn.sym_water_heater": "Water heater", - "furn.sym_toilet": "Toilet", - "furn.sym_bathtub": "Bathtub", + "furn.sym_toilet": "Floor-standing toilet", + "furn.sym_toilet_built_in": "Built-in toilet", + "furn.sym_bathtub": "Rectangular bathtub", + "furn.sym_bathtub_corner": "Corner bathtub", "furn.sym_shower": "Shower", "furn.sym_sink": "Washbasin", - "furn.sym_kitchen_sink": "Kitchen sink", - "furn.sym_bidet": "Bidet", + "furn.sym_kitchen_sink": "Single kitchen sink", + "furn.sym_kitchen_sink_double": "Double kitchen sink", + "furn.sym_bidet": "Floor-standing bidet", + "furn.sym_bidet_built_in": "Built-in bidet", "furn.sym_stairs": "Stairs", "furn.sym_fireplace": "Fireplace", "furn.sym_plant": "Plant", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index b67fa936..59297f71 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -900,34 +900,90 @@ "furn.group_other": "Прочее", "furn.width": "Ширина", "furn.depth": "Глубина", - "furn.pick_hint": "Выберите символ и кликните по плану.", + "furn.back_to_categories": "Все категории", + "furn.pick_hint": "Выберите категорию, затем символ.", "furn.place_hint": "Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.", - "furn.sym_sofa": "Диван", - "furn.sym_armchair": "Кресло", - "furn.sym_coffee_table": "Журнальный столик", - "furn.sym_table_dining": "Обеденный стол", + "furn.cat_air_conditioner": "Кондиционеры", + "furn.cat_armchair": "Кресла", + "furn.cat_bathtub": "Ванны", + "furn.cat_bed": "Кровати", + "furn.cat_bidet": "Биде", + "furn.cat_boiler": "Бойлеры", + "furn.cat_chair": "Стулья", + "furn.cat_coffee_table": "Журнальные столы", + "furn.cat_cooktop": "Варочные панели", + "furn.cat_dining_table": "Обеденные столы", + "furn.cat_dishwasher": "Посудомоечные машины", + "furn.cat_dryer": "Сушильные машины", + "furn.cat_fireplace": "Камины", + "furn.cat_fridge": "Холодильники", + "furn.cat_kitchen_cabinet": "Кухонные шкафы", + "furn.cat_kitchen_sink": "Кухонные мойки", + "furn.cat_nightstand": "Тумбы", + "furn.cat_plant": "Растения", + "furn.cat_rug": "Ковры", + "furn.cat_shelving": "Стеллажи", + "furn.cat_shower": "Душевые", + "furn.cat_sink": "Раковины", + "furn.cat_sofa": "Диваны", + "furn.cat_stairs": "Лестницы", + "furn.cat_toilet": "Унитазы", + "furn.cat_tv": "Телевизоры", + "furn.cat_wardrobe": "Шкафы", + "furn.cat_washer": "Стиральные машины", + "furn.cat_work_table": "Рабочие столы", + "furn.sym_sofa": "Диван двухместный", + "furn.sym_sofa_three_seat": "Диван трёхместный", + "furn.sym_sofa_corner_right": "Угловой диван, правый", + "furn.sym_armchair": "Кресло мягкое", + "furn.sym_armchair_office": "Кресло офисное", + "furn.sym_coffee_table": "Журнальный стол, прямоугольный", + "furn.sym_coffee_table_round": "Журнальный стол, круглый", + "furn.sym_coffee_table_oval": "Журнальный стол, овальный", + "furn.sym_coffee_table_rounded": "Журнальный стол, скруглённый", + "furn.sym_table_dining": "Обеденный стол, прямоугольный", "furn.sym_table_round": "Круглый стол", + "furn.sym_table_dining_oval": "Обеденный стол, овальный", + "furn.sym_table_dining_rounded": "Обеденный стол, скруглённый", "furn.sym_chair": "Стул", - "furn.sym_desk": "Письменный стол", + "furn.sym_chair_bar": "Барный стул", + "furn.sym_desk": "Рабочий стол, прямоугольный", + "furn.sym_desk_corner": "Рабочий стол, угловой", "furn.sym_bed_double": "Двуспальная кровать", "furn.sym_bed_single": "Односпальная кровать", - "furn.sym_nightstand": "Тумбочка", - "furn.sym_wardrobe": "Шкаф", - "furn.sym_bookshelf": "Стеллаж", + "furn.sym_nightstand": "Тумба прикроватная", + "furn.sym_cabinet_tv": "Тумба под телевизор", + "furn.sym_cabinet_shoe": "Тумба для обуви", + "furn.sym_cabinet_sink": "Тумба под раковину", + "furn.sym_wardrobe": "Шкаф гардеробный", + "furn.sym_bookshelf": "Шкаф книжный", + "furn.sym_wall_unit": "Шкаф-стенка", + "furn.sym_kitchen_floor": "Кухонный напольный модуль", + "furn.sym_kitchen_floor_corner": "Кухонный напольный угловой модуль", + "furn.sym_kitchen_wall": "Кухонный навесной модуль", + "furn.sym_kitchen_wall_corner": "Кухонный навесной угловой модуль", + "furn.sym_shelf_floor": "Стеллаж напольный", + "furn.sym_shelf_wall": "Полка настенная", "furn.sym_fridge": "Холодильник", - "furn.sym_stove": "Плита", + "furn.sym_stove": "Варочная панель, 4 конфорки", + "furn.sym_cooktop_two": "Варочная панель, 2 конфорки", "furn.sym_dishwasher": "Посудомоечная машина", "furn.sym_washer": "Стиральная машина", "furn.sym_dryer": "Сушильная машина", - "furn.sym_tv": "Телевизор", + "furn.sym_tv": "Телевизор на подставке", + "furn.sym_tv_wall": "Телевизор на кронштейне", "furn.sym_ac": "Кондиционер", "furn.sym_water_heater": "Бойлер", - "furn.sym_toilet": "Унитаз", - "furn.sym_bathtub": "Ванна", + "furn.sym_toilet": "Унитаз напольный", + "furn.sym_toilet_built_in": "Унитаз встроенный", + "furn.sym_bathtub": "Ванна прямоугольная", + "furn.sym_bathtub_corner": "Ванна угловая", "furn.sym_shower": "Душ", "furn.sym_sink": "Раковина", - "furn.sym_kitchen_sink": "Кухонная мойка", - "furn.sym_bidet": "Биде", + "furn.sym_kitchen_sink": "Кухонная мойка одинарная", + "furn.sym_kitchen_sink_double": "Кухонная мойка двойная", + "furn.sym_bidet": "Биде напольное", + "furn.sym_bidet_built_in": "Биде встроенное", "furn.sym_stairs": "Лестница", "furn.sym_fireplace": "Камин", "furn.sym_plant": "Растение", diff --git a/src/styles/dialogs.styles.ts b/src/styles/dialogs.styles.ts index d3efb259..788afd83 100644 --- a/src/styles/dialogs.styles.ts +++ b/src/styles/dialogs.styles.ts @@ -695,6 +695,11 @@ export const dialogsStyles = css` flex-wrap: wrap; gap: 4px; } + .furnback { + min-height: 34px; + gap: 5px; + margin: 2px 0 4px; + } .furnitem { display: flex; flex-direction: column; @@ -722,6 +727,9 @@ export const dialogsStyles = css` height: 40px; color: var(--primary-text-color, currentColor); } + .furncategory { width: 92px; min-height: 76px; } + .furncatprev { width: 48px; height: 48px; } + .furnvariants .furnitem { min-height: 74px; } .furnsize { display: flex; align-items: center; diff --git a/test/furniture-assets.test.mjs b/test/furniture-assets.test.mjs new file mode 100644 index 00000000..7aa1aaaa --- /dev/null +++ b/test/furniture-assets.test.mjs @@ -0,0 +1,52 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { GENERATED_FURNITURE_PLAN } from '../test-build/furniture-plan-art.generated.js'; +import { FURNITURE } from '../test-build/furniture.js'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const MANIFEST = JSON.parse(fs.readFileSync( + path.join(ROOT, 'assets', 'furniture', 'houseplan-0.3.0', 'pack.json'), 'utf8')); + +test('the vendored designer pack and generated modules stay in sync', () => { + const result = spawnSync(process.execPath, ['scripts/generate-furniture-assets.mjs', '--check'], { + cwd: ROOT, + encoding: 'utf8', + }); + assert.equal(result.status, 0, result.stderr || result.stdout); + assert.match(result.stdout, /44 plan symbols, 33 menu icons/); +}); + +test('the designer pack has the reviewed cardinality and operations', () => { + assert.equal(GENERATED_FURNITURE_PLAN.length, 44); + assert.equal(MANIFEST.menu_icons.length, 33); + assert.equal(MANIFEST.symbols.filter((s) => s.operation === 'replace').length, 18); + assert.equal(MANIFEST.symbols.filter((s) => s.operation === 'add').length, 26); + assert.equal(FURNITURE.length, 56); +}); + +test('menu-only artwork never becomes an empty user-facing category', () => { + const categories = new Set(FURNITURE.map((symbol) => symbol.category)); + for (const id of ['computer', 'oven', 'hood', 'exercise']) assert.equal(categories.has(id), false); + for (const id of ['coffee_table', 'bed', 'kitchen_cabinet', 'toilet']) assert.equal(categories.has(id), true); +}); + +test('front-view menu artwork is reachable only through the lazy editor graph', () => { + const card = fs.readFileSync(path.join(ROOT, 'src', 'houseplan-card.ts'), 'utf8'); + const furniture = fs.readFileSync(path.join(ROOT, 'src', 'furniture.ts'), 'utf8'); + const runtime = fs.readFileSync(path.join(ROOT, 'src', 'houseplan-editor-runtime.ts'), 'utf8'); + assert.doesNotMatch(card, /furniture-menu-art\.generated/); + assert.doesNotMatch(furniture, /furniture-menu-art\.generated/); + assert.match(runtime, /from '\.\/furniture-menu-art\.generated'/); +}); + +test('release provenance is normalized to the repository MIT grant', () => { + assert.equal(MANIFEST.author, 'Sergey Matyushin (Matysh)'); + assert.equal(MANIFEST.license, 'MIT'); + assert.match(fs.readFileSync( + path.join(ROOT, 'assets', 'furniture', 'houseplan-0.3.0', 'README.md'), 'utf8'), + /issuecomment-5454085168/); +}); diff --git a/test/furniture.test.mjs b/test/furniture.test.mjs index 4a274083..944d98c3 100644 --- a/test/furniture.test.mjs +++ b/test/furniture.test.mjs @@ -5,7 +5,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { FURNITURE, FURNITURE_GROUPS, furnitureSymbol, furnitureOfGroup, - furnitureDefaultCm, furniturePathD, furnitureCorners, furnitureResize, + furnitureDefaultCm, furniturePathD, furnitureGraphic, furnitureCorners, furnitureResize, snapFurnitureToWall, cmToNorm, normToCm, clampFurnSize, clampFurnCm, FURN_MIN_N, FURN_MIN_CM, FURN_MAX_CM, FURN_WALL_CELLS, } from '../test-build/furniture.js'; @@ -25,13 +25,21 @@ test('every symbol is well formed: unique id, a known group, positive default si seen.add(s.id); assert.ok(FURNITURE_GROUPS.includes(s.group), `${s.id}: unknown group ${s.group}`); assert.ok(s.w > 0 && s.h > 0, `${s.id}: default size must be positive`); - assert.ok(s.g.length > 0, `${s.id}: nothing to draw`); - // the unit box is the whole contract: nothing may stick out of 0..1 - for (const p of s.g) { - const nums = p.slice(1); - for (const v of nums) assert.ok(v >= -0.001 && v <= 1.001, `${s.id}: ${v} outside the unit box`); + assert.match(s.category, /^[a-z0-9_]+$/, `${s.id}: invalid category`); + assert.ok(furnitureGraphic(s.id)?.d, `${s.id}: nothing to draw`); + if (s.g) { + // Retained primitive art stays in the legacy unit box. + for (const p of s.g) { + const nums = p.slice(1); + for (const v of nums) assert.ok(v >= -0.001 && v <= 1.001, `${s.id}: ${v} outside the unit box`); + } + } else { + assert.deepEqual([s.art.viewW, s.art.viewH], [s.w, s.h], `${s.id}: manifest and SVG viewBox differ`); } } + assert.equal(FURNITURE.length, 56); + assert.equal(FURNITURE.filter((s) => s.art).length, 44); + assert.equal(FURNITURE.filter((s) => s.g).length, 12); }); test('the three groups the owner named are all populated, and every symbol is in exactly one', () => { @@ -43,10 +51,10 @@ test('the three groups the owner named are all populated, and every symbol is in test('the owner’s default sizes are the ones stored', () => { // owner’s list, read as width ALONG the back edge x depth - assert.deepEqual(furnitureDefaultCm('sofa'), { w: 220, h: 90 }); + assert.deepEqual(furnitureDefaultCm('sofa'), { w: 180, h: 90 }); assert.deepEqual(furnitureDefaultCm('bed_double'), { w: 160, h: 200 }); assert.deepEqual(furnitureDefaultCm('bed_single'), { w: 90, h: 200 }); - assert.deepEqual(furnitureDefaultCm('table_dining'), { w: 140, h: 80 }); + assert.deepEqual(furnitureDefaultCm('table_dining'), { w: 160, h: 90 }); assert.deepEqual(furnitureDefaultCm('toilet'), { w: 40, h: 70 }); assert.deepEqual(furnitureDefaultCm('bathtub'), { w: 170, h: 75 }); assert.deepEqual(furnitureDefaultCm('shower'), { w: 90, h: 90 }); @@ -55,9 +63,11 @@ test('the owner’s default sizes are the ones stored', () => { assert.deepEqual(furnitureDefaultCm('fridge'), { w: 60, h: 65 }); assert.deepEqual(furnitureDefaultCm('washer'), { w: 60, h: 60 }); assert.deepEqual(furnitureDefaultCm('dishwasher'), { w: 60, h: 60 }); - assert.deepEqual(furnitureDefaultCm('wardrobe'), { w: 100, h: 60 }); - assert.deepEqual(furnitureDefaultCm('chair'), { w: 45, h: 45 }); - assert.deepEqual(furnitureDefaultCm('desk'), { w: 120, h: 60 }); + assert.deepEqual(furnitureDefaultCm('wardrobe'), { w: 180, h: 60 }); + assert.deepEqual(furnitureDefaultCm('chair'), { w: 50, h: 50 }); + assert.deepEqual(furnitureDefaultCm('desk'), { w: 140, h: 70 }); + assert.deepEqual(furnitureDefaultCm('sofa_corner_right'), { w: 260, h: 170 }); + assert.deepEqual(furnitureDefaultCm('kitchen_sink_double'), { w: 90, h: 50 }); }); test('an unknown symbol is data, not a crash', () => { @@ -98,24 +108,23 @@ test('sizes are clamped, not trusted', () => { // ------------------------------- the drawing -------------------------------- -test('the path is generated at the real size and is a plain, fill-free outline', () => { - const d = furniturePathD('sofa', 440, 180); - assert.ok(d.startsWith('M0 0H440V180H0Z'), d.slice(0, 40)); - // …and the same symbol at half the size is half the numbers - const half = furniturePathD('sofa', 220, 90); - assert.ok(half.startsWith('M0 0H220V90H0Z')); +test('designer paths keep their native viewBox; retained paths still scale from the unit box', () => { + const sofa = furnitureGraphic('sofa'); + assert.deepEqual([sofa.viewW, sofa.viewH], [180, 90]); + assert.ok(sofa.d.length > 10); + const fridge = furniturePathD('fridge', 60, 65); + assert.ok(fridge.startsWith('M0 0H60V65H0Z'), fridge.slice(0, 40)); // a degenerate box draws nothing rather than NaNs assert.equal(furniturePathD('sofa', 0, 10), ''); assert.ok(!/NaN/.test(furniturePathD('toilet', 40, 70))); }); -test('every symbol generates a finite path with no NaN at any aspect ratio', () => { +test('every symbol exposes one finite path and a positive native coordinate box', () => { for (const s of FURNITURE) { - for (const [w, h] of [[100, 100], [400, 25], [25, 400]]) { - const d = furniturePathD(s.id, w, h); - assert.ok(d.length > 0, `${s.id}: empty path`); - assert.ok(!/NaN|Infinity|undefined/.test(d), `${s.id}: ${d.slice(0, 60)}`); - } + const art = furnitureGraphic(s.id); + assert.ok(art.viewW > 0 && art.viewH > 0, `${s.id}: invalid viewBox`); + assert.ok(art.d.length > 0, `${s.id}: empty path`); + assert.ok(!/NaN|Infinity|undefined/.test(art.d), `${s.id}: ${art.d.slice(0, 60)}`); } }); diff --git a/test/golden-matrix.test.mjs b/test/golden-matrix.test.mjs index 632d92dd..ee697270 100644 --- a/test/golden-matrix.test.mjs +++ b/test/golden-matrix.test.mjs @@ -805,6 +805,17 @@ test('editor tray golden contract covers every adaptive width in English and Rus ])); }); +test('issue 159 goldens cover both palette levels and every plan-art compatibility class', () => { + const furniture = GOLDEN_SCENARIOS.filter((scenario) => scenario.furniturePalette); + assert.deepEqual(new Set(furniture.map((scenario) => scenario.theme)), new Set(['dark', 'light'])); + assert.deepEqual(new Set(furniture.map((scenario) => scenario.furniturePalette)), + new Set(['categories', 'variants'])); + const plan = GOLDEN_SCENARIOS.find((scenario) => scenario.id === 'furniture-plan-art-dark'); + assert.deepEqual(new Set(plan.decorOverride.filter((shape) => shape.kind === 'furniture') + .map((shape) => shape.symbol)), + new Set(['sofa', 'fridge', 'sofa_corner_right'])); +}); + test('device dialog goldens expose the complete light-source controls at desktop and mobile widths', () => { const dialogs = GOLDEN_SCENARIOS.filter((scenario) => scenario.id.startsWith('device-dialog-')); assert.equal(dialogs.length, 3); diff --git a/test/i18n.test.mjs b/test/i18n.test.mjs index c536b868..22787460 100644 --- a/test/i18n.test.mjs +++ b/test/i18n.test.mjs @@ -359,8 +359,8 @@ test('i18n: German values equal to English are explicitly reviewed', () => { 'gs.optimize_live_names', 'gs.optimize_details', 'furn.symbol', - 'furn.sym_sofa', - 'furn.sym_bidet', + 'furn.cat_sofa', + 'furn.cat_bidet', 'vac.diag_platform', 'vac.diag_status', 'vac.diag_position',